> ## Documentation Index
> Fetch the complete documentation index at: https://docs.leafage.chaintable.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Adding a new chain

> Set up write-side data export and read-side state queries for a new EVM-compatible chain

Adding a new chain involves two independent tasks: getting the chain's execution client to export data (the **write side**), and getting leafage-evm to execute calls for the chain correctly (the **read side**). The two sides can progress separately.

<Info>
  If the client the new chain uses is already in the [list of adapted clients](https://github.com/Chaintable/leafage-evm#supported-write-node-repositories) (for example, yet another OP Stack chain), the write side usually only needs a new deployment; no code changes are required.
</Info>

## Write side: integrate pipeline into the execution client

### Choose an adaptation path

```text theme={null}
Is the client written in Rust?
    Yes → Reth adaptation (RPC Tracer mode only)
    No ↓
Does the fork have tracing.Hooks and tracers.LiveDirectory? (Geth v1.14.0+)
    Yes → Standard Geth adaptation
    No → Legacy Geth adaptation
```

|                  | Standard (Geth v1.14.0+) | Legacy (Geth \< v1.14.0)  | Reth                  |
| ---------------- | ------------------------ | ------------------------- | --------------------- |
| Tracer interface | `tracing.Hooks`          | `vm.EVMLogger`            | `revm-inspectors`     |
| pipeline code    | Go module dependency     | Embedded in source        | Reimplemented in Rust |
| Integration mode | Live Tracer + RPC        | Live Tracer               | RPC only              |
| Core EVM changes | Inject hooks             | Distribute hooks manually | Not needed            |

The three adaptation guides are in the pipeline repository:
[standard](https://github.com/Chaintable/pipeline/blob/main/docs/skills/adapt-pipeline-geth/references/adaptation-guide.md),
[legacy](https://github.com/Chaintable/pipeline/blob/main/docs/skills/adapt-pipeline-legacy/references/adaptation-guide-legacy.md),
[Reth](https://github.com/Chaintable/pipeline/blob/main/docs/skills/adapt-pipeline-reth/references/adaptation-guide-reth.md).

### What to change

Using a standard Geth fork as an example:

<Steps>
  <Step title="Extend the tracing hooks">
    Add `OnCommit` and `OnBlockDBStart` in `core/tracing/hooks.go`.
  </Step>

  <Step title="Export the state diff">
    Add `StateDiff()` in `core/state/statedb.go` to convert the commit set into pipeline's types.
  </Step>

  <Step title="Distribute the hooks">
    Call the new hooks on the block processing path in `core/blockchain.go`, and compute the reorg and publish the Kafka notification when the canonical head is determined.
  </Step>

  <Step title="Register the live tracer">
    Create `eth/tracers/live/pipeline.go` and register the pipeline tracer in `LiveDirectory`.
  </Step>

  <Step title="Add the RPC">
    Implement `trace_debankBlock` and register the `trace` namespace in `eth/backend.go`.
  </Step>

  <Step title="Validate">
    Once `go build ./...` passes, run a real chain and compare the output of `trace_debankBlock` with the objects on S3.
  </Step>
</Steps>

The reference implementation is the diff of the [write node](/en/components/go-ethereum-x#added-integration-points) against upstream, about 2000 lines in total.

### Adapting with Claude Code

The pipeline repository ships with adaptation skills that detect the client type automatically and guide you stage by stage:

```text theme={null}
/adapt-pipeline /path/to/your-client
```

It scans for signals such as `Cargo.toml`, `go.mod`, `tracing.Hooks`, and `vm.EVMLogger` to determine the client type, checks whether an integration already exists, and then routes to the corresponding specialized skill: `/adapt-pipeline-geth`, `/adapt-pipeline-legacy`, or `/adapt-pipeline-reth`.

The flow for each stage is: probe the code structure → consult the guide → generate the changes → run `go build` or `cargo check` to validate.

## Read side: add an executor to leafage-evm

If the new chain's EVM behavior is identical to a chain that is already supported, just start with the corresponding `--evm-type` and the correct `--chain-cfg` (chain ID). A new executor is only needed when the behavior differs.

What to change:

| Location                                       | Content                                                        |
| ---------------------------------------------- | -------------------------------------------------------------- |
| `crates/leafage-evm-chains/src/{chain}/`       | Hard fork spec and chain-specific precompiles                  |
| `crates/leafage-evm-rpc/src/api_impl/{chain}/` | `EvmExecutor` trait implementation                             |
| `crates/leafage-evm-rpc/src/api_impl/core.rs`  | Add a branch to `MultiChainCfgEnv`                             |
| `crates/leafage-evm-rpc/src/api_impl/build.rs` | Add a branch to the build path                                 |
| `bin/leafage-evm/src/standalone.rs`            | The list of `--evm-type` values and configuration construction |

A typical configuration branch:

```rust theme={null}
"citrea" => {
    let mut chain_cfg = CfgEnv::new_with_spec(CitreaHardfork::from(MainnetSpecId::AMSTERDAM));
    chain_cfg.disable_balance_check = true;
    chain_cfg.disable_eip3607 = true;
    chain_cfg.disable_block_gas_limit = true;
    chain_cfg.disable_base_fee = true;
    chain_cfg.chain_id = chain_id;
    chain_cfg.tx_gas_limit_cap = Some(gas_cap);
    Ok(MultiChainCfgEnv::Citrea(chain_cfg))
}
```

The `disable_*` settings are standard for state queries: `eth_call` does not need to check balances, the base fee, or the block gas limit.

<Tip>
  Start with the closest chain in `leafage-evm-chains`. OP Stack chains can follow `base` or `mantle`, standalone EVM-compatible chains can follow `citrea` or `iotex`, and chains that need custom precompiles can follow `bsc` or `cosmos`.
</Tip>

### Historical ranges without data

Some chains have historical ranges where block diffs cannot be obtained (typically OP pre-bedrock). Use `--historical-rpc` to specify an external RPC and `--historical-height` to specify the fork height; queries below the threshold are forwarded out.

## Validation

<Steps>
  <Step title="Compare state">
    For the same batch of addresses and storage slots, compare the `eth_getBalance`, `eth_getStorageAt`, and `eth_getCode` return values from leafage-evm and the chain's official full node.
  </Step>

  <Step title="Compare call results">
    Use `leafage-bench` to send the same corpus to both leafage-evm and a full node, and compare the `eth_call` return values and latency.

    ```bash theme={null}
    ./target/release/leafage-bench run \
      --corpus bin/leafage-bench/corpus/corpus.json \
      --target http://leafage-evm:8545 \
      --compare http://node:8545
    ```
  </Step>

  <Step title="Watch reorgs">
    Watch the `changeType: 2` notifications for a while and confirm that leafage-evm's chain head follows correctly. Chains with deeper reorgs need a correspondingly larger `--catchup-safe-depth`.
  </Step>
</Steps>

## Deployment

Deploying a new chain is identical to existing chains; only the `chainID` differs: Kafka topics, S3 prefixes, and etcd keys all start with the chain ID, so they are naturally isolated. nodex-proxy does not need a restart; it discovers the new chain's nodes through etcd.

See the [deployment guide](/en/guides/deployment) for the specific steps.
