-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Add support for extending providers #3932
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
🦋 Changeset detectedLatest commit: a60b1cd The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
packages/hardhat-core/src/internal/core/providers/lazy-initialization.ts
Outdated
Show resolved
Hide resolved
packages/hardhat-core/src/internal/core/providers/lazy-initialization.ts
Outdated
Show resolved
Hide resolved
I think we can rule out this option because the provider created in
Agree that it's not worth it, especially because, worst case, you can just use a dummy
What if we don't do a full implementation, and only implement the minimal things we need to prevent breaking |
|
Also, I think that's the only option that doesn't need a change in |
| ): void; | ||
| } | ||
|
|
||
| export type ProviderFactory = () => Promise<EthereumProvider>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One convention we have is that anything not under src/internal is public. So this is (technically) adding a type to our public API. I don't mind if there's a good reason to do it, but I'm not sure if it makes sense to expose this type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I think that even thought this looks like an innocuous enough type, there's no real reason to make it public. Maybe if the plan was to make LazyInitializationProvider a core functionality (and to make it public) it'd make more sense.
Would it make sense to move the type to src/internal/core/providers/lazy-initialization.ts? where LazyInitializationProvider lives
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that makes sense. I'm also fine with just inlining this type, since it's short enough. Whatever you prefer!
b1b20b5 to
8c4ea0e
Compare
8c4ea0e to
f245b7c
Compare
Agree! the second point most of all I think makes it the better option by far. Now I'm writing this, releasing some memory and making |
I like it. Just a minor comment: we try to avoid using non-null assertinos (the |
44884a9 to
6362de0
Compare
| /** | ||
| * A class that delays the (async) creation of its internal provider until the first call | ||
| * to a JSON RPC method via request/send/sendAsync. | ||
| * Trying to use the EventEmitter API without calling request first (initializing the provider) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this line is out of date now, isn't it?
|
Closing in favor of #4008, which includes these changes. |
Extend providers
This is a first implementation to allow for the provider to be wrapped and extended with extra functionality.
The DSL mimics the way DSL
extendConfigandextendEnvironmentwork, but it allows for anasyncwrapping to be made.Hardhat will "compile" the provider into
hre.network.providerfrom the combination of all extender functions it finds, in order.The API looks like this:
Considerations
The new composed provider will run after the first call to
requestis made, unlike before, where it was built when first accessed (lazyObject). Because of this it was (currently) decided that any call to a sync method, i.e. EventEmitter methods, will throw informing the user that she needs to callrequestfirst.This should be documented.
Currently, we have a problem with
hardhat-ethersregarding this new implementation.hardhat-ethersextends the environment and hooks two events to the provider by callingcreateProviderProxy, here.The provider will now be a
LazyInitializationProviderwhich causes that line to throw because.request()hasn't been called yetIt's a bit tricky to fix, the options I have in mind are:
.request()hasn't been called. This will work but doing a full implementation might be tricky. For example, should we supportsetMaxListeners? and should we delete listeners when.off()is called? Maybe we could have aprivate EventEmitterand then just move the listeners by looping thoughObject.keys(privateEmitter.eventNames()).init()method on our providers that can be called to force what.request()does underneath. This will make eitherEthereumProviderorEIP1193Providermuddy in my opinionextendProvidermethod to extend the ethers provider instead of doing it onextendEnvironment. This sounds good on paper but it poses two problems. First what method to call to initialize the provider, second that hre.ethers is alazyObjectand this would initialize it immediatelyI think the first option is the best one but I'm not completely sure (and I might be missing some other easier version!), I've implemented it locally already and seems to work well
TODO
ProviderWrapperpart of the public API. Maybe best for a different PR?