Infura – How to Deploy a Smart Contract Using Infura and NodeJS

contract-deploymentinfurajavascriptnodejsweb3js-v1.x

I have an application written in NodeJS that each time it starts has to deploy a smart contract. Once the contract is deployed this application will interact with it making some transactions and listening for its events.

The part about making transactions, invoking smart contract functions and listening events works fine, the once thing I am not be able to do is deploy the smart contract with NodeJS.

To deploy it I have to use truffle via command line and then launch my NodeJS application passing it the smart contract address. And in this way all works fine.

I found a very ugly workaround doing it automatically by running the console commands directly from the NodeJS application (process exec, catch its output, parse string and find the contract address… and… yes, it works!).

Obviously I’ve tried to do it via Infura, but didn’t work. Why? I don’t know, but I can explain what I did. Since deploying a smart contract is not more or less than making a normal transaction, I prepared it, signed it and sending it in the same way used for the other normal transactions and contract function call I cited above. It seems to works! But each time I get two similar errors:

  • gas price * gas + value are not sufficient
  • if I raise the gas I get gas limit exceeded

I am almost sure that it is not a problem about the gas, but something else wrong in the process prepare > sign > send.

Please, note that I have no need to compile the smart contract at each run! ABI and bytecode are the same at each run, so migrate/deploy it without recompile is enough.

This is the code of the function in NodeJS that I use to deploy the smart contract which fails and returns gas errors:

const amountToSend = 0.010000;

const contract_PrintProofOfWork = new web3.eth.Contract(contractAbi, process.env.SMART_CONTRACT_ADDRESS, {
    from: web3.eth.defaultAccount,
    gasPrice: '10000000'
});

async function getBalance(address) {
    let destinationBalanceWei = await web3.eth.getBalance(address);
    let destinationBalance = web3.utils.fromWei(destinationBalanceWei, 'ether');
    return destinationBalance;
}

const getCurrentGasPrices = async () => {
    let response = await axios.get('https://ethgasstation.info/json/ethgasAPI.json');
    let prices = {
        low: response.data.safeLow / 10,
        medium: response.data.average / 10,
        high: response.data.fast / 10
    };
    log("\r\n");
    log('Current ETH Gas Prices (in GWEI):');
    log(`Low: ${prices.low} (transaction completes in < 30 minutes)`);
    log(`Standard: ${prices.medium} (transaction completes in < 5 minutes)`);
    log(`Fast: ${prices.high} (transaction completes in < 2 minutes)`);
    log("\r\n");
    return prices
};

// This function is used to deploy contract
const deploy = async () => {

    log(process.env.WALLET_ADDRESS);
    log(`Local PC wallet balance is currently ${await getBalance(web3.eth.defaultAccount)} ETH`.cyan);

    let deploy = contract_PrintProofOfWork.deploy({
        data: contractByteCode
        //arguments: []
    }).encodeABI();

    let nonce = await web3.eth.getTransactionCount(web3.eth.defaultAccount);
    log(`The outgoing transaction count for your wallet address is: ${nonce}`.yellow);

    // Fetch the current transaction gas prices from https://ethgasstation.info/
    let gasPrices = await getCurrentGasPrices();

    // Build a new transaction object and sign it locally
    let transactionObject = {
        "to": process.env.DESTINATION_WALLET_ADDRESS,
        "value": web3.utils.toHex(web3.utils.toWei(amountToSend.toString(), 'ether')),
        "gas": 8000000,
        "gasPrice": gasPrices.high * 1000000000, // converts the gwei price to wei
        "nonce": nonce,
        "chainId": 3 // EIP 155 chainId - mainnet: 1, ropsten: 3, rinkeby: 4 (https://ethereum.stackexchange.com/questions/17051/how-to-select-a-network-id-or-is-there-a-list-of-network-ids/17101#17101)
    };

    web3.eth.accounts.signTransaction(transactionObject, process.env.WALLET_PRIVATE_KEY, function (error, signedTx) {
        if (error) {
            log(`${error}`.red);
        } else {
            web3.eth.sendSignedTransaction(signedTx.rawTransaction)
                .on('confirmation', function (number) {//dostuff
                    log(`Success: ${number}`.green);

                });

            // Note this address. It will be used to create contract instance from Angular 5 application.
            //console.log("contract deployed to", result.options.address);
        }
    }).catch(function(err){
        log(`${err}`.red);
    });
};

// Call deploy function.
deploy();

Errors:

(node:23315) UnhandledPromiseRejectionWarning: Error: Node error:
{"code":-32000,"message":"insufficient funds for gas * price + value"}
at Function.validate (/Users/sahelanthropus/IdeaProjects/ThesisPrototype/prototype-local-simple/node_modules/web3-providers/dist/web3-providers.cjs.js:349:18)
at /Users/sahelanthropus/IdeaProjects/ThesisPrototype/prototype-local-simple/node_modules/web3-providers/dist/web3-providers.cjs.js:692:57
at processTicksAndRejections (internal/process/next_tick.js:81:5)

Or if I raise the gas to be used:

(node:23329) UnhandledPromiseRejectionWarning: Error: Node error:
{"code":-32000,"message":"exceeds block gas limit"}
at Function.validate (/Users/sahelanthropus/IdeaProjects/ThesisPrototype/prototype-local-simple/node_modules/web3-providers/dist/web3-providers.cjs.js:349:18)
at /Users/sahelanthropus/IdeaProjects/ThesisPrototype/prototype-local-simple/node_modules/web3-providers/dist/web3-providers.cjs.js:692:57
at processTicksAndRejections (internal/process/next_tick.js:81:5)


Can anyone simply show me how I can deploy a smart contract using web3js e Infura even without referring to the code and the problem I have exposed in this question?


Update:

enter image description here

Best Answer

This is what my working dir looks like:

PS C:\Projects\Eth_Dev\simple> ls


    Directory: C:\Projects\Eth_Dev\simple


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       27/02/2019     21:25                contracts
d-----       27/02/2019     21:22                node_modules
d-----       27/02/2019     21:25                test
-a----       27/02/2019     21:25            691 compile.js
-a----       27/02/2019     21:29            907 deploy.js
-a----       27/02/2019     21:22         152654 package-lock.json
-a----       27/02/2019     21:28            339 package.json

The contents of package.json:

{
  "name": "myContract",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "mocha"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "ganache-cli": "^6.2.5",
    "mocha": "^5.2.0",
    "solc": "^0.4.25",
    "truffle-hdwallet-provider": "0.0.3",
    "web3": "^1.0.0-beta.35"
  }
}

The only other command I had to run in addition to npm install was npm install --global --production windows-build-tools

The contents of contracts\myContract.sol

pragma solidity ^0.4.17;

contract MyContract {
    string public message;

    function MyContract(string initialMessage) public {
        message = initialMessage;
    }

    function setMessage(string newMessage) public {
        message = newMessage;
    }
}

This is a deploy.js script that seems to work. Running 'node deploy.js' does the deploy to the Rinkeby network

const HDWalletProvider = require('truffle-hdwallet-provider');
const Web3 = require('web3');
const {interface, bytecode} = require('./compile');

const provider = new HDWalletProvider(
  'your twelve word mnemonic',
  'https://rinkeby.infura.io/v3/YOUR_API_KEY'
);

const web3 = new Web3(provider);

const deploy = async () => {
  const accounts = await web3.eth.getAccounts();
  console.log('Attempting to deploy from account', accounts[0]);


  const result = await new web3.eth.Contract(JSON.parse(interface))
    .deploy({data:bytecode, arguments:['This is deployed!']})
    .send({gas:'1000000', from: accounts[0]});

  console.log('Contract deployed to ', result.options.address);
};
deploy();

There is a reference to a compile file. The compile.js looks like this:

const path = require('path');  //builds a path from the current file.
const fs = require('fs');

//Need the solidity compiler
const solc = require('solc');

//__dirname will get the inbox dir path.
//look in the contracts folder and get a path to the Inbox.sol file.
const myContractPath = path.resolve(__dirname,'contracts','myContract.sol');

//Read in the contents of the file. The raw source code.
const source = fs.readFileSync(myContractPath,'utf8');

//console.log(solc.compile(source,1));
//This will export the compiled file. Make it available
//At the moment only interested in the Inbox contract.
module.exports = solc.compile(source,1).contracts[':MyContract'];

And the output:

PS C:\Projects\Eth_Dev\simple> node .\deploy.js
Attempting to deploy from account 0x15ec...B
Contract deployed to  0x5...2DA9269a
Related Topic