Code
let tx = {
to: childData.address,
value: ethers.utils.parseEther('0.1')
};
wallet.sendTransaction(tx)
.then((tx) => {
console.log(tx.hash);
})
.catch((err) => {
console.log(err);
});
Error:
Error: nonce has already been used (version=4.0.23) { Error: nonce has already been used (version=4.0.23)
complete error log:
reason: 'nonce has already been used',
code: 'NONCE_EXPIRED',
transaction:
{ nonce: 97,
gasPrice: BigNumber { _hex: '0x3b9aca00' },
gasLimit: BigNumber { _hex: '0x5208' },
to: '0xEB21076E53E905c70b60354cF17c2599eB2e35EB',
value: BigNumber { _hex: '0x016345785d8a0000' },
data: '0x',
chainId: 4,
v: 43,
r:
'0x02c87a81367ec869eda61a7444afca53a528b8e1f7980f7e596c7312de85ffcf',
s:
'0x19b3cebbb190e2dcc9e49fb13de960067ab5e778ba1680b2818c47e95578f8e3',
from: '0xa88744Cd5F813ae6ba275Ee1BbbE5219DfC1c1A1',
hash:
'0x1f1ce79022d0b4c38db8587b4137c5273a06eaaf04c0ecebe7ca4e03d2c8dd39' },
transactionHash:
'0x1f1ce79022d0b4c38db8587b4137c5273a06eaaf04c0ecebe7ca4e03d2c8dd39' }
How can I resolve this ?
This is likely caused by sending multiple transactions immediately after each other, before the network propagates the “pending” transaction count.
I have an example nonce manager I will be publishing soon, but basically you will need to pass the nonce in and manually increment it for each transaction.
Make sense? I’ll aim to get my example NonceManager (which extends Signer) up this week.
Thanks for your quick reply Richard,
I'll wait for the nonce manager commit, in the meanwhile I'll manage with web3
let tx = {
to: childData.address,
nonce: web3.eth.getTransactionCount(accountAddress, 'pending'),
value: ethers.utils.parseEther('0.1')
};
You can do it the same way (except with provider.getTransactionCount(accountAddress, “pending”). But if you execute too many transactions too quickly, the “pending” will still get overrun.
I would recommend for now, if you are executing many transactions, you can do something like:
let baseNonce = provider.getTransacrionCount(wallet.getAddress());
let nonceOffset = 0;
function getNonce() {
return baseNonce.then((nonce) => (nonce + (nonceOffset++)));
}
let tx0 = { to: a0, value: v0, nonce: getNonce() };
wallet.sendTransaction(tx0);
let tx1 = { to: a1, value: v1, nonce: getNonce() };
wallet.sendTransaction(tx1);
And so on. Every call to getNonce gives you the next nonce. That’s basically all the NonceManager does. :)
(typed on my iPhone; there may be typos, but you should get the gist)
Closing this now, but if you still have any issues, please feel free to re-open.
Thanks! :)
Hi @ricmoo
I have an example nonce manager I will be publishing soon
Can you direct me to docs on your nonce manager? I did not find it anywhere.
Or is some vanilla nonce incrementation still the way to go?
@gitpusha
There isn't anything in the docs yet, and it is still part of the experimental package, but if you'd like to try it out, here is the code.
You should be able to use:
const { NonceManager } = require("@ethersproject/experimental");
const managedSigner = new NonceManager(mySigner);
And then just use the managedSigner instead of the signer.
Make sure if you use the @ethersproject/experimental package, you lock in the exact version (i.e. 5.0.0-beta.134, not a ranged version, like ^5.0.0-beta.134) since that package could change wildly. :)
@ricmoo
Looks great, thanks!
But from what I can tell upon looking at the source, there is no built-in fail-save for tx in the nonce-chain that failed, right?
Say for example I batch the following transactions using the NonceManager
nonce-2
nonce-3
nonce-4
Now they are all "pending" and setTransactionCount will have set _initialPromise to 4 and _deltaCount to 0.
Now my relay node wants to send some new tx off, while the previous batch still hasnt been mined.
nonce-5
nonce-6
Now nonce-5 and nonce-6 join the previous pending tx
Now the previous batch has been mined, but ~nonce-3 had an out of gas error.~ wasnt mined.
so the nonce chain is broken
nonce-2 ✔️
nonce-3 đź”´
nonce-4 pending until a tx with nonce-3 is mined
nonce-5 pending until a tx with nonce-3 is mined
nonce-6 pending until a tx with nonce-3 is mined
This is a a contrived example because I am not 100% sure when exactly an account's nonce is incremented and when not? For example, the latter would not be a problem, if out of gas tx nonce-3 still led to the signer's nonce being incremented. if the NonceManager took care of monitoring which nonce in the chain wasnt mined and then sending a new tx with the nonce of the unmined one, to swap it in fix the broken link.
If, however, an account's nonce is not incremented for failed TX (which I believe to be the case) then my question is:
EDITS:
I was wrong. A nonce seems to be incremented for any mined tx. And with that new insight I believe your NonceManager already takes care of that but I will ask anyways:
1) How does the NonceManager handle such a out of sync scenario?
2) Do you have any code to handle such a scenario, where the nonce manager basically is aware of any blanks from failed tx in the nonce chain, and fills them retroactively, by resubmitting the same Transaction with the same nonce again?
Thanks for your input as always.
one more question
await provider.getTransactionCount(address, "pending") - that seems to be an undocumented feature already existing in ethers v4?
Is it reliable in v4?
So say, with a contrived example, if I send transactions asynchronously and manually increment the nonce like this........
let nonce = 1;
sendTx
nonce = await provider.getTransactionCount(myAddress, "pending")
=> nonce == 2
sendTx
nonce++; (2 -> 3)
sendTx
nonce++ (3 -> 4)
sendTx
let pendingNonce = await provider.getTransactionCount(myAddress, "pending")
....... then: nonce == 4 && pendingNonce == nonce
That reliable works and the await provider.getTransactionCount(myAddress, "pending") will not be overrun, correct?
The pending blockTag is not terribly reliable, and I won’t recommend it. The implementation for Parity and Geth is quite different, where one examines the total transaction pool and one examines the transactions expected to go into the next block (I forget which is which), and many backends simply ignore it and use latest internally if pending is used.
Basically, unless you are running your own infrastructure which does what you want it to do, I won’t depend on it. :)
Oh, also which NonceManager are you looking at? The one in the v5 experimental package? I’ll get more documentation on it prepared soon, but there are certainly a lot of caveats. All transactions must be sent through it; if you have a separate computer (or even separate process or separate instance) sending transactions from the same account will cause things to not go smoothly.
Also, the current version does not support rebroadcast. A node will only hold onto a handful of “future” nonces for a given account. If you tried to send nonce 0 through 100, the network might only remember 0 through 5, and once 5 was mined, you would need to resend 6 and so on.
Dependencies are another problem it does not solve. If a future tx requires an earlier one to succeed, there will need to be part of the API that lets you specify this, which I’m still working through, because it means that TD cannot even be broadcast until the earlier is mined.
It would also be nice if there was a way to serialize the pending tasks and state of the NonceManager, so you could use it to manage a lot of transactions, but have a save feature so you could “turn it off and turn it on again”.
That is why it is in the experimental package. There is still a lot to figure out. :)
Basically, unless you are running your own infrastructure which does what you want it to do, I won’t depend on it. :)
Ok
Regarding Nonce Manager. Ah ok. Great to know that you are working on it. I guess their wont be a magical catch-all solution any time soon, but every little helps.
For our implementation the safest way is just to synchronise the transaction sending instead of batching it. But of course this is also the slowest solution. So not really willing to give up on asynchronous batch tx sends just yet.
Most helpful comment
You can do it the same way (except with
provider.getTransactionCount(accountAddress, “pending”). But if you execute too many transactions too quickly, the “pending” will still get overrun.I would recommend for now, if you are executing many transactions, you can do something like:
And so on. Every call to getNonce gives you the next nonce. That’s basically all the NonceManager does. :)
(typed on my iPhone; there may be typos, but you should get the gist)