Original Article: How To Decipher A Smart Contract Method Call | by Howard | Sep 18, 2017
In previous articles of this series, we explored how Solidity represents complex data structures in EVM storage. However, data is useless without interaction. Smart contracts act as intermediaries between data and the external world.
This article examines how Solidity and the EVM enable external programs to call contract methods, triggering state changes.
Understanding Contract Transactions
An example transaction that sets a state variable to 0x1
:
pragma solidity ^0.4.11;
contract C {
uint256 a;
function setA(uint256 _a) {
a = _a;
}
function getA() returns(uint256) {
return a;
}
}
Transaction Input Data:
0xee919d500000000000000000000000000000000000000000000000000000000000000001
Breakdown:
- Method Selector (4 bytes):
0xee919d5
(Keccak256 hash ofsetA(uint256)
) - Argument (32 bytes):
0x1
Application Binary Interface (ABI)
The ABI specifies how EVM languages interpret input data for interoperability:
- Encodes method calls with structured parameters
- First 4 bytes always represent the method selector
- Arguments follow in 32-byte chunks
Calling Getter Methods
For read-only methods like getA()
, use eth_call
RPC to simulate locally without gas costs:
>>> sha3("getA()")[0:8].hex() # Method selector
'd46300fd'
Assembly for External Method Calls
Compiled contracts process raw input data through assembly:
sub_0: assembly {
// Method selector matching
and(div(calldataload(0x0), 0x100000000000000000000000000000000000000000000000000000000), 0xffffffff)
0xee919d50
dup2
eq
tag_2
jumpi
// No match → revert
tag_1:
0x0
dup1
revert
// Execute setA
tag_2:
calldataload(0x4) // Load argument
sstore(0x0, @arg1) // Store to storage
jump(tag_3)
tag_3:
stop
}
Handling Multiple Methods
For contracts with multiple methods, the compiler generates sequential checks:
methodSelector = calldata[0:4]
if methodSelector == "0x9cdcf9b":
goto setB
elsif methodSelector == "0xee919d50":
goto setA
else:
revert
ABI Encoding Complex Calls
Dynamic Arrays
Uses head-tail encoding:
- Head: References to tail positions
- Tail: Actual array data with length prefixes
Example (3 dynamic arrays):
HEAD:
0x60 → Array1 position
0xe0 → Array2 position
0x160 → Array3 position
TAIL:
0x60: [length, element1, element2, element3]
0xe0: [length, element1, element2, element3]
0x160: [length, element1, element2, element3]
Strings/Bytes
Packed into 32-byte chunks with length prefixes:
0x60:
0x04 (length)
"aaaa" + zero-padding
Nested Arrays
Each nesting level adds indirection:
Outer array → Pointer → Inner array → Pointer → Elements
Gas Cost Optimization
ABI design reflects gas cost structures:
- Zero bytes cost 4 gas (vs 68 for non-zero)
- Method selectors truncated to 4 bytes to save gas
- Padding uses zeros for cost efficiency
Conclusion
Smart contracts process raw bytes through ABI-defined patterns:
- Transactions are like HTTP requests
- ABI is the serialization format
- EVM executes low-level operations
- Method calling is a high-level abstraction
FAQ
Q: Why 4-byte method selectors?
A: Gas optimization - shorter selectors cost less than full 32-byte hashes.
Q: How are dynamic arrays stored?
A: Using head-tail encoding with references to actual data locations.
Q: What's the gas cost for negative integers?
A: Expensive! Negative values are padded with 1's (non-zero bytes).