Output format
This reference documents the structure of data returned by the library's tracing functions.
Basic structure
For traceState
and Tracer.traceState
, the output maps account addresses to state changes:
import type { TraceStateResult, LabeledState } from "@polareth/evmstate";
import type { Address } from "tevm";
type TraceOutput = TraceStateResult;
// which is equivalent to:
type TraceOutputMap = Map<Address, LabeledState>; // `get`, `has`, `set` all normalize the address to lowercase
For watchState
, the output focuses on a single address and includes the transaction hash:
import type { Hex } from "tevm";
import type { LabeledState } from "@polareth/evmstate";
type WatchOutput = LabeledState & { txHash: Hex };
Account state
Each account in the output has these properties:
import type { Hex } from "tevm";
import type { LabeledStorageState } from "@polareth/evmstate";
type LabeledState = {
// Intrinsic properties
balance: {
current: bigint,
next?: bigint,
modified: boolean
},
nonce: {
current: number,
next?: number,
modified: boolean
},
code: {
current: Hex,
next?: Hex,
modified: boolean
},
// Storage state
storage: {
[variableName: string]: LabeledStorageState
}
}
Storage variables
The storage
field contains labeled variables with detailed traces:
import type { LabeledStorageStateTrace } from "@polareth/evmstate";
type LabeledStorageState = {
"variableName": {
name: string,
type?: string,
kind?: "primitive" | "mapping" | "dynamic_array" | "static_array" | "struct" | "bytes",
trace: Array<LabeledStorageStateTrace>
}
}
Each LabeledStorageStateTrace
represents a specific storage access:
import type { Hex } from "tevm";
import type { PathSegment } from "@polareth/evmstate";
type LabeledStorageStateTrace = {
slots: Array<Hex>, // Storage slots accessed
path: Array<PathSegment>, // Semantic path components
fullExpression: string, // Human-readable expression (e.g., "balances[0x1234]")
current?: {
hex: Hex,
decoded: any // typed if storage layout is provided
},
next?: {
hex: Hex,
decoded: any // typed if storage layout is provided
},
modified: boolean,
note?: string // any exception/error during exploration
}
Proxy contracts
Proxy contracts are labeled using the implementation contract's ABI and storage layout when possible:
output.json
{
"__implementation": {
"name": "__implementation",
"type": "address",
"kind": "primitive",
"trace": [
{
"current": { "hex": "<implementation>", "decoded": "<implementation>" },
"next": { "hex": "<implementation>", "decoded": "<implementation>" },
"modified": true,
// EIP-1967 implementation slot: bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)
"slots": ["0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"],
"path": [],
"fullExpression": "__implementation",
},
],
},
"__admin": {
"name": "__admin",
"type": "address",
"kind": "primitive",
"trace": [
{
"current": { "hex": "<admin>", "decoded": "<admin>" },
"modified": false,
// EIP-1967 admin slot: bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)
"slots": ["0xb53127684a568b3173ae13b9f8a6016e019b2c8e8cbb2a6e0a23387fdaa12345"],
"path": [],
"fullExpression": "__admin",
},
],
},
}