When I deploy 2 smart contracts at the same time to Hedera Testnet, I get the following error:
nonce has already been used [ See: ]
This occurs intermittently, roughly about 20% of the time. Their occurrence appears to be non-deterministic, although I am unable confirm this.
My deployment code is:
const deploymentSigner: Signer = (await hre.ethers.getSigners())[0];
// console.log('Deployment signer address', (await deploymentSigner.getAddress()));
const scDeploymentPromises: Promise<Contract>[] = [];
for (let idx = 0; idx < scNamesToDeploy.length; ++idx) {
const scName = scNamesToDeploy[idx];
const scFactory: ContractFactory =
await hre.ethers.getContractFactory(scName);
console.log(`Deploying ${scName} on ${networkName} ...`);
const sc: Contract = await scFactory.deploy();
// NOTE deployment with constructor params not needed for these particular SCs
const scDeploymentPromise: Promise<Contract> = sc.deployed();
scDeploymentPromises.push(scDeploymentPromise);
}
// NOTE collect deployment promises without `await`-ing them,
// so as to be able to run them in parallel.
const deployedScs: Contract[] = await Promise.all(scDeploymentPromises);
deployedScs.forEach((sc, idx) => {
const scName = scNamesToDeploy[idx];
console.log(`Deployed ${scName} on ${networkName} at ${sc.address}`);
});
Where scNamesToDeploy
is an array of strings, initialised elsewhere,
containing the smart contract names that I'd like to deploy.
Worth noting that if I change the code within the first loop to do
await sc.deployed()
... and therefore skip the 2nd loop, this error stops occurring. Therefore I think it has something to do with the 2nd deployment transaction occurring too quickly after the 1st one. But, technically, this should be possible, I should be able to submit both transactions within the same "block" even. ("block" is in quotes because Hedera does not have blocks, AFAICT.)
Details
Versions
I'm using hardhat ([email protected]
) + ethers.js ([email protected]
via @nomicfoundation/[email protected]
).
Error details
This is the full error message:
nonce has already been used [ See: ] (error={"name":"ProviderError","_stack":"ProviderError: [Request ID: 356c0cee-949f-4f52-b936-25bbba9ef603] Nonce too low\n at HttpProvider.request (/Users/user/code/hedera/hedera-scratch/chain/node_modules/hardhat/src/internal/core/providers/http.ts:88:21)\n at processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async EthersProviderWrapper.send (/Users/user/code/hedera/hedera-scratch/chain/node_modules/@nomiclabs/hardhat-ethers/src/internal/ethers-provider-wrapper.ts:13:20)","code":32001,"_isProviderError":true}, method="sendTransaction", transaction=undefined, code=NONCE_EXPIRED, version=providers/5.7.2)\
Network config
This is from the networks
section of hardhat.config.js
:
hederatestnet: {
chainId: 296,
url: '',
// gasPrice: Math.floor(rskTestnetMinimumGasPrice * 1.1),
gasMultiplier: hederaTestnetGasMultiplier,
accounts,
},
hederatestnetrelay: {
chainId: 296,
url: 'http://localhost:7546',
// gasPrice: Math.floor(rskTestnetMinimumGasPrice * 1.1),
gasMultiplier: hederaTestnetGasMultiplier,
accounts,
},
Note that I'm using hederatestnetrelay
which is a local instance of hedera-json-rpc-relay
connected to the Hedera Testnet, as that seems to be less flaky than connecting to the public RPC endpoint directly, where I get different intermittent errors ("Unknown error invoking RPC").
When I deploy 2 smart contracts at the same time to Hedera Testnet, I get the following error:
nonce has already been used [ See: https://links.ethers/v5-errors-NONCE_EXPIRED ]
This occurs intermittently, roughly about 20% of the time. Their occurrence appears to be non-deterministic, although I am unable confirm this.
My deployment code is:
const deploymentSigner: Signer = (await hre.ethers.getSigners())[0];
// console.log('Deployment signer address', (await deploymentSigner.getAddress()));
const scDeploymentPromises: Promise<Contract>[] = [];
for (let idx = 0; idx < scNamesToDeploy.length; ++idx) {
const scName = scNamesToDeploy[idx];
const scFactory: ContractFactory =
await hre.ethers.getContractFactory(scName);
console.log(`Deploying ${scName} on ${networkName} ...`);
const sc: Contract = await scFactory.deploy();
// NOTE deployment with constructor params not needed for these particular SCs
const scDeploymentPromise: Promise<Contract> = sc.deployed();
scDeploymentPromises.push(scDeploymentPromise);
}
// NOTE collect deployment promises without `await`-ing them,
// so as to be able to run them in parallel.
const deployedScs: Contract[] = await Promise.all(scDeploymentPromises);
deployedScs.forEach((sc, idx) => {
const scName = scNamesToDeploy[idx];
console.log(`Deployed ${scName} on ${networkName} at ${sc.address}`);
});
Where scNamesToDeploy
is an array of strings, initialised elsewhere,
containing the smart contract names that I'd like to deploy.
Worth noting that if I change the code within the first loop to do
await sc.deployed()
... and therefore skip the 2nd loop, this error stops occurring. Therefore I think it has something to do with the 2nd deployment transaction occurring too quickly after the 1st one. But, technically, this should be possible, I should be able to submit both transactions within the same "block" even. ("block" is in quotes because Hedera does not have blocks, AFAICT.)
Details
Versions
I'm using hardhat ([email protected]
) + ethers.js ([email protected]
via @nomicfoundation/[email protected]
).
Error details
This is the full error message:
nonce has already been used [ See: https://links.ethers/v5-errors-NONCE_EXPIRED ] (error={"name":"ProviderError","_stack":"ProviderError: [Request ID: 356c0cee-949f-4f52-b936-25bbba9ef603] Nonce too low\n at HttpProvider.request (/Users/user/code/hedera/hedera-scratch/chain/node_modules/hardhat/src/internal/core/providers/http.ts:88:21)\n at processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async EthersProviderWrapper.send (/Users/user/code/hedera/hedera-scratch/chain/node_modules/@nomiclabs/hardhat-ethers/src/internal/ethers-provider-wrapper.ts:13:20)","code":32001,"_isProviderError":true}, method="sendTransaction", transaction=undefined, code=NONCE_EXPIRED, version=providers/5.7.2)\
Network config
This is from the networks
section of hardhat.config.js
:
hederatestnet: {
chainId: 296,
url: 'https://testnet.hashio.io/api',
// gasPrice: Math.floor(rskTestnetMinimumGasPrice * 1.1),
gasMultiplier: hederaTestnetGasMultiplier,
accounts,
},
hederatestnetrelay: {
chainId: 296,
url: 'http://localhost:7546',
// gasPrice: Math.floor(rskTestnetMinimumGasPrice * 1.1),
gasMultiplier: hederaTestnetGasMultiplier,
accounts,
},
Note that I'm using hederatestnetrelay
which is a local instance of hedera-json-rpc-relay
connected to the Hedera Testnet, as that seems to be less flaky than connecting to the public RPC endpoint directly, where I get different intermittent errors ("Unknown error invoking RPC").
1 Answer
Reset to default 5The nonce has already been used
error occurs when a transactions is submitted with a nonce value that is the same as or less that another transaction that it (the same EOA) has submitted in the previously.
The correct behaviour is for the EOA to increment the nonce value by 1 (or more) for each subsequent transaction. However, your seeing this error indicates that this isn't happening, and your own assessment that
Therefore I think it has something to do with the 2nd deployment transaction occurring too quickly after the 1st one.
is in the right direction. It seems that ethers.js is reusing the same nonce value in 2 transactions, simply because they are occurring more or less at the same time.
This is possibly because it under the hood, ethers.js would need to use the eth_getTransactionCount
RPC endpoint, and that goes over the network, and therefore it may retrieve a "stale" value, especially when making 2 requests milliseconds apart from each other.
try this out manually:
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["0x...your deployment EOA addres...","latest"],"id":1}'
one way to work around this would be to override the transaction parameters for your deployment transaction to manually specify the nonce:
const deploymentSigner: Signer = (await hre.ethers.getSigners())[0];
// console.log('Deployment signer address', (await deploymentSigner.getAddress()));
const deploymentSignerInitialNonce = await deploymentSigner.getTransactionCount();
const scDeploymentPromises: Promise<Contract>[] = [];
for (let idx = 0; idx < scNamesToDeploy.length; ++idx) {
// NOTE manually override the nonce value to solve for intermittent failures
// that otherwise occur with concurrent deployment transactions.
// This is intended to **only** work if the deployment account is **not** submitting
// any other transactions to the network at the same time.
const deploymentTxOverrides = {
nonce: (deploymentSignerInitialNonce + idx),
};
// console.log('Deployment Tx Overrides', deploymentTxOverrides);
const scName = scNamesToDeploy[idx];
const scFactory: ContractFactory =
await hre.ethers.getContractFactory(scName);
console.log(`Deploying ${scName} on ${networkName} ...`);
const sc: Contract = await scFactory.deploy(deploymentTxOverrides);
// TODO deployment with constructor params (but not needed in this case)
const scDeploymentPromise: Promise<Contract> = sc.deployed();
scDeploymentPromises.push(scDeploymentPromise);
}
// NOTE collect deployment promises without `await`-ing them,
// so as to be able to run them in parallel.
const deployedScs: Contract[] = await Promise.all(scDeploymentPromises);
deployedScs.forEach((sc, idx) => {
const scName = scNamesToDeploy[idx];
console.log(`Deployed ${scName} on ${networkName} at ${sc.address}`);
deploymentCacheNetwork[scName] = sc.address;
});
Specifically:
(1) await deploymentSigner.getTransactionCount();
manually retrieves eth_getTransactionCount
for the EOA deploying the SCs
(2) { nonce: (deploymentSignerInitialNonce + idx) };
increments the nonce by 1 each run within the 1st for-loop
(3) await scFactory.deploy(deploymentTxOverrides);
performs the deployment transaction with the manually specified nonce value