> 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/bridge-flows.md).

# Bridge Flows

This page documents the complete flow for each bridge direction, including all steps, verifications, and error handling.

## EVM → Casper Flow (Lock and Mint)

### Overview

When bridging from an EVM chain to Casper:

1. User locks ERC-20 tokens in the MultiSigLocker contract
2. Relayers wait for finality and generate proofs
3. Relayers submit block headers and mint attestations to Casper
4. CEP-18 tokens are minted to the user's Casper address

### Detailed Flow

```mermaid
sequenceDiagram
    participant User
    participant MetaMask
    participant Locker as MultiSigLocker
    participant EVMChain as EVM Chain
    participant R1 as Relayer 1
    participant R2 as Relayer 2
    participant R3 as Relayer 3
    participant P2P as P2P Network
    participant EthClient
    participant TokenFactory
    participant CEP18 as CEP-18 Token

    Note over User,CEP18: Phase 1: Token Approval
    User->>MetaMask: Initiate bridge
    MetaMask->>Locker: approve(locker, amount)
    Locker-->>MetaMask: Approval confirmed

    Note over User,CEP18: Phase 2: Lock Tokens
    MetaMask->>Locker: lock(token, amount, casperRecipient)
    Locker->>Locker: Transfer tokens to locker
    Locker->>EVMChain: Emit Locked event
    Locker-->>MetaMask: Lock tx confirmed

    Note over User,CEP18: Phase 3: Wait for Finality
    EVMChain->>EVMChain: Mine ~64 blocks (~13 min)
    R1->>EVMChain: Poll for finalized blocks
    R2->>EVMChain: Poll for finalized blocks
    R3->>EVMChain: Poll for finalized blocks

    Note over User,CEP18: Phase 4: Generate Proofs
    R1->>EVMChain: Get block header + receipts
    R1->>R1: Generate MPT proof
    R2->>EVMChain: Get block header + receipts
    R2->>R2: Generate MPT proof
    R3->>EVMChain: Get block header + receipts
    R3->>R3: Generate MPT proof

    Note over User,CEP18: Phase 5: Sign & Exchange
    R1->>R1: Sign block header attestation
    R1->>P2P: Broadcast signature
    R2->>R2: Sign block header attestation
    R2->>P2P: Broadcast signature
    R3->>R3: Sign block header attestation
    R3->>P2P: Broadcast signature
    P2P-->>R1: Collect signatures
    P2P-->>R2: Collect signatures
    P2P-->>R3: Collect signatures

    Note over User,CEP18: Phase 6: Submit to Casper
    R1->>EthClient: submit_block_header_with_signatures()
    EthClient->>EthClient: Verify M-of-N signatures
    EthClient->>EthClient: Store block header

    R1->>TokenFactory: mint_with_signatures(attestation, proof, signatures)
    TokenFactory->>EthClient: get_receipts_root(chain_id, block)
    EthClient-->>TokenFactory: Return receipts root
    TokenFactory->>TokenFactory: Verify MPT proof
    TokenFactory->>TokenFactory: Verify M-of-N signatures
    TokenFactory->>CEP18: mint(recipient, amount)
    CEP18-->>User: Tokens minted
```

### Step-by-Step Details

#### Phase 1: Token Approval

**User Action:** Approve the MultiSigLocker to spend tokens

```solidity
// User calls on the ERC-20 token contract
IERC20(tokenAddress).approve(lockerAddress, amount);
```

**Verification:**

* Token contract verifies user has sufficient balance
* Allowance is set for the locker contract

#### Phase 2: Lock Tokens

**User Action:** Lock tokens in the bridge

```solidity
// User calls on the MultiSigLocker
MultiSigLocker.lock(
    tokenAddress,      // ERC-20 token to bridge
    amount,            // Amount to bridge
    casperRecipient    // bytes32 Casper public key
);
```

**Contract Logic:**

1. Transfer tokens from user to locker
2. Increment nonce for replay protection
3. Emit `Locked` event with all details

**Event Emitted (V10 — trailing `chainId` distinguishes it from legacy deployments):**

```solidity
event Locked(
    address indexed token,
    address indexed sender,
    uint256 amount,
    bytes32 indexed casperRecipient,
    uint256 nonce,
    uint256 chainId
);
```

#### Phase 3: Wait for Finality

**Why Wait?**

* Ethereum can reorganize (reorg) recent blocks
* A transaction might be reversed if its block is orphaned
* 64 blocks (\~13 minutes) provides strong finality guarantees

**Relayer Logic:**

```typescript
// Poll for finalized block
const latestFinalized = await provider.getBlock('finalized');
const eventBlock = lockEvent.blockNumber;

if (latestFinalized.number >= eventBlock) {
    // Safe to process
    processLockEvent(lockEvent);
}
```

#### Phase 4: Generate Proofs

**MPT Proof Generation:**

```typescript
// Get the transaction receipt
const receipt = await provider.getTransactionReceipt(txHash);

// Get the block with receipts
const block = await provider.getBlock(blockNumber);

// Generate MPT proof for the receipt
const proof = await generateReceiptProof(
    block.receiptsRoot,
    receipt.index,
    receipts
);
```

**Proof Structure:**

```typescript
interface ReceiptProof {
    key: Buffer;           // RLP-encoded receipt index
    proof: Buffer[];       // MPT proof nodes
    value: Buffer;         // RLP-encoded receipt
}
```

#### Phase 5: Sign and Exchange

**Attestation Hash Computation:**

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

// Sign the hash
const signature = await relayerWallet.signMessage(
    ethers.getBytes(blockHeaderHash)
);
```

**P2P Message Exchange:**

```typescript
// Broadcast signature to P2P network
await p2p.publish('block-headers', {
    chainId,
    blockNumber,
    blockHash,
    stateRoot,
    receiptsRoot,
    signature,
    relayerAddress
});

// Collect signatures from other relayers
const signatures = await collectSignatures(blockHeaderHash, threshold);
```

#### Phase 6: Submit to Casper

**Block Header Submission:**

```rust
// EthClient::submit_block_header_with_signatures
pub fn submit_block_header_with_signatures(
    &mut self,
    chain_id: u64,
    block_number: u64,
    block_hash: H256,
    state_root: H256,
    receipts_root: H256,
    signatures: Vec<Vec<u8>>,
) {
    // 1. Compute attestation hash
    let hash = compute_block_header_attestation_hash(
        chain_id, block_number, block_hash, state_root, receipts_root
    );

    // 2. Verify M-of-N signatures
    let valid_sigs = self.verify_signatures(hash, &signatures);
    require!(valid_sigs >= self.threshold);

    // 3. Store block header
    self.block_headers.set((chain_id, block_number), BlockHeader {
        block_hash, state_root, receipts_root
    });
}
```

**Mint Submission:**

```rust
// TokenFactory::mint_with_signatures
pub fn mint_with_signatures(
    &mut self,
    source_chain_id: u64,
    eth_block_number: u64,
    eth_tx_index: u64,
    eth_log_index: u32,
    erc20_token: H160,
    sender: H160,
    amount: U256,
    casper_recipient: H256,
    receipts_root: H256,
    signatures: Vec<Vec<u8>>,
) {
    // 1. Verify M-of-N signatures on attestation
    let hash = compute_lock_attestation_hash(...);
    let valid_sigs = verify_signatures(hash, &signatures);
    require!(valid_sigs >= self.threshold);

    // 2. Check not already processed (replay protection)
    // event_id is keccak256(source_chain_id ‖ nonce) — the locker's
    // stable deposit nonce, not positional block/tx/log-index fields
    let event_hash = compute_event_id(...);
    require!(!self.processed_events.get(&event_hash));

    // 3. Get CEP-18 token for this ERC-20
    let cep18 = self.get_cep18_for_chain(source_chain_id, erc20_token);

    // 4. Mint tokens to recipient
    cep18.mint(casper_recipient, amount);

    // 5. Mark event as processed
    self.processed_events.set(event_hash, true);
}
```

***

## Casper → EVM Flow (Burn and Unlock)

### Overview

When bridging from Casper to an EVM chain:

1. User burns CEP-18 tokens on Casper
2. Relayers detect the burn event (instant finality)
3. Relayers collect signatures and submit unlock to EVM
4. ERC-20 tokens are transferred to the user's EVM address

### Detailed Flow

```mermaid
sequenceDiagram
    participant User
    participant CasperWallet
    participant TokenFactory
    participant CEP18 as CEP-18 Token
    participant R1 as Relayer 1
    participant R2 as Relayer 2
    participant R3 as Relayer 3
    participant P2P as P2P Network
    participant Locker as MultiSigLocker

    Note over User,Locker: Phase 1: Burn Tokens
    User->>CasperWallet: Initiate bridge
    CasperWallet->>TokenFactory: burn(cep18_address, amount, eth_recipient, target_chain_id)
    TokenFactory->>CEP18: burn(sender, amount)
    CEP18->>CEP18: Reduce balance
    TokenFactory->>TokenFactory: Emit BurnEvent
    TokenFactory-->>CasperWallet: Deploy confirmed

    Note over User,Locker: Phase 2: Detect Event (Instant Finality)
    R1->>TokenFactory: Query events
    R1->>R1: Detect BurnEvent
    R2->>TokenFactory: Query events
    R2->>R2: Detect BurnEvent
    R3->>TokenFactory: Query events
    R3->>R3: Detect BurnEvent

    Note over User,Locker: Phase 3: Generate State Proof
    R1->>R1: Create burn attestation
    R2->>R2: Create burn attestation
    R3->>R3: Create burn attestation

    Note over User,Locker: Phase 4: Sign & Exchange
    R1->>R1: Sign attestation
    R1->>P2P: Broadcast signature
    R2->>R2: Sign attestation
    R2->>P2P: Broadcast signature
    R3->>R3: Sign attestation
    R3->>P2P: Broadcast signature
    P2P-->>R1: Collect signatures

    Note over User,Locker: Phase 5: Submit to EVM
    R1->>Locker: unlock(attestation, signatures)
    Locker->>Locker: Verify M-of-N signatures
    Locker->>Locker: Check nonce not processed
    Locker->>User: Transfer ERC-20 tokens
```

### Step-by-Step Details

#### Phase 1: Burn Tokens

**User Action:** Burn CEP-18 tokens

```rust
// User calls TokenFactory::burn
pub fn burn(
    &mut self,
    cep18_address: Address,
    amount: U256,
    eth_recipient: H160,
    target_chain_id: u64,
) {
    // 1. Verify user has sufficient balance
    let cep18 = Cep18::new(cep18_address);
    let balance = cep18.balance_of(caller());
    require!(balance >= amount);

    // 2. Burn the tokens
    cep18.burn(caller(), amount);

    // 3. Get the corresponding EVM token
    let evm_token = self.get_evm_token_for_cep18(cep18_address);

    // 4. Increment nonce
    let nonce = self.burn_nonce.get() + 1;
    self.burn_nonce.set(nonce);

    // 5. Emit burn event
    emit_event(TokensBurned {
        token: cep18_address,
        sender: caller(),
        amount,
        eth_recipient,
        target_chain_id,
        nonce,
    });
}
```

#### Phase 2: Detect Event

**Why Instant Finality?**

* Casper uses Highway consensus with instant finality
* Once a block is added, it cannot be reverted
* No need to wait for additional confirmations

**Relayer Detection:**

```typescript
// Query Casper events
const events = await casperClient.getEvents(
    tokenFactoryHash,
    lastProcessedBlock
);

// Filter for burn events
const burnEvents = events.filter(e => e.type === 'BurnEvent');

for (const event of burnEvents) {
    await processBurnEvent(event);
}
```

#### Phase 3: Create Attestation

**Burn Attestation Structure:**

```typescript
interface BurnAttestation {
    sourceChainId: bigint;        // Casper chain identifier
    destinationChainId: bigint;   // Target EVM chain ID
    cep18Token: string;           // CEP-18 contract hash
    evmToken: string;             // Corresponding ERC-20 address
    sender: string;               // Casper account hash
    evmRecipient: string;         // EVM address to receive tokens
    amount: bigint;               // Amount burned
    nonce: bigint;                // Replay protection nonce
    casperBlockHeight: bigint;    // Block where burn occurred
}
```

#### Phase 4: Sign and Exchange

**Attestation Hash:**

```typescript
const burnHash = ethers.solidityPackedKeccak256(
    ['uint64', 'uint64', 'bytes32', 'address', 'bytes32', 'address', 'uint256', 'uint64'],
    [
        attestation.sourceChainId,
        attestation.destinationChainId,
        attestation.cep18Token,
        attestation.evmToken,
        attestation.sender,
        attestation.evmRecipient,
        attestation.amount,
        attestation.nonce
    ]
);

// Sign
const signature = await relayerWallet.signMessage(ethers.getBytes(burnHash));
```

#### Phase 5: Submit to EVM

**Unlock Transaction:**

```solidity
function unlock(
    BurnAttestation calldata attestation,
    bytes[] calldata signatures
) external {
    // 1. Verify M-of-N signatures
    bytes32 hash = computeBurnAttestationHash(attestation);
    uint256 validSignatures = 0;

    for (uint i = 0; i < signatures.length; i++) {
        address signer = recoverSigner(hash, signatures[i]);
        if (isRelayer[signer]) {
            validSignatures++;
        }
    }
    require(validSignatures >= threshold, "Insufficient signatures");

    // 2. Check nonce not processed (replay protection)
    bytes32 nonceKey = keccak256(abi.encodePacked(
        attestation.sourceChainId,
        attestation.nonce
    ));
    require(!processedNonces[nonceKey], "Already processed");
    processedNonces[nonceKey] = true;

    // 3. Transfer tokens from locker to recipient
    IERC20(attestation.evmToken).transfer(
        attestation.evmRecipient,
        attestation.amount
    );

    // 4. Emit event (4th field is the attestation's Casper state root)
    emit Unlocked(
        attestation.evmToken,
        attestation.evmRecipient,
        attestation.amount,
        attestation.casperStateRoot,
        attestation.casperEventKey
    );
}
```

***

## Native Token Flows

### wCSPR/sCSPR to EVM (Lock Native on Casper)

```mermaid
sequenceDiagram
    participant User
    participant TokenFactory
    participant Relayers
    participant Locker as MultiSigLocker

    User->>TokenFactory: lock_native(amount, evmRecipient) + CSPR
    TokenFactory->>TokenFactory: Hold native CSPR
    TokenFactory->>TokenFactory: Emit NativeLockEvent
    Relayers->>Relayers: Collect signatures
    Relayers->>Locker: mintWrapped(attestation, signatures)
    Locker->>User: wCSPR minted on EVM
```

### Wrapped wCSPR/sCSPR → Casper (Unlock Native on Casper)

```mermaid
sequenceDiagram
    participant User
    participant Locker as MultiSigLocker
    participant Relayers
    participant TokenFactory

    User->>Locker: burnWrapped(wCSPR, amount, casperRecipient)
    Locker->>Locker: Burn wCSPR
    Locker->>Locker: Emit WrappedBurnEvent
    Relayers->>Relayers: Collect signatures
    Relayers->>TokenFactory: unlock_native_with_signatures()
    TokenFactory->>User: Native CSPR transferred
```

***

## Error Handling

### Common Failure Scenarios

| Scenario                    | Detection                   | Recovery                          |
| --------------------------- | --------------------------- | --------------------------------- |
| User rejects tx             | Wallet returns rejection    | User must retry                   |
| Insufficient gas            | Transaction reverts         | User adds more ETH/CSPR           |
| Finality timeout            | No finality after 30 min    | Alert operators                   |
| Signature collection failed | \<M signatures after 10 min | Retry P2P broadcast               |
| Proof verification failed   | Contract reverts            | Investigate data mismatch         |
| Nonce already processed     | Contract reverts            | Transaction was already processed |

### Retry Logic

```typescript
async function submitWithRetry(
    submission: () => Promise<void>,
    maxAttempts: number = 3
): Promise<void> {
    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
        try {
            await submission();
            return;
        } catch (error) {
            if (attempt === maxAttempts) throw error;

            // Exponential backoff
            await sleep(1000 * Math.pow(2, attempt));
        }
    }
}
```

### Idempotency

All bridge operations are idempotent:

* Nonces prevent double-processing
* Re-submitting a processed transaction harmlessly reverts
* Relayers can safely retry failed submissions


---

# 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/bridge-flows.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.
