### Clone and Setup evm-trace Development Environment Source: https://github.com/apeworx/evm-trace/blob/main/CONTRIBUTING.md This bash script clones the evm-trace repository, sets up a Python virtual environment, installs the project in editable mode, and installs development dependencies. Ensure you have Python 3 and Git installed. ```bash # clone the github repo and navigate into the folder git clone https://github.com/ApeWorX/evm-trace.git cd evm-trace # create and load a virtual environment python3 -m venv venv source venv/bin/activate # install evm-trace into the virtual environment python setup.py install # install the developer dependencies (-e is interactive mode) pip install -e .'[dev]' ``` -------------------------------- ### Install evm-trace from source using setuptools Source: https://github.com/apeworx/evm-trace/blob/main/README.md Installs the most up-to-date version of evm-trace by cloning the repository and using setuptools. ```bash git clone https://github.com/ApeWorX/evm-trace.git cd evm-trace python3 setup.py install ``` -------------------------------- ### Install evm-trace using pip Source: https://github.com/apeworx/evm-trace/blob/main/README.md Installs the latest release of the evm-trace package using pip, the Python package installer. ```bash pip install evm-trace ``` -------------------------------- ### Install and Configure Pre-Commit Hooks Source: https://github.com/apeworx/evm-trace/blob/main/CONTRIBUTING.md These bash commands install the pre-commit framework and set up the local Git hooks for the evm-trace project. This ensures code consistency and linting checks before each commit. ```bash pip install pre-commit pre-commit install ``` -------------------------------- ### Get Geth Style Traces using web3.py Source: https://github.com/apeworx/evm-trace/blob/main/README.md Retrieves transaction trace frames from a Geth-compatible node using web3.py. It requires a running Ethereum node with the debug_traceTransaction RPC enabled. ```python from web3 import HTTPProvider, Web3 from evm_trace import TraceFrame web3 = Web3(HTTPProvider("https://path.to.my.node")) txn_hash = "0x..." struct_logs = web3.manager.request_blocking("debug_traceTransaction", [txn_hash]).structLogs for item in struct_logs: frame = TraceFrame.model_validate(item) ``` -------------------------------- ### Get Parity Style Traces using web3.py Source: https://github.com/apeworx/evm-trace/blob/main/README.md Retrieves transaction trace objects from a Parity-compatible node using web3.py. It requires a running Ethereum node with the trace_transaction RPC enabled. ```python from evm_trace import CallType, ParityTraceList raw_trace_list = web3.manager.request_blocking("trace_transaction", [txn_hash]) trace_list = ParityTraceList.model_validate(raw_trace_list) ``` -------------------------------- ### Generate Call-Tree from Geth Trace Source: https://github.com/apeworx/evm-trace/blob/main/README.md Constructs a call-tree node from Geth-style trace frames. This function helps visualize the transaction's execution flow. ```python from evm_trace import CallType, get_calltree_from_geth_trace root_node_kwargs = { "gas_cost": 10000000, "gas_limit": 10000000000, "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "calldata": "0x00", "value": 1000, "call_type": CallType.CALL, } # Where `trace` is a `TraceFrame` (see example above) calltree = get_calltree_from_geth_trace(trace, **root_node_kwargs) ``` -------------------------------- ### Generate Call-Tree from Parity Trace Source: https://github.com/apeworx/evm-trace/blob/main/README.md Constructs a call-tree node from Parity-style trace objects. This function helps visualize the transaction's execution flow. ```python from evm_trace import get_calltree_from_parity_trace tree = get_calltree_from_parity_trace(trace_list) ``` -------------------------------- ### Create an EventNode Manually Source: https://context7.com/apeworx/evm-trace/llms.txt Shows how to manually create an `EventNode` instance. This is useful for testing or constructing event data programmatically. It requires specifying the `call_type`, `depth`, `topics` (including the event selector as the first topic), and `data`. The example uses the `Transfer` event selector. ```python from evm_trace.base import EventNode, CallType from eth_pydantic_types import HexBytes # Create an EventNode manually event = EventNode( call_type=CallType.EVENT, # Always EVENT for EventNode depth=1, topics=[ HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), # Transfer selector HexBytes("0x000000000000000000000000sender_address_here"), # from (indexed) HexBytes("0x000000000000000000000000receiver_address_here"), # to (indexed) ], data=HexBytes("0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"), # amount ) ``` -------------------------------- ### Generate Gas Report from Call-Tree Source: https://github.com/apeworx/evm-trace/blob/main/README.md Generates a gas report from a call-tree, providing insights into gas consumption during transaction execution. This function requires a pre-generated call-tree. ```python from evm_trace.gas import get_gas_report # see examples above for creating a calltree calltree = get_calltree_from_geth_trace(trace, **root_node_kwargs) gas_report = get_gas_report(calltree) ``` -------------------------------- ### Merge Multiple Gas Reports Source: https://github.com/apeworx/evm-trace/blob/main/README.md Combines multiple gas reports into a single, consolidated report. This is useful for comparing gas usage across different traces or scenarios. ```python from evm_trace.gas import merge_reports # Pass two or more Dict[Any, Dict[Any, List[int]]] to combine reports where List[int] is the gas used. # Customize the values of Any accordingly: # 1. The first Any represents the bytes from the address. # 2. The second Any represents the method selector. # For example, you may replace addresses with token names or selector bytes with signature call strings. ``` -------------------------------- ### Access EventNode Selector Source: https://context7.com/apeworx/evm-trace/llms.txt Retrieves the event selector from an `EventNode` object. The selector is always the first topic in the `topics` list. The example demonstrates checking if the selector matches the known selector for the `Transfer` event. ```python from evm_trace.base import EventNode, CallType from eth_pydantic_types import HexBytes # Create an EventNode manually (example from above) event = EventNode( call_type=CallType.EVENT, depth=1, topics=[ HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), # Transfer selector HexBytes("0x000000000000000000000000sender_address_here"), # from (indexed) HexBytes("0x000000000000000000000000receiver_address_here"), # to (indexed) ], data=HexBytes("0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"), # amount ) # Access the selector (first topic) transfer_selector = event.selector print(f"Is Transfer event: {transfer_selector.hex() == 'ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'}") ``` -------------------------------- ### Create and Access CallTreeNode in Python Source: https://context7.com/apeworx/evm-trace/llms.txt Demonstrates how to manually create a `CallTreeNode` instance, populating it with details such as call type, address, gas usage, calldata, and sub-calls. It also shows how to access its properties like address, method selector, and gas cost. ```python from evm_trace import CallTreeNode, CallType from eth_pydantic_types import HexBytes # Create a CallTreeNode manually node = CallTreeNode( call_type=CallType.CALL, address=HexBytes("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"), value=1000000000000000000, # 1 ETH in wei depth=0, gas_limit=500000, gas_cost=45000, calldata=HexBytes("0xa9059cbb0000000000000000000000001234567890123456789012345678901234567890"), returndata=HexBytes("0x0000000000000000000000000000000000000000000000000000000000000001"), failed=False, selfdestruct=False, calls=[], # Sub-calls would go here events=[], # Events emitted in this call ) # Access properties print(f"Address: {node.address.hex()}") print(f"Method selector: {node.calldata[:4].hex()}") # First 4 bytes print(f"Call succeeded: {not node.failed}") print(f"Gas used: {node.gas_cost}") # String representation shows tree structure print(str(node)) ``` -------------------------------- ### Build Call Tree from Geth Trace Logs (Python) Source: https://context7.com/apeworx/evm-trace/llms.txt Illustrates how to use `get_calltree_from_geth_trace` to construct a hierarchical `CallTreeNode` from an iterator of `TraceFrame` objects. It requires initial parameters defining the root call context and shows how to traverse the resulting tree structure. ```python from evm_trace import CallType, TraceFrame, get_calltree_from_geth_trace, create_trace_frames # First, get trace frames (using create_trace_frames for proper CREATE handling) raw_struct_logs = web3.manager.request_blocking("debug_traceTransaction", [txn_hash]).structLogs trace_frames = create_trace_frames(iter(raw_struct_logs)) # Define root node parameters (the initial transaction context) root_node_kwargs = { "gas_cost": 21000, # Base transaction gas cost "gas_limit": 500000, # Transaction gas limit "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", # Contract being called "calldata": "0xa9059cbb000000000000000000000000...", # Transaction input data "value": 0, # ETH value sent "call_type": CallType.CALL, # Initial call type } # Build the call tree calltree = get_calltree_from_geth_trace(trace_frames, **root_node_kwargs) # Navigate the tree print(f"Root call type: {calltree.call_type}") print(f"Root address: {calltree.address.hex()}") print(f"Number of sub-calls: {len(calltree.calls)}") print(f"Transaction failed: {calltree.failed}") # Access nested calls for subcall in calltree.calls: print(f" Sub-call to: {subcall.address.hex()}") print(f" Call type: {subcall.call_type}") print(f" Gas used: {subcall.gas_cost}") # Pretty print the entire tree print(calltree) ``` -------------------------------- ### Parse Geth Trace Logs into TraceFrame Objects (Python) Source: https://context7.com/apeworx/evm-trace/llms.txt Demonstrates how to connect to an Ethereum node, fetch raw transaction trace data using debug_traceTransaction, and parse each struct log entry into a TraceFrame object. It shows how to access properties like program counter, opcode, gas, depth, stack, and memory. ```python from web3 import HTTPProvider, Web3 from evm_trace import TraceFrame # Connect to an Ethereum node with debug API enabled web3 = Web3(HTTPProvider("https://mainnet.infura.io/v3/YOUR_PROJECT_ID")) # Get raw trace data from a transaction txn_hash = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" trace_response = web3.manager.request_blocking("debug_traceTransaction", [txn_hash]) # Parse each struct log into a TraceFrame object frames = [] for log in trace_response.structLogs: frame = TraceFrame.model_validate(log) frames.append(frame) # Access frame properties print(f"PC: {frame.pc}, Op: {frame.op}, Gas: {frame.gas}, Depth: {frame.depth}") # Stack is available as list of HexBytes if frame.stack: print(f"Stack top: {frame.stack[-1].hex()}") # Memory can be accessed via get() method # frame.memory.get(offset, size) extracts bytes from memory ``` -------------------------------- ### Create Custom Report with Contract and Method Names Source: https://context7.com/apeworx/evm-trace/llms.txt Generates a custom report mapping contract names and method names to their respective gas costs. It uses helper dictionaries `address_to_name` and `selector_to_name` to provide human-readable names, falling back to hexadecimal representations if names are not found. Assumes `combined_report` is available. ```python # Custom report keys - you can replace address/selector with meaningful names custom_report = {} for addr, methods in combined_report.items(): contract_name = address_to_name.get(addr.hex(), addr.hex()[:10]) custom_report[contract_name] = {} for selector, costs in methods.items(): method_name = selector_to_name.get(selector.hex(), selector.hex()) custom_report[contract_name][method_name] = costs ``` -------------------------------- ### Build Call Tree from Geth Call Tracer Response Source: https://context7.com/apeworx/evm-trace/llms.txt The `get_calltree_from_geth_call_trace` function constructs a `CallTreeNode` from the output of `debug_traceTransaction` when using the `callTracer`. This method is efficient for analyzing simple call trees and requires the `evm_trace` library. It takes a dictionary representing the call trace as input and returns a `CallTreeNode` object. ```python from evm_trace import get_calltree_from_geth_call_trace # Get call trace using the callTracer txn_hash = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" call_trace = web3.manager.request_blocking( "debug_traceTransaction", [txn_hash, {"tracer": "callTracer"}] ) # The response is already structured as a call tree # Example response structure: call_trace_data = { "type": "CALL", "from": "0xSenderAddress", "to": "0xContractAddress", "value": "0x0", "gas": "0x7a120", "gasUsed": "0x5208", "input": "0xa9059cbb...", "output": "0x0000000000000000000000000000000000000000000000000000000000000001", "calls": [ { "type": "STATICCALL", "from": "0xContractAddress", "to": "0xOtherContract", "gas": "0x50000", "gasUsed": "0x1000", "input": "0x70a08231...", "output": "0x..." } ] } # Build CallTreeNode from call trace calltree = get_calltree_from_geth_call_trace(call_trace_data) # Access the tree structure print(f"Root: {calltree.call_type} to {calltree.address.hex()}") print(f"Gas limit: {calltree.gas_limit}, Gas used: {calltree.gas_cost}") print(f"Return data: {calltree.returndata.hex()}") # Iterate through all calls recursively def print_calls(node, indent=0): prefix = " " * indent print(f"{prefix}{node.call_type.value}: {node.address.hex()[:10]}...") for call in node.calls: print_calls(call, indent + 1) print_calls(calltree) ``` -------------------------------- ### Navigate and Display EVM Call Tree Source: https://context7.com/apeworx/evm-trace/llms.txt Iterate through sub-calls within a CallTreeNode to display call details like type and address. The `CallTreeNode` itself can be printed to visualize the entire call tree structure, showing nested calls and events. ```python for i, subcall in enumerate(calltree.calls): print(f"Sub-call {i}: {subcall.call_type} to {subcall.address.hex()}") # Display full tree visualization print(calltree) ``` -------------------------------- ### Create TraceFrame Iterator for CREATE Operations (Python) Source: https://context7.com/apeworx/evm-trace/llms.txt Provides a function `create_trace_frames` that iterates over raw struct logs from `debug_traceTransaction`. It includes special handling for CREATE and CREATE2 opcodes, automatically populating the `contract_address` field for newly created contracts. ```python from evm_trace import create_trace_frames # Raw struct logs from debug_traceTransaction raw_struct_logs = [ {"pc": 0, "op": "PUSH1", "gas": 100000, "gasCost": 3, "depth": 1, "stack": [], "memory": []}, {"pc": 2, "op": "CREATE", "gas": 99997, "gasCost": 32000, "depth": 1, "stack": ["0x1000", "0x0", "0x100"], "memory": []}, {"pc": 3, "op": "PUSH1", "gas": 67997, "gasCost": 3, "depth": 2, "stack": [], "memory": []}, # ... more frames ] # Create iterator that properly handles CREATE opcodes for frame in create_trace_frames(iter(raw_struct_logs)): print(f"Op: {frame.op}, Depth: {frame.depth}") # For CREATE/CREATE2 frames, contract_address will be populated if frame.contract_address: print(f"Created contract at: {frame.contract_address.hex()}") ``` -------------------------------- ### Build Call Tree from Parity Traces Source: https://context7.com/apeworx/evm-trace/llms.txt The `get_calltree_from_parity_trace` function converts a flat list of `ParityTrace` objects, obtained from `trace_transaction` RPC calls, into a hierarchical `CallTreeNode` structure. It utilizes the `traceAddress` field within each `ParityTrace` to accurately reconstruct the nested call hierarchy. This function requires the `evm_trace` library and a `ParityTraceList` object as input, returning a `CallTreeNode` representing the root of the call tree. ```python from evm_trace import ParityTraceList, get_calltree_from_parity_trace # Get and parse parity traces txn_hash = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" raw_traces = web3.manager.request_blocking("trace_transaction", [txn_hash]) trace_list = ParityTraceList.model_validate(raw_traces) # Build call tree from parity traces calltree = get_calltree_from_parity_trace(trace_list) # The tree structure is now available print(f"Transaction called: {calltree.address.hex()}") print(f"Call type: {calltree.call_type}") print(f"Value transferred: {calltree.value} wei") print(f"Gas limit: {calltree.gas_limit}") print(f"Gas used: {calltree.gas_cost}") print(f"Failed: {calltree.failed}") ``` -------------------------------- ### Access Event Data from Call Tree Source: https://context7.com/apeworx/evm-trace/llms.txt Demonstrates how to access event data emitted during transaction execution from a `CallTreeNode`. It iterates through the events associated with a call tree, printing the event selector, all topics, event data, and depth. Requires `get_calltree_from_parity_trace` and `EventNode`, `CallType` from `evm_trace.base`. ```python from evm_trace.base import EventNode, CallType from eth_pydantic_types import HexBytes # Events are typically extracted from CallTreeNode.events calltree = get_calltree_from_parity_trace(trace_list) # Access events from a call node for event in calltree.events: print(f"Event selector: {event.selector.hex()}") # First topic is the selector print(f"All topics: {[t.hex() for t in event.topics]}") print(f"Event data: {event.data.hex()}") print(f"Depth: {event.depth}") ``` -------------------------------- ### Generate and Analyze EVM Gas Report in Python Source: https://context7.com/apeworx/evm-trace/llms.txt The `get_gas_report` function processes a `CallTreeNode` to generate a structured report detailing gas consumption organized by contract address and method selector. This aids in identifying gas-intensive operations. ```python from evm_trace.gas import get_gas_report, merge_reports # Assuming you have a calltree from previous examples # calltree = get_calltree_from_geth_call_trace(call_trace_data) # Generate gas report # gas_report = get_gas_report(calltree) # Report structure: {address: {method_selector: [gas_costs]}} # Example output: # { # b'\xd8\xdaK\xf2...': { # Contract address bytes # b'\xa9\x05\x9c\xbb': [45000], # transfer() gas costs # b'p\xa0\x82\x31': [2100, 2100], # balanceOf() called twice # }, # b'\x12\x34\x56\x78...': { # b'\x23\xb8r\xdd': [15000], # transferFrom() # } # } # Iterate through the report # for address, methods in gas_report.items(): # print(f"Contract: {address.hex()}") # for selector, gas_costs in methods.items(): # print(f" Method {selector.hex()}: {gas_costs}") # print(f" Total: {sum(gas_costs)}, Avg: {sum(gas_costs)/len(gas_costs):.0f}") ``` -------------------------------- ### Parse Parity/OpenEthereum Transaction Traces Source: https://context7.com/apeworx/evm-trace/llms.txt The `ParityTraceList` class is designed to parse trace data obtained from Parity or OpenEthereum's `trace_transaction` RPC method. It processes a list of raw trace dictionaries, converting them into a structured list of `ParityTrace` objects. Each `ParityTrace` object contains details about the action, result, and its position within the call hierarchy using `traceAddress`. This requires the `evm_trace` library and Pydantic for validation. ```python from evm_trace import ParityTraceList # Get raw trace from trace_transaction RPC txn_hash = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" raw_traces = web3.manager.request_blocking("trace_transaction", [txn_hash]) # Example raw trace structure: raw_traces_example = [ { "action": { "callType": "call", "from": "0xSender", "gas": "0x7a120", "input": "0xa9059cbb...", "to": "0xContract", "value": "0x0" }, "blockHash": "0xBlockHash", "result": { "gasUsed": "0x5208", "output": "0x0000...0001" }, "subtraces": 1, "traceAddress": [], "transactionHash": "0xTxHash", "type": "call" }, { "action": { "callType": "staticcall", "from": "0xContract", "gas": "0x50000", "input": "0x70a08231...", "to": "0xOtherContract", "value": "0x0" }, "blockHash": "0xBlockHash", "result": { "gasUsed": "0x1000", "output": "0x..." }, "subtraces": 0, "traceAddress": [0], "transactionHash": "0xTxHash", "type": "call" } ] # Parse into typed model trace_list = ParityTraceList.model_validate(raw_traces) # Access individual traces for trace in trace_list.root: print(f"Type: {trace.call_type}") print(f"Trace address: {trace.trace_address}") # Position in call hierarchy print(f"Subtraces: {trace.subtraces}") if trace.error: print(f"Error: {trace.error}") elif trace.result: print(f"Gas used: {trace.result.gas_used}") ``` -------------------------------- ### Merge Multiple EVM Gas Reports in Python Source: https://context7.com/apeworx/evm-trace/llms.txt The `merge_reports` function consolidates multiple gas reports, generated by `get_gas_report`, into a single, unified report. This is particularly useful for aggregating gas usage data from various transactions or test executions. ```python from evm_trace.gas import get_gas_report, merge_reports # Generate reports from multiple transactions # report1 = get_gas_report(calltree1) # report2 = get_gas_report(calltree2) # report3 = get_gas_report(calltree3) # Merge all reports # combined_report = merge_reports(report1, report2, report3) # The merged report combines gas costs for same address/method pairs ``` -------------------------------- ### Analyze Combined Report Data Source: https://context7.com/apeworx/evm-trace/llms.txt Iterates through a combined report of contract method calls, printing statistics for methods called multiple times. It calculates the minimum, maximum, and average gas costs for each method. Assumes `combined_report` is a dictionary where keys are addresses and values are dictionaries of method selectors to lists of gas costs. ```python # Analyze combined data for address, methods in combined_report.items(): for selector, gas_costs in methods.items(): if len(gas_costs) > 1: print(f"Method {selector.hex()} called {len(gas_costs)} times") print(f" Min: {min(gas_costs)}, Max: {max(gas_costs)}") print(f" Avg: {sum(gas_costs)/len(gas_costs):.0f}") ``` -------------------------------- ### Merge Custom Reports Source: https://context7.com/apeworx/evm-trace/llms.txt Merges two custom reports into a final report. This function assumes a `merge_reports` function is defined elsewhere, which handles the logic for combining the report dictionaries. The input `custom_report` and `another_custom_report` are expected to have a similar structure. ```python # Merge custom reports final_report = merge_reports(custom_report, another_custom_report) ``` -------------------------------- ### Utilize CallType Enum for EVM Operations in Python Source: https://context7.com/apeworx/evm-trace/llms.txt The `CallType` enum provides constants for various EVM call operation types, including standard calls, delegate calls, static calls, contract creation, and special types for events and internal calls. These can be used for comparisons and checks. ```python from evm_trace import CallType # Available call types print(CallType.CALL.value) # "CALL" - Standard external call print(CallType.DELEGATECALL.value) # "DELEGATECALL" - Call with caller's context print(CallType.STATICCALL.value) # "STATICCALL" - Read-only call print(CallType.CALLCODE.value) # "CALLCODE" - Deprecated, similar to delegatecall print(CallType.CREATE.value) # "CREATE" - Contract creation print(CallType.CREATE2.value) # "CREATE2" - Deterministic contract creation print(CallType.SELFDESTRUCT.value) # "SELFDESTRUCT" - Contract destruction print(CallType.EVENT.value) # "EVENT" - Log emission print(CallType.INTERNAL.value) # "INTERNAL" - Non-opcode internal call # Compare call types call_type = CallType.CALL if call_type == CallType.CALL: print("This is a standard CALL") # Can compare with string values too if call_type == "CALL": print("String comparison works") # Check if call type is in a set of types from evm_trace.enums import CALL_OPCODES if call_type in CALL_OPCODES: print("This is a call-type opcode") ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.