> For the complete documentation index, see [llms.txt](https://docs.astralbeam.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.astralbeam.io/for-developers/multi-chain.md).

# Multi-Chain Support

AstralBeam supports multiple EVM chains connecting to Casper Network. This page documents the multi-chain architecture and how chain isolation is maintained.

## Overview

The bridge supports bridging tokens between Casper and multiple EVM chains simultaneously:

```mermaid
flowchart TB
    subgraph EVMChains[EVM Chains Testnets]
        subgraph Active[Active Networks]
            Sepolia[Ethereum Sepolia Chain ID: 11155111]
            Base[Base Sepolia Chain ID: 84532]
            Polygon[Polygon Amoy Chain ID: 80002]
        end
        subgraph ComingSoon[Coming Soon]
            Arbitrum[Arbitrum Sepolia Chain ID: 421614]
            BSC[BNB Testnet Chain ID: 97]
        end
    end

    subgraph Casper[Casper Network]
        TokenFactory[TokenFactory Single Instance]
        EthClient[EthClient Multi-Chain Storage]
    end

    Sepolia <--> TokenFactory
    Base <--> TokenFactory
    Polygon <--> TokenFactory
    Arbitrum -.-> TokenFactory
    BSC -.-> TokenFactory
    TokenFactory <--> EthClient
```

## Chain Identification

### Chain IDs

Each EVM chain is uniquely identified by its Chain ID:

**Supported Testnets:**

| Network                 | Chain ID | Block Time | Chain Default (global floor applies) | Protocol `finalized` Lag                                             | Status                                          |
| ----------------------- | -------- | ---------- | ------------------------------------ | -------------------------------------------------------------------- | ----------------------------------------------- |
| Ethereum Sepolia        | 11155111 | \~12s      | 6                                    | Protocol tag; testnet lag not measured in the sourced mainnet sample | **Active**                                      |
| Base Sepolia            | 84532    | \~2s       | 64                                   | Protocol tag; testnet lag not measured in the sourced mainnet sample | **Active**                                      |
| Polygon Amoy            | 80002    | \~2s       | 15                                   | 0.1 min measured; deterministic milestone finality                   | **Active**                                      |
| Arbitrum Sepolia        | 421614   | \~0.25s    | 12                                   | Protocol tag; not measured in the 2026-07-26 sample                  | Coming Soon                                     |
| BNB Smart Chain Testnet | 97       | \~3s       | 6                                    | Protocol tag; deterministic PoSA finality                            | Coming Soon                                     |
| Robinhood Chain Testnet | 46630    | \~0.4s     | 6000                                 | `finalized` tag; 33.1 min / 4,916-block measured peak                | **Active; finalized-mode round trip certified** |

**Corresponding Mainnets (future):**

| Network          | Chain ID | Block Time | Chain Default (global floor applies) | Protocol `finalized` Lag                               |
| ---------------- | -------- | ---------- | ------------------------------------ | ------------------------------------------------------ |
| Ethereum Mainnet | 1        | \~12s      | 64                                   | 14.4 min measured                                      |
| Base Mainnet     | 8453     | \~2s       | 64                                   | 16.0 min measured                                      |
| Arbitrum One     | 42161    | \~0.25s    | 64                                   | 17.0 min measured                                      |
| Polygon Mainnet  | 137      | \~2s       | 15                                   | 0.1 min measured; deterministic milestone finality     |
| BNB Smart Chain  | 56       | \~3s       | 15                                   | \~0 min measured; deterministic PoSA finality          |
| Robinhood Chain  | 4663     | \~0.1s     | 15000                                | `finalized` tag; 19.9 min / 11,956-block measured peak |

Robinhood mainnet retains a NULL locker, remains disabled, and requires separate deployment approval. Testnet points to verified V16 locker `0xaF0856690910F75DFA48e5F7CA4Af669be6c338d` and TEST token `0xc65db6eE6f16919332275B23D80C71A0dd478EF6`; its finalized-mode lock → mint → burn → unlock round trip is certified and the testnet route is enabled. Measurements were captured from production RPCs on 2026-07-26 and will drift with chain conditions. The relayer does not infer finality from these wall-clock values: it reads each chain's `finalized` block tag and also retains the configured depth floor. Polygon and BNB are deterministic-finality chains, not optimistic rollups. Robinhood's floors cover the measured sawtooth peak plus roughly 22–25% headroom; the protocol-native tag remains the primary gate. Robinhood testnet was measured at 0.404 seconds over 500 blocks (Blockscout average 414 ms), so its configured block time is 0.4 seconds—not the 0.1-second mainnet value.

### Cold-start scan lookback

When no persisted cursor exists, the EVM→Casper worker derives its log-scan lookback from wall-clock time rather than using a flat block count:

```
max(1000, ceil(3600 / blockTimeSeconds))
```

The 1,000-block floor preserves the historical minimum, while the one-hour budget prevents fast chains from silently skipping older locks or wrapped burns after a cursorless restart. At Robinhood's configured block times this yields 36,000 blocks on mainnet and 9,000 on testnet; both scan paths already split ranges into 5,000-block `eth_getLogs` chunks. Normal operation still relies on persisted per-chain cursors, so this is a recovery floor rather than a substitute for durable state.

### Chain ID in Attestations

All attestations include the source chain ID for security:

```typescript
interface BlockHeaderAttestation {
    chainId: number;         // Source EVM chain
    blockNumber: bigint;
    blockHash: string;
    stateRoot: string;
    receiptsRoot: string;
}

interface LockAttestation {
    sourceChainId: bigint;   // Where tokens were locked
    evmBlockNumber: bigint;
    evmTxIndex: number;
    evmLogIndex: number;
    evmToken: string;
    casperRecipient: string;
    amount: bigint;
    nonce: bigint;
}
```

## Per-Chain Contracts

### EVM Side: Separate Lockers

Each EVM chain has its own MultiSigLocker deployment:

```mermaid
flowchart TB
    subgraph Sepolia[Ethereum Sepolia]
        SepoliaLocker[MultiSigLocker 0x...]
        SepoliaUSDC[USDC 0x1c7D...]
        SepoliaWETH[WETH 0xfFf9...]
    end

    subgraph Base[Base Sepolia]
        BaseLocker[MultiSigLocker 0x...]
        BaseUSDC[USDC 0x036C...]
        BaseWETH[WETH 0x4200...]
    end

    SepoliaUSDC --> SepoliaLocker
    SepoliaWETH --> SepoliaLocker
    BaseUSDC --> BaseLocker
    BaseWETH --> BaseLocker
```

**Why Separate Lockers?**

* Tokens on different chains are not fungible
* Liquidity is isolated per chain
* Security: compromise of one chain doesn't affect others
* Independent upgrades and maintenance

### Casper Side: Unified Entry Point

A single TokenFactory handles all EVM chains:

```mermaid
flowchart TB
    subgraph TokenFactory[TokenFactory Casper]
        TokenMappings[Token Mappings]

        subgraph Sepolia[Sepolia 11155111]
            S_USDC[0x1c7D... → CEP18-A]
            S_WETH[0xfFf9... → CEP18-B]
        end

        subgraph Base[Base 84532]
            B_USDC[0x036C... → CEP18-C]
            B_WETH[0x4200... → CEP18-D]
        end
    end
```

**Key Points:**

* Same CEP-18 token can represent tokens from multiple chains
* Or separate CEP-18 per source chain (configurable)
* Token mappings include chain ID

## Block Header Storage

### Chain-Aware Storage

The EthClient stores block headers per-chain:

```rust
// Storage key: (chain_id, block_number)
block_headers: Mapping<(u64, u64), BlockHeader>

// Example entries:
// (11155111, 1000000) → Sepolia block 1M
// (84532, 1000000)    → Base block 1M (different block!)
// (11155111, 1000001) → Sepolia block 1M+1
```

## Cross-Chain Replay Protection

### Attack Vector

Without chain ID in attestation hashes, an attacker could:

1. Lock tokens on Sepolia
2. Get valid signatures for the lock attestation
3. Replay the same attestation claiming it's from Base
4. Mint tokens twice!

### Prevention

Chain ID is included in all attestation hashes:

```typescript
// Block header attestation hash
const hash = ethers.solidityPackedKeccak256(
    ['uint64', 'uint64', 'bytes32', 'bytes32', 'bytes32'],
    [
        chainId,        // FIRST: prevents replay
        blockNumber,
        blockHash,
        stateRoot,
        receiptsRoot
    ]
);
```

This means:

* Signatures for Sepolia attestation ≠ valid for Base
* Hash changes with chain ID → signatures invalid
* Each chain requires its own valid signatures

## Token Tracking

### Source Chain Recording

When CEP-18 tokens are minted, the source chain is recorded:

```rust
struct MintRecord {
    source_chain_id: u64,   // Where tokens came from
    evm_token: Address,     // Original ERC-20 address
    amount: U256,
    recipient: AccountHash,
    nonce: u64,
}
```

### Burning to Correct Chain

When burning CEP-18 to bridge back:

```mermaid
flowchart LR
    subgraph Casper
        CEP18[CEP-18 USDC from Sepolia]
    end

    subgraph Burn[Burn Decision]
        Check{"Source Chain?"}
    end

    subgraph Unlock
        Sepolia[Unlock on Sepolia ]
        Base[Unlock on Base ✗]
    end

    CEP18 --> Check
    Check -->|11155111| Sepolia
    Check -->|84532| Base
```

**Rules:**

* Tokens can only be burned to their source chain
* Attempting to unlock on wrong chain fails
* Prevents liquidity mismatch

## Multi-Chain Configuration

### Relayer Configuration

Each relayer is configured with multiple chain handlers:

```typescript
// config.ts
export const chainConfigs: EVMChainConfig[] = [
    {
        chainId: 11155111,
        name: 'Sepolia',
        rpcUrl: 'https://rpc.sepolia.org',
        lockerAddress: '0x...',
        confirmations: 64,  // Finality blocks
    },
    {
        chainId: 84532,
        name: 'Base Sepolia',
        rpcUrl: 'https://sepolia.base.org',
        lockerAddress: '0x...',
        confirmations: 64,
    }
];
```

### Worker Architecture

Workers handle multiple chains concurrently:

```mermaid
flowchart TB
    subgraph Relayer[Relayer Node]
        subgraph EVM2Casper[EVM→Casper Worker]
            SepoliaHandler[Sepolia Handler]
            BaseHandler[Base Handler]
        end

        subgraph Casper2EVM[Casper→EVM Worker]
            C2E[Unified Handler routes by destination]
        end
    end

    SepoliaHandler --> SepoliaRPC[Sepolia RPC]
    BaseHandler --> BaseRPC[Base RPC]
    C2E --> SepoliaRPC
    C2E --> BaseRPC
```

## Adding New Chains

### Prerequisites

To add a new EVM chain:

1. **EVM Compatibility**: Chain must be EVM-compatible
2. **Finality Model**: Understand finality requirements
3. **RPC Access**: Reliable RPC endpoints available
4. **Token Addresses**: Know addresses of tokens to bridge

### Deployment Steps

```mermaid
flowchart TB
    A[1. Deploy MultiSigLocker on new chain] --> B
    B[2. Register relayers on new locker] --> C
    C[3. Register chain in TokenFactory] --> D
    D[4. Register token mappings per token] --> E
    E[5. Update relayer configs add chain handler]
```

### Configuration Example

Adding Arbitrum Sepolia:

```typescript
// 1. Add to chain configs
const arbitrumConfig: EVMChainConfig = {
    chainId: 421614,
    name: 'Arbitrum Sepolia',
    rpcUrl: 'https://sepolia-rollup.arbitrum.io/rpc',
    lockerAddress: '0x...', // After deployment
    confirmations: 64,
};

// 2. Deploy locker
// npx hardhat deploy --network arbitrum-sepolia

// 3. Register in TokenFactory (Casper)
// bridge-cli casper admin register_chain 421614 "Arbitrum Sepolia"

// 4. Register token mappings
// bridge-cli casper admin register_token 421614 0xUSDC... hash-...
```

## Liquidity Management

### Per-Chain Liquidity

Each chain's locker has independent liquidity:

```
Sepolia Locker:
  - USDC: 10,000 locked
  - WETH: 5 locked

Base Locker:
  - USDC: 5,000 locked
  - WETH: 2 locked

Casper (Total Minted):
  - USDC: 15,000 CEP-18 (10k from Sepolia + 5k from Base)
  - WETH: 7 CEP-18 (5 from Sepolia + 2 from Base)
```

### Liquidity Queries

```bash
# Query per-chain liquidity via API
curl "https://api.csprbridge.com/api/v1/tokens" | jq '
  .tokens[] | {
    symbol,
    liquidity: .liquidityPerChain
  }
'

# Example response:
{
  "symbol": "USDC",
  "liquidity": {
    "11155111": "10000000000",  // 10,000 USDC on Sepolia
    "84532": "5000000000"        // 5,000 USDC on Base
  }
}
```

## Monitoring Multi-Chain

### Per-Chain Metrics

Key metrics to monitor per chain:

| Metric                          | Labels                   | Description             |
| ------------------------------- | ------------------------ | ----------------------- |
| `bridge_events_detected_total`  | `chain_id`, `event_type` | Events per chain        |
| `bridge_blocks_submitted_total` | `chain_id`               | Block headers per chain |
| `bridge_attestations_total`     | `chain_id`, `direction`  | Attestations per chain  |
| `bridge_latency_seconds`        | `chain_id`, `operation`  | Latency per chain       |

### Grafana Dashboard

The multi-chain dashboard shows:

* Transaction volume per chain
* Success rates per chain
* Latency comparison across chains
* Liquidity levels per chain

```mermaid
flowchart LR
    subgraph Dashboard[Grafana Dashboard]
        Overview[Overview All Chains]
        Sepolia[Sepolia Panel]
        Base[Base Panel]
        Alerts[Alert Panel]
    end

    Overview --> Sepolia
    Overview --> Base
    Sepolia --> Alerts
    Base --> Alerts
```

## Security Considerations

### Chain Isolation

* Each chain's security is independent
* Compromise of one chain doesn't affect others
* Relayer keys are shared, but attestations are chain-specific

### Validation Rules

```rust
// TokenFactory validation
fn validate_mint(attestation: &LockAttestation, proof: &Proof) {
    // 1. Get receipts root for THIS chain
    let receipts_root = eth_client.get_receipts_root(
        attestation.source_chain_id,  // Must match!
        attestation.eth_block_number
    );

    // 2. Verify proof against chain-specific root
    assert!(verify_mpt_proof(receipts_root, proof));

    // 3. Verify attestation signed with chain_id included
    let hash = compute_lock_hash_with_chain_id(attestation);
    assert!(verify_signatures(hash, signatures));
}
```

### Operational Security

| Risk                   | Mitigation                       |
| ---------------------- | -------------------------------- |
| Wrong chain submission | Chain ID validation in contracts |
| Replay attack          | Chain ID in attestation hashes   |
| Liquidity drain        | Per-chain nonce tracking         |
| Configuration error    | Automated config validation      |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.astralbeam.io/for-developers/multi-chain.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
