8

I deployed my first contract by using browser-solidity to make bytecode and web3.eth.sendRawTransaction to deploy. The following is the transaction and the deployed contract code.

https://ropsten.etherscan.io/tx/0x74426948905cd6e70e8b9d64a660b3c179b7c8a224ca5cd0234842768eb501db

contract code

pragma solidity ^0.4.0;
contract Addition {
  int num = 0;
  function add(int a){
    num += a;
  }
  function get() returns(int){
    return num;
  }
}

Then, I want to call the two methods by web3.eth.call. However, I can't find a way how to generate the "data" param. Could you please give me any advices?

const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider("http://xxx:xxx"));

const result = web3.eth.call({ to: "0x692a70d2e424a56d2c6c27aa97d1a86395877b3a", data: "???" });

https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethcall

I think gas is needed to call but this example does not mention about it. On JavaScript VM on browser-solidity, the prices are showed when I execute the method like below.

Result: "0x"
Transaction cost: 41713 gas. 
Execution cost: 20249 gas.

Update 1

Seems this page describes about it.

I then wanted to call the method "double" with the number "5". The Contract ABI documentation says that you have to take the first 4 bytes of the Keccak hash. The method signature is

double(int)

Which gives the hash with web3.sha3("double(int)":

6740d36c7d42fba12b8eb3b047193c9761224c267d7a2e96dc50949860652317

The first four bytes, prefixed with "0x" are:

0x6740d36c

The documentation then tells to take the parameter, encode it in hex and pad it left to 32 bytes. Which would be the following, using the number "5":

0x0000000000000000000000000000000000000000000000000000000000000005

In total, the encoded string should look like this:

0x6740d36c0000000000000000000000000000000000000000000000000000000000000005

How to call a contract method using the eth_call JSON-RPC API


Update 2

It does not work.

const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider("http://xxx:xxx"));

var result = web3.eth.call({ to: "0x692a70d2e424a56d2c6c27aa97d1a86395877b3a", data: "0x6d4ce63c" // var hash = web3.sha3("get()"); get first 4 byte });

console.log(result); // 0x

var result = web3.eth.call({ to: "0x692a70d2e424a56d2c6c27aa97d1a86395877b3a", data: "0x250fc6d90000000000000000000000000000000000000000000000000000000000000001" // var hash = web3.sha3("add(int)"); get first 4 byte and param (1) });

console.log(result); // 0x

var result = web3.eth.call({ to: "0x692a70d2e424a56d2c6c27aa97d1a86395877b3a", data: "0x6d4ce63c" // var hash = web3.sha3("get()"); get first 4 byte });

console.log(result); // 0x => I expected 1


Updata 3

I changed from add(int) to add(int256) but still does not work.

var result = web3.eth.call({
    to: "0x692a70d2e424a56d2c6c27aa97d1a86395877b3a",
    data: "0x87db03b70000000000000000000000000000000000000000000000000000000000000001" // var hash = web3.sha3("add(int256)"); get first 4 byte and param (1)
});

However browser-solidity debugger shows the same data. So I think this data is correct.


Update 4

Seems I have to send transaction before call. I can imagine. I will try it.

Call. It is a read-only operation and will not consume any Ether.

What is the difference between a transaction and a call?


Update 5

Although I think found the answer but still it does not work. I'll take a break..

(1) sendRawTransaction

const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider("http://xxxx:xxxx"));

const Tx = require('ethereumjs-tx'); const privateKey = new Buffer('xxxx', 'hex')

const rawTx = { nonce: '0x03', gasPrice: '0x53d9', gasLimit: '0x53d9', to: '0x692a70d2e424a56d2c6c27aa97d1a86395877b3a', data: '0x87db03b70000000000000000000000000000000000000000000000000000000000000001' // add(int256) with param 1 }

const tx = new Tx(rawTx); tx.sign(privateKey);

const serializedTx = tx.serialize();

console.log('0x' + serializedTx.toString('hex')); // 0xf885038253d98253d994692a70d2e424a56d2c6c27aa97d1a86395877b3a80a487db03b700000000000000000000000000000000000000000000000000000000000000011ba0ec6de7c9c1f31dc95014156479925b2ebd933b715f2bb4450f5d74992d7b3423a016f6a7c6e1885412400a622f82ffa033936c06755896096c7638936c065571c1

web3.eth.sendRawTransaction('0x' + serializedTx.toString('hex'), function(err, hash) { if (!err) { console.log(hash); // 0x26db79c7c3c65992f17f4f62d5d191705933e785dff54db6d27e6f3a92d7117e } else { console.log(err); } });

https://ropsten.etherscan.io/tx/0x26db79c7c3c65992f17f4f62d5d191705933e785dff54db6d27e6f3a92d7117e

(2) call get()

const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider("http://xxxx:xxxx"));

const result = web3.eth.call({ to: "0x692a70d2e424a56d2c6c27aa97d1a86395877b3a", data: "0x6d4ce63c" });

console.log(result); // 0x => I expected 1

YakovL
  • 123
  • 7
zono
  • 1,473
  • 5
  • 17
  • 30

2 Answers2

11

You don't need to do this all by yourself. You can instead use getData method.

const contract = new web3.eth.Contract(contractABI, contractAddress);
const callData = contract.functionName.getData(functionParameters);
const result = web3.eth.call({
    to: "0x692a70d2e424a56d2c6c27aa97d1a86395877b3a", 
    data: callData
});

web3.eth.call executes the call in VM of the node but this is not mined. This is kind of dry run. If you instead want to send a transaction to blockchain use web3.eth.sendTransaction.

Prashant Prabhakar Singh
  • 8,026
  • 5
  • 40
  • 79
  • 2
    Thank you. I was feeling that is annoying. (hash and get 4bytes and add param...) I'll try to use it. – zono Jul 17 '17 at 06:49
  • I will also check about web3.eth.sendTransaction – zono Jul 17 '17 at 06:57
  • Sure. It's really annoying. I was also stuck in doing all this unnecessary stuff until I discovered getData. – Prashant Prabhakar Singh Jul 17 '17 at 07:06
  • 1
    I believe it's const contract = new web3.eth.Contract(contractABI, contractAddress) these days (web3 1.6.1) – YakovL May 05 '22 at 12:52
  • by the way, the other parts seem to be different, too. I'm not sure if getData still exists; I've found that we can do const call = universeContract.methods[methodName](callParameters) (callParameters is the argument of call or an array containing that argument). However, I haven't found how to prepare a signed call yet and haven't called it actually – YakovL May 06 '22 at 11:04
  • 1
    You can have a look at another question here that has parts of getting data and sendng tx. – Prashant Prabhakar Singh May 07 '22 at 13:35
3

Even I had to go through the same process until i discovered, the below:

use the web3.js library which helps in doing the same. Just download web3.js using NPM (or anywhere else) and get the modules required for the task, by:

const_ = require('lodash');
const SolidityFunction = require('web3/lib/web3/function');

You can use the web3.js library along with your ABI to find the function definition, using the below code:

var ABI = JSON.parse(<your_ABI>);
var functionDef = new SolidityFunction('', _.find(ABI, { name: '<your_function_name>' }), '');

After this, you can call the method toPayload which will help you converting the values that you want to pass to your function into HEX Data.

var payloadData = functionDef.toPayload([<value_for_var_1>, <value_for_var_2>, <value_for_var_3>, <value_for_var_4>]).data;

payloadData can be used as Value for the DATA property. Example:

var rawTx = {
 to: <to_address>,
 data: payloadData,
 value: '0x0',
 from: <from_address>,
 nonce: nonce,// You need to Get this using web3.eth.getTransactionCount
 gasLimit: gasLimit, // Get this by web3.eth.estimateGas
 gasPrice: gasPrice // use, web3.eth.gasPrice
}

You can use this approach and forget about how many zeros to pad with. As the toPayload function handles everything. You have a raw transaction object, you can use ethereumjs-tx to sign your transaction and use web3.eth.sendRawTransaction to call your Contract Function.

Hope this helps.

farhankhwaja
  • 473
  • 2
  • 12