1

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

5
  • It seems like you want to deploy on Ropsten. Have you checked if your account with the private key process.env.WALLET_PRIVATE_KEY has enough Ropsten testnet ether? Commented Feb 22, 2019 at 10:07
  • Yes, I am able to deploy contracts using truffle with same account in Ropsten. I have explained that I found a workaround using truffle to deploy and extract the contract address from the console output and passing it to NodeJS.
    – kitsune
    Commented Feb 22, 2019 at 10:59
  • With truffle, did you also deploy via infura? With the same account? Commented Feb 22, 2019 at 12:16
  • The second error you got is irrelevant to the problem you encounter. The block gas limit on Ropsten is 8000000, so an error will occur when you increase your tx gas limit over 8M Commented Feb 22, 2019 at 12:19
  • Refer to this comment: yes, I can!
    – kitsune
    Commented Feb 25, 2019 at 14:19

1 Answer 1

3
+50

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
8
  • Thank you very much, as soon as I have time in the next few days I will test your solution, if it works I will report it as an answer to my question. Please, be patient.
    – kitsune
    Commented Feb 26, 2019 at 15:10
  • I get some errors, can you specify the versions of solc, web3js and truffle-hdwallet-provider? Mine are: "dependencies": { "solc": "^0.5.4", "truffle-hdwallet-provider": "^1.0.4", "web3": "^1.0.0-beta.46" } and I am not be able to run your scripts.
    – kitsune
    Commented Feb 27, 2019 at 11:37
  • I am not able to run solc.compile(source,1) but using solc.compile(source,1) works, but gives me error about > A valid JSON document must be either an array or an object value. Please recheck your answer, this solution is not working.
    – kitsune
    Commented Feb 27, 2019 at 13:01
  • Thank you very much, I really appreciate your efforts: but it still not works, I updated the question with two screenshots. I use the same menmonic and the same address in both cases, but I am able to deploy contracts only using Truffle (same configurations).
    – kitsune
    Commented Mar 1, 2019 at 15:49
  • Are you able to post your test project to github/bitbucket ? Commented Mar 2, 2019 at 8:35

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.