ethers-io / ethers.js

Complete Ethereum library and wallet implementation in JavaScript.
https://ethers.org/
MIT License
7.89k stars 1.82k forks source link

Unknown Account #948

Closed xmaysonnave closed 3 years ago

xmaysonnave commented 4 years ago

Environment Metamask Mobile -> v0.2.19 Ethers -> 5.0.5

Remote debugged with Chrome DevTools This issue do not happen on Firefox and Chrome desktop. It occurs only on Metamask Mobile.

1 - Ethers is loaded dynamically :

Loaded $:/ipfs/library/ethers:
 https://cdn.jsdelivr.net/npm/ethers@5.0.5/dist/ethers.umd.min.js

2 - Ethers is instantiated with the metamask provider and the getAccount trick The account is properly retrieved

// https://github.com/ethers-io/ethers.js/issues/433
const account = await this.getAccount(provider)
const web3 = new root.ethers.providers.Web3Provider(provider, 'any')

I use the 'any' parameter as advised here: https://github.com/ethers-io/ethers.js/issues/899 I didn't do this test without this parameter though.

3 - Here is the code who crashes

const abi = ['function setContenthash(bytes32 node, bytes calldata hash)']
const iface = new window.ethers.utils.Interface(abi)
const data = iface.encodeFunctionData('setContenthash', [
  domainHash,
  encoded
])
const signer = web3.getSigner()
const tx = await signer.sendTransaction({ to: resolver, data: data })
await tx.wait()

Report:

1 - Ethers generates an exception : Error: unknown account #0 (operation="getAddress", code=UNSUPPORTED_OPERATION, version=providers/5.0.4) Minor remark -> the signature is still the 5.0.4 and not the 5.0.5

2 - Metamask Mobile generates as well an exception: VM13:8 MetaMask: 'eth_accounts' unexpectedly updated accounts. Please report this bug.

Workaround : const signer = web3.getSigner(account) When I explicitly set the account while retrieving the signer both exceptions are gone.

Thanks

ricmoo commented 4 years ago

The versions are intentionally not synchronized, so the version of the sub-module is fine. :)

I’m not sure what this.getAccount is. Is that a function you added? Or part of a framework or MetaMask?

Is MetaMask enabled? Using the await window.ethereum.enable()? The MetaMask plug-in needs access enabled before any account operations can be used. This is an API I would eventually like to add, but need to coordinate a bit with the various client to make sure it works across the board.

xmaysonnave commented 4 years ago

Hi Richard. Yes you're right. This method is an implementation on my side. Basically it retrieves an account. What I debugged is at this stage an account has been selected. When I retrieve a signer with this account as a parameters it works(explicit), without any parameter it doesn't work (implicit). It used to work with previous versions. I don't have this problem on desktop (metamask v8.0.x) Thanks

On Mon, 13 Jul 2020, 15:38 Richard Moore, notifications@github.com wrote:

The versions are intentionally not synchronized, so the version of the sub-module is fine. :)

I’m not sure what this.getAccount is. Is that a function you added? Or part of a framework or MetaMask?

Is MetaMask enabled? Using the await window.ethereum.enable()? The MetaMask plug-in needs access enabled before any account operations can be used. This is an API I would eventually like to add, but need to coordinate a bit with the various client to make sure it works across the board.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ethers-io/ethers.js/issues/948#issuecomment-657465426, or unsubscribe https://github.com/notifications/unsubscribe-auth/AE47PBGH6AM5IL7IZC4S6M3R3LMLBANCNFSM4OYKEGZA .

ricmoo commented 3 years ago

Is this still a problem? Was it a MetaMAsk issue?

xmaysonnave commented 3 years ago

1 - I work around the issue when requesting a signer with an explicit selected account instead of the implicit selected account provider.getSigner(account) vs provider.getSigner()

2 - Metamask Mobile issued several releases in the mean time. I will do some testing to verify if the problem remains

Thanks for your support

ricmoo commented 3 years ago

(out of curiosity, how do you get the explicit account to use? provider.accounts?)

xmaysonnave commented 3 years ago

As I work with Metamask I have several ways to achieve it. 1 - The first step is to use @metamask/detect-provider Basically async on mobile, sync on desktop 2 - It's probably easier to let you browse the code: https://github.com/xmaysonnave/tiddlywiki-ipfs/blob/795cc95a661442dce50dcdb0807ea2c7868a92cd/src/plugins/ipfs/ethereum-library/index.js#L332 Thanks

ricmoo commented 3 years ago

I'm going to close this now as stale. I haven't tested it, but I'm guessing there is also some issue with .enable(), which I assume that the module MetaMask forces you to use?

If this is still a problem though, please re-open.

Thanks! :)

chrisfranko commented 2 years ago

Just to bring this back up, It is still an issue. At expanse we have a public load balanced node. Its load balanced between 6 nodes in various locations and server farms. When using hardhat for testing, everything is fine. When we start to use the live net we get this error when calling methods on contracts. I noticed that 5 out of 6 times it was failing. I thought it was me.

After like 4 hrs of debugging I realized the ONE difference on the ONE node was that it had a native account. That account is being used as account 0.

If I connect directly to the node that has a native account 0, everything works like it should 100% of the time.

Currently I'm doing..

const wallet = new ethers.Wallet(privkey, provider);
const signer = wallet.provider.getSigner();
let Contract = await ethers.getContractFactory("Menta", signer);
contract = await Contract.attach(contractAddress.mainnet);
balance = await contract.balanceOf(owner);
console.log(`BalanceOf: ${balance}`);

For some reason this only works if the provider im connecting to already has a local account even though I thought I was supplying one.

chrisfranko commented 2 years ago

This does work though

signer = wallet.provider.getSigner(wallet.address);

If the wallet.address isnt provided then it tries to use getAddress on account 0 on the provider youre using.

ricmoo commented 2 years ago

It probably doesn’t return a Signer that works though, does it?

If you are connected to a public node, all you are doing is connecting to a providers internal accounts, and by passing in an explicit address you are telling it to not bypass any checks. But once you try using this signer to sign anything or send a transaction it should fail.

chrisfranko commented 2 years ago

You're right, im a doofus.

So, passing it the entire wallet object works and lets you send a tx as well.

lopeselio commented 2 years ago
const loadWeb3 = () => {
    provider = new ethers.providers.Web3Provider(window.ethereum)
    window.ethereum.enable()
    signer = provider.getSigner()

    console.log('signer', signer);
}
lopeselio commented 2 years ago
const loadWeb3 = () => {
    provider = new ethers.providers.Web3Provider(window.ethereum)
    window.ethereum.enable()
    signer = provider.getSigner()

    console.log('signer', signer);
}

If you call window.ethereum.enable() , it works perfectly!