'Web3.js - How to sign an approve transaction for a swap?

I want to programmatically swap two tokens. I have to approve the amount first. How do I approve using web3.js ?

Here's what I've come up with so far but I get the error Error: Returned error: nonce too low

const myAddress = "my-wallet-address"
const privateKey = Buffer.from('my-private-key', 'hex');

const test = async () => {
    const allowed = await tokenContract.methods.allowance(myAddress, 'UniswapV2Router02ContractAddress').call()

    const encodedABI = tokenContract.methods.approve('UniswapV2Router02ContractAddress', amountIn).encodeABI();
    const tx = {        
        from: myAddress,
        to: 'UniswapV2Router02ContractAddress',
        gas: 2000000,
        data: encodedABI
    };
    
    const customCommon = Common.default.forCustomChain(
        'mainnet',
        {
          name: 'SAMPLE testnet',
          networkId: custom-testnet-id,
          chainId: custom-testnet-id,
        },
        'petersburg',
      )

    const txTx = new Tx(tx, {common: customCommon});
    txTx.sign(privateKey);

    // below gives true, true
    console.log(txTx.validate(), ethUtil.bufferToHex(txTx.getSenderAddress()) === ethUtil.bufferToHex(ethUtil.privateToAddress(privateKey)))
    
    const serializedTx = txTx.serialize();

    // below line results in error
    await web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex'))

    // await uniswapV2Router02Contract.methods.swapExactTokensForTokensSupportingFeeOnTransferTokens(amountIn, amountOutMin, path, myAddress, deadline).send({from: myAddress})
}

test()

The Error: Returned error: nonce too low error was discussed in the last answer in this post: web3.eth.sendSignedTransaction() always return "Returned error: nonce too low" however I've verified that my private key belongs to the sender (me) of the transaction so I'm clueless



Solution 1:[1]

Here's the code:

var Web3 = require('web3');
var web3 = new Web3("rpc-node-link");
var Tx = require('ethereumjs-tx').Transaction;
var Common = require('ethereumjs-common');
var ethUtil = require('ethereumjs-util')

web3.eth.getTransactionCount(myAddress, async (err, txCount) => {
        const encodedABI = contractWhichisApproving.methods.approve(contractThatWantsToSpendContractWhichisApprovingsToken, amountIn).encodeABI();
        const tx = {
            nonce: web3.utils.toHex(txCount),
            from: myAddress,
            to: contractWhichisApprovingAddress,
            gas: 2000000,
            data: encodedABI
        };

        const customCommon = Common.default.forCustomChain(
            'mainnet',
            {
            name: 'Example testnet',
            networkId: custom-testnet-id,
            chainId: custom-testnet-id,
            },
            'petersburg',
        )

        const txTx = new Tx(tx, {common: customCommon});
        txTx.sign(privateKey);

        // following line logs true, true
        console.log(txTx.validate(), ethUtil.bufferToHex(txTx.getSenderAddress()) === ethUtil.bufferToHex(ethUtil.privateToAddress(privateKey)))

        const serializedTx = txTx.serialize();
        console.log('sending')
        await web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex')).on('receipt', console.log);
        console.log('done')
    });

Reference using Custom Chain: https://github.com/ethereumjs/ethereumjs-tx/blob/master/examples/custom-chain-tx.ts

Solution 2:[2]

Let's first understand what's Nonce in Ethereum.

Based on the Ethereum paper: Nonce is the scalar value equal to number of transactions sent from the address (wallet)...

The transaction including: transfer, create contract...

The Nonce too low error can be raise when you use your account in different places. Especially when you use the private key to sign the transaction.

Example:

  • An address have 5 transactions, sent from MM (PC1). Nonce is 5.
  • You perform a transaction at another place, using private key (PC2). Nonce is 6. But, the MM doesn't have knowledge about this.
  • Back to PC1, perform another transaction on MM, the Nonce should be 7, but MM say it 6. This lead to the Nonce too low issue.

For a fix, we can simply reset MM wallet:

From MM menu choose Setting => Advance Setting => Reset.


Why reset MM can solve the nonce problems?

  • By reset, the MM can sync the number of transactions of address. So the nonce is increased nicely.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ayudh
Solution 2