cctp
Small Trezor-backed CLI for bridging USDC with
cctp-rs.
The first supported route is Ethereum mainnet to HyperEVM. The CLI uses Alloy's Trezor signer support and defaults to waiting for any permissionless relayer to complete the destination mint.
Install
The source repository, published crate, and installed command are all named
cctp.
Development
This repository includes a Nix flake for a reproducible Rust development shell:
Run the standard local checks through the shell:
Release
Releases publish to crates.io from .github/workflows/release.yml using crates.io
Trusted Publishing. The cctp crate owner must configure this on crates.io
before pushing a release tag:
- Open the
cctpcrate on crates.io. - Go to Settings -> Trusted Publishing.
- Add a GitHub publisher with:
- Repository owner:
suchapalaver - Repository name:
cctp - Workflow filename:
release.yml - Environment: leave blank unless the GitHub workflow is updated to use one.
- Repository owner:
To verify the workflow without publishing, run the Release workflow manually from
GitHub Actions. The manual path runs tests, clippy, package listing, and
cargo publish --dry-run; it skips crates.io OIDC authentication and the final
publish step. The tag path is the only workflow path that exchanges GitHub's
OIDC token for a short-lived crates.io token.
To publish, bump Cargo.toml, commit the release, then push a v* tag. The tag
workflow exchanges GitHub's OIDC token for a short-lived crates.io token and runs
cargo publish --locked. After the first Trusted Publishing tag release
succeeds, remove the old CARGO_REGISTRY_TOKEN repository secret from GitHub.
Usage
The CLI also loads .env from the current directory or a parent directory
before resolving configuration. Keep real RPC URLs in local .env;
.env.example documents the supported variable names and .env is ignored by
git.
By default this sends standard-finality CCTP v2 transactions. To request fast
finality, pass --fast:
For fast transfers, the CLI fetches the live route fee from CCTP before any
signing prompt, prints the fee and cap in the bridge intent, and fails closed if
the fee cannot be resolved. When --max-fee-usdc is omitted, the CLI uses the
live fee plus a 20% buffer as the transaction maxFee.
To provide a manual cap, pass --max-fee-usdc. The CLI still fetches the live
fee first and rejects the run if the manual cap is below the current required
fee:
By default the CLI waits for any relayer to complete the mint on HyperEVM. It uses a read-only HyperEVM provider and does not initialize a destination signer or require HyperEVM gas.
To self-relay, add --self-relay; the relay account must hold HyperEVM gas.
The relay signer defaults to --trezor-account, but can be selected
independently with --relay-trezor-account.
Before any signing prompt, the CLI verifies both RPC providers report the
expected chain IDs, resolves the CCTP contracts, and prints a bridge intent with
the active Trezor account roles, derivation paths, chain bindings, addresses,
amount, resolved fast-transfer fee and cap when applicable, approval spender,
destination MessageTransmitter, and relay policy. A live run requires typing
CONFIRM after reviewing that intent. Use --dry-run to render the same intent
without sending transactions, or --yes for explicit non-interactive
automation.
The bridge intent also prints provenance for high-impact configuration values, including route, amount, recipient, wallet accounts, relay mode, fast mode, fee cap, and RPC endpoint roles. RPC endpoints are redacted to scheme, port, and a masked host suffix so API keys in host labels, paths, usernames, passwords, or query strings are not shown.
Configuration
Configuration is treated as a service boundary. Raw CLI/env input is resolved
once into a validated BridgeConfig; execution code consumes that immutable
config instead of reading flags or environment variables directly.
Precedence is:
- CLI flags.
- Environment variables for RPC URLs:
ETHEREUM_RPC_URLandHYPEREVM_RPC_URL. - TOML config file passed with
--config. - Built-in defaults for route, wallet, account, relay mode, and transfer mode.
amount, ethereum_rpc, and hyperevm_rpc must be supplied by CLI, env, or
config file. Example:
= "10.25"
= "https://..."
= "https://..."
= "0x0000000000000000000000000000000000000000"
= 0
= false
= false
= false
Run with:
Local config files can contain RPC URLs with API keys. Keep those files local:
cctp.toml, cctp.local.toml, *.local.toml, .env, and .env.* are ignored
by git. Commit only sanitized examples.
Domain primitives are shared with cctp-rs where they belong. The CLI uses
CctpV2Route for route validation and UsdcAmount for six-decimal USDC amount
parsing. Wallet backends, RPC endpoints, dry-run behavior, and relay policy stay
in the CLI because they are application concerns.