Onchain.Trace (onchain_evm v0.4.0)

Copy Markdown View Source

Debug and trace API wrapper for Ethereum nodes.

Provides access to the debug_* JSON-RPC namespace for transaction tracing, call tracing, and direct storage slot reads. These methods are supported by reth, geth, Erigon, and Nethermind — any full node with debug APIs enabled.

Not a core dependency — the library works with any RPC endpoint. This module provides enhanced debugging and inspection capabilities when available.

Tracer Types

  • "callTracer" (default) — returns the call tree: type, from, to, gas, value, input, output, and nested calls
  • "prestateTracer" — returns the pre-execution state: balance, nonce, code, and storage for each touched account

Performance Note

For faster EVM simulation, pass a local node URL (e.g. http://localhost:8545) as :rpc_url to Onchain.EVM.simulate_*/3. Local nodes eliminate network latency and rate limits — no special integration needed.

Error Format

  • Address validation: {:error, {:invalid_address, input}}
  • Data validation: {:error, {:invalid_data, input}}
  • Block validation: {:error, {:invalid_block, input}}
  • Tx hash validation: {:error, {:invalid_tx_hash, input}} (must be 32 bytes)
  • Slot validation: {:error, {:invalid_slot, input}} (must be 0x hex)
  • RPC/network errors: {:error, {:rpc_error, %{code: integer, message: string}}}

Functions

FunctionPurpose
trace_transaction/2Full execution trace of a mined transaction
trace_transaction!/2Same, raises on error
trace_call/3Trace a call without mining it
trace_call!/3Same, raises on error
storage_at/3Read a contract storage slot directly
storage_at!/3Same, raises on error
available?/1Check if debug/trace APIs are supported

API Functions

FunctionArityDescriptionParam Kinds
available?1Check if debug/trace APIs are supported by the connected node.opts: value
storage_at!3Read a contract storage slot directly. Raises on error.address: value, slot: value, opts: value
storage_at3Read a contract storage slot directly (eth_getStorageAt).address: value, slot: value, opts: value
trace_call!3Trace a call without mining it. Raises on error.call_params: value, block: value, opts: value
trace_call3Trace a call without mining it (debug_traceCall).call_params: value, block: value, opts: value
trace_transaction!2Get a full execution trace of a mined transaction. Raises on error.tx_hash: value, opts: value
trace_transaction2Get a full execution trace of a mined transaction (debug_traceTransaction).tx_hash: value, opts: value

Summary

Types

RPC/network errors from the Ethereum node.

All possible errors from trace functions.

Validation errors from Elixir-side input checks.

Functions

Check if debug/trace APIs are supported by the connected node.

Read a contract storage slot directly (eth_getStorageAt).

Read a contract storage slot directly. Raises on error.

Trace a call without mining it (debug_traceCall).

Trace a call without mining it. Raises on error.

Get a full execution trace of a mined transaction (debug_traceTransaction).

Get a full execution trace of a mined transaction. Raises on error.

Types

rpc_error()

@type rpc_error() :: {:rpc_error, map()}

RPC/network errors from the Ethereum node.

trace_error()

@type trace_error() :: validation_error() | rpc_error()

All possible errors from trace functions.

validation_error()

@type validation_error() ::
  {:invalid_address, term()}
  | {:invalid_data, term()}
  | {:invalid_block, term()}
  | {:invalid_tx_hash, term()}
  | {:invalid_tracer, term()}
  | {:invalid_slot, term()}
  | {:invalid_value, term()}
  | {:missing_param, atom()}

Validation errors from Elixir-side input checks.

Functions

available?(opts \\ [])

@spec available?(keyword()) :: boolean()

Check if debug/trace APIs are supported by the connected node.

Parameters

  • opts - Options: :rpc_url, :timeout (default: [], value)

Returns

true if the node supports debug_traceCall, false otherwise (boolean)

# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout",
      kind: :value
    }
  },
  returns: %{
    type: :boolean,
    description: "true if the node supports debug_traceCall, false otherwise"
  }
}

storage_at(address, slot, opts \\ [])

@spec storage_at(String.t() | binary(), String.t(), keyword()) ::
  {:ok, String.t()}
  | {:error,
     {:invalid_address, term()}
     | {:invalid_slot, term()}
     | {:invalid_block, term()}
     | rpc_error()}

Read a contract storage slot directly (eth_getStorageAt).

Parameters

  • address - Contract address as 0x hex string or 20-byte binary (value)
  • slot - Storage slot position as 0x-prefixed hex string (32 bytes) (value)
  • opts - Options: :rpc_url, :timeout, :block (default: [], value)

Returns

32-byte hex value at the storage slot ({:ok, hex_string} | {:error, {:invalid_address, term} | {:invalid_slot, term} | {:invalid_block, term} | rpc_error()})

# descripex:contract
%{
  params: %{
    slot: %{
      description: "Storage slot position as 0x-prefixed hex string (32 bytes)",
      kind: :value
    },
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :block",
      kind: :value
    },
    address: %{
      description: "Contract address as 0x hex string or 20-byte binary",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, hex_string} | {:error, {:invalid_address, term} | {:invalid_slot, term} | {:invalid_block, term} | rpc_error()}",
    description: "32-byte hex value at the storage slot",
    example: "0x0000000000000000000000000000000000000000000000000000000000000001"
  }
}

storage_at!(address, slot, opts \\ [])

@spec storage_at!(String.t() | binary(), String.t(), keyword()) :: String.t()

Read a contract storage slot directly. Raises on error.

Parameters

  • address - Contract address as 0x hex string or 20-byte binary (value)
  • slot - Storage slot position as 0x-prefixed hex string (value)
  • opts - Options: :rpc_url, :timeout, :block (default: [], value)

Returns

32-byte hex value at the storage slot (string)

# descripex:contract
%{
  params: %{
    slot: %{
      description: "Storage slot position as 0x-prefixed hex string",
      kind: :value
    },
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :block",
      kind: :value
    },
    address: %{
      description: "Contract address as 0x hex string or 20-byte binary",
      kind: :value
    }
  },
  returns: %{
    type: :string,
    description: "32-byte hex value at the storage slot"
  }
}

trace_call(call_params, block \\ "latest", opts \\ [])

@spec trace_call(map(), non_neg_integer() | String.t(), keyword()) ::
  {:ok, map()} | {:error, trace_error()}

Trace a call without mining it (debug_traceCall).

Parameters

  • call_params - Call parameters map with :to (address), :data (hex calldata), and optional :from (address), :value (hex wei) (value)
  • block - Block number (integer), tag ("latest", "finalized", etc.), or "0x..." hex (default: "latest", value)
  • opts - Options: :rpc_url, :timeout, :tracer ("callTracer" or "prestateTracer", default: "callTracer") (default: [], value)

Returns

Raw trace output from the node (shape depends on tracer type) ({:ok, map} | {:error, trace_error()})

# descripex:contract
%{
  params: %{
    block: %{
      default: "latest",
      description: "Block number (integer), tag (\"latest\", \"finalized\", etc.), or \"0x...\" hex",
      kind: :value
    },
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :tracer (\"callTracer\" or \"prestateTracer\", default: \"callTracer\")",
      kind: :value
    },
    call_params: %{
      description: "Call parameters map with :to (address), :data (hex calldata), and optional :from (address), :value (hex wei)",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, map} | {:error, trace_error()}",
    description: "Raw trace output from the node (shape depends on tracer type)"
  }
}

trace_call!(call_params, block \\ "latest", opts \\ [])

@spec trace_call!(map(), non_neg_integer() | String.t(), keyword()) :: map()

Trace a call without mining it. Raises on error.

Parameters

  • call_params - Call parameters map (see trace_call/3) (value)
  • block - Block identifier (default: "latest", value)
  • opts - Options: :rpc_url, :timeout, :tracer (default: [], value)

Returns

Raw trace output from the node (map)

# descripex:contract
%{
  params: %{
    block: %{default: "latest", description: "Block identifier", kind: :value},
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :tracer",
      kind: :value
    },
    call_params: %{
      description: "Call parameters map (see trace_call/3)",
      kind: :value
    }
  },
  returns: %{type: :map, description: "Raw trace output from the node"}
}

trace_transaction(tx_hash, opts \\ [])

@spec trace_transaction(
  String.t(),
  keyword()
) ::
  {:ok, map()}
  | {:error,
     {:invalid_tx_hash, term()} | {:invalid_tracer, term()} | rpc_error()}

Get a full execution trace of a mined transaction (debug_traceTransaction).

Parameters

  • tx_hash - 0x-prefixed hex transaction hash (value)
  • opts - Options: :rpc_url, :timeout, :tracer ("callTracer" or "prestateTracer", default: "callTracer") (default: [], value)

Returns

Raw trace output from the node (shape depends on tracer type) ({:ok, map} | {:error, {:invalid_tx_hash, term} | {:invalid_tracer, term} | rpc_error()})

# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :tracer (\"callTracer\" or \"prestateTracer\", default: \"callTracer\")",
      kind: :value
    },
    tx_hash: %{description: "0x-prefixed hex transaction hash", kind: :value}
  },
  returns: %{
    type: "{:ok, map} | {:error, {:invalid_tx_hash, term} | {:invalid_tracer, term} | rpc_error()}",
    description: "Raw trace output from the node (shape depends on tracer type)"
  }
}

trace_transaction!(tx_hash, opts \\ [])

@spec trace_transaction!(
  String.t(),
  keyword()
) :: map()

Get a full execution trace of a mined transaction. Raises on error.

Parameters

  • tx_hash - 0x-prefixed hex transaction hash (value)
  • opts - Options: :rpc_url, :timeout, :tracer (default: [], value)

Returns

Raw trace output from the node (map)

# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :tracer",
      kind: :value
    },
    tx_hash: %{description: "0x-prefixed hex transaction hash", kind: :value}
  },
  returns: %{type: :map, description: "Raw trace output from the node"}
}