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

Opcode: 0x73 Introduced: Frontier (EVM genesis) PUSH20 pushes a 20-byte immediate value from the bytecode onto the stack. The 20 bytes immediately following the opcode are read and zero-padded to 256 bits.

Specification

Stack Input:
[]
Stack Output:
value (uint256, 20 bytes from bytecode)
Gas Cost: 3 (GasFastestStep) Bytecode: 1 byte opcode + 20 bytes immediate data Operation:
value = read_bytes(pc + 1, 20)  // Big-endian
stack.push(value)
pc += 21

Behavior

PUSH20 reads 20 bytes from bytecode starting at position pc + 1, interprets them as a big-endian unsigned integer, and pushes the result onto the stack. Key characteristics:
  • Reads exactly 20 bytes following opcode
  • Big-endian byte order (most significant byte first)
  • Zero-padded to 256 bits if less than 32 bytes
  • InvalidOpcode if insufficient bytecode remaining
  • PC advances by 21 (opcode + data)

Examples

Basic Usage

import { handler_0x73_PUSH20 } from '@tevm/voltaire/evm/stack/handlers';
import { createFrame } from '@tevm/voltaire/evm/Frame';

// Bytecode with PUSH20
const bytecode = new Uint8Array([
  0x73,  // PUSH20
  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14   // 20 bytes: 0102030405060708090a0b0c0d0e0f1011121314
]);

const frame = createFrame({
  bytecode,
  pc: 0,
  stack: [],
  gasRemaining: 1000n
});

const err = handler_0x73_PUSH20(frame);

console.log(frame.stack); // [0x0102030405060708090a0b0c0d0e0f1011121314000000000000000000000000n]
console.log(frame.pc); // 21
console.log(frame.gasRemaining); // 997n (3 gas consumed)

Solidity Compilation

contract Example {
    // Addresses use PUSH20
    address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    // PUSH20 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
}

Assembly Usage

assembly {
    // Push 20-byte value
    push20 0xffffffffffffffffffffffffffffffffffffffff
    
    // Example: address literal
}

Gas Cost

Cost: 3 gas (GasFastestStep) All PUSH1-32 instructions cost the same despite different data sizes. Bytecode size impact:
  • PUSH20: 21 bytes (1 opcode + 20 data)
  • PUSH32: 33 bytes (1 opcode + 32 data)
Comparison:
OpcodeGasBytesUse Case
PUSH021Zero constant (Shanghai+)
PUSH132Small numbers (0-255)
| PUSH20 | 3 | 21 | Address literals |

Common Usage

Address Constants

contract Uniswap {
    // WETH address
    address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    // Compiled to:
    // PUSH20 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
}

Big-Endian Encoding

// Bytecode: PUSH20 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14
// Reads as: 0x0102030405060708090a0b0c0d0e0f1011121314

// Most significant byte first
// Byte 0: 0x01 (highest significance)
// Byte 19: 0x14 (lowest significance)

Implementation

/**
 * Read immediate data from bytecode for PUSH operations
 */
function readImmediate(bytecode: Uint8Array, pc: number, size: number): bigint | null {
  if (pc + 1 + size > bytecode.length) {
    return null;
  }

  let result = 0n;
  for (let i = 0; i < size; i++) {
    result = (result << 8n) | BigInt(bytecode[pc + 1 + i]);
  }
  return result;
}

/**
 * PUSH20 opcode (0x73) - Push 20 bytes onto stack
 *
 * Stack: [] => [value]
 * Gas: 3 (GasFastestStep)
 */
export function handler_0x73_PUSH20(frame: FrameType): EvmError | null {
  const gasErr = consumeGas(frame, FastestStep);
  if (gasErr) return gasErr;

  const value = readImmediate(frame.bytecode, frame.pc, 20);
  if (value === null) {
    return { type: "InvalidOpcode" };
  }

  const pushErr = pushStack(frame, value);
  if (pushErr) return pushErr;

  frame.pc += 21;
  return null;
}

Edge Cases

Insufficient Bytecode

// Bytecode ends before 20 bytes read
const bytecode = new Uint8Array([0x73, 0x01]); // Only 1 byte instead of 20
const frame = createFrame({ bytecode, pc: 0 });

const err = handler_0x73_PUSH20(frame);
console.log(err); // { type: "InvalidOpcode" }

Stack Overflow

// Stack at maximum capacity
const frame = createFrame({
  stack: new Array(1024).fill(0n),
  bytecode: new Uint8Array([0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
});

const err = handler_0x73_PUSH20(frame);
console.log(err); // { type: "StackOverflow" }

Out of Gas

// Insufficient gas
const frame = createFrame({
  gasRemaining: 2n,  // Need 3 gas
  bytecode: new Uint8Array([0x73, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])
});

const err = handler_0x73_PUSH20(frame);
console.log(err); // { type: "OutOfGas" }

Maximum Value

// All bytes 0xFF
const bytecode = new Uint8Array([0x73, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]);
const frame = createFrame({ bytecode, pc: 0 });

handler_0x73_PUSH20(frame);
console.log(frame.stack[0]); // 0xffffffffffffffffffffffffffffffffffffffff000000000000000000000000n

References