Search code examples
ethereumweb3js

Cannot call function within deployed contract


I'm trying to get a deployed HelloWorld contract to run within a node app. I want to run the call() function to check it like this:

const deployed = helloWorldContract.new({
  from: acct1,
  data: compiled.contracts[':HelloWorld'].bytecode,
  gas: 151972,
  gasPrice: 5
}, (error, contract) => {
    if(!error){
      console.log(contract.displayMessage.call());
    } else {
      console.log(error);
    }
});

Here is the contract for reference:

contract HelloWorld {
  function displayMessage() public constant returns (string){
    return "hello from smart contract - {name}";
  }
}

When I try console.log(contract.displayMessage.call()) in the callback, that returns: TypeError: Cannot read property 'call' of undefined, but, when I log console.log(contract.displayMessage) it returns this:

{ [Function: bound ]
   request: [Function: bound ],
   call: [Function: bound ],
   sendTransaction: [Function: bound ],
   estimateGas: [Function: bound ],
   getData: [Function: bound ],
   '': [Circular] }

What am I doing wrong here? How can I run the function call within the deployed contract?


Solution

  • I think your issue may be caused by the .new constructor. I personally don't recommend using it because it is weird. Instead, you should deploy the bytecode as a standard transaction.

    Anyways, if you look at the source code of .new you'll see that the callback is actually called twice. It is completely non standard and as far as I know, undocumented.

    The first time the callback is called after the transaction is sent, and the contract object will have transactionHash set.

    The second time the callback is called, the contract object should have the address property set. This is what you want, because without the address property, you cannot invoke the contract methods.

    In short, try this

    const deployed = helloWorldContract.new({
      from: acct1,
      data: compiled.contracts[':HelloWorld'].bytecode,
      gas: 151972,
      gasPrice: 5
    }, (error, contract) => {
        if (error){
          console.error(error);
        } else {
          console.log(contract);
          if (contract.address) {
            console.log(contract.displayMessage());
          }
        }
    });
    

    To deploy a contract without using .new method, you first need to generate the contract bytecode and ABI. You can get it from using solc or online solidity compiler or whatever other way.

    Then to deploy the contract you use web3.eth.sendTransaction with data parameter set to the bytecode, and an empty to address. sendTransaction will return you a transactionHash, which you need to wait to be mined and confirmed. The easiest way to do this is through polling - a good starting point can be this method I wrote - https://gist.github.com/gaiazov/17c9fc7fdedf297e82386b74b34c61cd

    If your contract takes constructor arguments, they are appended to the bytecode, e.g. data: bytecode + encodedConstructorArguments.