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
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")
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
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,
)
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 Type | Chains | Typical Time |
|---|
| Fast Transfer | Most EVM chains | ~8-20 seconds |
| Standard Transfer | Non-instant finality | ~13-19 minutes |
| Standard Transfer | Instant 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