MetaMask / metamask-sdk

The simplest yet most secure way to connect your blockchain-based applications to millions of MetaMask Wallet users.
https://metamask.io/sdk/
Other
179 stars 111 forks source link

eth_getBalance method no response after connection complete #144

Closed jeremy-kunzhou closed 1 year ago

jeremy-kunzhou commented 1 year ago

Describe the bug no response from getBalance function, the process halt. I tried to upgrade to latest version of metamask-sdk, but no help. tried chain 1 and chain 80001, there are same issues

To Reproduce Steps to reproduce the behaviour: 1 After download the latest code from main branch, open packages/example/reactNativeDemo 2 run command yarn install after dependency initialization, run yarn start 3 click connect button 4 after connection complete, show 'connected account' 5 the balance shows zero, this is the initial value for balance in useState hook. 6 add a new button and call getBalance directly 7 add log before and after provider.getBalances(), found no response from promise 8 tried to use eth_getBalance directly, the same issue happened

image

9 when tried to switch to metamask app and stay for a while, then switch back to demo app, the following log will show in console LogBox.js:148 Message ignored because invalid key exchange status BDu/cfp1DqNe5YMopOY8NTO50xnnvi+iFEKFM64aeAJu4rhYudLCSiGcvUvhKkuWt4CkxvDm9iBPq/ZnBmV9Yi4a/uj+TkbLCCk6O2UvSs0coQfY+0uYk54l9ooEuWJHIXQS5cUKLZLotrPPIpYix1w=

image

after that, the response returned. 10 tried to get response following the same stems from 8 to 9. Failed to get response returned

Expected behaviour the response should be returned

Screenshots If applicable, add screenshots to help explain your problem.

Provide environment information

Additional context

the following is the code i used in getBalance function

  const getBalance = async () => {
    if (!ethereum?.selectedAddress) {
      return;
    }
    console.log('before all');
    console.log('current chainId', ethereum?.chainId);
    console.log('isConnected', ethereum.isConnected());

    console.log('before eth_getBalance');
    const result1 = (await ethereum?.request({
      method: 'eth_getBalance',
      params: [ethereum.selectedAddress, 'latest'],
    })) as string[];
    console.log('eth_getBalance RESULT', result1?.[0]);
    console.log('after eth_getBalance');

    console.log('before getBalance');
    const bal =
      (await provider?.getBalance(ethereum?.selectedAddress)) ??
      ethers.BigNumber.from(0);
    console.log('after getBalance');

    setBalance(ethers.utils.formatEther(bal));
    console.log('after all');
  };
TayfunCesur commented 1 year ago

Hello, I'm having the similar issue. I'm not able to make any view calls with the provider in the example.

I prepared a simple ERC20 balanceOf call to demonstrate the issue.

      console.log("Before")
      const abi = [
        "function balanceOf(address account) external view returns (uint256)"]
      const BUSD = new ethers.Contract("0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56", abi, provider)
      const balance = await BUSD.balanceOf("0xf977814e90da44bfa03b6295a0616a897441acec")
      console.log("After")

I don't see the log "After" here ☝️

However, if I use JsonRpcProvider provider like this, it works.

const BUSD = new ethers.Contract("0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56", abi, new ethers.providers.JsonRpcProvider("https://bsc-dataseed.binance.org/"))

So I feel, both issue are related, and I don't know why new ethers.providers.Web3Provider(ethereum), doesn't work. ethereum is coming from sdk.getProvider().

Could you give us some guidance guys?

Please copy paste this function to play with the issue,

const connect = async () => {
    try {
      const result = (await ethereum?.request({
        method: 'eth_requestAccounts',
      })) as string[];
      console.log('RESULT', result?.[0]);
      setConnected(true);
      setAccount(result?.[0]);
      console.log("Before")
      const abi = [
        "function balanceOf(address account) external view returns (uint256)"]
      const BUSD = new ethers.Contract("0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56", abi, provider)
      const balance = await BUSD.balanceOf("0xf977814e90da44bfa03b6295a0616a897441acec")
      console.log("After")
      console.log(balance)
    } catch (e) {
      console.log('ERROR', e);
    }
  };
dms120 commented 1 year ago

const BUSD = new ethers.Contract("0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56", abi, new ethers.providers.JsonRpcProvider("https://bsc-dataseed.binance.org/"))

@TayfunCesur This will work because you are using the RPC url "bsc-dataseed.binance.org" as provider and not Metamask which can be a good solution.

Of course, you can run into problems since we never know how reliable these RPCs are, Metamask will rely on Infura to perform calls and even knowing that problems can happen, they won't last.

TayfunCesur commented 1 year ago

const BUSD = new ethers.Contract("0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56", abi, new ethers.providers.JsonRpcProvider("https://bsc-dataseed.binance.org/"))

@TayfunCesur This will work because you are using the RPC url "bsc-dataseed.binance.org" as provider and not Metamask which can be a good solution.

Of course, you can run into problems since we never know how reliable these RPCs are, Metamask will rely on Infura to perform calls and even knowing that problems can happen, they won't last.

It was a workaround :( I found some chainId: RPC map and listen the chainId, find corresponding RPC. But I prefer to rely on Metamask completely on that time. I hope the guys would help us out here.

dms120 commented 1 year ago

@TayfunCesur It is a good workaround, no doubt! I'm considering doing the same on my implementation because even if these requests start requiring the redirect it will not be the greatest user experience especially if you have multiple requests to perform on the same page. (unless it starts being possible to call a batch of requests)

jeremy-kunzhou commented 1 year ago

This is a quite strange problem. Because on browser, the etherum provider that metamask plugin injected works well. I understand that the mechanizm of them is quite different regarding implementation.

But making them behaivor consistency is part of the aim of metamask sdk, isn't? it is noticable based on the development document

dms120 commented 1 year ago

Ideally, this read/query methods shouldn't need user interaction but I'm not aware of the mobile limitations.

abigail-0111 commented 1 year ago

Good workaround and read/query work fine. Then i want to transfer ERC20 tokens like contract.transfer(), it should on matamsk sdk because need provider to sign. Have some guidance?

TayfunCesur commented 1 year ago

Good workaround and read/query work fine. Then i want to transfer ERC20 tokens like contract.transfer(), it should on matamsk sdk because need provider to sign. Have some guidance?

Exactly. For those transactions that will do state change, requires signer instead of provider. So metamask is required, and I didn't face any problem for those with metamask-sdk. Problem I face is only for view calls. (can't do anything with eth_call)

andreahaku commented 1 year ago

this should be fixed with the coming version 0.4.0. we'll be testing it before closing the issue. Thanks for reporting and debugging the issue.

TayfunCesur commented 1 year ago

Hey @andreahaku , @abretonc7s Thanks for your effort. I've tested the latest reactNativeDemo, which uses "@metamask/sdk": "^0.4.1",,

christopherferreira9 commented 1 year ago

Hi @jeremy-kunzhou ! We've included important stability fixes with the most recent versions of the MetaMask Mobile Wallet. Can you please try with the most recent version? Also, please refer to the latest version of the reactNativeDemo we've just updated here

jeremy-kunzhou commented 1 year ago

@christopherferreira9 Hi, just got time to test on latest reactNativeDemo, which is

  "@metamask/sdk": "^0.5.0",

On android, using metamask 7.2.0(1142)

After the same code integrated, when call method eth_getBalance using ethereum.request, the following warning shows in console:

 LOG  before all
 LOG  current chainId 0x13881
 LOG  isConnected true
 LOG  before eth_getBalance
 WARN  Possible Unhandled Promise Rejection (id: 1):
Error: MetaMask is not connected/installed, please call eth_requestAccounts to connect first.
Error: MetaMask is not connected/installed, please call eth_requestAccounts to connect first.
    at ?anon_0_ (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:115054:139)
    at next (native)
    at anonymous (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:101697:39)
    at tryCallTwo (/root/react-native/ReactAndroid/hermes-engine/.cxx/Release/5e6w3h5p/arm64-v8a/lib/InternalBytecode/InternalBytecode.js:61:9)
    at doResolve (/root/react-native/ReactAndroid/hermes-engine/.cxx/Release/5e6w3h5p/arm64-v8a/lib/InternalBytecode/InternalBytecode.js:216:25)
    at Promise (/root/react-native/ReactAndroid/hermes-engine/.cxx/Release/5e6w3h5p/arm64-v8a/lib/InternalBytecode/InternalBytecode.js:82:14)
    at a (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:101676:36)
    at l (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:115041:17)
    at ?anon_0_ (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:115066:17)
    at next (native)
    at anonymous (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:101697:39)
    at tryCallTwo (/root/react-native/ReactAndroid/hermes-engine/.cxx/Release/5e6w3h5p/arm64-v8a/lib/InternalBytecode/InternalBytecode.js:61:9)
    at doResolve (/root/react-native/ReactAndroid/hermes-engine/.cxx/Release/5e6w3h5p/arm64-v8a/lib/InternalBytecode/InternalBytecode.js:216:25)
    at Promise (/root/react-native/ReactAndroid/hermes-engine/.cxx/Release/5e6w3h5p/arm64-v8a/lib/InternalBytecode/InternalBytecode.js:82:14)
    at a (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:101676:36)
    at anonymous (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:115065:15)
    at ?anon_0_ (http://localhost:9097/src/views/DappView.bundle?platform=android&app=com.reactnativedemo&modulesOnly=true&dev=true&minify=false&runModule=true&shallow=true:92:73)
    at next (native)
    at asyncGeneratorStep (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:22716:26)
    at _next (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:22735:29)
    at anonymous (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:22740:14)
    at tryCallTwo (/root/react-native/ReactAndroid/hermes-engine/.cxx/Release/5e6w3h5p/arm64-v8a/lib/InternalBytecode/InternalBytecode.js:61:9)
    at doResolve (/root/react-native/ReactAndroid/hermes-engine/.cxx/Release/5e6w3h5p/arm64-v8a/lib/InternalBytecode/InternalBytecode.js:216:25)
    at Promise (/root/react-native/ReactAndroid/hermes-engine/.cxx/Release/5e6w3h5p/arm64-v8a/lib/InternalBytecode/InternalBytecode.js:82:14)
    at anonymous (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:22732:25)
    at apply (native)
    at getBalance (http://localhost:9097/src/views/DappView.bundle?platform=android&app=com.reactnativedemo&modulesOnly=true&dev=true&minify=false&runModule=true&shallow=true:105:27)
    at _performTransitionSideEffects (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:51285:22)
    at _receiveSignal (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:51241:45)
    at onResponderRelease (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:51108:34)
    at apply (native)
    at invokeGuardedCallbackProd (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:63074:21)
    at apply (native)
    at invokeGuardedCallback (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:63160:42)
    at apply (native)
    at invokeGuardedCallbackAndCatchFirstError (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:63163:36)
    at executeDispatch (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:63227:48)
    at executeDispatchesInOrder (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:63244:26)
    at executeDispatchesAndRelease (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:64350:35)
    at executeDispatchesAndReleaseTopLevel (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:64357:43)
    at forEach (native)
    at forEachAccumulated (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:63686:22)
    at runEventsInBatch (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:64368:27)
    at runExtractedPluginEventsInBatch (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:64427:25)
    at anonymous (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:64408:42)
    at batchedUpdates$1 (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:74998:20)
    at batchedUpdates (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:64339:36)
    at _receiveRootNodeIDEvent (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:64407:23)
    at receiveTouches (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:64450:34)
    at apply (native)
    at __callFunction (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:4544:36)
    at anonymous (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:4305:31)
    at __guard (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:4495:15)
    at callFunctionReturnFlushedQueue (http://localhost:9097/index.bundle?platform=android&dev=true&minify=false&app=com.reactnativedemo&modulesOnly=false&runModule=true:4304:21)

Obvioursly, it means that the metamask is not connected. I don't think this warning makes sense. Because the connection status is true and i could sign the message successfully.

jeremy-kunzhou commented 1 year ago

By the way, if metamask-sdk don't suggest developer to use its provider to read or query and only for user sign purpose, that's ok. We could use jrpcprovider to achieve the same aim.

Therefore, in the example, the function regarding getBalance should be misleading for some reason. Anyhow, thanks for the good work and look forward to making sdk works better in react native with community.

abretonc7s commented 1 year ago

Could you please confirm the status of the issue? We have added connection improvement with each release as well as wallet upgrade which should prevent these issues.

waynechan-bw commented 1 year ago

same issue happened in cases below. the promise never resolved when the request is made

ethereum.request({
  method: 'eth_blockNumber',
  params: [],
})

and

 ethereum.request({
  method: 'eth_chainId',
  params: [],
})

however, the response will be returned once we trigger a action which opened metamask and redirect back the to page. e.g. wallet_switchEthereumChain or eth_signTypedData_v4

christopherferreira9 commented 1 year ago

Hi @waynechan-bw, @jeremy-kunzhou! We are working on implementing a new feature that will allow read calls happen without having the need to open the MetaMask mobile Wallet. We hope to have this feature implemented by the end of the month. In the meantime we will be closing this issue. Thank your reporting and feel free to open a new issue if you need more assistance.