CCIP v2.0.0 USDCTokenPool API Reference
Summary
USDCTokenPool is a USDC-specific [TokenPool](/ccip/evm/api-reference/v2.0.0/token-pool) implementation that uses Circle’s Cross Chain Transfer Protocol (CCTP) to burn USDC on the source chain and mint USDC on the destination chain.
It is the CCTP V1 pool variant. A separate contract, USDCTokenPoolCCTPV2, inherits from this pool and overrides only the functionality needed for CCTP V2.
Contract
chains/evm/contracts/pools/USDC/USDCTokenPool.sol
Import
import {USDCTokenPool} from "chainlink-ccip/chains/evm/contracts/pools/USDC/USDCTokenPool.sol";
Inheritance
TokenPoolITypeAndVersionAuthorizedCallers([GitHub][1])
typeAndVersion
function typeAndVersion() external pure virtual override returns (string memory);
Returns:
"USDCTokenPool 1.6.5-dev"([GitHub][1])
State
Constants
uint256 internal constant SOURCE_POOL_DATA_LENGTH = 64;
releaseOrMint expects sourcePoolData to be ABI-decoded into a payload occupying two 32-byte words (64 bytes total). ([GitHub][1])
Immutables
uint32 public immutable i_supportedUSDCVersion;
ITokenMessenger public immutable i_tokenMessenger;
CCTPMessageTransmitterProxy public immutable i_messageTransmitterProxy;
uint32 public immutable i_localDomainIdentifier;
i_supportedUSDCVersionis the supported CCTP message format version (V1 uses0). ([GitHub][1])
Storage
mapping(uint64 chainSelector => Domain CCTPDomain) internal s_chainToDomain;
Maps a CCIP chainSelector to the destination CCTP Domain config. ([GitHub][1])
Constructor
constructor(
ITokenMessenger tokenMessenger,
CCTPMessageTransmitterProxy cctpMessageTransmitterProxy,
IERC20 token,
address[] memory allowlist,
address rmnProxy,
address router,
uint32 supportedUSDCVersion
)
TokenPool(token, 6, allowlist, rmnProxy, router)
AuthorizedCallers(new address)
Key behaviors:
-
Validates non-zero
tokenMessenger. -
Reads the local message transmitter from the token messenger and validates:
- message transmitter
version()matchessupportedUSDCVersion - token messenger
messageBodyVersion()matchessupportedUSDCVersion
- message transmitter
-
Validates the transmitter proxy is configured with the expected transmitter.
-
Sets
i_localDomainIdentifier = transmitter.localDomain(). -
Grants
tokenMessengermax allowance to burn tokens from this pool. -
Emits
ConfigSet(tokenMessenger). ([GitHub][1])
External API
lockOrBurn
function lockOrBurn(
Pool.LockOrBurnInV1 calldata lockOrBurnIn
) public virtual override returns (Pool.LockOrBurnOutV1 memory);
Burns USDC via i_tokenMessenger.depositForBurnWithCaller and returns destPoolData containing:
nonce(returned by CCTP V1)sourceDomain(i_localDomainIdentifier)
Emits LockedOrBurned. ([GitHub][1])
Reverts (selection):
UnknownDomain(uint64 domain)if destination domain not enabled.InvalidReceiver(bytes receiver)ifreceiver.length != 32. ([GitHub][1])
releaseOrMint
function releaseOrMint(
Pool.ReleaseOrMintInV1 calldata releaseOrMintIn
) public virtual override returns (Pool.ReleaseOrMintOutV1 memory);
Mints USDC on destination by forwarding the offchain-provided (message, attestation) to i_messageTransmitterProxy.receiveMessage(...).
Flow:
- Validates
sourcePoolData.length == SOURCE_POOL_DATA_LENGTH. - Decodes
sourcePoolDataasUSDCSourcePoolDataCodec.SourceTokenDataPayloadV1. - Decodes
offchainTokenDataasMessageAndAttestation. - Validates the USDC message against the expected payload (domain, nonce, version).
- Calls
receiveMessagevia the transmitter proxy. - Emits
ReleasedOrMinted. - Returns
destinationAmount = sourceDenominatedAmount. ([GitHub][1])
Reverts (selection):
InvalidSourcePoolDataLength(uint256 length)InvalidMessageLength(uint256 length)InvalidMessageVersion(uint32 expected, uint32 got)InvalidSourceDomain(uint32 expected, uint32 got)InvalidDestinationDomain(uint32 expected, uint32 got)InvalidNonce(uint64 expected, uint64 got)UnlockingUSDCFailed()ifreceiveMessagereturns false. ([GitHub][1])
getDomain
function getDomain(
uint64 chainSelector
) external view returns (Domain memory);
Returns the configured destination Domain for chainSelector. ([GitHub][1])
setDomains
function setDomains(
DomainUpdate[] calldata domains
) external onlyOwner;
Sets s_chainToDomain[destChainSelector] for each entry and emits DomainsSet(domains).
Reverts:
InvalidDomain(DomainUpdate domain)ifallowedCaller == bytes32(0)ordestChainSelector == 0. ([GitHub][1])
Events
event DomainsSet(DomainUpdate[]);
event ConfigSet(address tokenMessenger);
(Also emits inherited TokenPool events such as LockedOrBurned and ReleasedOrMinted.) ([GitHub][1])
Errors
error UnknownDomain(uint64 domain);
error UnlockingUSDCFailed();
error InvalidConfig();
error InvalidDomain(DomainUpdate domain);
error InvalidMessageVersion(uint32 expected, uint32 got);
error InvalidTokenMessengerVersion(uint32 expected, uint32 got);
error InvalidNonce(uint64 expected, uint64 got);
error InvalidSourceDomain(uint32 expected, uint32 got);
error InvalidDestinationDomain(uint32 expected, uint32 got);
error InvalidReceiver(bytes receiver);
error InvalidTransmitterInProxy();
error InvalidSourcePoolDataLength(uint256 length);
error InvalidMessageLength(uint256 length);
Additional errors can surface from inherited TokenPool validation (chain allowlist, ramps, RMN, etc.). ([GitHub][1])
Structs
MessageAndAttestation
struct MessageAndAttestation {
bytes message;
bytes attestation;
}
Offchain-supplied CCTP message + attestation pair. ([GitHub][1])
DomainUpdate
struct DomainUpdate {
bytes32 allowedCaller;
bytes32 mintRecipient;
uint32 domainIdentifier;
uint64 destChainSelector;
bool enabled;
}
Used by setDomains. ([GitHub][1])
Domain
struct Domain {
bytes32 allowedCaller;
bytes32 mintRecipient;
uint32 domainIdentifier;
bool enabled;
}
Stored per destination chain selector. ([GitHub][1])
Internal Functions
_onlyOnRamp
Overrides on-ramp caller validation to allow proxy-mediated invocation:
- requires supported chain
- then validates caller via
AuthorizedCallers(_validateCaller()). ([GitHub][1])
_onlyOffRamp
Same pattern as _onlyOnRamp for off-ramp paths. ([GitHub][1])
_validateMessage
Parses the CCTP V1 message header fields and validates:
version == i_supportedUSDCVersionsourceDomain == payload.sourceDomaindestinationDomain == i_localDomainIdentifiernonce == payload.nonce([GitHub][1])
Security model
- Owner controls domain routing (
setDomains). - Only authorized callers can invoke ramp entrypoints (proxy-safe via
AuthorizedCallers). releaseOrMintis fail-closed on malformedsourcePoolDataor invalid message fields.- Minting depends on Circle’s message transmitter acceptance; false return reverts.
- Unlimited allowance is granted to
tokenMessengerto burn USDC from the pool. ([GitHub][1])