Deploy contract from NodeJS using web3

  • I'm trying to create and deploy a smart contract through NodeJS using web3, but it is not working as I get a "web3.eth.contract.new is not a function". Here is my code:

    var web3 = require('ethereum.js');
    
    var solc = require('solc');
    
    
    web3.setProvider(new web3.providers.HttpProvider('http://localhost:8101/'));
    
    var input = 'contract Hello{ string h = "hello"; function g() constant returns(string){ return h; } }';
    var output = solc.compile(input, 1);
    
    
    for (var contractName in output.contracts) {
    
    
        var bc = output.contracts[contractName].bytecode;
        var abi =  output.contracts[contractName].interface;
    
    
        var contact = web3.eth.contract.new(abi,{from: web3.eth.accounts[0], data: bc});
    
    
        if (typeof contact.address !== 'undefined') {
             console.log('Contract mined! address: ' + contact.address + ' transactionHash: ' + contact.transactionHash);
        }
    
    }
    

    Any idea on how to solve this issue? Thanks in advance.

    I guess you are spelling your contract instance as "contact" on purpose? Very confusing!

  • Here is my deploy script that also handles waiting until the transaction is included in a block:

    // Copyright 2017 https://tokenmarket.net - MIT licensed
    //
    // Run with Node 7.x as:
    //
    // node --harmony-async-await  deploy.js
    //
    
    let fs = require("fs");
    let Web3 = require('web3'); // https://www.npmjs.com/package/web3
    
    // Create a web3 connection to a running geth node over JSON-RPC running at
    // http://localhost:8545
    // For geth VPS server + SSH tunneling see
    // https://gist.github.com/miohtama/ce612b35415e74268ff243af645048f4
    let web3 = new Web3();
    web3.setProvider(new web3.providers.HttpProvider('http://localhost:8545'));
    
    // Read the compiled contract code
    // Compile with
    // solc SampleContract.sol --combined-json abi,asm,ast,bin,bin-runtime,clone-bin,devdoc,interface,opcodes,srcmap,srcmap-runtime,userdoc > contracts.json
    let source = fs.readFileSync("contracts.json");
    let contracts = JSON.parse(source)["contracts"];
    
    // ABI description as JSON structure
    let abi = JSON.parse(contracts.SampleContract.abi);
    
    // Smart contract EVM bytecode as hex
    let code = '0x' + contracts.SampleContract.bin;
    
    // Create Contract proxy class
    let SampleContract = web3.eth.contract(abi);
    
    // Unlock the coinbase account to make transactions out of it
    console.log("Unlocking coinbase account");
    var password = "";
    try {
      web3.personal.unlockAccount(web3.eth.coinbase, password);
    } catch(e) {
      console.log(e);
      return;
    }
    
    
    console.log("Deploying the contract");
    let contract = SampleContract.new({from: web3.eth.coinbase, gas: 1000000, data: code});
    
    // Transaction has entered to geth memory pool
    console.log("Your contract is being deployed in transaction at http://testnet.etherscan.io/tx/" + contract.transactionHash);
    
    // http://stackoverflow.com/questions/951021/what-is-the-javascript-version-of-sleep
    function sleep(ms) {
      return new Promise(resolve => setTimeout(resolve, ms));
    }
    
    // We need to wait until any miner has included the transaction
    // in a block to get the address of the contract
    async function waitBlock() {
      while (true) {
        let receipt = web3.eth.getTransactionReceipt(contract.transactionHash);
        if (receipt && receipt.contractAddress) {
          console.log("Your contract has been deployed at http://testnet.etherscan.io/address/" + receipt.contractAddress);
          console.log("Note that it might take 30 - 90 sceonds for the block to propagate befor it's visible in etherscan.io");
          break;
        }
        console.log("Waiting a mined block to include your contract... currently in block " + web3.eth.blockNumber);
        await sleep(4000);
      }
    }
    
    waitBlock();
    

    Thank you so much for this! Can you please explain how do you get the file `contracts.json` from the contract source code?

    @jeff Compiling with Solidity `solc`. See the full tutorial link in the blog post.

    This gives me TypeError: web3.eth.contract is not a function

    @MikkoOhtamaa In the blog tutorial you create a private key and its corresponding address. I don't see you using it in deploying the contract. How to use that external private key based address to deploy the contract?

  • Before starting: Make sure eth and personal APIs are enabled on your node. For geth you do that with these switches: --rpcapi eth,personal

    Here is how I did it with web3.js version 1.0:

    const Web3 = require('web3');
    const solc = require('solc');
    
    // For localhost
    const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
    
    let source = 'contract Hello{ string h = "hello"; function g() constant returns(string){ return h; } }';
    
    let helloCompiled = solc.compile(source, 1).contracts[':Hello'];
    

    You may need to unlock the address which is going to deploy the contract (Make sure there is enough funds on this address):

    web3.eth.personal.unlockAccount(<address>, <password>).
        then(() => { console.log('Account unlocked.'); }).
        catch(console.error);
    
    let Hello = new web3.eth.Contract(JSON.parse(helloCompiled.interface), null, { 
        data: '0x' + helloCompiled.bytecode 
    });
    

    OPTIONAL: Use average gas price to deploy (If you use too low gas price, the transaction may get stuck):

    web3.eth.getGasPrice().
       then((averageGasPrice) => {
           console.log("Average gas price: " + averageGasPrice);
           gasPrice = averageGasPrice;
       }).
       catch(console.error);
    

    Estimate gas to deploy:

    Hello.deploy().estimateGas().
        then((estimatedGas) => {
            console.log("Estimated gas: " + estimatedGas);
            gas = estimatedGas;
        }).
        catch(console.error);
    

    And finally deploy:

    Hello.deploy().send({
        from: <address>,
        gasPrice: gasPrice, 
        gas: gas
    }).then((instance) => { 
        console.log("Contract mined at " + instance.options.address);
        helloInstance = instance; 
    });
    
  • See the API documentation for contract.

    You're passing the abi variable as a parameter to .new(), whereas you should be passing it to the contract() method itself. In your code, the contract method isn't being passed anything, which is why it's complaining.

    var MyContract = web3.eth.contract(abiArray);
    
    var contractInstance = MyContract.new([contructorParam1] [, contructorParam2], {data: '0x12345...', from: myAccount, gas: 1000000});
    

    Related thread: What are the arguments to `new` from a contract object?

    Hey, thanks for your reply. However: var MyContract = web3.eth.contract(abi); var contract = MyContract.new({from: web3.eth.accounts[0], data: bc}); returns the same error: MyContract.new is not a function.

    Are you following a particular tutorial for this?

    Several, not one in particular as none seemed to work. However, thanks to another user I got rid of that error replacing the var web3 = require('ethereum.js'); with var Web3 = require('web3'); var web3 = new Web3(); A new problem arised: address of the contract stays undefined, even though a transaction is mined.

    You should use callback function to check the contract address. Because your next lines of code will not wait for the completion of deployment process.And that's why you are getting undefined every time.

  • For web3js > 1.0 you can use : myContract.deploy(options)

    const contract = new web3.eth.Contract(abi);
        contract.deploy({
          data: code,
        })
        .send({
          from: "0x1234567890123456789012345678901234567891",    gas: 1500000,
            gasPrice: '30000000000000'
        }).then(function(newContractInstance){
            console.log(newContractInstance.options.address) // instance with the new contract address
        });
    

    Where code is the bytecode and the used address should be unlocked

  • Yes, you must use web3.eth.contract(abi) to create an object, and then call new() on it passing the proper data to the transaction. The way you're doing, you're pretending the web3.eth.contract function itself has a new() property, while its the result of that function call which actually is an object with a new() function.

    Also, contact is a typo or is the proper name of your contRact? Ill suppose is a typo:

    var contract = web3.eth.contract(abi);
    
    var contractInstance = contract.new(parameters , {data : bc , from : anAddress... etc});
    

    That would work, would 'bc' var being the result of

    web3.eth.compilers.solidity(input);
    

    However, I never used the solc package, and I don't know if bc var is actually the exact thing you must pass on as data to the new() call. But I'm pretty sure this will help you, as your main problem is trying to call new() directly on web3.eth.contract function and not in its instance.

    EDIT:

    web3 methods can fail if you're doing

    var web3 = require('web3');
    

    or similar stuff. The proper way to instantiate web3 is:

    var Web3 = require('web3');
    var web3 = new Web3();  
    

    Then, the new() function should work properly.

    Hey, thanks for your reply. However: var MyContract = web3.eth.contract(abi); var contract = MyContract.new({from: web3.eth.accounts[0], data: bc}); returns the same error: MyContract.new is not a function.

  • Be aware that the web3 API has changed since version 1~beta

    You can use a library that encapsulates the complexity of the whole process: https://github.com/ledfusion/eth-tx

    $ npm install eth-tx
    

    On NodeJS:

    const ethTx = require("eth-tx");
    

    Compile:

    const { compileTo } = ethTx;
    const source = __dirname + "/contract-main.sol";
    const destination = __dirname + "../build/data.js";
    
    compileTo(source, destination, {})
        .then(() => { /* ... */})
        .catch(err => console.log("Error", err));
    

    Connect:

    ethTx.connect("http://localhost:8545").catch(console.log);
    

    Deploy the contract:

    const { wrapContract } = ethTx;
    
    const abi = [...];   // from "../build/data.js
    const byteCode = "0x12345...";
    
    const MyContract = wrapContract(abi, byteCode);
    
    MyContract.new("parameter-1", "parameter-2")
        .then(myContractInstance => {
            console.log("Deployed on", myContractInstance.$address);
    
            // ...
        })
        .catch(err => console.log("Error", err));
    

    Send your transactions:

    const options = {};
    myContractInstance.setHash("param-1", "param-2").send(options)
        .then(transaction => {
            console.log("Transaction", transaction);
    
            // ...
        })
        .catch(err => console.log("Error", err));
    

    Hope it helps!

  • I have written a script that compiles and deploys a contract on a ganache blockchain: https://github.com/Thykof/movaicode3/tree/master/src

    With these node modules versions:

      "dependencies": {
        "ganache-cli": "^6.12.2",
        "solc": "^0.8.1",
        "web3": "^1.3.4"
      }
    
    let account = '0x09B61...'
    // compile:
    var input = {
      language: 'Solidity',
      sources: {
        'test.sol': {
          content: 'contract C { function f() public { } }'
        }
      },
      settings: {
        outputSelection: {
          '*': {
            '*': ['*']
          }
        }
      }
    };
    var contractData = JSON.parse(solc.compile(JSON.stringify(input)));
    
    // prepare the transaction:
    var contractName = Object.keys(contractData.contracts)[0].split('.')[0]
    var contractCode = contractData.contracts[contractName + '.sol'][contractName]['evm']['bytecode']['object']
    var rawTx = {
      from: account,
      data: contractCode,
      gas: 2000000
    }
    
    // sign and send the transaction
    let contractAddress
    web3.eth.accounts.signTransaction(rawTx, '87e9b3...')
    .then((signedTx) => {
      // raw transaction string may be available in .raw or
      // .rawTransaction depending on which signTransaction
      // function was called
      const sentTx = web3.eth.sendSignedTransaction(signedTx.raw || signedTx.rawTransaction);
      sentTx.on("error", err => {
        // do something on transaction error
        console.log(err)
      });
      return sentTx.on("receipt", receipt => {
        // do something when receipt comes back
        console.log(receipt)
        contractAddress = receipt.contractAddress
      });
    }).catch((err) => {
      // do something when promise fails
      console.log(err)
    });
    
    // interact with the contract
    instance = new web3.eth.Contract(
        contractData.contracts[contractName + '.sol'][contractName].abi,
        contractAddress
    )
    instance.methods.f().send({
        from: account
    })
    .on('receipt', receipt => {
        console.log(receipt);
    })
    .on('error', function(error, receipt) {
      console.log(error, receipt);
    })
    

License under CC-BY-SA with attribution


Content dated before 7/24/2021 11:53 AM

Tags used