Skip to main content

Overview

CCTP V2 supports fee-free Standard Transfers and fee-based Fast Transfers. Fees are charged in USDC and deducted from the transfer amount.

Fee Structure

Standard Transfer

0 bps - No fee for all chains

Fast Transfer

1-14 bps - Varies by source chain

Fast Transfer Fees by Chain

Fees are denominated in basis points (bps), where 1 bps = 0.01%.
Source ChainFee (bps)Fee (%)Example (1000 USDC)
Ethereum1 bps0.01%$0.10
Arbitrum1 bps0.01%$0.10
Base1 bps0.01%$0.10
OP Mainnet1 bps0.01%$0.10
Linea14 bps0.14%$1.40
Instant Finality ChainsN/AN/AFast Transfer not available
Fees for Fast Transfer range from 1 to 14 basis points depending on the source chain. The SDK automatically queries the Iris API for current rates.

Fee Calculation

The SDK calculates the maximum fee (maxFee) automatically:
// From transfer.go:263-286
var maxFee *big.Int
if feeBps > 0 {
    // Add 1 bps buffer for fee fluctuations
    feeWithBuffer := big.NewInt(int64(feeBps + 1))
    maxFee = new(big.Int).Mul(t.params.Amount, feeWithBuffer)
    maxFee = maxFee.Div(maxFee, big.NewInt(10000))
    // Ensure minimum of 1 unit (smallest USDC unit)
    if maxFee.Cmp(big.NewInt(0)) == 0 {
        maxFee = big.NewInt(1)
    }
} else {
    // Standard Transfer with no fee - still need non-zero maxFee for contract
    maxFee = big.NewInt(1)
}

Key Points

  • 1 bps safety buffer is added to handle rate fluctuations
  • Minimum fee is 1 USDC unit (0.000001 USDC)
  • maxFee parameter sets an upper bound on the fee charged

Fee API

The SDK queries Circle’s Iris API for real-time fee information:
import (
    "context"
    "github.com/circlefin/cctp-go"
)

irisClient := cctp.NewIrisClient("https://iris-api.circle.com")

// Query fees for a specific route
feesResp, err := irisClient.GetTransferFees(
    context.Background(),
    0,  // Source domain (Ethereum)
    3,  // Destination domain (Arbitrum)
)
if err != nil {
    panic(err)
}

// feesResp.Data contains fee info for different finality thresholds
for _, feeInfo := range feesResp.Data {
    fmt.Printf("Threshold %d: %d bps\n", 
        feeInfo.FinalityThreshold, 
        feeInfo.MinimumFee)
}

Fee Response Structure

type FeesResponse struct {
    Data []FeeInfo `json:"data"`
}

type FeeInfo struct {
    FinalityThreshold uint32 `json:"finalityThreshold"` // 1000 or 2000
    MinimumFee        uint32 `json:"minimumFee"`        // Fee in basis points
}

Fee Examples

Example 1: Ethereum → Arbitrum (Fast Transfer)

// Transfer 1000 USDC from Ethereum to Arbitrum
amount := big.NewInt(1000_000000) // 1000 USDC (6 decimals)

// Fee: 1 bps + 1 bps buffer = 2 bps = 0.02%
// maxFee = (1000 USDC * 2) / 10000 = 0.20 USDC
// Actual fee charged: ~0.10 USDC (1 bps)
// Recipient receives: ~999.90 USDC

Example 2: Linea → Base (Fast Transfer)

// Transfer 1000 USDC from Linea to Base
amount := big.NewInt(1000_000000) // 1000 USDC (6 decimals)

// Fee: 14 bps + 1 bps buffer = 15 bps = 0.15%
// maxFee = (1000 USDC * 15) / 10000 = 1.50 USDC
// Actual fee charged: ~1.40 USDC (14 bps)
// Recipient receives: ~998.60 USDC

Example 3: Avalanche → Arbitrum (Standard Transfer)

// Transfer 1000 USDC from Avalanche to Arbitrum
amount := big.NewInt(1000_000000) // 1000 USDC (6 decimals)

// Standard Transfer - no fee
// maxFee = 1 (minimum required by contract)
// Actual fee charged: 0 USDC
// Recipient receives: 1000.00 USDC

Fee Fallback

If the Iris API is unavailable, the SDK uses a conservative fallback:
// From transfer.go:252-259
if err != nil {
    log.Warn("Failed to fetch fees from API, using fallback", "error", err)
    // Fallback to conservative maxFee
    feesResp = &FeesResponse{
        Data: []FeeInfo{
            {FinalityThreshold: 1000, MinimumFee: 14}, // Conservative: highest known fee
            {FinalityThreshold: 2000, MinimumFee: 0},
        },
    }
}
The fallback uses 14 bps (the highest known fee) to ensure the transaction succeeds even if actual fees are lower.

Gas Fees

In addition to CCTP transfer fees, you’ll pay gas fees on both chains:
  • Source chain: Gas for approve() (if needed) and depositForBurn()
  • Destination chain: Gas for receiveMessage()
Gas fees are paid in the native token of each chain (ETH, AVAX, MATIC, etc.), not USDC.

Optimizing Costs

1

Choose Standard Transfer when possible

Standard Transfer has no CCTP fee. Use it for:
  • Non-time-sensitive transfers
  • Transfers from instant finality chains
  • Cost-sensitive applications
2

Batch transfers

Consolidate multiple small transfers into fewer large transfers to minimize gas overhead
3

Monitor gas prices

Execute transfers during low gas periods to minimize on-chain costs
4

Use instant finality chains

When transferring from Avalanche, Polygon PoS, Sonic, Sei, XDC, or HyperEVM, Standard Transfer is both fast and free

Fee Recipients

Fees are collected by Circle and distributed to attesters and fee recipients configured in the TokenMessengerV2 contract:
// Fee recipient address (set per contract)
feeRecipient := common.HexToAddress("0x...")

Cost Comparison

Transfer AmountChain RouteFast Transfer FeeStandard Transfer Fee
100 USDCEthereum → Arbitrum$0.01 (1 bps)$0.00
1,000 USDCEthereum → Arbitrum$0.10 (1 bps)$0.00
10,000 USDCEthereum → Arbitrum$1.00 (1 bps)$0.00
1,000 USDCLinea → Base$1.40 (14 bps)$0.00
1,000 USDCAvalanche → ArbitrumN/A$0.00

See Also

Transfer Types

Learn about Fast and Standard transfers

Supported Chains

View fee rates for all chains

Finality

Understand finality thresholds

IrisClient

GetTransferFees API reference

Build docs developers (and LLMs) love