Overview
CashmereCCTP wraps Circle’s CCTP on every supported chain: the contract takes Cashmere’s fee, burns the USDC through Circle and emits aCashmereTransfer event. Cashmere’s relayers then mint on the destination chain, so a transfer is a single source-chain transaction.
Contract Repository: GitHub - cashmere-prod/contracts-prod. Addresses for every chain are on Contract Addresses.
Entry Points
From Quote to Call
Every transfer carries a quote signed by the Cashmere Gas API. The quote’sfee and deadline are part of the signed message, so pass them exactly as returned: any other value fails the signature check.
- Get a quote for the route (Backend APIs):
- EVM sources:
GET https://gas.cashmere.exchange/getEcdsaSig_native?localDomain=6&destinationDomain=3&isNative=true&isV2=true - Solana, Aptos and Sui sources:
GET https://gas.cashmere.exchange/getEd25519Sig_native?localDomain=9&destinationDomain=6&isNative=true&version=2
- EVM sources:
- Get Circle’s fee for the finality threshold:
GET https://iris-api.circle.com/v2/burn/USDC/fees/6/3returns an array offinalityThresholdandminimumFee(basis points, possibly fractional, such as1.3). - Call the V2 entry point with these values:
What arrives. The contract takes
amount × feeBP / 10000 from the USDC (feeBP is 0 today, at most 100, that is 1%), plus the quoted fee when isNative is false. On V2 Fast, Circle keeps up to maxFee when it mints. The recipient gets the rest.
Signed Payloads
The Gas API signs the same fields on every chain: local domain, destination domain, fee, deadline,isNative and the CCTP version. A V1 signature therefore never passes a V2 entry point, and the other way round.
EVM checks an ECDSA signature over the raw digest (no \x19Ethereum Signed Message prefix):
version=1 or version=2: without it the message has no version byte and the program rejects it (InvalidMessageData).
TransferParamsV2 (TransferParams without cctp_version for V1). Request their quotes with version=2.
message field of the quote is exactly these bytes, and publicKey is the signer key that the contracts store.
Recipient Encoding
Every chain takes the recipient as 32 bytes, and CCTP mints to whatever account those bytes name. An address in the wrong format doesn’t revert: the USDC arrives at an account nobody controls. The SDK’srecipientError(to, recipient) checks a recipient before you sign.
- To an EVM chain: the 20-byte address, left-padded to 32 bytes (
ethers.zeroPadValue(address, 32)). - To Solana (domain 5):
recipientis the wallet’s USDC associated token account (ATA) andsolanaOwneris the wallet itself, both as 32-byte hex. Cashmere’s relayers create the ATA if it doesn’t exist yet. - To Aptos or Sui: the 32-byte account address (
0xand 64 hex characters). An EVM address padded to 32 bytes is not an Aptos or Sui account. solanaOwneris 32 zero bytes for every destination other than Solana.
EVM Implementation
Interface
TransferV2
TransferV2WithPermit
Skip the approval transaction with an EIP-2612 permit for the same amount:Security Features
- Reentrancy: a lock flag in the contract state guards every transfer and fee withdrawal.
- Signed quotes: the fee, deadline, route and version are signed by the Cashmere signer and checked on chain.
- Fee cap: the percentage fee is at most 100 basis points (1%); it is 0 today.
- Pause: the admin can pause transfers.
Solana Implementation
Program ID
5RsvKL6LFq6yEFiAXEwgYHAN3aLFypeB4AaafdeDnHqM, domain 5. Account addresses, the address lookup table and the CCTP program IDs are on Contract Addresses.transfer_v2. transfer_v2 also needs a fresh keypair that signs as messageSentEventData, where Circle stores the burn message.
Aptos Implementation
Module
0xe49afb1896889e3e1dd7bcbb9e31d1df8221899d3d368e98588ac6b4d84b136d::transfer, domain 9. Aptos sends V2 Norm (min_finality_threshold = 2000).usdc_amount and burns it minus the USDC fee.
Sui Implementation
Calls go to the upgraded package
0x7eab670b3f3e3bb7a0451c7c6b6b627c09e282f0c78eac24966f6479c649d0d5; its types keep the original package ID 0xdf37112aacb7d3535f7b3b754b9a7b9fa1a6b3a756288dc734522c603c6f11a4. Domain 8. Sui sends V2 Norm (min_finality_threshold = 2000).complete_burn sends the message. The ticket and receipts can’t be stored or dropped, so the four calls must share a transaction.
client is a SuiGrpcClient from @mysten/sui/grpc: Sui fullnodes no longer serve JSON-RPC.
Domain IDs
Circle CCTP domain IDs differ from chain IDs. Use them in every Gas API request and contract call.
Every chain can send V2 Norm to every other chain, and receive V2 Fast from the Fast sources.
Chain IDs
Error Codes
EVM
BPTooHigh(), ZeroLimit(), WithdrawCooldownNotPassed() and WithdrawLimitExceeded() come from admin functions.
Solana
Anchor numbers each error enum from 6000, so the same code can mean different errors: identify them by name in the transaction logs.Aptos and Sui
Events
Every chain emits aCashmereTransfer event with the same fields, which Cashmere’s relayers and indexers read:
Admin Functions
Integrators never call these: they set the fee, the signer, the gas-drop limits and the pause flag, and withdraw collected fees. On EVM they are restricted toDEFAULT_ADMIN_ROLE (setFeeBP, setSigner, setMaxUSDCGasDrop, setMaxNativeGasDrop, setPaused, withdrawFee); Solana, Aptos and Sui have the equivalent owner-only instructions.
Best Practices
- Use V2 entry points on every route.
- Pass the quote’s
feeanddeadlineunchanged, and request a new quote if the deadline is close. - Check the recipient format for the destination chain before signing.
- Take
maxFeefrom Circle Iris for the threshold you send. - Report a transfer only once its transaction is confirmed, and keep the transaction hash: delivery on the destination chain is automatic.
- Use permits on EVM to skip the approval transaction.
View Source Code
Browse the contract repository