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

# Multi-Chain Operations

> Configure and operate Clavion across multiple EVM chains.

## What you will learn

* Which EVM chains Clavion supports and how to configure RPC endpoints
* How the RPC resolution logic determines single-chain vs. multi-chain mode
* How chain selection works via TxIntent
* DEX router addresses per chain
* How to add support for a new chain

## Supported chains

| Chain    | Chain ID | Environment Variable | Status              |
| -------- | -------- | -------------------- | ------------------- |
| Ethereum | 1        | `ISCL_RPC_URL_1`     | Supported           |
| Optimism | 10       | `ISCL_RPC_URL_10`    | Supported           |
| Arbitrum | 42161    | `ISCL_RPC_URL_42161` | Supported           |
| Base     | 8453     | `ISCL_RPC_URL_8453`  | Supported (default) |

Additional EVM chains can be added by configuring an RPC URL and updating the policy config.

## RPC configuration

### Single-chain setup

For a single chain, set one RPC URL:

<CodeGroup>
  ```bash ISCL_RPC_URL format theme={null}
  ISCL_RPC_URL_8453=https://base-mainnet.g.alchemy.com/v2/YOUR_KEY
  ```

  ```bash Legacy BASE_RPC_URL (equivalent to chain 8453) theme={null}
  BASE_RPC_URL=https://base-mainnet.g.alchemy.com/v2/YOUR_KEY
  ```
</CodeGroup>

A single URL creates a plain `ViemRpcClient`. All operations target this one chain.

### Multi-chain setup

For multiple chains, set multiple RPC URLs:

```bash theme={null}
ISCL_RPC_URL_1=https://eth-mainnet.g.alchemy.com/v2/KEY
ISCL_RPC_URL_10=https://opt-mainnet.g.alchemy.com/v2/KEY
ISCL_RPC_URL_42161=https://arb-mainnet.g.alchemy.com/v2/KEY
ISCL_RPC_URL_8453=https://base-mainnet.g.alchemy.com/v2/KEY
```

Multiple URLs create an `RpcRouter` that dispatches calls per chain ID.

### Resolution logic

The RPC resolution process runs at startup via `parseRpcEnv()` in `packages/core/src/rpc/parse-rpc-env.ts`:

<Steps>
  <Step title="Scan environment variables">
    Find all variables matching the pattern `ISCL_RPC_URL_{digits}`.
  </Step>

  <Step title="Apply BASE_RPC_URL fallback">
    If `BASE_RPC_URL` is set and `ISCL_RPC_URL_8453` is not, map `BASE_RPC_URL` to chain 8453.
  </Step>

  <Step title="Determine client type">
    * **One URL found** -- plain `ViemRpcClient`
    * **Multiple URLs found** -- `RpcRouter` (dispatches per chain)
    * **No URLs found** -- preflight and broadcast disabled (build-only mode)
  </Step>
</Steps>

<Note>
  `ISCL_RPC_URL_8453` always takes priority over `BASE_RPC_URL`. If both are set, the `ISCL_RPC_URL_8453` value is used.
</Note>

## How chain selection works

Every operation is scoped to a chain via the `TxIntent.chain.chainId` field:

```json theme={null}
{
  "chain": { "type": "evm", "chainId": 8453 }
}
```

This chain ID drives all downstream routing:

* **Transaction builders** select chain-specific contract addresses (e.g., Uniswap V3 router per chain)
* **Preflight simulation** uses the chain-scoped RPC for `eth_call` and `estimateGas`
* **Signing** uses the chain-scoped RPC for nonce, gas fees, and broadcast
* **Balance lookups** accept an optional `?chainId=N` query parameter:

```bash theme={null}
curl http://localhost:3100/v1/balance/0xToken/0xAccount?chainId=10
```

If no RPC is configured for the requested chain, the endpoint returns `502 no_rpc_client`.

## DEX router addresses

### Uniswap V3 routers

| Chain    | Chain ID | Router Address                               |
| -------- | -------- | -------------------------------------------- |
| Ethereum | 1        | `0x68b3465833fb72B5A828cCEEEAA56DFb8BA3DaFE` |
| Optimism | 10       | `0x68b3465833fb72B5A828cCEEEAA56DFb8BA3DaFE` |
| Arbitrum | 42161    | `0x68b3465833fb72B5A828cCEEEAA56DFb8BA3DaFE` |
| Base     | 8453     | `0x2626664c2603336E57B271c5C0b26F421741e481` |

<Warning>
  Base uses a **different** Uniswap V3 router address than the other chains. Make sure you use the correct address for your target chain.
</Warning>

### 1inch AggregationRouterV6

Same address on all supported chains:

```text theme={null}
0x111111125421cA6dc452d289314280a0f8842A65
```

<Note>
  The 1inch router requires the `ONEINCH_API_KEY` environment variable. When not set, 1inch intents silently fall back to Uniswap V3.
</Note>

## Policy configuration for multi-chain

### allowedChains

The `allowedChains` field in PolicyConfig controls which chains are permitted:

```json theme={null}
{
  "allowedChains": [1, 10, 42161, 8453]
}
```

A TxIntent targeting a chain not in this list is denied with: `"Chain N not in allowed chains [...]"`.

### Token and contract allowlists

Allowlists are **not chain-scoped** in PolicyConfig v1. The same token address may exist on multiple chains (e.g., USDC has different addresses per chain). Include all relevant addresses:

```json theme={null}
{
  "tokenAllowlist": [
    "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
    "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85"
  ]
}
```

<Tip>
  USDC has a different contract address on each chain. When configuring multi-chain, make sure to include the USDC address for every chain you support.
</Tip>

## Adding a new chain

<Steps>
  <Step title="Configure RPC">
    Set `ISCL_RPC_URL_{chainId}` with a valid RPC endpoint for the new chain.
  </Step>

  <Step title="Update policy">
    Add the chain ID to `allowedChains` in your PolicyConfig.
  </Step>

  <Step title="Add router addresses (for swaps)">
    If Uniswap V3 is deployed on the chain, add its router address to `UNISWAP_V3_ROUTERS` in `packages/core/src/tx/builders/swap-builder.ts`. The 1inch router address is the same on all chains.
  </Step>

  <Step title="Add token addresses">
    Add chain-specific token addresses to your allowlists.
  </Step>
</Steps>

<Note>
  No code changes are required for basic transfers and approvals -- only RPC configuration and policy updates.
</Note>

## Troubleshooting

<Accordion title="PreflightService requires an RPC client for chain N">
  **Cause:** No `ISCL_RPC_URL_{N}` environment variable set for the requested chain.

  **Fix:** Add `ISCL_RPC_URL_{N}=https://your-rpc-provider/...` to your environment.
</Accordion>

<Accordion title="Chain N not in allowed chains">
  **Cause:** PolicyConfig `allowedChains` does not include the requested chain ID.

  **Fix:** Update your policy config to include the chain ID in `allowedChains`.
</Accordion>

<Accordion title="Balance lookup requires an RPC client for chain N">
  **Cause:** Same as preflight -- no RPC configured for that chain.

  **Fix:** Add the appropriate `ISCL_RPC_URL_{N}` variable.
</Accordion>

<Accordion title="Transactions work on chain A but not chain B">
  Verify all of the following:

  1. `ISCL_RPC_URL_{B}` is set and the URL is valid
  2. Chain B is in `allowedChains`
  3. For swaps: the router address is configured for chain B
  4. Token addresses in allowlists exist on chain B (addresses differ per chain)
</Accordion>

## Next steps

* [Configuration Reference](/reference/config-reference) -- Full environment variable reference and PolicyConfig options
* [Production Deployment](/guides/production-deployment) -- Production deployment with Docker and compose
* [Policy Engine](/concepts/policy-engine) -- Policy rules and enforcement
