-
Notifications
You must be signed in to change notification settings - Fork 1.5k
fix: use up-to-date chainId/accounts when querying EIP1193-derived wallets #749
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
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
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.
wow this is really subtle, nice catch. i have a concern about the metamask change, see comment
the non-null assertions felt a little clunky but i went through them all and i didn't see anything problematic; it's maybe slightly worse because we're now relying on the coinbase wallet/metamask providers to set properties correctly, but doesn't really bother me
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.
lgtm, nice catch
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.
Looks good to me! Left some overall nits / comments, but nothing blocking
| // Wallets may resolve eth_chainId and hang on eth_accounts pending user interaction, which may include changing | ||
| // chains; they should be requested serially, with accounts first, so that the chainId can settle. | ||
| const accounts = await this.provider.request<string[]>({ method: 'eth_requestAccounts' }) | ||
| const chainId = await this.provider.request<string>({ method: 'eth_chainId' }) |
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.
nit: probably out of scope for this PR, but I would consider adding to a todo list: potential candidate for abstracting away? Looks like a common code path here and few lines earlier.
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 that web3-react is the abstraction but, more importantly, I don't want to make this a large change because it's meant to be a bug fix.
packages/eip1193/src/index.ts
Outdated
| try { | ||
| // Wallets may resolve eth_chainId and hang on eth_accounts pending user interaction, which may include changing | ||
| // chains; they should be requested serially, with accounts first, so that the chainId can settle. | ||
| const accounts = await (eager ? this.provider.request({ method: 'eth_accounts' }) : this.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.
Question about this eager flag. I don't see it being used before? We were running eth_accounts in both instances. Is this change intentional?
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.
Ok, I see this has to do with eager that can be true or false, depending on the call-site. Is there a better way to describe its intent? Is it something like connect if already authorised vs explicitly authorise? In that case, why fallback to eth_accounts when eth_requestAccounts failed? This is probably a bit out of scope for this PR, but a good opportunity to expand my web3-react knowledge a bit!
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 believe this is done because EIP1193 does not necessarily support eth_requestAccounts, so a fallback is necessary. You don't see this in other connectors because they are not explicitly EIP1193 connectors.
The codepaths for connectEagerly and activate were identical except for the usage of eth_accounts vs eth_requestAccounts, so I added connect as a shared method to express that with less code duplication.
I've refactored to activateAccounts(requestAccounts: () => Promise<string[]>).
| .then(() => this.activate(desiredChainId)) | ||
| // Wallets may resolve eth_chainId and hang on eth_accounts pending user interaction, which may include changing | ||
| // chains; they should be requested serially, with accounts first, so that the chainId can settle. | ||
| const accounts = await this.provider.request({ method: 'eth_requestAccounts' }) as string[] |
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.
follow-up to my previous question: why no fallback to eth_accounts here?
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.
MetaMask is known to implement this method, whereas a pure EIP1193 may not.
| .then((chainId) => parseChainId(chainId)) | ||
|
|
||
| this.actions.update({ chainId, accounts }) | ||
| this.actions.update({ chainId: parseChainId(chainId), accounts }) |
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.
nit: maybe chainId should already be parsed? parseChainId(await ....), otherwise I think this line is a bit confusing: chainId: parseChainId(chainId) vs this.actions.update({ chainId })
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.
If considered a worthy nit, this most likely applies to other files too.
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.
fwiw, I agree with you here, but I don't want to change it because it expands the scope of this PR - I've omitted it to de-risk this bug fix.
Serially requests
accountsand thenchainId, so that if a user changes chains whilst connecting, the new chain (after connection /accounts) will be used. Practically, this prevents web3-react from using incorrect initial state when connecting to certain wallets, in certain cases where the user selects a non-active chain. This is resolved by waiting foraccountsto settle before requestingchainId.For
chainIdandaccounts, the values were fetched using aPromise.all. In some wallets,chainIdresolves immediately, and then the user is prompted to change chains. In these cases,chainIdwill be outdated by the timeaccountsresolves:eth_chainIdresolves to "stale" chainchainChangedis emittedeth_accountsresolves, resolving thePromise.allwith an out-of-datechainIdNB: WalletConnect did not exhibit this bug, but I updated it to keep it in sync with other connectors' idioms. It likely already had this "fix" in it, based on existing comments.