Ethereum Virtual Machine (EVM) assembler and disassembler library for working with EVM bytecode and assembly instructions
—
Comprehensive support for all Ethereum hard forks with proper opcode evolution, gas cost updates, and utilities for fork selection and block number conversion. PyEVMAsm accurately handles the historical development of the EVM instruction set across all major Ethereum upgrades.
Default fork configuration and comprehensive fork support information.
DEFAULT_FORK: str # Current default fork name ("istanbul")Usage Examples:
from pyevmasm import DEFAULT_FORK
print(f"Default fork: {DEFAULT_FORK}") # istanbul
# Supported forks can be accessed via instruction_tables.keys()
from pyevmasm import instruction_tables
supported_forks = tuple(instruction_tables.keys())
print(f"Supported forks: {supported_forks}")
# ('frontier', 'homestead', 'tangerine_whistle', 'spurious_dragon',
# 'byzantium', 'constantinople', 'petersburg', 'serenity', 'istanbul')
# Check if a fork is supported
fork_name = "byzantium"
if fork_name in instruction_tables:
print(f"{fork_name} is supported")Fork-specific instruction lookup tables providing accurate opcode definitions for each Ethereum hard fork.
instruction_tables: dict # Mapping of fork names to InstructionTable instances
# Individual fork instruction tables
frontier_instruction_table: InstructionTable
homestead_instruction_table: InstructionTable
tangerine_whistle_instruction_table: InstructionTable
spurious_dragon_instruction_table: InstructionTable
byzantium_instruction_table: InstructionTable
constantinople_instruction_table: InstructionTable
serenity_instruction_table: InstructionTable
istanbul_instruction_table: InstructionTableUsage Examples:
from pyevmasm import instruction_tables
# Access fork-specific instruction table
byzantium_table = instruction_tables["byzantium"]
frontier_table = instruction_tables["frontier"]
# Look up instructions by opcode
returndatasize = byzantium_table[0x3d] # RETURNDATASIZE
print(f"Byzantium 0x3d: {returndatasize.name}") # RETURNDATASIZE
# Same opcode in frontier (before RETURNDATASIZE existed)
try:
frontier_returndatasize = frontier_table[0x3d]
print(f"Frontier 0x3d: {frontier_returndatasize.name}") # INVALID
except KeyError:
print("0x3d not defined in frontier")
# Look up by mnemonic
push1 = byzantium_table["PUSH1"]
print(f"PUSH1 opcode: 0x{push1.opcode:02x}") # 0x60Fork-specific instruction lookup and iteration capabilities.
class InstructionTable:
"""
EVM Instruction factory providing fork-specific instruction definitions.
Implements an immutable, iterable instruction lookup table indexed by
both mnemonic and opcode.
"""
def __getitem__(self, key) -> Instruction:
"""
Get instruction by opcode (int) or mnemonic (str).
Parameters:
- key (int | str): Opcode value (0x00-0xFF) or mnemonic name
Returns:
Instruction: New Instruction instance for the specified opcode/mnemonic
Raises:
KeyError: If opcode/mnemonic not found in this fork
"""
def get(self, key, default=None) -> Instruction:
"""
Get instruction with default fallback.
Parameters:
- key (int | str): Opcode value or mnemonic name
- default: Default value if key not found
Returns:
Instruction | default: Instruction instance or default value
"""
def __contains__(self, key) -> bool:
"""Check if opcode or mnemonic exists in this fork."""
def __iter__(self):
"""Iterate over all instructions in opcode order."""
def keys(self):
"""Get sorted list of all opcodes in this fork."""Usage Examples:
from pyevmasm import instruction_tables
# Get instruction table for specific fork
istanbul_table = instruction_tables["istanbul"]
# Multiple ways to access instructions
push1_by_opcode = istanbul_table[0x60] # By opcode
push1_by_name = istanbul_table["PUSH1"] # By mnemonic
print(f"Same instruction: {push1_by_opcode.name == push1_by_name.name}") # True
# Safe access with default
unknown = istanbul_table.get(0xff, None) # SELFDESTRUCT or None depending on context
valid_instr = istanbul_table.get("ADD") # Returns ADD instruction
# Check existence
has_returndatasize = "RETURNDATASIZE" in istanbul_table # True
has_returndatasize_frontier = "RETURNDATASIZE" in instruction_tables["frontier"] # False
# Iterate all instructions
for instruction in istanbul_table:
if instruction.is_arithmetic:
print(f"Arithmetic: {instruction.name}")
# Get all opcodes
all_opcodes = istanbul_table.keys()
print(f"Total opcodes in Istanbul: {len(all_opcodes)}")Utility function to determine the appropriate fork for a given Ethereum block number.
def block_to_fork(block_number: int) -> str:
"""
Convert Ethereum block number to appropriate fork name.
Parameters:
- block_number (int): Ethereum block number
Returns:
str: Fork name appropriate for the given block number
Example:
>>> block_to_fork(0)
'frontier'
>>> block_to_fork(4370000)
'byzantium'
>>> block_to_fork(7280000)
'petersburg'
"""Usage Examples:
from pyevmasm import block_to_fork
# Historical block analysis
print(f"Block 0: {block_to_fork(0)}") # frontier
print(f"Block 1150000: {block_to_fork(1150000)}") # homestead
print(f"Block 4370000: {block_to_fork(4370000)}") # byzantium
print(f"Block 7280000: {block_to_fork(7280000)}") # petersburg
print(f"Recent block: {block_to_fork(15000000)}") # petersburg (latest)
# Use in analysis
def analyze_transaction_bytecode(bytecode, block_number):
fork = block_to_fork(block_number)
return disassemble_hex(bytecode, fork=fork)Fork Compatibility Examples:
from pyevmasm import assemble_hex, disassemble_one
# Instructions available in different forks
def test_fork_compatibility():
# This works in all forks
basic_code = assemble_hex("PUSH1 0x01\nADD", fork="frontier")
# DELEGATECALL introduced in homestead
try:
homestead_code = assemble_hex("DELEGATECALL", fork="homestead") # Works
frontier_code = assemble_hex("DELEGATECALL", fork="frontier") # Fails
except AssembleError:
print("DELEGATECALL not available in frontier")
# RETURNDATASIZE introduced in byzantium
byzantium_instr = disassemble_one(b'\x3d', fork="byzantium") # RETURNDATASIZE
frontier_instr = disassemble_one(b'\x3d', fork="frontier") # INVALID
# Gas costs change between forks
balance_frontier = disassemble_one(b'\x31', fork="frontier") # fee: 20
balance_istanbul = disassemble_one(b'\x31', fork="istanbul") # fee: 700
print(f"BALANCE gas: frontier={balance_frontier.fee}, istanbul={balance_istanbul.fee}")
# Choose appropriate fork for analysis
def analyze_historical_transaction(bytecode, block_number):
fork = block_to_fork(block_number)
instructions = list(disassemble_all(bytecode, fork=fork))
# Analysis with correct historical opcodes
for instr in instructions:
if instr.name == "INVALID":
print(f"Invalid opcode 0x{instr.opcode:02x} at block {block_number} (fork: {fork})")DEFAULT_FORK for current/modern bytecode analysisblock_to_fork() for historical transaction analysisfrom pyevmasm import instruction_tables, block_to_fork
def version_aware_analysis(bytecode, context_block=None):
if context_block:
fork = block_to_fork(context_block)
else:
fork = DEFAULT_FORK
table = instruction_tables[fork]
# Proceed with fork-appropriate analysis
return disassemble_all(bytecode, fork=fork)Install with Tessl CLI
npx tessl i tessl/pypi-pyevmasm