-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
fix: use up-to-date chainId/accounts when querying EIP1193-derived wallets #749
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.
|
||
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
accounts
and 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 foraccounts
to settle before requestingchainId
.For
chainId
andaccounts
, the values were fetched using aPromise.all
. In some wallets,chainId
resolves immediately, and then the user is prompted to change chains. In these cases,chainId
will be outdated by the timeaccounts
resolves:eth_chainId
resolves to "stale" chainchainChanged
is emittedeth_accounts
resolves, resolving thePromise.all
with an out-of-datechainId
NB: 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.