wevm / wagmi

Reactive primitives for Ethereum apps
https://wagmi.sh
MIT License
6.03k stars 1.08k forks source link

[bug] WalletConnect Transaction promises not returning #116

Closed critesjosh closed 2 years ago

critesjosh commented 2 years ago

Is there an existing issue for this?

Package Version

0.2.1

Current Behavior

I am using the package in my next.js application.

When I send transactions to a wallet connected with WalletConnect, the transaction promise never returns.

In the following code snippet, the transaction is picked up by the wallet and will successfully send, but the notifications aren't displayed and the receipt isn't logged.

I tried this with multiple wallets (Metamask and Valora) and neither of them work. Metamask works as expected.

  const setStorage = async (values) => {
    let tx = await contract.store(values.number);

    notification.open({
      message: "Updating Storage",
      description: `Contract storage updating to: ${values.number}`,
    });

    let receipt = await tx.wait();
    console.log("receipt", receipt);
}

Expected Behavior

I expect the transaction promise to resolve when it is sent by the wallet.

Steps To Reproduce

No response

Anything else?

No response

tmm commented 2 years ago

@critesjosh any chance you can create a basic CodeSandbox to help with debugging?

You can fork this template to get started.

critesjosh commented 2 years ago

Yea I will work on adding the code there. In the meantime, I narrowed down the problem a bit. It has to do with the WalletConnectConnector rpc options. The dapp seems to connect to the RPC network at 1 initially be default, even when I scan the QR code and log in with my wallet that is connecting to network with id 44787 (Celo alfajores testnet). If I manually refresh the page, the WalletConnect provider updates the correct one (http endpoint defined in the options at 44787). If I set the http endpoint for network id 1 as the endpoint for my desired network, it works as expected (see the screenshot).

Screen Shot 2022-01-29 at 12 40 36 PM

Interestingly, when I associate the http endpoint for the alfajores testnet network in the rpc options at position 1, the transaction receipts are returned as expected. When the dapp is connected to the alfajores testnet properly via WC at rpc option 44787, the transaction receipts are not returned.

If I omit WC rpc option 1 altogether (which is what I want, I don't want to connect to ETH mainnet), I see the following error when trying to read contract data.

Screen Shot 2022-01-29 at 12 49 12 PM
tmm commented 2 years ago

What does your ether.js provider setup look like?

critesjosh commented 2 years ago

Here are the connectors and the root component.

const connectors = [
  new InjectedConnector({ chains: [Celo, Alfajores, Localhost] }),
  new WalletConnectConnector({
    chains: [Celo, Alfajores, Localhost],
    options: {
      qrcode: true,
      rpc: {
        // There is a bug where on the first connection the provider URL defaults to network id 1 (ETH mainnet).
        // Set the RPC endpoint of your default network of choice as the #1
        // 1: "https://alfajores-forno.celo-testnet.org",
        44787: "https://alfajores-forno.celo-testnet.org",
        42220: "https://forno.celo.org",
      },
    },
  }),
];

function MyApp({ Component, pageProps }) {
  return (
    <>
      <Head>
        <title>Celo DApp Starter</title>
        <meta name="description" content="Celo DApp Starter" />
      </Head>
      <Provider connectors={connectors}>
        <Component {...pageProps} />
      </Provider>
    </>
  );
}
critesjosh commented 2 years ago

I've been trying to update the codesandbox, but the site won't save my changes. 🤷

tmm commented 2 years ago

You might need to fork it or sign in.

critesjosh commented 2 years ago

ok i got it. you can view the example here: https://codesandbox.io/s/staging-tree-kotvr?file=/src/index.tsx

try connecting to the dapp using WalletConnect with a wallet that is not connected to mainnet. the app will log the http connection of theSigner. It will show a connection to infura at first:

Screen Shot 2022-01-31 at 3 05 14 PM

and if you refresh the page, it should update to the correct endpoint.

Screen Shot 2022-01-31 at 3 07 02 PM
tmm commented 2 years ago

Thanks! Will check out the CodeSandbox when I have a moment.

In the meantime, I would try updating the connectors function to use the chainId. Maybe something like this:

import { getNetwork } from '@ethersproject/providers'
import { providers } from 'ethers'

const infuraId = 'Your Infura API Key'
const chains = [Celo, Alfajores]

type ConnectorsConfig = { chainId?: number };
const connectors = (_config: ConnectorsConfig) => {
  const network = getNetwork(chainId ?? defaultChain.id)
  const infuraRpcUrl = providers.InfuraProvider.getUrl(network, infuraId).url
  return [
    new InjectedConnector({ chains }),
    new WalletConnectConnector({
      chains,
      options: {
        infuraId,
        qrcode: true,
        rpc: {
          [`${network.chainId}`]: infuraRpcUrl,
        },
      },
    }),
  ]
}
critesjosh commented 2 years ago

Unfortunately Infura doesn't support Celo yet.

tmm commented 2 years ago

Oh that was just a convenience method to get the RPC URL. You can swap that with this:

const rpcMap = {
  44787: "https://alfajores-forno.celo-testnet.org",
  42220: "https://forno.celo.org",
}

const connectors = (_config: ConnectorsConfig) => {
  const rpcUrl = rpcMap[chainId]
  return [
    new InjectedConnector({ chains }),
    new WalletConnectConnector({
      chains,
      options: {
        qrcode: true,
        rpc: {
          [`${chainId}`]: rpcUrl,
        },
      },
    }),
  ]
}
critesjosh commented 2 years ago

just tried the updates you suggested, still no luck

https://codesandbox.io/s/staging-tree-kotvr?file=/src/index.tsx:432-433

critesjosh commented 2 years ago

Perhaps this is a bug with the useSigner hook? When I login with wallet connect, it shows the correct address from useAccount and the correct network from useNetwork. The problem is when I try to read the provider info associated with the signer. The http connection is trying to connect to https://mainnet.infura.io/v3/undefined instead of the provided url.

Update: When I log the connector returned by useConnect, it prints output when the WalletConnect modal pops up, before I scan the QR code. The default http connection is to ETH mainnet via infura. When I scan the QR code, the network and account data updates, but the http connection info does not update. The RPC provider associated with WalletConnect does update. So the RPC and signer connection info in the provider is correct, but the http connection info is not. Does the WalletConnect connector connect via http or rpc or both?

critesjosh commented 2 years ago

Just curious why wagmi uses @walletconnect/ethereum-provider (here) instead of @walletconnect/web3-provider?

web3-provider works for me as expected.

pyk commented 2 years ago

currently I stumbled upon this problem.

Can confirm that somehow useSigner() returns the signer with mainnet rpc by default. The app need to be reloaded first after connecting via WalletConnect.

The next problem is when I send the transaction using the following code:

const vaultContract = new ethers.Contract(metadata.vaultAddress, VaultABI, signer);
const result = await vaultContract.mint(address, { value: ethers.utils.parseUnits(mintState.amount ? mintState.amount.toString() : "0", metadata.collateralDecimals) });

Request to confirm the tx is appear on the wallet. when transaction is confirmed via the wallet, transaction succeed but the promise is never return.

I tried to use useContractWrite but got no response as well. It only doesn't work when user is connected via Walletconnect.

I tried to create and sign tx manually but error due to signer.sign is not supported:

const unsignedTx = await vaultContract.populateTransaction.mint(address, { value: ethers.utils.parseUnits(mintState.amount ? mintState.amount.toString() : "0", metadata.collateralDecimals) });
const signedTx = await signer.signTransaction(unsignedTx);

@critesjosh Would u mind to share ur solution using @walletconnect/web3-provider? Ty!

critesjosh commented 2 years ago

I was testing another web3-provider with another repo. it doesn't actually fix the problem sorry. I pulled wagmi and tested it locally with @walletconnect/web3-provider instead of @walletconnect/ethereum-provider and it still isn't working.

I think it may be a problem with ethers wrapping the WalletConnectProvider. For some reason the http connection in the ethers Web3Provider doesn't update when the connection to the mobile wallet is made. The signer on the WalletConnectProvider does update as expected.

gitcoinbot commented 2 years ago

Issue Status: 1. Open 2. Started 3. Submitted 4. Done


This issue now has a funding of 250.0 cUSD (249.76 USD @ $1.0/cUSD) attached to it.

gitcoinbot commented 2 years ago

Issue Status: 1. Open 2. Started 3. Submitted 4. Done


Work has been started.

These users each claimed they can complete the work by 264 years, 9 months from now. Please review their action plans below:

1) maxx6262 has been approved to start work.

I think with use of try/catch block (and debug with full verbose run) we would find that makes this issue making no return from your promise. 2) mauricelc92 has been approved to start work.

I believe this has to do with the poor support for promises in the Web3 library itself and not WalletConnect for the sending of transactions.

I implemented this at my work for our Wallet and ran into a similar issue.

My action plan would be to try out the event subscriptions to see if that works as that's the recommended way in my experience. I have a decent amount of experience integrating WalletConnect as I did it for my company. I should be able to debug it for you. 3) wartstone has been approved to start work.

  1. I am a senior full stack blockchain developer
  2. I've loaded a demo with similar situation with you. I have a solution for this bug.

Learn more on the Gitcoin Issue Details page.

tmm commented 2 years ago

@critesjosh what wallet are you using? Standard Celo?

critesjosh commented 2 years ago

@critesjosh what wallet are you using? Standard Celo?

The Valora wallets (celo mobile wallets) for android, testnet and mainnet.

maxx6262 commented 2 years ago

@critesjosh I tried to connect both with Valora (mainnet) and testnet app (on alfalojes) but it souds also app can't match QR code... I tried with Trust Wallet with Celo wallet and I get error message. With multiple wallet, I get only ethereum mainnet able to connect, isn't it ?

critesjosh commented 2 years ago

@maxx6262 i am able to connect using the testnet wallet and Valora. I am testing on this site

maxx6262 commented 2 years ago

That's right, I'm now also connected . Thank you ;)

maxx6262 commented 2 years ago

Did anyone find anything more than @critesjosh ?

I stayed tonight on This issue but the lonely one way to use promise is when I use others web3 library I already used.

Now all sign transactions are ok, I'm trying to make the more log I'm able and to switch any piece at each step to get point when issue would appear....

maxx6262 commented 2 years ago

Does anyone get an error due to bad privider we can't use for Celo ?

maxx6262 commented 2 years ago

I'm going to make clean repo to submit you @critesjosh . I've got each time Promise return both on success and returned transactions with a simple storage contract I deployed on Alfajoles.. To be sure about that, Iadded logs on console but also alert messages with TX on fullfilled transactions and error messages on returned o returned/wrongs. I've deleted all Web3 and others library to just keep etherjs, ans wagmi hooks to interact on chain. I 've linked Alfajoles wallet and I' now getting all Promise's retuns as expected. Please let me touch in case you'd to be still in troubles with this. Here is codesandbox repo I worked on

gitcoinbot commented 2 years ago

Issue Status: 1. Open 2. Started 3. Submitted 4. Done


Work for 250.0 cUSD (248.6 USD @ $0.99/cUSD) has been submitted by:

  1. @maxx6262

@ericnakagawa please take a look at the submitted work:


maxx6262 commented 2 years ago

@critesjosh I know, but you didn't get Promise return when calling 'store' function isn't ? Isn't it because you tried to call function by calling 'contract.store(values.number)' instead of using 'useContractWrite' hook inside your code as I maked on my submission ?

maxx6262 commented 2 years ago

You have to define Hook before rendering function 👍 const [ { data, nberror, nbloading }, write ] = useContractWrite( { addressOrName: StorageContractAddressTestnet, contractInterface: StorageAbi, }, 'store', { args: '9', } ); and you get Promise return inside button tag : <button onClick={ async function() { write() .then( (rep) => { if (!rep.ok) { alert(rep.error); console.error({ "message": rep.error }) } else { notification.open({ "tx": rep.hash , "rep": rep }) }}) .catch(err => console.error({ "message": err })) }}>SetStorage </button>

wartstone commented 2 years ago

I though it would be something related with walletconnect. I switched to other networks(like matic or rinkeby) and providers also didn't get updated. good to see @maxx6262 solved this.

zxqx commented 2 years ago

@maxx6262 Not able to run this app (either locally or in CodeSandbox) without compile errors.

critesjosh commented 2 years ago

@critesjosh I know, but you didn't get Promise return when calling 'store' function isn't ? Isn't it because you tried to call function by calling 'contract.store(values.number)' instead of using 'useContractWrite' hook inside your code as I maked on my submission ?

This doesn't solve the original issue, it uses a different hook. You are correct in that I am using the useContract hook, but since it is creating an ethers.js Contract instance (described here) I would expect a function call to return the same data as the ethers contract.

wartstone commented 2 years ago

i'll try to check the walletconnect side since it seems it's not getting sovled yet

zxqx commented 2 years ago

Might be worth taking a look at this issue in the @walletconnect monorepo: https://github.com/WalletConnect/walletconnect-monorepo/issues/663.

It looks like it could be the same or a similar issue, and I was able to successfully use the workaround posted at the bottom of OP's post, although it only worked when executing a transaction right after connecting -- hard refreshing after connecting and executing a transaction did not work.

maxx6262 commented 2 years ago

Sorry, please try this opensandbox

I connect with Wallet Connect on Alfajores I can read nb value (result on console) I can confirm write transaction and get response (I display it into console)

Here is new Rep

maxx6262 commented 2 years ago

You'll see below that I get

image

critesjosh commented 2 years ago

@maxx6262 I am not getting any response in the console when sending the update transaction from your codesandbox. I am using the Valora Alfajores testnet wallet for Android connected via WalletConnect.

There is another issue, which is described above. The first time I log in to your app using wallet connect, I get an error when trying to send a transaction. Here is the error:

Screen Shot 2022-02-10 at 12 00 47 PM

If I refresh the UI, it works as expected. This just happens when the connection is first established.

maxx6262 commented 2 years ago

Thanks @critesjosh ... unbelievable that I get returns and no other one...

That's why I keeped this one because it sounds having different comportment from my device and yours... (Did you also use Avast wich currently throw security alarm and disallowed me to reach any part of walletconnect as from siasky.net

I had same with Dacade projet last week

I'm now on this one getting back on first way : decompose wagmi write components and walletConnect Connector into all web3 steps from web3 and web3.eth (to try as I saw in official documentation when this bug appeared at version 1.3.0). Maybe by calling full sending request from Web3, will we able to use all handlers ? as .on("transactionHash", callback()) or .on('receipt', callback()) ...

maxx6262 commented 2 years ago

I attached Log file :

I see on this that the bug about first connection souds due to "Infura" provider being first loaded before next rendering call. I'm trying to fix it by founding a way to set again provider as new connection being setted.

@critesjosh : When Connection is OK, you'll see that I get Promises return into console as requested, do you still have no returns from your device ? On console : image image

he91x.csb.app-1644585291729.log

maxx6262 commented 2 years ago

I just find how I succesfully get these returns within 3 steps to reproduce :

  1. I connect first time with Valora app (Alfajores testnet)
  2. I've to refresh page and click again on WalletConnect
  3. I disconnect from Valora app and click on "store" button and reconnect wallet with new QR code And then I get all returns without issue....

I'm searching what would be unreachable datas that get dapp on second connection, but I don't see at the moment why WalletConnect has this bug...

As you'll see above, it sounds as valora get connection out...

https://user-images.githubusercontent.com/37675007/153658655-8e37c6a1-0272-4353-a799-5c51a697d3cd.mp4

maxx6262 commented 2 years ago

i'm currently testing on this dapp with traceable call requests.

wartstone commented 2 years ago

@maxx6262 ya,

  1. i think the "unexpected token i in json" problem is probably due to the useSigner() returns the signer with mainnet rpc.
  2. yes, it's related with walletconnect. i'm checking the relevant walletconnect logics as well.
maxx6262 commented 2 years ago

.

Yes it first try to reach transaction request via Infura, despite I maked manually provider without it.

For the second issue, WalletConnect sounds unable to sync as UI being refreshed (new wctoken ?).

In same time, connector doesn't reach disconnection from wallet app....

maxx6262 commented 2 years ago

I tried to make new Provider from WalletConnectProvider to overrice current one to see if it could fix it... I returned this parsed into ethers.providers.Web3Provider and it just fix reading calll return but there is still same issue each 1 session from 2.... for I don't see full explanation at the moment... I had another enjoyment when I thought it was fixed (lol) Here is new new Repo and codeqsandbox

I'm now seeing to set good Signer to see if it would help, and then in case it would not fix it, I think I'll reach into Connector builders...

rcstanciu commented 2 years ago

As a workaround, I have set the chainId property to the working network id, in the options object when instantiating the WalletConnectConnector:

const connectors = ({ chainId }: { chainId?: number | undefined }) => {
  return [
    new WalletConnectConnector({
      chains,
      options: {
        qrcode: true,
        chainId: networkId,
        rpc: {
          [networkId]: alchemyApiUrl,
        },
      },
    }),
  ]
}

As seen in the WalletConnect source code (https://github.com/WalletConnect/walletconnect-monorepo/blob/ad1670bff2186b6e0a1456fd3704c560edf11125/packages/ethereum-provider/src/index.ts#L88), if chainId is not provided, the default one is used in the constructor (Mainnet). Although it should update to the selected network, somehow it does not do that.

maxx6262 commented 2 years ago

In fact I think due to Hook process, it needs first rendering without our settings before WalletConnect uses chainId as requested, that sounds why we've to make refresh and reconnect wallet to get this fully operational as attended...

wartstone commented 2 years ago

the workaround does work if we add the chainId in the EthereumProvider. it seems EthereumProvider does try to update when network connected or signer_events gets updated. I'm checking isCompatibleChainId method. EthereumProvider would first check whether isCompatibleChainId and then update the chainid. it could be the cause.

neuroswish commented 2 years ago

anyone find a solution to this? I agree with @maxx6262 that I think it's a hooks issue. On first mount for me (on mainnet), trying the useSigner hook doesn't return a promise properly. When I disconnect and reconnect my wallet, however, it works fine

octavioamu commented 2 years ago

having a similar issue trying to listen an event "SignMsg" from contract, I need to reload after connect the wallet to be able to listen events, seems is looking on the wrong network. What Im trying to do is login someone with gnosis (with walletconnect).
https://gallery.mirror.xyz/GzHNX1UvpNmBDQjQ0pQnE_wsdxoS0_yWocRGOssH4Xw

tmm commented 2 years ago

Check out the upcoming version of wagmi 0.3.0-next.13 (and corresponding docs). This might be solved there.

octavioamu commented 2 years ago

@tmm I did the migration to the new version and sadly wasn't fixed. I will explain in case we are talking about different problems. First I connect the wallet using walletconnect, pasting the code of qrcode in gnosis app metamask and gnosis on rinkeby. The connections works fine but when I fire the signmessage method with a new signer (refetch old getSigner()) I console log the signer and get this: Screen Shot 2022-04-07 at 14 06 45

If I just reload the page the signer now is rinkeby (the right one) Screen Shot 2022-04-07 at 14 08 13

This is how my connectors is setup:

const infuraId = process.env.NEXT_PUBLIC_INFURA_ID
const chains = defaultChains
const defaultChain = chain.mainnet

const connectors = ({ chainId }) => {
  const chain = chains.find((x) => x.id === chainId) ?? defaultChain
  const rpcUrl = chain.rpcUrls.infura
      ? `${chain.rpcUrls.infura}/${infuraId}`
      : typeof chain.rpcUrls.default === 'string'
      ? chain.rpcUrls.default
      : chain.rpcUrls.default[0]

  return [
    new InjectedConnector({
      chains,
      options: { shimDisconnect: true },
    }),
    new WalletConnectConnector({
      options: {
        qrcode: true,
        rpc: {
          [chain.id]: rpcUrl,
        },
      },
    }),
    new CoinbaseWalletConnector({
      options: {
        appName: 'My wagmi app',
        chainId: chain.id,
        jsonRpcUrl: rpcUrl,
      },
    }),
  ]
}

export default connectors;

So seems the network by default is set to mainnet, then it change to the right one but doesn't change chainId on the connectors as the new WalletConnectConnector was already setup with defaultChain and that is chain.mainnet On solution I was thinking todo is to look the environment and check if it is "development" use defaultChain= chain.rinkeby but I was looking for a better and more dynamic solution.

Any ideas? Thanks in advance.