Output format — evmstate
Skip to content

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",
      },
    ],
  },
}

Next steps