Hey this is a very basic question but I am getting problem in it. I have following code:
var nonce = await web3.eth.getTransactionCount(fromAccount);
hashes.map(async hash => {
console.log(nonce)
const account = await web3.eth.accounts.privateKeyToAccount(process.env.PRIVATE_KEY_1)
const transaction = await contract.methods.submitHash(hash);
const options = {
nonce: web3.utils.toHex(nonce),
to : transaction._parent._address,
data : transaction.encodeABI(),
gas : await transaction.estimateGas({from: account.address}),
gasPrice: web3.utils.toHex(web3.utils.toWei('20', 'gwei')),
};
const signed = await web3.eth.accounts.signTransaction(options, account.privateKey);
const receipt = await web3.eth.sendSignedTransaction(signed.rawTransaction);
console.log(receipt)
nonce ++}
I want to increase nonce only once all the awaits are executed each time ie, I want to get 1,2,3,4.. as nonce value. But the problem is due to asynchronus nature nonce gets the same value in each loop ie 1,1,1,1.. How to increase it by one in each loop execution?
The problem is that you're using map
where you want a for
loop. There are two problems with that:
It doesn't make sense to use map
when you're not using the array it returns (someone, somewhere, is teaching this anti-pattern and doing a disservice to their students); and
map
doesn't do anything with the promise your async
function returns, so all of your async
functions run in parallel
With a for
loop, you don't have those problems:
let nonce = await web3.eth.getTransactionCount(fromAccount);
for (const hash of hashes) {
console.log(nonce);
const account = await web3.eth.accounts.privateKeyToAccount(process.env.PRIVATE_KEY_1);
const transaction = await contract.methods.submitHash(hash);
const options = {
nonce : web3.utils.toHex(nonce),
to : transaction._parent._address,
data : transaction.encodeABI(),
gas : await transaction.estimateGas({from: account.address}),
gasPrice: web3.utils.toHex(web3.utils.toWei('20', 'gwei')),
};
const signed = await web3.eth.accounts.signTransaction(options, account.privateKey);
const receipt = await web3.eth.sendSignedTransaction(signed.rawTransaction);
console.log(receipt);
++nonce;
}
Since your for
is inside a context where await
works (an async
function or, soon, the top level of a module), it waits at the await
expressions before continuing its logic.
If the work can be done in parallel, then you could use map
and await Promise.all
on the array it returns. Within the map
callback, use nonce + index
instead of incrementing since index
will be 0
for the first hash, 1
for the next, etc.:
// In parallel
let nonce = await web3.eth.getTransactionCount(fromAccount);
const results = await Promise.all(hashes.map(async (hash, index) => {
const thisNonce = nonce + index;
console.log(thisNonce);
const account = await web3.eth.accounts.privateKeyToAccount(process.env.PRIVATE_KEY_1);
const transaction = await contract.methods.submitHash(hash);
const options = {
nonce : web3.utils.toHex(thisNonce),
to : transaction._parent._address,
data : transaction.encodeABI(),
gas : await transaction.estimateGas({from: account.address}),
gasPrice: web3.utils.toHex(web3.utils.toWei('20', 'gwei')),
};
const signed = await web3.eth.accounts.signTransaction(options, account.privateKey);
const receipt = await web3.eth.sendSignedTransaction(signed.rawTransaction);
console.log(receipt);
}));
// If you're going to keep using `nonce`, do `nonce += results.length` here.