Overview
The TransferOrchestrator coordinates the entire CCTP transfer workflow, handling balance checks, approvals, burning, attestation retrieval, and minting. It provides progress updates throughout the process and manages all blockchain interactions.
TransferOrchestrator Type
type TransferOrchestrator struct {
wallet * wallet . Wallet
irisClient * IrisClient
sourceClient * ethclient . Client
destClient * ethclient . Client
params * TransferParams
}
Creating an Orchestrator
func NewTransferOrchestrator (
w * wallet . Wallet ,
params * TransferParams ,
irisBaseURL string ,
) ( * TransferOrchestrator , error )
Wallet instance for signing transactions
Transfer parameters (chains, amount, recipient)
Iris API base URL for attestations
import (
" github.com/circlefin/cctp-go "
" github.com/circlefin/cctp-go/internal/wallet "
)
// Get chain configurations
sourceChain , _ := cctp . GetChainByName ( cctp . Ethereum , false )
destChain , _ := cctp . GetChainByName ( cctp . Avalanche , false )
// Create wallet
wallet , _ := wallet . NewWallet ( privateKey )
// Configure transfer
params := & cctp . TransferParams {
SourceChain : sourceChain ,
DestChain : destChain ,
Amount : big . NewInt ( 1000000 ), // 1 USDC (6 decimals)
RecipientAddress : common . HexToAddress ( "0x..." ),
TransferType : cctp . TransferTypeAuto ,
}
// Create orchestrator
orchestrator , err := cctp . NewTransferOrchestrator (
wallet ,
params ,
"https://iris-api.circle.com" ,
)
if err != nil {
log . Fatal ( err )
}
defer orchestrator . Close ()
Transfer Parameters
type TransferParams struct {
SourceChain * Chain
DestChain * Chain
Amount * big . Int
RecipientAddress common . Address
Testnet bool
TransferType TransferType
CachedBalance * big . Int // Optional: skip balance check
}
Source chain configuration
Destination chain configuration
Transfer amount in USDC’s smallest unit (6 decimals)
Recipient address on destination chain
Transfer mode: TransferTypeAuto, TransferTypeFast, or TransferTypeStandard
Pre-fetched balance to skip redundant RPC call
Transfer Types
type TransferType string
const (
TransferTypeFast TransferType = "fast" // ~8-20 seconds, with fee
TransferTypeStandard TransferType = "standard" // ~13-19 minutes, no fee
TransferTypeAuto TransferType = "auto" // Auto-select based on chain
)
TransferTypeAuto automatically selects Standard Transfer for instant finality chains (Avalanche, Polygon, etc.) and Fast Transfer for others.
Executing a Transfer
func ( t * TransferOrchestrator ) Execute (
ctx context . Context ,
updates chan <- TransferUpdate ,
) error
Context for cancellation and timeout
updates
chan<- TransferUpdate
required
Channel for receiving progress updates
ctx := context . Background ()
updates := make ( chan cctp . TransferUpdate )
// Execute transfer in goroutine
go func () {
err := orchestrator . Execute ( ctx , updates )
if err != nil {
log . Printf ( "Transfer failed: %v " , err )
}
}()
// Monitor progress
for update := range updates {
switch update . Step {
case cctp . StepCheckBalance :
fmt . Printf ( "[Balance] %s \n " , update . Message )
case cctp . StepApproving :
fmt . Printf ( "[Approve] %s (tx: %s ) \n " , update . Message , update . TxHash )
case cctp . StepBurning :
fmt . Printf ( "[Burn] %s (tx: %s ) \n " , update . Message , update . TxHash )
case cctp . StepPollingAttestation :
fmt . Printf ( "[Attestation] %s \n " , update . Message )
case cctp . StepMinting :
fmt . Printf ( "[Mint] %s (tx: %s ) \n " , update . Message , update . TxHash )
case cctp . StepComplete :
fmt . Printf ( "[Success] %s \n " , update . Message )
case cctp . StepError :
fmt . Printf ( "[Error] %v \n " , update . Error )
}
}
Transfer Steps
The orchestrator progresses through these steps:
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"
)
Balance Check
Verifies wallet has sufficient USDC balance on source chain. balance , err := util . GetUSDCBalance ( ctx , sourceClient , usdcAddress , wallet . Address )
Allowance Check
Checks if TokenMessengerV2 has approval to spend USDC. allowance , err := bind . Call ( usdcInstance , & bind . CallOpts { Context : ctx },
ierc20 . PackAllowance ( wallet . Address , tokenMessengerAddr ),
ierc20 . UnpackAllowance )
Approval (if needed)
If allowance is insufficient, approve TokenMessengerV2 to spend USDC. approveTx , err := bind . Transact ( usdcInstance , auth ,
ierc20 . PackApprove ( tokenMessengerAddr , amount ))
Burn USDC
Call depositForBurn on TokenMessengerV2 to burn USDC on source chain. burnData := tokenMessengerV2 . PackDepositForBurn (
amount ,
destDomain ,
recipientBytes32 ,
burnToken ,
destinationCaller ,
maxFee ,
minFinalityThreshold ,
)
Poll for Attestation
Wait for Circle’s attestation service to sign the message. msg , err := irisClient . PollForAttestation (
ctx ,
sourceDomain ,
burnTxHash ,
progressCallback ,
)
Mint USDC
Call receiveMessage on MessageTransmitterV2 to mint USDC on destination chain. mintData := messageTransmitterV2 . PackReceiveMessage (
messageBytes ,
attestationBytes ,
)
Transfer Updates
type TransferUpdate struct {
Step TransferStep
Message string
TxHash string
Error error
SourceChain * Chain
DestChain * Chain
}
Current step in the transfer process
Human-readable status message
Transaction hash (when applicable)
Error details if step failed
Fee Calculation
The orchestrator automatically queries and applies appropriate fees:
// Fetch fee information from Iris API
feesResp , err := irisClient . GetTransferFees ( ctx , sourceDomain , destDomain )
// Find fee for selected finality threshold
var feeBps uint32 = 0
for _ , feeInfo := range feesResp . Data {
if feeInfo . FinalityThreshold == minFinalityThreshold {
feeBps = feeInfo . MinimumFee
break
}
}
// Calculate maxFee with 1 bps safety buffer
maxFee = ( amount * ( feeBps + 1 )) / 10000
The SDK adds a 1 basis point buffer to protect against fee fluctuations between query and execution.
Error Handling
The orchestrator performs validation before submitting transactions:
Balance Validation
Pre-flight Check
if balance . Cmp ( amount ) < 0 {
err := fmt . Errorf ( "insufficient USDC balance: have %s , need %s " ,
util . FormatUSDCBalance ( balance ),
util . FormatUSDCBalance ( amount ))
updates <- TransferUpdate { Step : StepError , Error : err }
return err
}
Resource Management
Always close the orchestrator to release RPC connections:
func ( t * TransferOrchestrator ) Close ()
orchestrator , err := cctp . NewTransferOrchestrator ( wallet , params , irisURL )
if err != nil {
log . Fatal ( err )
}
defer orchestrator . Close ()
// Execute transfer
err = orchestrator . Execute ( ctx , updates )
Complete Example
package main
import (
" context "
" fmt "
" log "
" math/big "
" os "
" github.com/circlefin/cctp-go "
" github.com/circlefin/cctp-go/internal/wallet "
" github.com/ethereum/go-ethereum/common "
)
func main () {
// Setup
privateKey := os . Getenv ( "PRIVATE_KEY" )
wallet , _ := wallet . NewWallet ( privateKey )
sourceChain , _ := cctp . GetChainByName ( cctp . Ethereum , false )
destChain , _ := cctp . GetChainByName ( cctp . Base , false )
// Configure transfer
params := & cctp . TransferParams {
SourceChain : sourceChain ,
DestChain : destChain ,
Amount : big . NewInt ( 10_000000 ), // 10 USDC
RecipientAddress : common . HexToAddress ( "0x..." ),
TransferType : cctp . TransferTypeFast ,
}
// Create orchestrator
orchestrator , err := cctp . NewTransferOrchestrator (
wallet ,
params ,
"https://iris-api.circle.com" ,
)
if err != nil {
log . Fatal ( err )
}
defer orchestrator . Close ()
// Execute transfer
ctx := context . Background ()
updates := make ( chan cctp . TransferUpdate )
go func () {
err := orchestrator . Execute ( ctx , updates )
if err != nil {
log . Printf ( "Transfer failed: %v " , err )
}
}()
// Monitor progress
for update := range updates {
if update . Step == cctp . StepComplete {
fmt . Println ( "Transfer complete!" )
fmt . Printf ( "Mint tx: %s \n " , update . TxHash )
break
}
if update . Step == cctp . StepError {
log . Fatal ( update . Error )
}
fmt . Printf ( " %s : %s \n " , update . Step , update . Message )
}
}
Next Steps
Iris Client Learn about attestation polling
Contract Bindings Understand V2 contract interfaces