I've been struggling a bit with calling methods that change the state of the contract. Take, for example, the following contract:
contract C {
uint[] numbers;
function initNumbers() {
numbers.push(1);
numbers.push(2);
}
function stateChanger(uint a) {
numbers.push(a);
}
}
After deploying, the initNumbers() method has to be called/sent as a transaction:
c.initNumbers({from:eth.accounts[0],gas:400000});
due to the fact that it changes the state (executes write operations) on the block chain.
How is it possible to call the second method that has an argument but also changes the state (stores it in the contract)?
I've tried the following but got a BigNumber exception:
c.stateChanger({from:web3.eth.accounts[0],gas:400000}).call(3);
The stack trace was:
/usr/lib/node_modules/web3/node_modules/bignumber.js/bignumber.js:1209
throw error;
^
BigNumber Error: new BigNumber() not a number: [object Object]
at raise (/usr/lib/node_modules/web3/node_modules/bignumber.js/bignumber.js:1177:25)
at /usr/lib/node_modules/web3/node_modules/bignumber.js/bignumber.js:1165:33
at new BigNumber (/usr/lib/node_modules/web3/node_modules/bignumber.js/bignumber.js:193:67)
at new BigNumber (/usr/lib/node_modules/web3/node_modules/bignumber.js/bignumber.js:203:25)
at toBigNumber (/usr/lib/node_modules/web3/lib/utils/utils.js:367:12)
at Object.toTwosComplement (/usr/lib/node_modules/web3/lib/utils/utils.js:378:21)
at formatInputInt [as _inputFormatter] (/usr/lib/node_modules/web3/lib/solidity/formatters.js:40:38)
at SolidityType.encode (/usr/lib/node_modules/web3/lib/solidity/type.js:179:17)
at /usr/lib/node_modules/web3/lib/solidity/coder.js:86:29
at Array.map (native)
Best Answer
Here's your function slightly modified by adding
public
tonumbers
so we can peek at the data:Using the
stripCrLf
script in How to load Solidity source file into geth, I've flattened the source code using the following command:I'm running a dev network using the following command (I have my password in
passwordfile
):I paste the flattened code in the
geth
command line:And compile the code using the following command:
Here is the Application Binary Interface:
Deploy the contract on the blockchain:
Call your
initNumbers()
:Calling
stateChanger(...)
- Method 1Calling
stateChanger(...)
- Method 2Calling
stateChanger(...)
- Method 3And following is how to use the raw data
sendTransaction(...)
format.Find the function signature. Note that the
uint
parameter is actually auint256
(see the ABI format above):Here is 5 encoded in hex and padded to 32 bytes:
Join the data together, removing the
0x
from the hex(5) string:Here's how to
sendTransaction(...)
. Theto:
parameter is the address of the mined contract:Bonus 1 -
estimateGas(...)
Since we have worked out the data to send with
sendTransaction(...)
above.Here is the gas used by the
sendTransaction(...)
call above:You can use the same data format to estimate the gas using the
estimateGas(...)
call:Bonus 2 - The Transaction Data
Set
debug.verbosity(7)
and watch the transaction get removed from the transaction pool after it is executed:The different methods of calling (
c.stateChanger(3, {from:eth.accounts[0], gas: 400000})
andc.stateChanger.sendTransaction(4, {from: eth.accounts[0], gas: 400000})
) are converted to thesendTransaction(...)
format.