When your transaction contains data, it is most likely addressed to a
contract address. That doesn’t mean you cannot send a data payload to
an EOA—that is completely valid in the Ethereum protocol. However, in
that case, the interpretation of the data is up to the wallet you use
to access the EOA. It is ignored by the Ethereum protocol. Most
wallets also ignore any data received in a transaction to an EOA they
control. In the future, it is possible that standards may emerge that
allow wallets to interpret data the way contracts do, thereby allowing
transactions to invoke functions running inside user wallets. The
critical difference is that any interpretation of the data payload by
an EOA is not subject to Ethereum’s consensus rules, unlike a contract
execution.
For now, let’s assume your transaction is delivering data to a
contract address. In that case, the data will be interpreted by the
EVM as a contract invocation. Most contracts use this data more
specifically as a function invocation, calling the named function and
passing any encoded arguments to the function.
The data payload sent to an ABI-compatible contract (which you can
assume all contracts are) is a hex-serialized encoding of:
A function selector
The first 4 bytes of the Keccak-256 hash of the function’s prototype. This allows the contract to unambiguously identify which
function you wish to invoke.
The function arguments
The function’s arguments, encoded according to the rules for the various elementary types defined in the ABI specification.
In solidity_faucet_example, we defined a function for withdrawals:
function withdraw(uint withdraw_amount) public {
The prototype of a function is defined as the string containing the
name of the function, followed by the data types of each of its
arguments, enclosed in parentheses and separated by commas. The
function name here is withdraw and it takes a single argument that is
a uint (which is an alias for uint256), so the prototype of withdraw
would be:
withdraw(uint256)
Let’s calculate the Keccak-256 hash of this string:
> web3.sha3("withdraw(uint256)");
'0x2e1a7d4d13322e7b96f9a57413e1525c250fb7a9021cf91d1540d5b69f16a49f'
The first 4 bytes of the hash are 0x2e1a7d4d. That’s our "function
selector" value, which will tell the contract which function we want
to call.
Next, let’s calculate a value to pass as the argument withdraw_amount.
We want to withdraw 0.01 ether. Let’s encode that to a hex-serialized
big-endian unsigned 256-bit integer, denominated in wei:
> withdraw_amount = web3.toWei(0.01, "ether");
'10000000000000000'
> withdraw_amount_hex = web3.toHex(withdraw_amount);
'0x2386f26fc10000'
Now, we add the function selector to the amount (padded to 32 bytes):
2e1a7d4d000000000000000000000000000000000000000000000000002386f26fc10000
That’s the data payload for our transaction, invoking the withdraw
function and requesting 0.01 ether as the withdraw_amount.