I'm facing a strange issue when testing with ganache and increasing my blockTime to greater than 0 to simulate network congestion and failures.
The following code works just fine (meaning it fails and returns the reason for failure) when ganache's blockTime is 0:
try {
let tx = await contractWithSigner.contractFunction(someData, { gasLimit: 1 });
await tx.wait();
} catch (e) { console.log(e); }
As soon as I increase ganache's blockTime to any arbitrary number: ganache-cli -b 5, the same code never resolves the tx.wait() and ethers is stuck in a loop of sending out a call for transactionReceipt forever.
I can see in ganache's output that it does indeed send out the failed transaction receipt message in the block immediately following the contract call. Is my assumption on how this should work incorrect?
issue still relevant
Thanks @Benjythebee, I have this issue opened in a tab to investigate once v5.2 is released, so this is good to know. :)
This is still relevant and I am facing the same issue. the transaction also never gets completed by the blockchain. are there any workarounds?
I just noticed your transaction has gasLimit: 1. Such a transaction cannot fail because it cannot be accepted by a node. When you try to submit such a transaction with to geth or parity nodes, it immediately errors you saying your gasLimit is less than minimal required.
But if you submit to ganache and if it accepts (when it shouldn't), ethers.js might think it will be mined soon, but such a transaction cannot be included in a block. So this may be the reason why tx.wait() will never resolve.
Can you try with a higher gasLimit, basic is 21000. But in case your transaction also contains some input data, it will be more. Instead of an explicit value, you may just not pass a gasLimit. Ethers.js should populate that for you anyway.
Related to gasLimit, I would also get wait() to never resolve when the transaction takes too long (on mainnet)
One of xDai chain users also reported about infinite tx.wait():
I am trying interact with a contract on xDai, but whenever I make the contract call through ethers.js, I end up getting a tx that never gets submitted. To be clear, the tx does not error (I get a valid tx object with a hash after calling contract.functions.methodName()), its just that it never ends up on the network (e.g. calling await tx.wait() stalls forever).
For context, we are using the rpc.xdaichain.com endpoint (tried both https and wss)
Ah, when using the websocket we actually get a return error after a while:
{"jsonrpc":"2.0","error":{"code":-32017,"message":"Timeout","data":"Nethermind.JsonRpc.Modules.ModuleRentalTimeoutException: Unable to rent an instance of IEthRpcModule. Too many concurrent requests.\\n at Nethermind.JsonRpc.Modules.BoundedModulePool`1.SlowPath() in /src/Nethermind/Nethermind.JsonRpc/Modules/BoundedModulePool.cs:line 58\\n at Nethermind.JsonRpc.Modules.RpcModuleProvider.<>c__DisplayClass15_0`1.<<Register>b__0>d.MoveNext() in /src/Nethermind/Nethermind.JsonRpc/Modules/RpcModuleProvider.cs:line 74\\n--- End of stack trace from previous location ---\\n at Nethermind.JsonRpc.JsonRpcService.ExecuteAsync(JsonRpcRequest request, String methodName, ValueTuple`2 method, JsonRpcContext context) in /src/Nethermind/Nethermind.JsonRpc/JsonRpcService.cs:line 162\\n at Nethermind.JsonRpc.JsonRpcService.ExecuteRequestAsync(JsonRpcRequest rpcRequest, JsonRpcContext context) in /src/Nethermind/Nethermind.JsonRpc/JsonRpcService.cs:line 115\\n at Nethermind.JsonRpc.JsonRpcService.SendRequestAsync(JsonRpcRequest rpcRequest, JsonRpcContext context) in /src/Nethermind/Nethermind.JsonRpc/JsonRpcService.cs:line 105"},"id":2}
The error Timeout is returned by Nethermind client when the transaction cannot be handled in time. But tx.wait() stalls. Can the reason be in the Timeout error message? I guess, ethers.js should handle any errors.
Can you give advice on how to set a timeout on ethers.js level? For example, in web3 we have transactionBlockTimeout, transactionConfirmationBlocks, and transactionPollingTimeout options.
Hey all, I figured out the issue and its very noob of me 馃檲 the account that I was doing the transaction on had no currency, so it couldn't pay the gas fees.
I still think this is somewhat of an issue because without a manual timeout catch, you would have no way of picking up this error. So a good suggestion for a PR is to check the accounts balance first and return an error, or to use a timeout. (I am now manually checking balance first which is also actually okay - main point is still that a useful error message is better than an infinite wait)
Hello everyone,
I'm having the same issue as stated on the title. My tx.wait() never resolves, even thought the transaction has been successfuly mined.
try {
const tx = await router.swapExactTokensForTokens(
amountIn,
amountOutMin,
[data.WBNB, token],
data.recipient,
Date.now() + 1000 * 60 * 5, // 5 minutes
{
gasLimit: data.gasLimit,
gasPrice: ethers.utils.parseUnits(`${data.gasPrice}`, "gwei"),
nonce: null,
}
);
receipt = await tx.wait();
if (receipt.status) {
console.log(`Transaction receipt : https://www.bscscan.com/tx/${receipt.logs[1].transactionHash}\n`);
}
} catch (err) {
let error = JSON.parse(JSON.stringify(err));
[...]
I'm using the BSC testnet to try out the script. If I go to the bscscan testnet site I can see that my transaction was successful. I don't know what else to do.
Facing the same issue of tx.wait() not resolving or returning an error when a transaction is not picked up by the mining pool on Polygon/Matic.
Ideally, for a transaction that is submitted to nodes but isn't mined in a certain amount of time, we would want tx.wait() to return an error of some kind. Right now it appears to just get stuck. @ricmoo any ideas on a temporary fix?
I still face this issue
let transaction = await myContract.myMethod()
transaction.hash
is "wrong"?.
By "wrong", I mean that if I copy the hash returned by the above transaction and paste it on etherscan or polygonscan, it sometimes doesn't exists.
if I copy the hash returned by the above transaction and paste it on etherscan or polygonscan
Occasionally, it might not immediately reflect and their indexer might take some more time. But is it the case that even if you wait long enough, it doesn't reflect?
Interesting that many are facing this issue. @ricmoo Looking at the above comments I think whenever a user submits some transaction, the eth client gives a false positive response. And due to some sort of problem in the node, the tx doesn't stay in the mempool or broadcasted to other nodes. So ethers keeps waiting for the transactionHash to be mined. One possible solution could be to confirm if the tx actually made it to the mempool using provider.getTransaction, if that gives null then there was some problem and it may retry broadcasting or simply throw error to user.
If it turns out the above is really the issue, in the meantime, one may try using FallbackProvider, which allows you to use multiple eth nodes, like Infura/Alchemy/QuickNode under a single ethers provider, so when you submit a transaction it is sent to all the nodes. So you have a better chance to get your tx in the mempool.
Use provider.getTransactionReceipt(tx.hash); to fetch the receipt. Put the whole thing into a loop and poll for the result.
Break the loop once the tx receipt is received.
Snippet:
const tx = await router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
....
....
...
);
console.log('txn hash', tx.hash);
console.log(`Fetching txn receipt....`);
let receipt = null;
while (receipt === null) {
try {
receipt = await provider.getTransactionReceipt(tx.hash);
if (receipt === null) {
console.log(`Trying again to fetch txn receipt....`);
continue;
}
console.log(`Receipt confirmations:`, receipt.confirmations);
console.info(
`Transaction receipt : https://www.bscscan.com/tx/${receipt.logs[1].transactionHash}`
);
} catch (e) {
console.log(`Receipt error:`, e);
break;
}
}
Use
provider.getTransactionReceipt(tx.hash);to fetch the receipt. Put the whole thing into a loop and poll for the result.Break the loop once the tx receipt is received.
Snippet:
const tx = await router.swapExactTokensForTokensSupportingFeeOnTransferTokens( .... .... ... ); console.log('txn hash', tx.hash); console.log(`Fetching txn receipt....`); let receipt = null; while (receipt === null) { try { receipt = await provider.getTransactionReceipt(tx.hash); if (receipt === null) { console.log(`Trying again to fetch txn receipt....`); continue; } console.log(`Receipt confirmations:`, receipt.confirmations); console.info( `Transaction receipt : https://www.bscscan.com/tx/${receipt.logs[1].transactionHash}` ); } catch (e) { console.log(`Receipt error:`, e); break; } }
I have a feeling this is a pretty dirty way of doing it;
Are you suggesting this while loop should replace tx.wait() or be an in-between step?
Are you suggesting this while loop should replace tx.wait() or be an in-between step?
You may replace tx.wait() with the while loop until it fetches the value.
O you may replace this whole code by - starting a wss provider Transfer event listener and listen for the tx events. Keep looking out for your tx events inside the callback. Exit once you get the receipt.
@zemse I do actually use getTransaction (for example, in the JsonRpcSigner), and even if a transaction is in a mempool, it doesn't mean it is in the mempool of the node that was queried, and even if it is, some mempools, do not expose that upwards, so a null response doesn't necessarily mean it wasn't accepted in to the mempool.
I have noticed on Ganache, sending a gasLimit of 1 borks the whole thing. It silently allows it despite the fact the tx will never get mined. I need to bug them to fix that; it should throw if there intrinsic gas limit is not met. In v6, the network object has a computeIntrinsicGas on it, which might be useful, but I'd still prefer to rely on nodes replying.
I think there is a separate issue going on the xDai network. I'm investigating all these right now, trying to figure out the least-worst things to do and diagnose the problems. :)
Use provider.getTransactionReceipt(tx.hash); to fetch the receipt. Put the whole thing into a loop and poll for the result.
This is a scary way to do it. That should already be what the internal .wait is doing, except the .wait enforces exponential back-off and will use additional hints from the provider to optimize the network usage.
If you need an immediate solution, I would recommend using the provider.waitForTransaction(hash, confirms?, timeout?) which allows a timeout.
I'm looking into this now, and will let you know what I find shortly. :)
I have also opened https://github.com/trufflesuite/ganache-cli/issues/835 to solve the gasLimit: 1 issue.
I think there is a second bug going on here too though, within ethers, so I will leave this open, but will be tracked in and updated in #1479.
Truffle has a fixed version. I'm not sure if it has been released yet (if you check the ticket they indicate how to use it), but I'm going to close this issue and track the second issue mentioned in #1479.
Thanks! :)
Most helpful comment
issue still relevant