Skip to main content
This example demonstrates advanced attestation polling using the IrisClient with a progress callback for tracking the polling status.

What This Example Demonstrates

  • Creating an Iris client for attestation services
  • Polling for attestations with progress tracking
  • Using callbacks to monitor polling progress
  • Working with message and attestation data
  • Context management and error handling

Complete Code Example

This example shows how to poll for attestations with progress callbacks:
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)
}

Breaking Down the Example

1

Create Iris Client

Initialize the IrisClient with the appropriate API endpoint:
irisClient := cctp.NewIrisClient("https://iris-api.circle.com")
For testnet, use:
irisClient := cctp.NewIrisClient("https://iris-api-sandbox.circle.com")
2

Define Progress Callback

Create a callback function to track polling progress:
progressCallback := func(attempt int, elapsed time.Duration) {
    fmt.Printf("Attempt %d - Elapsed: %v\n", attempt, elapsed)
}
The callback receives:
  • attempt: The current polling attempt number
  • elapsed: Time elapsed since polling started
3

Poll with Context

Use a context to control the polling lifecycle:
ctx := context.Background()

// Or with timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()

msg, err := irisClient.PollForAttestation(
    ctx,
    0,       // source domain
    "0x...", // burn tx hash
    progressCallback,
)
4

Handle the Message

Once the attestation is received, access the message data:
if err != nil {
    panic(err)
}

fmt.Printf("Attestation: %s\n", msg.Attestation)
fmt.Printf("Message: %s\n", msg.Message)
fmt.Printf("Status: %s\n", msg.Status)
fmt.Printf("CCTP Version: %d\n", msg.CctpVersion)

Advanced Example with Progress Tracking

package main

import (
    "context"
    "fmt"
    "time"

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

func main() {
    irisClient := cctp.NewIrisClient("https://iris-api.circle.com")
    
    // Create a context with timeout
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
    defer cancel()
    
    // Progress callback with detailed logging
    progressCallback := func(attempt int, elapsed time.Duration) {
        fmt.Printf("[%s] Polling attempt %d\n", 
            elapsed.Round(time.Second), attempt)
        
        if attempt%10 == 0 {
            fmt.Printf("Still waiting after %d attempts...\n", attempt)
        }
    }
    
    fmt.Println("Starting attestation polling...")
    msg, err := irisClient.PollForAttestation(
        ctx,
        0,
        "0xYOUR_BURN_TX_HASH",
        progressCallback,
    )
    
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    
    fmt.Println("\nAttestation received successfully!")
    fmt.Printf("Attestation: %s\n", msg.Attestation)
    
    // Access decoded message fields if available
    if msg.DecodedMessage != nil {
        fmt.Printf("Source Domain: %s\n", msg.DecodedMessage.SourceDomain)
        fmt.Printf("Destination Domain: %s\n", msg.DecodedMessage.DestinationDomain)
        fmt.Printf("Nonce: %s\n", msg.DecodedMessage.Nonce)
    }
}

Message Structure

The Message struct returned by PollForAttestation contains:
type Message struct {
    Message        string            // The encoded CCTP message
    EventNonce     string            // Event nonce
    Attestation    string            // The attestation signature
    DecodedMessage *DecodedMessage   // Decoded message fields
    CctpVersion    int               // CCTP version
    Status         AttestationStatus // "pending" or "complete"
    DelayReason    string            // Reason for delay if any
}

type DecodedMessage struct {
    SourceDomain              string
    DestinationDomain         string
    Nonce                     string
    Sender                    string
    Recipient                 string
    DestinationCaller         string
    MinFinalityThreshold      string
    FinalityThresholdExecuted string
    MessageBody               string
}

Polling Behavior

Automatic Retry LogicThe PollForAttestation method implements:
  • Exponential backoff starting at 2 seconds
  • Maximum backoff of 10 seconds between attempts
  • Automatic handling of 404 errors (up to 3 consecutive attempts)
  • Continues until attestation is ready or context is cancelled

Expected Timing

Transfer TypeChainsTypical Time
Fast TransferMost EVM chains~8-20 seconds
Standard TransferNon-instant finality~13-19 minutes
Standard TransferInstant finality chains*~8 seconds
*Instant finality chains: Avalanche, Polygon PoS, Sei, Sonic, XDC, HyperEVM, Arc Testnet

Error Handling

msg, err := irisClient.PollForAttestation(ctx, sourceDomain, txHash, callback)
if err != nil {
    switch {
    case errors.Is(err, context.DeadlineExceeded):
        fmt.Println("Polling timed out")
    case errors.Is(err, context.Canceled):
        fmt.Println("Polling was cancelled")
    case strings.Contains(err.Error(), "rate limit"):
        fmt.Println("Rate limit exceeded, wait before retrying")
    default:
        fmt.Printf("Polling error: %v\n", err)
    }
    return
}

Timeout Recommendations

// 2 minutes should be sufficient for fast transfers
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
Use different timeout values based on the transfer type and source chain to optimize your application’s responsiveness.

Next Steps

Iris Client Reference

Complete API reference for IrisClient

Transfer Orchestration

Learn about the full transfer workflow

Build docs developers (and LLMs) love