Skip to content

Commit 148c209

Browse files
Merge pull request #4006 from OriginTrail/imrpovment/nonce-handling-for-tx
Bump gas price on nonce issue
2 parents 8e11adb + 6e192b2 commit 148c209

File tree

2 files changed

+65
-23
lines changed

2 files changed

+65
-23
lines changed

src/constants/constants.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,9 @@ export const EXPECTED_TRANSACTION_ERRORS = {
712712
NODE_NOT_AWARDED: 'NodeNotAwarded',
713713
WRONG_MERKLE_PROOF: 'WrongMerkleProof',
714714
NO_MINTED_ASSETS: 'NoMintedAssets',
715+
NONCE_TOO_LOW: 'nonce too low',
716+
REPLACEMENT_UNDERPRICED: 'replacement transaction underpriced',
717+
ALREADY_KNOWN: 'already known',
715718
};
716719

717720
/**

src/modules/blockchain/implementation/web3-service.js

Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
TRANSACTION_PRIORITY,
2424
CONTRACT_FUNCTION_GAS_LIMIT_INCREASE_FACTORS,
2525
ABIs,
26+
EXPECTED_TRANSACTION_ERRORS,
2627
} from '../../../constants/constants.js';
2728
import Web3ServiceValidator from './web3-service-validator.js';
2829

@@ -561,8 +562,10 @@ class Web3Service {
561562
operationalWallet,
562563
) {
563564
let result;
564-
const gasPrice = predefinedGasPrice ?? (await this.getGasPrice());
565+
let gasPrice = predefinedGasPrice ?? (await this.getGasPrice());
565566
let gasLimit;
567+
let retryCount = 0;
568+
const maxRetries = 3;
566569

567570
try {
568571
/* eslint-disable no-await-in-loop */
@@ -577,33 +580,69 @@ class Web3Service {
577580

578581
gasLimit = gasLimit.mul(gasLimitMultiplier * 100).div(100);
579582

580-
this.logger.debug(
581-
`Sending signed transaction ${functionName} to the blockchain ${this.getBlockchainId()}` +
582-
` with gas limit: ${gasLimit.toString()} and gasPrice ${gasPrice.toString()}. ` +
583-
`Transaction queue length: ${this.getTotalTransactionQueueLength()}. Wallet used: ${
584-
operationalWallet.address
585-
}`,
586-
);
583+
while (retryCount < maxRetries) {
584+
try {
585+
this.logger.debug(
586+
`Sending signed transaction ${functionName} to the blockchain ${this.getBlockchainId()}` +
587+
` with gas limit: ${gasLimit.toString()} and gasPrice ${gasPrice.toString()}. ` +
588+
`Transaction queue length: ${this.getTotalTransactionQueueLength()}. Wallet used: ${
589+
operationalWallet.address
590+
}${retryCount > 0 ? ` (retry ${retryCount})` : ''}`,
591+
);
587592

588-
const tx = await contractInstance.connect(operationalWallet)[functionName](...args, {
589-
gasPrice,
590-
gasLimit,
591-
});
593+
const tx = await contractInstance
594+
.connect(operationalWallet)
595+
[functionName](...args, {
596+
gasPrice,
597+
gasLimit,
598+
});
592599

593-
try {
594-
result = await this.provider.waitForTransaction(
595-
tx.hash,
596-
TRANSACTION_CONFIRMATIONS,
597-
TRANSACTION_POLLING_TIMEOUT_MILLIS,
598-
);
600+
try {
601+
result = await this.provider.waitForTransaction(
602+
tx.hash,
603+
TRANSACTION_CONFIRMATIONS,
604+
TRANSACTION_POLLING_TIMEOUT_MILLIS,
605+
);
599606

600-
if (result.status === 0) {
601-
await this.provider.call(tx, tx.blockNumber);
607+
if (result.status === 0) {
608+
await this.provider.call(tx, tx.blockNumber);
609+
}
610+
} catch (error) {
611+
this._decodeWaitForTxError(contractInstance, functionName, error, args);
612+
}
613+
return result;
614+
} catch (error) {
615+
const errorMessage = error.message.toLowerCase();
616+
617+
// Check for nonce-related errors
618+
if (
619+
errorMessage.includes(
620+
EXPECTED_TRANSACTION_ERRORS.NONCE_TOO_LOW.toLowerCase(),
621+
) ||
622+
errorMessage.includes(
623+
EXPECTED_TRANSACTION_ERRORS.REPLACEMENT_UNDERPRICED.toLowerCase(),
624+
) ||
625+
errorMessage.includes(EXPECTED_TRANSACTION_ERRORS.ALREADY_KNOWN.toLowerCase())
626+
) {
627+
retryCount += 1;
628+
if (retryCount < maxRetries) {
629+
// Increase gas price by 20% for nonce errors
630+
gasPrice = Math.ceil(gasPrice * 1.2);
631+
this.logger.warn(
632+
`Nonce error detected for ${functionName}. Retrying with increased gas price: ${gasPrice} (retry ${retryCount}/${maxRetries})`,
633+
);
634+
continue;
635+
} else {
636+
this.logger.error(
637+
`Max retries (${maxRetries}) reached for nonce error in ${functionName}. Final gas price: ${gasPrice}`,
638+
);
639+
}
640+
}
641+
642+
// If it's not a nonce error or we've exhausted retries, re-throw the error
643+
throw error;
602644
}
603-
} catch (error) {
604-
this._decodeWaitForTxError(contractInstance, functionName, error, args);
605645
}
606-
return result;
607646
}
608647

609648
_decodeEstimateGasError(contractInstance, functionName, error, args) {

0 commit comments

Comments
 (0)