Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 17
types: export EIP1193Provider and LegacyEthereumProvider interfaces#140
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
1ea71a635ec38ade8d6ef28a8129a53994fe3c377147f7023a8ddce8bbffb397b176aa6a4ab9b90c23d6199f1defe10d3c940bdc982ea00bFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import { Web3Provider } from '@ethersproject/providers'; | ||
| import EthQuery from '@metamask/eth-query'; | ||
| import { expectAssignable, expectNotAssignable } from 'tsd'; | ||
| import type { JsonRpcRequest, LegacyEthereumProvider } from '.'; | ||
| // Known legacy providers | ||
| expectAssignable<LegacyEthereumProvider>(new EthQuery({} as any)); | ||
MajorLift marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| expectAssignable<LegacyEthereumProvider>(new Web3Provider({} as any)); | ||
| expectAssignable<LegacyEthereumProvider>({ | ||
| send: async (method: string, params: string[]) => | ||
| Promise.resolve([method, params]), | ||
| }); | ||
| expectAssignable<LegacyEthereumProvider>({ | ||
| // eslint-disable-next-line @typescript-eslint/no-empty-function | ||
| send: (_req: JsonRpcRequest, _cb: () => void) => {}, | ||
| }); | ||
| expectAssignable<LegacyEthereumProvider>({ | ||
| send: async (req: JsonRpcRequest, _cb: (_x: null, _result: null) => void) => | ||
| Promise.resolve(req), | ||
| }); | ||
| expectNotAssignable<LegacyEthereumProvider>({ foo: '123' }); | ||
| expectNotAssignable<LegacyEthereumProvider>({ send: '123' }); | ||
| expectNotAssignable<LegacyEthereumProvider>({ | ||
| send: (method: string, params: string[]) => [method, params], | ||
| }); | ||
| expectNotAssignable<LegacyEthereumProvider>({ | ||
| send: async ( | ||
| req: JsonRpcRequest, | ||
| _cb: (_x: null, _result: undefined) => void, | ||
| ) => Promise.resolve(req), | ||
| }); | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,92 @@ | ||||||
| import type SafeEventEmitter from '@metamask/safe-event-emitter'; | ||||||
| import type { JsonRpcParams, JsonRpcRequest, Json } from './json'; | ||||||
| import type { PartialOrAbsent } from './misc'; | ||||||
| /** | ||||||
| * An interface for the EIP-1193 specification for an Ethereum JavaScript Provider. | ||||||
| * | ||||||
| * @see [EIP-1193]{@link https://eips.ethereum.org/EIPS/eip-1193}. | ||||||
| * @see [BaseProvider]{@link https://github.com/MetaMask/providers/blob/main/src/BaseProvider.ts} in package [@metamask/providers]{@link https://www.npmjs.com/package/@metamask/providers}. | ||||||
| */ | ||||||
| export type EIP1193Provider = SafeEventEmitter & { | ||||||
| /** | ||||||
| * Submits an RPC request for the given method, with the given params. | ||||||
| * Resolves with the result of the method call, or rejects on error. | ||||||
| * | ||||||
| * @param args - The RPC request arguments. | ||||||
| * @param args.method - The RPC method name. | ||||||
| * @param args.params - The parameters for the RPC method. | ||||||
| * @returns A Promise that resolves with the result of the RPC method, | ||||||
| * or rejects if an error is encountered. | ||||||
| */ | ||||||
| request<Params extends JsonRpcParams, Result extends Json>( | ||||||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this method signature always compatible with the one in BaseProvider? If not, do we plan on changing BaseProvider to match this? | ||||||
| args: Params, | ||||||
| ): Promise<PartialOrAbsent<Result>>; | ||||||
| }; | ||||||
| /** | ||||||
| * The interface for a legacy Ethereum provider. | ||||||
| * | ||||||
| * A provider of this type should be acceptable by either `eth-query`, `ethjs-query`, or Ethers' v5 `Web3Provider`. | ||||||
| */ | ||||||
| export type LegacyEthereumProvider = | ||||||
| | LegacyEthersProvider | ||||||
| | LegacyEthJsQueryProvider | ||||||
| | LegacyWeb3Provider; | ||||||
| type LegacyEthersProvider = { | ||||||
| /** | ||||||
| * Send a provider request asynchronously. (ethers v5 Web3Provider) | ||||||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To make this a little cleaner, what do you think about adding a comment above | ||||||
| * | ||||||
| * @param method - The RPC method to call. | ||||||
| * @param params - Array with method parameters. | ||||||
| * @returns A promise resolving with the result of the RPC call, or rejecting on failure. | ||||||
| */ | ||||||
| send(method: string, params: any[]): Promise<Json>; | ||||||
| ||||||
| send(method: string,params: any[]): Promise<Json>; | |
| send(method: string,params: unknown[]): Promise<Json>; |
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.
Are we trying to align with Ethers v5 here? If so, its signature for send is here: https://github.com/ethers-io/ethers.js/blob/v5.7.2/packages/providers/src.ts/web3-provider.ts#L19
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 but here it should be the Ethers providers themselves (as opposed to the ExternalProvider, which maps to what can be passed into ethers for wrapping)
The any is lifted straight from there (they don't have an interface for it AFAICTbut they're all identical): https://github.com/ethers-io/ethers.js/blob/0bfa7f497dc5793b66df7adfb42c6b846c51d794/packages/providers/src.ts/web3-provider.ts#L166
Is there still a reason to prefer unknown?
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 but here it should be the Ethers providers themselves (as opposed to the ExternalProvider, which maps to what can be passed into ethers for wrapping)
Ah. I must be misunderstanding how this type is intended to be used, then. Sorry for being dense, but would you mind providing an example?
MajorLiftOct 16, 2023 •
edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
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 we should still use unknown here. The range of types that can be assigned to params is exactly the same whether we use any[] or unknown[]. Any errors resulting from unknown will also cause any to fail -- just at runtime and silently.
We can always use @ts-expect-error if an error blocks something. At least then we can be aware that there's a dragon to account for.
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.
@mcmire It's roughly the same difference as between MetaMaskInPageProvider and EthQuery on the MM side - one (the signature of send you're linking) is what the library exposes externally. The other (intended to be represented here) is the externally constructed provider that gets passed in as constructor argument.
So an Ethers provider shouldn't (necessarily) satisfy this interface. But an object passed into Ethers as an ExternalProvider should.
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.
guessing this commented fn signature doesn't work for some reason?
MajorLiftOct 4, 2023 •
edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
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.
Does defining send as a generic cause issues? I think this comment should be removed either way.
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.
Do we also need to include sendAsync in this type? Or do we need to define two types for Ethers v5, one that contains send, another that contains sendAsync?
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.
Ethers itself (which this is intended to represent) only has send, not sendAsync: https://docs.ethers.org/v5/api/providers/jsonrpc-provider/#JsonRpcProvider-send
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, this seems overly strict. The params here can be anything — eth-query / ethjs-query doesn't put a restriction on what they can be.
Where did you derive this type from?
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.
From here: https://github.com/MetaMask/eth-query/blob/main/index.d.ts
First, we can see here that the missing parts are auto-filled in ethjs-rpc (actual provider for eth-query) so we can at least see that the same interface is implicit.
As for extraneous parameters: On one hand, the more backwards-compatible approach would be something like Json, EverythingButNull or even unknown. But at the same time I'm thinking that users of this type could benefit from spotting unintentionally unsupported API-usage (as any non-standard fields would have to be recognized by the actual provider).
Are we aware of any places where that could already be a thing?
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'm wary of doing this, but I don't know why yet. I'll have to think about this.
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.
Do we need this type? Our provider technically supports send, but it's even more deprecated than sendAsync is. I can't recall a time where we've used it internally.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
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.
do we need a sendAsync scenario?