Deep Dive into Ethereum Virtual Machine Part 4 — External Method Calls in Smart Contracts

·

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:

  1. Method Selector (4 bytes): 0xee919d5 (Keccak256 hash of setA(uint256))
  2. Argument (32 bytes): 0x1

Application Binary Interface (ABI)

The ABI specifies how EVM languages interpret input data for interoperability:

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:

  1. Head: References to tail positions
  2. 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:

Conclusion

Smart contracts process raw bytes through ABI-defined patterns:

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).