[Ethereum] Truffle + Infura on Mainnet: Nonce too low error


I have the following code:

const Contract = artifacts.require('./Market.sol');

module.exports = async function(callback) {
  let instance = await Contract.deployed();

  for(var i = 0; i <=200; i++){
     await instance.createListing(i, 4, {from: address, gas: 5000000, gasPrice: 2000000000})}


This works on Truffle and Ganache, works on Ropsten via Infura (executed via truffle exec script.js --network ropsten).

However, when I'm deploying the contract on mainnet and running truffle exec script.js --network live, it works for the first 2-3 iterations, and then I'm getting:

UnhandledPromiseRejectionWarning: Error: nonce too low

I tried reinstalling MetaMask, but the issue is still there. What could be the reason of this, and is there a way to add a chunk of code in the script or truffle.js that could help to deal with it?

Best Answer

If you're using Truffle with Infura directly (which it sounds like you are, although it's unclear why you mention MetaMask), you need to be aware that Infura is running on a server farm. If you submit a transaction, the node that receives it first may not be the same node that handles the next transaction, or that handles the eth_getTransactionCount call that gets the nonce for the next transaction.

On Ropsten, transactions will propagate quickly, and will quickly be mined, but this isn't always true on mainnet, so you need some kind of client-side nonce management.

MetaMask handles this using some special nonce-tracking middleware. It's rather disappointing that Truffle's HDWalletProvider doesn't use this middleware, since HDWalletProvider already re-uses several pieces of MetaMask middleware, and I'm half tempted to raise a pull request to include it.

In any case, here's a truffle.js that implements this via a slightly hacky workaround:

var HDWalletProvider = require("truffle-hdwallet-provider")
var NonceTrackerSubprovider = require("web3-provider-engine/subproviders/nonce-tracker")

module.exports = {
  networks: {
    mainnet: {
      network_id: "1",
      provider: function () {
        var wallet = new HDWalletProvider('candy maple cake sugar pudding cream honey rich smooth crumble sweet treat', 'https://mainnet.infura.io')
        var nonceTracker = new NonceTrackerSubprovider()
        return wallet