pyrevm: Python bindings for revm EVM
pyrevm provides Python bindings to revm, a high-performance Ethereum Virtual Machine (EVM) written in Rust. It enables Python developers to simulate Ethereum transactions, execute smart contracts, and interact with the EVM environment directly from Python, offering a fast and efficient way to prototype and test EVM-based logic. The current version is 0.3.7, and the project maintains an active development cadence, often mirroring the underlying revm library's updates.
Common errors
-
No module named 'pyrevm'
cause The pyrevm library is not installed or not available in the current Python environment.fixRun `pip install pyrevm` in your terminal to install the library. -
PyRevmError: EVM ran out of gas
cause The transaction or call simulation exceeded the provided gas limit, or the block's gas limit.fixIncrease the `gas_limit` in your `TxEnv` and `BlockEnv` objects. For example, set `gas_limit=1_000_000_000` for a high limit during development, or analyze the required gas for your specific operation. -
OSError: [Errno 99] Cannot assign requested address: Couldn't connect to remote EVM.
cause While less common for pyrevm (which is usually local), if you are attempting to integrate with a remote EVM or a service that pyrevm might internally try to connect to, this indicates a network connectivity issue or an invalid address/port.fixVerify that any target addresses or network configurations in your setup are correct and reachable. Ensure no firewalls are blocking connections if attempting to reach an external service. For local pyrevm, this error is generally not applicable unless misconfigured. -
PyRevmError: Invalid bytecode/contract code or input data
cause The bytecode provided to `insert_account_info` or the `data` field in `TxEnv` is malformed, not valid EVM bytecode, or does not match the expected function signature.fixDouble-check the source of your contract bytecode and ensure it is correctly compiled and passed as raw bytes. Verify that function selectors and arguments in `tx_env.data` are correctly formatted for the contract you are interacting with.
Warnings
- gotcha pyrevm builds on a Rust core (revm). If pre-built wheels are not available for your specific Python version and operating system/architecture, you might need a Rust toolchain installed for `pip install` to compile from source. This is common on less mainstream platforms or with newer Python versions immediately after release.
- gotcha The EVM instance in `pyrevm` holds an in-memory state. Any changes (like balance transfers or contract state modifications) are local to that EVM instance. If you create a new `EVM` object, it starts with an empty state unless explicitly populated. State changes from one `EVM` object do not automatically persist to another.
- breaking API changes in the underlying `revm` Rust library can lead to breaking changes in `pyrevm`'s Python API. While efforts are made to maintain compatibility, major version bumps of `revm` often cascade, affecting method signatures, class structures, or enum variants in `pyrevm`.
Install
-
pip install pyrevm
Imports
- EVM
from pyrevm import EVM
- CfgEnv
from pyrevm import CfgEnv
- BlockEnv
from pyrevm import BlockEnv
- TxEnv
from pyrevm import TxEnv
Quickstart
from pyrevm import EVM, CfgEnv, BlockEnv, TxEnv
# Example setup: a simple contract and an account
# Contract that returns 0 for a specific function call
CONTRACT_CODE = bytes.fromhex("6080604052348015600f57600080fd5b506004361060285760003560e01c806306fdde0314602d575b600080fd5b603a603c565b005b60005481565b")
CONTRACT_ADDRESS = bytes.fromhex("1234567890123456789012345678901234567890")
CALLER_ADDRESS = bytes.fromhex("abcdabcdabcdabcdabcdabcdabcdabcdabcdabcd")
# Function selector for the example contract (returns 0)
FUNCTION_SELECTOR = bytes.fromhex("06fdde03")
def run_evm_simulation():
# 1. Initialize environment configurations
cfg_env = CfgEnv(chain_id=1)
block_env = BlockEnv(
number=1, timestamp=1, gas_limit=1_000_000_000,
miner=bytes.fromhex("0000000000000000000000000000000000000000"), basefee=1
)
tx_env = TxEnv(
caller=CALLER_ADDRESS, gas_limit=1_000_000_000, gas_price=1, value=0,
data=FUNCTION_SELECTOR, transact_to=CONTRACT_ADDRESS
)
# 2. Create an EVM instance
evm = EVM(
cfg=cfg_env,
block=block_env,
tx=tx_env # Initial transaction environment
)
# 3. Insert initial state (e.g., contract code and account balances)
evm.insert_account_info(
CONTRACT_ADDRESS,
code=CONTRACT_CODE,
balance=0,
nonce=0
)
# Give caller some balance for transactions
evm.insert_account_info(
CALLER_ADDRESS,
balance=1_000_000_000_000_000_000, # 1 ETH
nonce=0
)
# 4. Perform a contract call
call_result = evm.call_raw() # Uses tx_env set during EVM init
print(f"--- Contract Call Simulation ---")
print(f"Status: {call_result.status}")
print(f"Gas Used: {call_result.gas_used}")
print(f"Exit Reason: {call_result.exit_reason}")
print(f"Output: {call_result.result.hex()}")
# 5. Perform a simple value transfer transaction
recipient_address = bytes.fromhex("feeefeeefeeefeeefeeefeeefeeefeeefeeefeee")
transfer_amount = 1000 # wei
# Update tx_env for the transaction
tx_env.data = b'' # No data for simple transfer
tx_env.transact_to = recipient_address
tx_env.value = transfer_amount
# Perform the transaction
transact_result = evm.transact_raw() # Uses updated tx_env
print(f"\n--- Value Transfer Simulation ---")
print(f"Status: {transact_result.status}")
print(f"Gas Used: {transact_result.gas_used}")
print(f"Exit Reason: {transact_result.exit_reason}")
print(f"Output: {transact_result.result.hex()}")
if __name__ == "__main__":
run_evm_simulation()