1

I'm having a bit of a headache trying to understand how to fix the 'transaction replaced' error when using ethers-js. I use await tx.wait(). I even use sleep for at least 10 seconds between transactions. And still sometime it throws that error. First of all, is it smth that one should avoid, is it avoidable, what happens when a tx is replaced? Is there a way to listen or monitor the state of a tx to continue only after it was mined to avoid the replacement? I tried the wait() function, awaited for the status 1, checked the blockNumber to appear in the tx receipt, but it seems like all of this being automatically checked inside the wait() func, And I still get transaction replacement sometimes.... Ho to avoid it? Thank you.

Also yes I tried even adding a nonce to tx manually, but it seems it changes nothing..

here'the code

try {
    const data = await getTools(acc, 'goerli', 'Ethereum Testnet Goerli', 'ethusdt');

    const { provider, signer, theChain, theToken } = data;

    const { tokenBalance, tokenBalanceInEther } = await getBalance(theChain, provider, signer, theToken);

    if (+tokenBalanceInEther < 0.0001) throw new Error('Not enough Assets!!!\n');

    const randomAmount = getRandomAmount(0.001, 0.002);
    console.log('The sent amount is', randomAmount);
    console.log('Sending the transaction...');

    const goerliToBaseContract = new ethers.Contract(base.GOERLI_TO_BASE_CONTRACT_ADDRESS, base.GOERLI_TO_BASE_CONTRACT_ABI, signer);

    const gasPrice = await provider.getGasPrice();

    const txEstimateGas = (
        +(await goerliToBaseContract.estimateGas.depositTransaction(acc.address, utils.parseEther(randomAmount), '0', false, '0x')) * 2
    ).toString();

    const tx = await goerliToBaseContract.functions.depositTransaction(acc.address, utils.parseEther(randomAmount), txEstimateGas, false, '0x', {
        gasLimit: txEstimateGas,
    });

    const receipt = await tx.wait();
    const taskReceipt: TransactionReceipt = await new Promise(async resolve => {
        const interval = setInterval(async () => {
            const status = receipt.status;
            const txHash = receipt.transactionHash;
            console.log('TX HASH', txHash);

            if (status && receipt.blockNumber) {
                clearInterval(interval);
                return resolve(receipt);
            }
        }, 1000);
    });

    return { taskReceipt, gasPrice, theToken };
} catch (error: any) {
    return { error };
}

1 Answer 1

1

The "transaction replaced" error message indicates that the transaction you submitted was no longer valid because another transaction with the same nonce was already mined before yours. This can happen when the transaction fee (gas price) you set is too low, and other transactions with higher fees are processed first by the network.

To avoid this error, you can set a higher gas price or increase the nonce of your transaction. However, it's important to note that increasing the nonce will only work if you're sure that the previous transaction failed and was not actually mined.

You mentioned that you tried to add a nonce manually, but it didn't work. One thing to check is whether the nonce you set is actually higher than the nonce of the previous transaction. You can check this by querying the account's transaction count using provider.getTransactionCount(address).

It's also important to note that waiting for a specific status or block number in the transaction receipt may not guarantee that the transaction won't be replaced. The only way to know for sure is to wait until the transaction is confirmed by a sufficient number of blocks (usually 12 for Ethereum). You can use the waitForTransactionConfirmation method provided by ethers.js to wait for confirmation.

Here's an updated version of your code that increases the nonce of the transaction to avoid replacement:

try {
    const data = await getTools(acc, 'goerli', 'Ethereum Testnet Goerli', 'ethusdt');

    const { provider, signer, theChain, theToken } = data;

    const { tokenBalance, tokenBalanceInEther } = await getBalance(theChain, provider, signer, theToken);

    if (+tokenBalanceInEther < 0.0001) throw new Error('Not enough Assets!!!\n');

    const randomAmount = getRandomAmount(0.001, 0.002);
    console.log('The sent amount is', randomAmount);
    console.log('Sending the transaction...');

    const goerliToBaseContract = new ethers.Contract(base.GOERLI_TO_BASE_CONTRACT_ADDRESS, base.GOERLI_TO_BASE_CONTRACT_ABI, signer);

    const gasPrice = await provider.getGasPrice();

    const txEstimateGas = (
        +(await goerliToBaseContract.estimateGas.depositTransaction(acc.address, utils.parseEther(randomAmount), '0', false, '0x')) * 2
    ).toString();

    const nonce = await provider.getTransactionCount(acc.address);

    const tx = await goerliToBaseContract.functions.depositTransaction(acc.address, utils.parseEther(randomAmount), txEstimateGas, false, '0x', {
        gasLimit: txEstimateGas,
        gasPrice: gasPrice.mul(2),
        nonce: nonce
    });

    const taskReceipt: TransactionReceipt = await tx.wait();
    const confirmation = await provider.waitForTransaction(taskReceipt.hash, 12);

    return { confirmation, gasPrice, theToken };
} catch (error: any) {
    return { error };
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.