[Ethereum] Retrieve data from smart contract using sendTransaction

contract-developmentminingsoliditytransactionsweb3js

Lets take a very basic smart contract that has one function in which you send a number and it will return the square. It has another function that returns a fixed string.

contract MyContract {
  uint public version;
  address public previousPublishedVersion;

  function ping() returns (string param){
    param = "pong";
  }

  function squareNumber(uint num) returns (uint256 numSqr) {
    numSqr = (num * num);
  }
}

Now to deploy the contract I use

myContract.new({
  data: MyContract.compiled_code,
  gas: gasNeeded * ethConfig.gas_factor,
  from: ethConfig.primary_address
}, (err, myContract) => {
    if(!err) {
     if(!myContract.address) {
         console.log('contract trasnaction hash ' + myContract.transactionHash)
     } else {
        console.log('contract address ' + myContract.address)
        resolve({
         primary_address: ethConfig.primary_address,
         contract_address: myContract.address,
         gas_estimate: gasNeeded
        })
     }
  } else {
    reject(err)
  }
});

This fires two callbacks, one with the transaction hash and one with the address once its mined. However this method is not there for sendTransaction. I have read that using getTransactionReceipt or checking block numbers is a good way to check whether the transaction has been mined or not.

  myContract.ping.call(null, (err, data) => {
    if (err) {
      reject(err)
    } else {
      resolve({
        primary_address: ethConfig.primary_address,
        returnValue: data
      })
    }
  })

But for squaring the number I use

  myContract.squareNumber.estimateGas(10, (err, gasNeeded) => {
    if (err) reject(err)
    myContract.squareNumber.sendTransaction(
      10,
      {
        gas: gasNeeded * config.ethConfig.gas_factor,
        from: config.ethConfig.primary_address
      }, (err, data) => {
      if (err) reject(err)
      resolve({
        primary_address: config.ethConfig.primary_address,
        numSquare : data
      })
    })
  })

Now for a call function, I can get the data directly without any mining to be done, however the docs say that the value returned from sendTransaction() is the hash of it, and then we are to use getTransactionReceipt() to actually confirm when the block was mined.

My question is simply this, how exactly do I structure my web3js code, to get the value returned (10*10) from the solidity function squareNumber().

I have wrapped all APIs shown above in promises but just skipped that part as its long and not related to web3js in any way.

Best Answer

After adding an event like SquareComputed (see How to get values returned by non constant transaction functions?) and web3.js Contract Events example

var event = myContractInstance.MyEvent({valueA: 23} [, additionalFilterObject])

// watch for changes
event.watch(function(error, result){
  if (!error)
    console.log(result);
});

this would lead to code like:

var event = myContractInstance.SquareComputed({});

// watch for changes
event.watch(function(error, result){
  if (!error)
    resolve({
        primary_address: config.ethConfig.primary_address,
        numSquare : result  // need to access the properties of result to obtain the actual square 
    })
  event.stopWatching();
});

Do the above before myContractInstance.squareNumber.sendTransaction. The resolve({numSquare:...}) in the original code should be moved to inside the watch as above.

Other web3.js APIs such as web3.eth.filter can also be used (see How do I retrieve the Voted events from The DAO).

This questions asks "how exactly do I structure my web3js code, to get the value returned (10*10) from the solidity function squareNumber()", otherwise for an overview see What is the difference between a transaction and a call?.