Skip to main content
Built on top of CCTP, this layer reuses MessageTransmitterV2 and Iris (Circle’s attestation API) to move arbitrary ERC-20 tokens across blockchains. Four additional contracts handle token registration, balance changes, and the developer-facing API.

Components

  • CrossChainTokenService: The singleton entry point on each domain. Registers tokens, deploys remote token contracts, and routes crosschain transfers.
  • TokenManager: Deployed per token on each domain. Moves tokens (mint, burn, lock, or unlock) and enforces per-token controls including rate limits, max transfer amount, and pause.
  • CrossChainToken: An ERC-20 with ERC20Permit deployed for tokens that are natively crosschain. Holders call crossChainTransfer directly on the token.
  • CrossChainTokenExecutable: Optional abstract helper for destination receivers. Integrators can implement executeWithCrossChainToken directly and return EXECUTE_SUCCESS. See Hooks.

Transfer flow

The following sequence shows a transfer from a source domain to a destination domain: The exact behavior at the source and destination TokenManager depends on the token’s manager type.

Component map

The CrossChainTokenService is one contract per domain. It fronts every TokenManager on that domain. A TokenManager is created for each registered token and is identified across all domains by its tokenId.

Token manager types

Every TokenManager has a type that determines how it moves tokens on the source and destination blockchains during a transfer. The type is set when the token is registered.
Solidity
Most tokens use the same type on all blockchains. Exception: the ownerless model pairs LOCK_UNLOCK on the home blockchain with NATIVE_CROSSCHAIN_TOKEN on remote blockchains. See Ownerless versus custom tokens.

How tokens are identified

Each token is identified by its tokenId, a bytes32 value derived from the address that registered the token and a caller-chosen salt. The same tokenId identifies the same token across every blockchain where it is registered. You do not construct a tokenId manually. The CrossChainTokenService returns it when you register or deploy a token, and exposes read helpers to rederive it later:
  • getOwnerlessTokenId(tokenAddress) for ownerless tokens
  • crossChainTokenId(deployer, salt) for native CrossChainToken deployments (deployCrossChainToken)
  • crossChainTokenId(deployer, customTokenDeploySalt(deployer, salt)) for custom tokens. Calling crossChainTokenId(deployer, salt) alone yields a different id. customTokenDeploySalt is also the storage salt used to derive the custom token’s TokenManager.
Because the derivation uses (msg.sender, salt), using the same registrar address and the same salt on every blockchain produces the same tokenId everywhere. This is what lets a single value identify a token on every supported blockchain without an offchain registry. Once you have a tokenId, look up the local contracts:
  • resolveTokenManager(tokenId) returns the TokenManager address.
  • resolveTokenAddress(tokenId) returns the underlying ERC-20 address.
Both revert with TokenNotRegistered(tokenId) if no token manager has been deployed for the supplied tokenId on the local domain. The predicted address is deterministic through CREATE3, so callers that need a non-throwing existence check can wrap the call in try/catch or perform their own extcodesize check.

Decimal consistency

The protocol does not scale token amounts when delivering transfers. A token must use the same number of decimals on every blockchain where it is deployed.

Transfer finality and fast burn allowance

Two transfer speeds are available, set by the minFinalityThreshold parameter on crossChainTransfer:
  • Standard transfer (minFinalityThreshold = 2000) waits for source blockchain finality before attestation.
  • Fast transfer (minFinalityThreshold = 1000) attests on soft finality and consumes a per-token fast burn allowance.
Each token has a fast burn allowance maintained by Circle. The allowance is denominated in USD and limits the total value that can be moved through fast transfers of that token before the allowance refills. Standard transfers do not consume the allowance. When a fast transfer is initiated, the protocol consumes more than the face value of the transfer from the allowance to account for short-term price movement of the token. Before initiating a fast transfer, query GET /v2/cctpx/allowances to confirm remaining capacity and that the token has FX pricing configured. If allowance is insufficient or pricing data is unavailable (for example FX_SYMBOL_NOT_CONFIGURED), Iris rejects the fast quote; there is no automatic fallback. Request a standard quote (omit PRE_FINALITY) and use minFinalityThreshold = 2000 instead. USDC and non-USDC fast transfers share the same MessageTransmitterV2 and finality thresholds, but each tracks its own allowance pool. USDC transfers consume the USDC allowance; non-USDC transfers consume a per-token allowance.

Forwarding service

An optional forwarding service delivers completed transfers to the recipient on the destination blockchain. When forwarding is used, Circle relays the Iris attestation and calls receiveMessage on the destination MessageTransmitterV2 on behalf of the sender, so neither the sender nor the recipient needs to submit the attestation manually. Forwarding is opt-in and must be requested when you obtain the fee quote from the Iris API. The fee quote response includes a signed claim that you pass as the claim parameter on crossChainTransfer. Transfers that omit the claim proceed without automatic relay, and you or the recipient are responsible for submitting the attestation to MessageTransmitterV2. You might choose not to use forwarding if you manage delivery yourself, if you are performing complex hook-based transactions that require manual relay sequencing, or if you have your own relayer infrastructure.

Reuse of CCTP infrastructure

No separate attestation service runs for non-USDC transfers. Every message flows through the same MessageTransmitterV2 and Iris (Circle’s attestation API) that CCTP uses for USDC transfers, including the same set of attesters, the same finality thresholds, and the same message verification logic. USDC and non-USDC transfers share the attestation pipeline but are otherwise independent. USDC transfer behavior is unchanged.