Skip to main content
This page is a placeholder. All examples on this page are currently AI-generated and are not correct. This documentation will be completed in the future with accurate, tested examples.

Overview

Context instructions provide access to information about the current execution environment, including addresses, call data, gas prices, and external account state. These opcodes (0x30-0x3f) form the foundation for contracts to understand their execution context and interact with the broader blockchain state.

Instruction Categories

Environment Context (0x30-0x34)

Basic execution environment information:
  • ADDRESS (0x30) - Get currently executing account address
  • BALANCE (0x31) - Get balance of an account
  • ORIGIN (0x32) - Get transaction origination address
  • CALLER (0x33) - Get immediate caller address
  • CALLVALUE (0x34) - Get deposited value in current call

Call Data Access (0x35-0x37)

Reading input data passed to the contract:
  • CALLDATALOAD (0x35) - Load 32 bytes from calldata
  • CALLDATASIZE (0x36) - Get size of calldata
  • CALLDATACOPY (0x37) - Copy calldata to memory

Code Introspection (0x38-0x39)

Accessing the currently executing code:
  • CODESIZE (0x38) - Get size of executing code
  • CODECOPY (0x39) - Copy executing code to memory

Transaction Context (0x3a)

Transaction-level information:
  • GASPRICE (0x3a) - Get transaction gas price

External Account Access (0x3b-0x3f)

Querying other accounts on the blockchain:
  • EXTCODESIZE (0x3b) - Get size of external account’s code
  • EXTCODECOPY (0x3c) - Copy external account’s code to memory
  • RETURNDATASIZE (0x3d) - Get size of return data from last call
  • RETURNDATACOPY (0x3e) - Copy return data to memory
  • EXTCODEHASH (0x3f) - Get keccak256 hash of external account’s code

Gas Costs

Context instructions have varying gas costs based on their complexity and hardfork: Simple Environment Access (2 gas):
  • ADDRESS, ORIGIN, CALLER, CALLVALUE
  • CALLDATASIZE, CODESIZE, GASPRICE, RETURNDATASIZE
Call Data Operations (3+ gas):
  • CALLDATALOAD: 3 gas
  • CALLDATACOPY, CODECOPY, RETURNDATACOPY: 3 + memory expansion + copy cost
External Account Access (700+ gas):
  • BALANCE: 700 gas (Istanbul+)
  • EXTCODESIZE: 700 gas (Tangerine Whistle+)
  • EXTCODECOPY: 700 + memory expansion + copy cost
  • EXTCODEHASH: 700 gas (Constantinople+)

Access Cost Evolution

External account access costs have changed significantly across hardforks to prevent DoS attacks:
OpcodeFrontierTangerine WhistleBerlin
BALANCE204002600 (cold) / 100 (warm)
EXTCODESIZE207002600 (cold) / 100 (warm)
EXTCODECOPY207002600 (cold) / 100 (warm)
EXTCODEHASH--2600 (cold) / 100 (warm)

Security Considerations

tx.origin vs msg.sender

Critical distinction:
  • ORIGIN (0x32) - Original transaction sender (never changes)
  • CALLER (0x33) - Immediate caller (changes with each call)
Vulnerability:
// VULNERABLE: Uses tx.origin for authorization
function withdraw() public {
    require(tx.origin == owner);  // Can be exploited!
    // ...
}
Attack scenario:
  1. Attacker deploys malicious contract
  2. Owner calls attacker’s contract
  3. Attacker’s contract calls victim’s withdraw()
  4. tx.origin == owner passes, funds stolen
Safe pattern:
// SAFE: Use msg.sender for authorization
function withdraw() public {
    require(msg.sender == owner);  // Correct!
    // ...
}

Return Data Bounds

RETURNDATACOPY can revert if copying beyond actual return data:
// Can revert if returndata size < offset + length
assembly {
    returndatacopy(dest, offset, length)
}

External Code Checks

EXTCODESIZE returns 0 for:
  • Externally owned accounts (EOAs)
  • Contracts during construction (before constructor completes)
Bypass example:
// INSUFFICIENT: Can be bypassed during construction
modifier onlyEOA() {
    uint256 size;
    assembly { size := extcodesize(caller()) }
    require(size == 0);
    _;
}

Common Patterns

Checking Contract vs EOA

function isContract(address addr) view returns (bool) {
    uint256 size;
    assembly { size := extcodesize(addr) }
    return size > 0;
}

Reading Call Data Efficiently

// Load single value
function getValue() pure returns (uint256 value) {
    assembly {
        value := calldataload(4)  // Skip function selector
    }
}

// Copy calldata to memory
function copyData(uint256 offset, uint256 length) pure returns (bytes memory) {
    bytes memory data = new bytes(length);
    assembly {
        calldatacopy(add(data, 0x20), offset, length)
    }
    return data;
}

Verifying Code Hash

function verifyImplementation(address impl, bytes32 expected) view returns (bool) {
    bytes32 hash;
    assembly { hash := extcodehash(impl) }
    return hash == expected;
}

Implementation Reference

Context instruction handlers are implemented in:
  • TypeScript: /src/evm/context/
  • Zig: /src/evm/context/handlers_context.zig
Each instruction follows the standard handler pattern:
  1. Pop operands from stack
  2. Consume gas (including memory expansion for copy operations)
  3. Access context information or external state
  4. Push result to stack
  5. Increment program counter

References