Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,51 @@

This repository contains production smart contracts for the Across Protocol cross-chain bridge.

## Architecture

Across uses a **hub-and-spoke** model with optimistic verification to enable fast cross-chain token transfers.

### Core Contracts

- **HubPool** (Ethereum L1) — Central contract that manages LP liquidity, validates cross-chain transfers via merkle root bundles, and coordinates rebalancing across all SpokePools. Uses UMA's Optimistic Oracle for dispute resolution.
- **SpokePool** (each L2/sidechain) — Deployed on every supported chain. Handles user deposits, relayer fills, and execution of merkle leaves (relayer refunds, slow fills). UUPS upgradeable. Chain-specific variants (e.g. `Arbitrum_SpokePool`, `Optimism_SpokePool`) override admin verification and bridge-specific logic.
- **Chain Adapters** (`contracts/chain-adapters/`) — Stateless contracts called via `delegatecall` from HubPool to bridge tokens and relay messages to each L2. Each adapter wraps a chain's native bridge (Arbitrum Inbox, OP Stack messenger, Polygon FxPortal, etc.). Also supports CCTP, LayerZero OFT, and Wormhole.

### Key Roles

| Role | Description |
|------|-------------|
| **Depositor** | Calls `deposit()` on origin SpokePool to initiate a cross-chain transfer |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we shouldn't specify a specific function since there are multiple entry points to depositing now, including sponsored, gasless flows?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also maybe worth noting the depositor is a just an end user, not technical, doesn't have any other requirements besides wanting to move money across chains

| **Relayer** | Fills deposits on destination chain by fronting tokens, later reimbursed via merkle proof |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth noting that relayers compete with each other on speed and also their ability to manage their cross chain inventory

| **Data Worker** | Off-chain agent that aggregates deposits/fills, constructs merkle trees, and calls `proposeRootBundle()` on HubPool (stakes a bond) |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Importantly also validates not just aggregates

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth noting that the dataworker is supposed to be able to handle sending a lot of on chain requests across different chains with a relatively longer lookback window than the relayer is expected to maintain.

The dataworker unlike the relayer is not expected to be "fast" but they are rpc- and to a lesser extent, compute-, intensive

| **Disputer** | Monitors proposed bundles; can call `disputeRootBundle()` during the challenge period if a bundle is invalid |
| **LP** | Deposits L1 tokens into HubPool to earn relay fees |

### Protocol Flow

1. **Deposit**: User locks tokens in origin SpokePool → `FundsDeposited` event emitted
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sets the amount of fees that they're willing to pay to a filler who fulfills their deposit.

Importantly, the relayer has a decision to determine if the deposit is profitable or not.

I think the profitability decision matrix is maybe important to reference because it might help contributors to be aware that fair prices need to be communicated to depositors somehow, currently done through a hosted API service or directly from an exclusive filler.

I think this will help improve future iterations of these contracts as they really should be "quote API aware" for example

2. **Fill**: Relayer sees event, calls `fillRelay()` on destination SpokePool → tokens sent to recipient
3. **Bundle Proposal**: Data worker aggregates fills across all chains into three merkle trees (pool rebalances, relayer refunds, slow fills) and proposes on HubPool
4. **Challenge Period**: Bundle is open for dispute (default 2 hours). If disputed, UMA oracle resolves
5. **Execution**: After liveness, `executeRootBundle()` sends tokens via adapters and relays roots to SpokePools
6. **Refund**: Relayers call `executeRelayerRefundLeaf()` with merkle proofs to claim repayment
7. **Slow Fill** (fallback): If no relayer fills before deadline, the protocol fills from SpokePool reserves via `executeSlowRelayLeaf()`

### Cross-Chain Ownership

HubPool on L1 owns all L2 SpokePools. Admin functions are relayed cross-chain via `relaySpokePoolAdminFunction()` through the appropriate chain adapter. Each SpokePool's `_requireAdminSender()` verifies the caller using chain-specific logic (address aliasing on Arbitrum, CrossDomainMessenger on OP Stack, etc.).

### Supported Chains

SpokePools exist for: Ethereum, Arbitrum, Optimism, Base, Polygon, Polygon zkEVM, zkSync, Scroll, Linea, Blast, Boba, Lisk, WorldChain, Ink, Lens, Mode, and others via `Universal_SpokePool` (generic OP Stack).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`Universal_SpokePool` (generic OP Stack).

This looks incorrect. Instead, it's a Spoke that can be used on any EVM-like chain: L2 or L1 by proving ongoing Ethereum state updates via ZK proofs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated 24c4460


### Key Libraries

- **CircleCCTPAdapter** — USDC bridging via Circle's CCTP (V1/V2), with burn-limit splitting for large transfers
- **OFTTransportAdapter** — Token bridging via LayerZero OFT standard
- **MerkleLib** — Merkle proof verification with bitmap tracking to prevent double-claiming
- **Lockable** — Custom reentrancy guard used across SpokePools
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this section is a bit bad. These are not key libraries (IMO). And token space is important to save. I'd remove it or perhaps just point to a libraries folder or something

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, removed it 24c4460


## Development Frameworks

- **Foundry** (primary) - Used for new tests and deployment scripts
Expand Down
Loading