cctp

package module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 23, 2026 License: GPL-3.0, LGPL-3.0 Imports: 17 Imported by: 0

README

CCTP Go SDK and CLI

A Go SDK and Terminal UI-based CLI tool for executing Circle's CCTP V2 cross-chain USDC transfers across all supported EVM chains.

Features

  • 📦 Importable Go SDK - Use CCTP functionality in your Go applications
  • 🚀 Fast Transfer support (~8-20 seconds for EVM chains, with fee)
  • ⏱️ Standard Transfer support (~13-19 minutes, no fee)
  • 🔗 Support for all CCTP V2 EVM chains (18 mainnet + 10 testnet chains)
  • 💼 Multiple wallet support (keystore files, private keys)
  • 📊 Real-time progress tracking with detailed fee information
  • 🎨 Beautiful terminal user interface

Usage

CLI Usage

Install the CLI with the following command:

go install github.com/circlefin/cctp-go/cmd/cctp

To use the CLI, you must have your wallet's private key stored in the Geth keystore.

Ensure that the path that Go builds executables is in your PATH variable.

The following are example CLI commands:

# Run with interactive TUI
./bin/cctp transfer

# Or run directly without building
go run ./cmd/cli transfer

# Use testnet
./bin/cctp transfer --testnet

# Specify transfer type
./bin/cctp transfer --type fast      # Fast Transfer (~8-20 seconds, with fee)
./bin/cctp transfer --type standard  # Standard Transfer (~13-19 minutes, no fee)
./bin/cctp transfer --type auto      # Auto-select based on chain (default)

# Pre-populate transfer details
./bin/cctp transfer --source ethereum --dest arbitrum --amount 100 --recipient 0x... --type fast

# Resume existing transfer
./bin/cctp resume 0x...  # Provide burn transaction hash

# Specify keystore directory
./bin/cctp --keystore ~/.ethereum/keystore transfer

For detailed CLI usage, see CLI-USAGE.md.

Library Usage

The CCTP SDK can be imported and used in your Go applications.

go get github.com/circlefin/cctp-go

Example usage in your Go application:

package main

import (
    "context"
    "fmt"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/circlefin/cctp-go"
)

func main() {
    // Get available chains
    chains := cctp.GetChains(false) // false = mainnet

    // Create an Iris client for attestations
    irisClient := cctp.NewIrisClient("https://iris-api.circle.com")

    // Poll for an attestation
    ctx := context.Background()
    msg, err := irisClient.PollForAttestation(
        ctx,
        0, // source domain (Ethereum)
        "0x...", // burn transaction hash
        nil, // optional progress callback
    )
    if err != nil {
        panic(err)
    }

    fmt.Printf("Attestation received: %s\n", msg.Attestation)
}
Overriding RPC Endpoints

The library provides default RPC endpoints for all supported chains, but integrating packages can override them with custom endpoints (e.g., from their own configuration or API keys):

package main

import (
    "github.com/circlefin/cctp-go"
)

func main() {
    // Get chains with default RPC endpoints
    chains := cctp.GetChains(false) // false = mainnet

    // Define custom RPC endpoints using the chain name constants
    // This provides IDE autocomplete and prevents typos
    customRPCs := map[string]string{
        cctp.Ethereum: "https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY",
        cctp.Arbitrum: "https://arb-mainnet.g.alchemy.com/v2/YOUR_API_KEY",
        cctp.Base:     "https://base-mainnet.g.alchemy.com/v2/YOUR_API_KEY",
    }

    // Apply RPC overrides
    chains = cctp.ApplyRPCOverrides(chains, customRPCs)

    // Now use chains with your custom RPC endpoints
    for _, chain := range chains {
        if chain.Name == cctp.Ethereum {
            fmt.Printf("Ethereum RPC: %s\n", chain.RPC)
        }
    }
}

Available Chain Constants:

Mainnet: cctp.Ethereum, cctp.Avalanche, cctp.OPMainnet, cctp.Arbitrum, cctp.Base, cctp.PolygonPoS, cctp.Unichain, cctp.Linea, cctp.Codex, cctp.Sonic, cctp.WorldChain, cctp.Sei, cctp.XDC, cctp.HyperEVM, cctp.Ink, cctp.Plume

Testnet: cctp.EthereumSepolia, cctp.AvalancheFuji, cctp.OPSepolia, cctp.ArbitrumSepolia, cctp.BaseSepolia, cctp.PolygonPoSAmoy, cctp.LineaSepolia, cctp.ArcTestnet, cctp.UnichainSepolia, cctp.CodexTestnet, cctp.SonicTestnet, cctp.WorldChainSepolia, cctp.SeiTestnet, cctp.XDCApothem, cctp.HyperEVMTestnet, cctp.InkTestnet, cctp.PlumeTestnet

Notes:

  • Use the chain name constants (e.g., cctp.Ethereum) for IDE autocomplete support and to avoid typos
  • You can still use string literals if needed, but they must match exactly (case-sensitive)
  • Empty string overrides are ignored (original RPC is preserved)
  • Non-matching chain names are ignored
  • The original chains slice is not mutated (returns a new slice)
Contract Interactions
package main

import (
    "context"
    "fmt"
    "math/big"

    "github.com/ethereum/go-ethereum/accounts/abi/bind/v2"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
    "github.com/circlefin/cctp-go/tokenmessenger"
)

func main() {
    // Connect to a chain
    client, err := ethclient.Dial("https://ethereum-rpc.publicnode.com")
    if err != nil {
        panic(err)
    }

    // Create V2 contract binding
    ctx := context.Background()
    ierc20 := tokenmessenger.NewIERC20()
    usdcAddress := common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")
    usdcInstance := ierc20.Instance(client, usdcAddress)

    // Check USDC balance using V2 bindings
    walletAddress := common.HexToAddress("0x...")
    balance, err := bind.Call(usdcInstance, &bind.CallOpts{Context: ctx},
        ierc20.PackBalanceOf(walletAddress), ierc20.UnpackBalanceOf)
    if err != nil {
        panic(err)
    }

    fmt.Printf("USDC Balance: %s\n", balance.String())
}
Full Transfer Example

See the CLI implementation for a complete example of orchestrating a full CCTP transfer.

Supported Chains

Mainnet (18 chains)
  • Ethereum (Domain 0)
  • Avalanche (Domain 1) - Instant finality
  • OP Mainnet (Domain 2)
  • Arbitrum (Domain 3)
  • Base (Domain 6)
  • Polygon PoS (Domain 7) - Instant finality
  • Unichain (Domain 10)
  • Linea (Domain 11)
  • Codex (Domain 12)
  • Sonic (Domain 13) - Instant finality
  • World Chain (Domain 14)
  • Sei (Domain 16) - Instant finality
  • XDC (Domain 18) - Instant finality
  • HyperEVM (Domain 19) - Instant finality
  • Ink (Domain 21)
  • Plume (Domain 22)
Testnet (10 chains)
  • Ethereum Sepolia, Arbitrum Sepolia, Avalanche Fuji, Base Sepolia, Polygon PoS Amoy, Linea Sepolia, Arc Testnet, Unichain Sepolia, Codex Testnet, Sonic Testnet, World Chain Sepolia, Sei Testnet, XDC Apothem, HyperEVM Testnet, Ink Testnet, Plume Testnet
Transfer Types

Fast Transfer (~8-20 seconds)

  • Available on most chains when used as source
  • Requires fee (1-14 bps depending on source chain)
  • Best for time-sensitive transfers
  • Not available when instant finality chains are the source

Standard Transfer (~13-19 minutes or ~8 seconds for instant finality chains)

  • Available on all chains
  • No fee
  • Best for cost-sensitive transfers
  • Instant finality chains: Avalanche, Polygon PoS, Sei, Sonic, XDC, HyperEVM, Arc Testnet

Setup and Building

Prerequisites
  • Go 1.25.2 or later
  • Task (optional, for using Taskfile commands)
Getting Started
# Clone the repository
git clone https://github.com/circlefin/cctp-go
cd cctp-go

# Install dependencies
go mod download

# Build the CLI binary (using Task)
task build

# Or build directly with Go
go build -o bin/cctp ./cmd/cli

# Run the CLI
./bin/cctp transfer

# Or run directly without building
go run ./cmd/cli transfer
Available Build Tasks
# Build the CLI binary
task build

# Run tests
task test

# Run tests with verbose output
task test-verbose

# Run tests with coverage report
task test-coverage

# Generate HTML coverage report
task test-coverage-html

# Clean build artifacts
task clean

Package Structure

cctp-go/
├── client.go          # Iris attestation client
├── transfer.go        # Transfer orchestration
├── chains.go          # Chain configurations
├── contracts.go       # Contract interactions
├── cmd/
│   └── cli/           # CLI application
│       └── main.go
└── internal/          # CLI-specific code
    ├── config/
    ├── ui/
    └── wallet/

API Documentation

Main Types
  • Chain - Blockchain configuration with RPC, contracts, and metadata
  • IrisClient - Client for Circle's attestation service with fee API support
  • TransferOrchestrator - Complete transfer workflow orchestration
  • TransferType - Transfer type enum (Fast, Standard, Auto)
  • TransferParams - Parameters for a cross-chain transfer
Key Functions
  • GetChains(testnet bool) - Get all configured chains
  • GetChainByDomain(domain uint32, testnet bool) - Find chain by domain ID
  • GetChainByName(name string, testnet bool) - Find chain by name
  • NewIrisClient(baseURL string) - Create attestation client
  • IrisClient.GetTransferFees(ctx, sourceDomain, destDomain) - Query transfer fees from API
  • NewTransferOrchestrator(...) - Create transfer orchestrator
V2 Contract Bindings

This library uses Go Ethereum V2 Contract Bindings for type-safe contract interactions:

  • NewIERC20() - Create ERC20 contract binding
  • NewTokenMessengerV2() - Create TokenMessengerV2 contract binding
  • NewMessageTransmitterV2() - Create MessageTransmitterV2 contract binding
  • BindCall() - Make read-only contract calls
  • BindTransact() - Send transactions to contracts
  • BindWaitMined() - Wait for transaction confirmation

Documentation

Index

Constants

View Source
const (
	Ethereum   = "Ethereum"
	Avalanche  = "Avalanche"
	OPMainnet  = "OP Mainnet"
	Arbitrum   = "Arbitrum"
	Base       = "Base"
	PolygonPoS = "Polygon PoS"
	Unichain   = "Unichain"
	Linea      = "Linea"
	Codex      = "Codex"
	Sonic      = "Sonic"
	WorldChain = "World Chain"
	Sei        = "Sei"
	XDC        = "XDC"
	HyperEVM   = "HyperEVM"
	Ink        = "Ink"
	Plume      = "Plume"
)

Chain name constants for mainnet chains

View Source
const (
	EthereumSepolia   = "Ethereum Sepolia"
	AvalancheFuji     = "Avalanche Fuji"
	OPSepolia         = "OP Sepolia"
	ArbitrumSepolia   = "Arbitrum Sepolia"
	BaseSepolia       = "Base Sepolia"
	PolygonPoSAmoy    = "Polygon PoS Amoy"
	LineaSepolia      = "Linea Sepolia"
	ArcTestnet        = "Arc Testnet"
	UnichainSepolia   = "Unichain Sepolia"
	CodexTestnet      = "Codex Testnet"
	SonicTestnet      = "Sonic Testnet"
	WorldChainSepolia = "World Chain Sepolia"
	SeiTestnet        = "Sei Testnet"
	XDCApothem        = "XDC Apothem"
	HyperEVMTestnet   = "HyperEVM Testnet"
	InkTestnet        = "Ink Testnet"
	PlumeTestnet      = "Plume Testnet"
)

Chain name constants for testnet chains

Variables

This section is empty.

Functions

func EstimatedAttestationTime

func EstimatedAttestationTime(instantFinality bool) time.Duration

EstimatedAttestationTime returns the estimated time for attestation based on the chain

Types

type AttestationStatus

type AttestationStatus string

AttestationStatus represents the status of an attestation

const (
	AttestationStatusPending  AttestationStatus = "pending"
	AttestationStatusComplete AttestationStatus = "complete"
)

type Chain

type Chain struct {
	Name                 string
	ChainID              *big.Int
	Domain               uint32
	RPC                  string
	TokenMessengerV2     string
	MessageTransmitterV2 string
	USDC                 string
	Explorer             string
	IsTestnet            bool
	InstantFinality      bool // Chains with instant finality don't need Fast Transfer distinction
}

Chain represents a blockchain configuration for CCTP V2

func ApplyRPCOverrides

func ApplyRPCOverrides(chains []Chain, overrides map[string]string) []Chain

ApplyRPCOverrides applies RPC URL overrides from a map to the given chains. The map keys should be chain names, and the values should be the override RPC URLs. This allows integrating packages to customize RPC endpoints based on their configuration.

func GetChainByDomain

func GetChainByDomain(domain uint32, testnet bool) (*Chain, error)

GetChainByDomain returns a chain by its domain ID

func GetChainByName

func GetChainByName(name string, testnet bool) (*Chain, error)

GetChainByName returns a chain by its name

func GetChains

func GetChains(testnet bool) []Chain

GetChains returns all configured chains

func GetMainnetChains

func GetMainnetChains() []Chain

GetMainnetChains returns all mainnet chain configurations

func GetTestnetChains

func GetTestnetChains() []Chain

GetTestnetChains returns all testnet chain configurations

type DecodedMessage

type DecodedMessage struct {
	SourceDomain              string `json:"sourceDomain"`
	DestinationDomain         string `json:"destinationDomain"`
	Nonce                     string `json:"nonce"`
	Sender                    string `json:"sender"`
	Recipient                 string `json:"recipient"`
	DestinationCaller         string `json:"destinationCaller"`
	MinFinalityThreshold      string `json:"minFinalityThreshold"`
	FinalityThresholdExecuted string `json:"finalityThresholdExecuted"`
	MessageBody               string `json:"messageBody"`
}

DecodedMessage represents the decoded CCTP message fields

type FeeInfo

type FeeInfo struct {
	FinalityThreshold uint32 `json:"finalityThreshold"`
	MinimumFee        uint32 `json:"minimumFee"` // in basis points (bps)
}

FeeInfo represents fee information for a specific finality threshold

type FeesResponse

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

FeesResponse represents the response from /v2/burn/USDC/fees

type IrisClient

type IrisClient struct {
	// contains filtered or unexported fields
}

IrisClient is a client for Circle's Iris attestation service

func NewIrisClient

func NewIrisClient(baseURL string) *IrisClient

NewIrisClient creates a new Iris API client

func (*IrisClient) GetMessages

func (c *IrisClient) GetMessages(ctx context.Context, sourceDomain uint32, txHash string) (*MessagesResponse, error)

GetMessages fetches messages and attestations for a transaction

func (*IrisClient) GetTransferFees

func (c *IrisClient) GetTransferFees(ctx context.Context, sourceDomain, destDomain uint32) (*FeesResponse, error)

GetTransferFees fetches the fee information for a transfer between two domains

func (*IrisClient) PollForAttestation

func (c *IrisClient) PollForAttestation(ctx context.Context, sourceDomain uint32, txHash string, progressCallback func(attempt int, elapsed time.Duration)) (*Message, error)

PollForAttestation polls for an attestation until it's ready or context is cancelled

type Message

type Message struct {
	Message        string            `json:"message"`
	EventNonce     string            `json:"eventNonce"`
	Attestation    string            `json:"attestation"`
	DecodedMessage *DecodedMessage   `json:"decodedMessage"`
	CctpVersion    int               `json:"cctpVersion"`
	Status         AttestationStatus `json:"status"`
	DelayReason    string            `json:"delayReason"`
}

Message represents a CCTP message

type MessagesResponse

type MessagesResponse struct {
	Messages []Message `json:"messages"`
}

MessagesResponse represents the response from /v2/messages

type TransferOrchestrator

type TransferOrchestrator struct {
	// contains filtered or unexported fields
}

TransferOrchestrator orchestrates the entire transfer workflow

func NewTransferOrchestrator

func NewTransferOrchestrator(
	w *wallet.Wallet,
	params *TransferParams,
	irisBaseURL string,
) (*TransferOrchestrator, error)

NewTransferOrchestrator creates a new transfer orchestrator

func (*TransferOrchestrator) Close

func (t *TransferOrchestrator) Close()

Close closes the RPC clients

func (*TransferOrchestrator) Execute

func (t *TransferOrchestrator) Execute(ctx context.Context, updates chan<- TransferUpdate) error

Execute executes the complete transfer workflow

type TransferParams

type TransferParams struct {
	SourceChain      *Chain
	DestChain        *Chain
	Amount           *big.Int
	RecipientAddress common.Address
	Testnet          bool
	TransferType     TransferType // Fast, Standard, or Auto (default: Auto)
	CachedBalance    *big.Int     // Optional: pre-fetched balance to skip redundant check
}

TransferParams holds parameters for a transfer

type TransferStep

type TransferStep string

TransferStep represents a step in the transfer process

const (
	StepCheckBalance       TransferStep = "checking_balance"
	StepCheckAllowance     TransferStep = "checking_allowance"
	StepApproving          TransferStep = "approving"
	StepApprovingWait      TransferStep = "approving_wait"
	StepBurning            TransferStep = "burning"
	StepBurningWait        TransferStep = "burning_wait"
	StepPollingAttestation TransferStep = "polling_attestation"
	StepMinting            TransferStep = "minting"
	StepMintingWait        TransferStep = "minting_wait"
	StepComplete           TransferStep = "complete"
	StepError              TransferStep = "error"
)

type TransferType

type TransferType string

TransferType represents the type of CCTP transfer

const (
	TransferTypeFast     TransferType = "fast"     // Fast Transfer (~8-20 seconds, with fee)
	TransferTypeStandard TransferType = "standard" // Standard Transfer (~13-19 minutes, no fee)
	TransferTypeAuto     TransferType = "auto"     // Auto-select based on chain capabilities
)

type TransferUpdate

type TransferUpdate struct {
	Step        TransferStep
	Message     string
	TxHash      string
	Error       error
	SourceChain *Chain
	DestChain   *Chain
}

TransferUpdate represents an update in the transfer process

Directories

Path Synopsis
cmd
cctp command
internal
ui

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL