9

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?

  • 2
    If it's a nonce error, that indicates you are creating a transaction which either doesn't have a nonce (and successive transactions need a bigger nonce) or the nonce is lower than previous transactions. Not sure why this would work on testnet but maybe they ignore the nonce. You just have to make sure each transaction has a nonce greater than any other processed transaction. I'm not sure if your library here supports that but that is what you need to change about your transaction. – xrd Mar 31 '18 at 3:34
  • 1
    This nonce problem could be due to chain reorgs. Since you're deploying everything from a single address, you could get the nonce once (web3.eth.getTransacctionCount(address, "pending")) and manually increment in your loop and pass it as parameter to createListing. This way you can exactly specify the order in which transactions should be processed. – ivicaa Mar 31 '18 at 4:33
  • @ivicaa thanks for the input. I'm not sure I understand how exactly I need to specify the nonce. I know the number of transactions on that account, so far it's 21. Where to specify the nonce and increment it starting from 21? – Ruham Mar 31 '18 at 5:57
  • If I remember it correctly, you’ll have to use as nonce 21+0, 21+1, ... – ivicaa Mar 31 '18 at 6:09
  • Yeah, but where to add it in terms of code? – Ruham Mar 31 '18 at 20:31
10

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()
        wallet.engine._providers.unshift(nonceTracker)
        nonceTracker.setEngine(wallet.engine)
        return wallet
      }
    }
  }
}
  • 2
    Example for last paragraph: const NonceSubprovider = require("web3-provider-engine/subproviders/nonce-tracker"); const createInfuraProvider = (secret, infuraUrl) => { const provider = new HDWalletProvider(secret, infuraUrl); provider.engine.addProvider(new NonceSubprovider()); return provider; } – viz Jun 2 '18 at 16:51
  • @viz Are you sure that's right? The order you add subproviders to the engine matters, and I think the NonceSubprovider would never be called, because it's added last, after all the other providers have called end – James_pic Jun 2 '18 at 20:34
  • 1
    So I've wrote this after seeing the internal code, and I believe one should add the provider before this line. But all the engine's start function does is just starting the block tracker. And then the info of tracked block will be handled by handleAsync with listed subproviders. So I believe you can just call stop and then add the relevant subprovider and restart it. That's the proper way but I just did it without stopping and restarting it, since I didn't think that the network result would comeback- – viz Jun 3 '18 at 15:02
  • -only between these two lines of code. So I think this is safe and it has resolved the nonce issue properly. And after quick reviewing the code, I belive end function you mentioned has nothing to do with how engine process the async request with all those subproviders. Let me know if I'm wrong ;) – viz Jun 3 '18 at 15:05
  • 1
    I've added some (fairly hacky) code that works around the issue. – James_pic Jun 25 '18 at 13:10
2

Not sure if this is still relevant, but I am going to leave this here for future generations.

I've been struggling to deploy with truffle + Infura on mainnet a lot lately. One and the same script seems to work on testnet (ropsten, rinkeby) and not work on mainnet. It fails especially miserably when performing transactions outside of the standard deployer.deploy(contract, params) (Much like what you do with instance.createListing()).

I've narrowed this down partially to web3.js (transactions were hanging or not being sent) and partially to truffles embedded setCompleted transaction being called all the time (causing nonce problems with Infura).

Firstly I tried using ethers.js in the migrations scripts and it improved a lot the things especially with the hanging transactions. However, I lost all the verboseness and the setCompleted stuff still caused a lot of trouble. I've moved to my own deployment scripts since then. I basically write a script like this and run it using node:

const etherlime = require('etherlime');

const ProductionFactory = require('./testContracts/ProductionFactory');
const ProductionFactoryProxy = require('./testContracts/ProductionFactoryProxy');
const IProductionFactory = require('./testContracts/IProductionFactory');

const defaultConfigs = {
    gasPrice: 20000000000, // Choose your configs wisely
    gasLimit: 4700000
}

const deployer = new etherlime.InfuraPrivateKeyDeployer('Your Private Key Here', 'ropsten', 'Your Infura Key', defaultConfigs);

const runUpgradeableFactory = async () => {
    const productionFactoryWrapper = await deployer.deploy(ProductionFactory);
    const proxyWrapper = await deployer.deploy(ProductionFactoryProxy, productionFactoryWrapper.contractAddress);
    const upgradeableFactoryWrapper = deployer.wrapDeployedContract(IProductionFactory, proxyWrapper.contractAddress);

    const initTransaction = await upgradeableFactoryWrapper.contract.init(randomAddress, defaultConfigs);
    const result = await upgradeableFactoryWrapper.verboseWaitForTransaction(initTransaction.hash, 'Init Factory');

}

runUpgradeableFactory();

P.S. I've since developed the handy tool etherlime to help me with verboseness.

  • 2
    This looks great, i'm going crazy trying to deploy on infura and this looks like what i need, gonna give this a try, already read your post on Medium, thanks! – Julien Jul 1 '18 at 1:35
  • i'm getting TypeError: Cannot read property 'actions' of undefined after the contract is deployed (using deployer.deploy(contract)), how can i solve that? and while i'm at it, how can i get the address of the newly deployed contract? maybe contractWrapper.contract.address? – Julien Jul 1 '18 at 3:04
  • 1
    @Julien the newly deployed contract address can be found in contractWrapper.contractAddress. As for the actions issue are you deploying using etherlime deploy? If the answer to both is yes - I suggest we take this off here to help you out more. I am generally available at this gitter – George Spasov Jul 1 '18 at 17:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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