Guidance for AI coding agents working in this repository. This is the canonical
agent-facing file; CLAUDE.md points here.
A Rust SDK for Circle's Cross-Chain Transfer Protocol (CCTP) with two layers that have different coverage:
- Bridge SDK (
CctpV2Bridge,Cctp,CctpV1/CctpV2traits) — burns USDC and relays attestations end-to-end. Source/destination chains must returntruefromNamedChain::supports_cctp_v2()(v2) orCctpV1::is_supported()(v1). Current coverage: 10 v2-capable chain families (7 v1 chain families plus Linea, Sonic, Sei) with their testnets. - Protocol parser (
DomainId,ParsedV2Message,ParsedV2MessageSummary) — recognizes all 21 CCTP v2 domain IDs Circle has announced, including non-EVM domains (Solana, Starknet Testnet). Parsing is independent of whether the bridge SDK can route to or from a given domain; see the README's "Protocol parser — additional domains" section for the parse-only list.
Supports both CCTP v1 (legacy) and v2 (current, fast transfers with sub-30s settlement).
| Task | Reach for | Notes |
|---|---|---|
| Bridge USDC on a modern chain | CctpV2Bridge |
The default. Permissionless mint — see relayer-race note below. |
| Bridge USDC on a v1-only legacy chain | Cctp |
You extract the message from chain yourself. |
| Submit a burn and let any relayer complete it | CctpV2Bridge::wait_for_receive |
Cheapest happy path. |
| Submit a burn and try to self-relay | CctpV2Bridge::mint_if_needed |
Returns MintResult::AlreadyRelayed if a relayer beat you. Do not use raw mint unless you've checked. |
| Check if a transfer already completed | CctpV2Bridge::is_message_received |
Returns bool. |
| Inspect a v2 message as JSON | ParsedV2MessageSummary::parse |
Returns ParseMessageError, not CctpError. |
| Drive contracts directly (custom hooks, batch flows) | TokenMessengerV2Contract / MessageTransmitterV2Contract |
Bridge types wrap these. |
| Look up chain config without a provider | CctpV1 / CctpV2 traits on NamedChain |
Pure functions: domain id, addresses, confirmation times. |
| Tune attestation polling | PollingConfig::fast_transfer() for v2 fast, PollingConfig::default() otherwise |
Customize via with_max_attempts, with_poll_interval_secs. |
- V2 on-chain messages have a zeroed nonce. The
MessageSentevent you read from the burn receipt is not the canonical message. Always use the message returned byCctpV2Bridge::get_attestation, which fetches the canonical version from Circle's Iris API. - V2 is permissionless. Third-party relayers (Synapse, LI.FI, others) watch
for burns and may complete the mint before you do. Prefer
mint_if_neededovermint; the former returnsMintResult::AlreadyRelayedinstead of surfacing a confusing on-chain revert. recipientisbytes32, notaddress. It's the 20-byte address left-padded to 32 bytes. The builder accepts anAddressand handles padding; if you go around the builder, do the padding yourself.- Mainnet vs testnet hit different Iris hosts (
iris-api.circle.comvsiris-api-sandbox.circle.com). Selection is automatic from the chain — but if you stub the API in tests, stub both.
All exports live in src/lib.rs under pub use. Quick map for navigation:
| Type / function | Purpose | Source |
|---|---|---|
Cctp, CctpBridge |
V1 bridge struct + trait | src/bridge/cctp.rs, src/bridge/bridge_trait.rs |
CctpV2Bridge (re-export of CctpV2) |
V2 bridge with fast-transfer support | src/bridge/v2.rs |
MintResult |
Minted(TxHash) / AlreadyRelayed |
src/bridge/v2.rs |
PollingConfig |
Attestation polling tuning | src/bridge/config.rs |
TokenState, batch_token_state |
ERC-20 allowance/balance helpers | src/bridge/ |
CctpV1, CctpV2 traits |
Chain config on NamedChain |
src/chain/config.rs, src/chain/v2.rs |
FastTransferFee |
Known(u32) / Unknown — fast-transfer fee status per chain |
src/chain/v2.rs |
CCTP_V2_*_MAINNET/TESTNET |
Unified v2 contract addresses | src/chain/addresses.rs |
TokenMessengerContract, MessageTransmitterContract |
V1 contract wrappers | src/contracts/ |
TokenMessengerV2Contract, MessageTransmitterV2Contract |
V2 contract wrappers | src/contracts/v2/ |
Erc20Contract |
Minimal ERC-20 wrapper | src/contracts/erc20.rs |
AttestationResponse, AttestationStatus, V2AttestationResponse |
Iris API response types | src/protocol/attestation.rs |
ParsedV2Message, ParsedV2MessageSummary, BurnMessageV2, MessageHeader, V2Message |
Canonical v2 message parsing | src/protocol/message.rs |
DomainId, FinalityThreshold |
Protocol constants | src/protocol/ |
CctpError, Result, ParseMessageError |
Error types | src/error.rs, src/protocol/message.rs |
ProviderConfig*, estimate_gas_with_buffer, calculate_gas_price_with_buffer |
Provider tuning helpers | src/provider.rs |
spans module |
OpenTelemetry instrumentation hooks | src/spans.rs |
src/lib.rs defines the public SDK surface. Bridge logic lives in src/bridge/
(cctp.rs for v1, v2.rs for v2, config.rs for polling, bridge_trait.rs
for the shared trait). Chain metadata is in src/chain/, protocol parsing and
types in src/protocol/, contract wrappers in src/contracts/, and shared
errors in src/error.rs. Runnable samples go in examples/, contract ABI
JSON in abis/, and CI/release automation in .github/workflows/.
cargo build --all-targets --all-features— builds library, tests, examples.cargo test --all-features --verbose— runs the full test suite.cargo test --doc --all-features --verbose— checks doctests.cargo clippy --all-targets --all-features -- -D warnings— matches the CI lint gate.cargo fmt --all -- --check— verifies formatting;cargo fmtapplies fixes.cargo build --example v2_integration_validation— validates a representative v2 example.pipx run reuse lint— SPDX/REUSE compliance check.
Rust 2021. Follow rustfmt output and keep clippy warning-free. snake_case
for functions and modules, PascalCase for types and traits, SCREAMING_SNAKE_CASE
for constants. Prefer explicit error types like Result<T, CctpError> over
generic wrappers, and document public APIs with ///. New Rust source files
should preserve the SPDX header pattern; markdown and config files are covered
by REUSE.toml annotations instead.
Most tests live inline under #[cfg(test)] beside the code they verify. Use
rstest for parameterized cases and insta when snapshotting stable URL or
message output. Test business logic, parsing edge cases, and protocol
regressions — avoid trivial tests that only restate Rust or dependency
behavior. testing-guidelines.md is the reference for what is worth testing.
Recent history follows Conventional Commit prefixes (fix:, refactor:,
build:, style:, release:). Keep subjects imperative, lowercase after the
prefix, and outcome-focused (what users gain, not which files changed). PRs
should complete .github/pull_request_template.md. Changes that affect chain
support must include address, domain ID, and attestation-flow validation
details.
- Add addresses to
src/chain/addresses.rs. - Add the domain ID to
src/protocol/domain_id.rs. - Implement the trait in
src/chain/config.rs(v1) orsrc/chain/v2.rs(v2). - Add confirmation times for fast and standard v2 transfers.
- Update the prose counts and chain lists in
README.mdandAGENTS.mdso the docs stay in sync with what the parser and bridge actually cover:- Every new
DomainIdvariant bumps the v2 domain ID count quoted in both files ("all 21 CCTP v2 domain IDs"). - If
NamedChain::supports_cctp_v2()returnstruefor the new chain, bump the v2-capable chain family count in this file (currently "10 v2-capable chain families (7 v1 chain families plus …)") and the bridge SDK mainnet/testnet count in the README's "Features" line, then add the chain to the "Mainnet" or "Testnet" list underBridge SDK — supported chains. - If the new domain is parse-only (no
supports_cctp_v2()entry), add it to the README'sProtocol parser — additional domainslist instead.
- Every new
Never commit private keys or API keys. Copy .env.example to .env for
testnet work and keep overrides local. When adding chain support, double-check
contract addresses, domain IDs, and any example code that exercises the new
path.
- Alloy — Ethereum interaction (providers, primitives, contract bindings).
- Circle Iris API — Attestation service. Mainnet:
iris-api.circle.com. Testnet:iris-api-sandbox.circle.com. - Contract ABIs —
abis/, bound via thesol!macro.