Basic usage — evmstate
Skip to content

Basic usage

This guide covers the fundamental ways to use the library to analyze and label state access patterns.

Core functions

The library provides three main entry points:

  1. traceState: Analyze state changes from a single transaction
  2. Tracer: Create a reusable instance for multiple traces
  3. watchState: Monitor ongoing state changes for a specific address

Tracing a transaction

There are three ways to trace a transaction:

  1. Using transaction calldata
  2. Using a contract ABI
  3. Using a transaction hash
calldata.ts
import { traceState } from "@polareth/evmstate";
 
const trace = await traceState({
  client,
  from: "0x111...",
  to: "0x222...",
  data: "0xabcd...",
  value: 0n,
});

Tracing options

There are various ways to configure the client:

  • Using an existing Tevm client (likely in forking mode)
  • Using a RPC url (which will internally create a Tevm memory client)

See the Tevm documentation for how to create and configure the client.

tevm-client.ts
import { createMemoryClient, http } from "tevm";
import { mainnet } from "tevm/common";
import { traceState } from "@polareth/evmstate";
 
const client = createMemoryClient({
  common: mainnet,
  fork: {
    transport: http("https://1.rpc.thirdweb.com"),
    blockTag: "latest",
  },
}); 
 
const trace = await traceState({
  client,
  from: "0x111...",
  to: "0x222...",
  data: "0xabcd...",
});

Finally, you will need to provide an url & API key for at least one supported explorer, so it can fetch ABIs and storage layouts:

explorers.ts
import { traceState } from "@polareth/evmstate";
 
const trace = await traceState({
  client,
  from: "0x111...",
  to: "0x222...",
  data: "0xabcd...",
  explorers: {
    etherscan: {
      baseUrl: "https://etherscan.io",
      apiKey: "your-api-key",
    },
    blockscout: {
      baseUrl: "https://blockscout.com",
      apiKey: "your-api-key",
    },
  },
});

Watching state changes

To monitor an address for state changes in real-time:

example.ts
import { watchState } from "@polareth/evmstate";
 
const unsubscribe = await watchState({
  rpcUrl: "https://1.rpc.thirdweb.com", // this or a Tevm client
  address: "0xContractAddress",
  // both storageLayout and abi are optional,
  // but providing them will avoid having to fetch
  storageLayout: layout, // this will type the state changes
  abi: abi,
  onStateChange: (stateChange) => {
    const trace = stateChange.storage?.balances.trace[0];
    if (trace?.modified) {
      console.log(`previous balance: ${trace.current?.decoded}`);
      console.log(`new balance: ${trace.next?.decoded}`);
      console.log(`mapping key: ${trace.path[0].key}`);
      console.log(`full expression: ${trace.fullExpression}`);
    }
  },
  onError: (err) => console.log(err),
  // ... same explorer options as traceState
});
 
// ...
 
unsubscribe();

Next steps