This article will show you how to send a state-changing transaction to the BSC / Eth blockchain using Node JS, Web 3 and JSON RPC.
A Smart Contract
For example, I have the following simple smart contract:
// SPDX-License-Identifier: RANDOM_TEXT
pragma solidity >=0.8.1;
contract Abitest{
uint256 public myNum;
constructor(){
myNum = 100;
}
function changeNumber(uint256 updateNum) public returns(uint256){
myNum = updateNum;
return myNum;
}
}
You can view this contract on the BSC testnet here:
https://bscscan.com/address/0x03B47F4DAcE6A815E49F9E44e39AF57780BcBc5D
This contract creates a public member – myNum – and assigns it the value of 100 upon initial contract creation.
We can update myNum by calling the changeNumber function.
The changeNumber function takes one parameter – a uint256 value updateNum.
Function Selectors and Hashed Parameters
To submit transactions to the Eth blockchain programatically, we first need to understand how to parse our data so it fits the right specifications.
All transactions in Eth are distilled into hexadecimal values. When creating transactions in our code, we need to convert our data into hex.
The changeNumber function itself, and the function parameter updateNum, need to be converted into hex before we can pass the information through to Web3.
How do we do this?
First, we convert the function into a hashed hex by using the keccack 256 hashing algorithm. We can use the tool here for convenience:
https://emn178.github.io/online-tools/keccak_256.html
From our smart contract, the function we wish to convert is:
changeNumber(uint256 updateNum)
To prepare for the conversion, we must remove all spaces and all parameter names. This gives us:
changeNumber(uint256)
We feed this string into the keccack256 hashing algorithm, which gives us:
07391dd6d93ee8ec80249248abc3ce0cef60e77c50c39dd324faad02b5e5ccbd
With Ethereum, and other Eth-based blockchains such as Binance Smart Chain, we only need the first 4 bytes of the hashed string for our function selector. The first 4 bytes of the following string are comprised of:
07391dd6
Finally, to make this a valid Keccack hex value, we need to prepend 0x. Our final function selector is:
0x07391dd6
Now that we have our function selector ready, we need to generate a hex for our function parameter. Recall that our changeNumber function takes one parameter, a uint256.
Just like with our function selector, when passing parameters into JSON RPC, we need to convert them into hex. In the case of integers, we need to generate a 32byte hex value, with the significant figures at the END of the string (in computer science terms, this is called ‘Endian’).
For example, if I wish to pass the number 2, the 64 byte hex is:
0000000000000000000000000000000000000000000000000000000000000002
If I wish to pass the number 123456, the 64 byte hex is:
000000000000000000000000000000000000000000000000000000000001e240
In the case of these parameters, the hex conversion does not require Keccack256 hashing. We simply need to perform a standard decimal to hex conversion. This tool is useful:
https://www.rapidtables.com/convert/number/decimal-to-hex.html
Remember, the hex string needs to be padded with leading 0s until it fills 32 bytes.
Also notice that the parameter hex values do not require a leading 0x!
Putting it Together
To prepare our request for JSON RPC, we simply need to concatenate the function selector and our parameter, like this:
0x07391dd6000000000000000000000000000000000000000000000000000000000001e240
The above example shows a function with only one parameter. If you’re doing this for a function with multiple parameters, all you have to do is append the remaining parameter hex strings to your master string.
That’s it! Now that you have your hex parameters ready, we can now proceed with our JS code to make it all work!
Check out part 2 of this article, where we look into how dynamic data types can be converted to ABI for JSON RPC consumption!