Skip to main content

Overview

The CctpClient enables bridging USDC between Solana and other blockchains using Circle’s Cross-Chain Transfer Protocol (CCTP).

Methods

bridgeUsdc

Bridges USDC from Solana to another chain using CCTP.
bridgeUsdc(
  amount: BN | number,
  domain: number,
  destinationAddress: PublicKey,
  params: {
    maxFee: BN;
    minFinalityThreshold: number;
    destinationCaller?: PublicKey;
  },
  txOptions?: TxOptions
): Promise<TransactionSignature>
amount
BN | number
required
Amount of USDC to bridge in smallest units (6 decimals)
domain
number
required
Destination domain (0=Ethereum, 1=Avalanche, 2=OP Mainnet, 3=Arbitrum, 6=Base, 7=Polygon)
destinationAddress
PublicKey
required
Destination address on target chain (EVM address as PublicKey)
params.maxFee
BN
required
Maximum fee willing to pay
params.minFinalityThreshold
number
required
Minimum finality threshold
params.destinationCaller
PublicKey
Optional caller on destination chain
txOptions
TxOptions
Transaction options
Example
import { BN } from "@coral-xyz/anchor";
import { PublicKey } from "@solana/web3.js";

// Bridge 100 USDC to Ethereum
const evmAddress = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb";
const evmAddressPubkey = new PublicKey(
  Buffer.from(evmAddress.slice(2), "hex")
);

const signature = await glamClient.cctp.bridgeUsdc(
  new BN(100_000000), // 100 USDC
  0, // Ethereum domain
  evmAddressPubkey,
  {
    maxFee: new BN(1000),
    minFinalityThreshold: 1
  }
);

console.log("Bridge transaction:", signature);

receiveUsdc

Receives USDC from another chain using CCTP attestation.
receiveUsdc(
  sourceDomain: number,
  params: {
    txHash?: string;
    nonce?: string;
  },
  txOptions?: TxOptions
): Promise<TransactionSignature>
sourceDomain
number
required
Source domain (0=Ethereum, 6=Base, etc.)
params.txHash
string
Source chain transaction hash
params.nonce
string
CCTP message nonce
txOptions
TxOptions
Transaction options
Example
// Receive USDC from Ethereum using transaction hash
const signature = await glamClient.cctp.receiveUsdc(
  0, // Ethereum domain
  {
    txHash: "0xabcdef..."
  }
);

console.log("Receive transaction:", signature);

getOutgoingBridgeEvents

Fetches outgoing bridge events (Solana → EVM) for the vault.
getOutgoingBridgeEvents(options: {
  batchSize?: number;
  commitment?: Commitment;
  txHashes?: string[];
  minSlot?: number;
}): Promise<CctpBridgeEvent[]>
options.batchSize
number
default:"1"
Batch size for RPC requests
options.commitment
Commitment
default:"confirmed"
Solana commitment level
options.txHashes
string[]
Specific transaction hashes to fetch
options.minSlot
number
default:"0"
Minimum slot to search from
Example
const outgoingEvents = await glamClient.cctp.getOutgoingBridgeEvents({
  minSlot: 250000000,
  batchSize: 5
});

outgoingEvents.forEach(event => {
  console.log(`Sent ${event.uiAmount} USDC to domain ${event.destinationDomain}`);
  console.log(`Status: ${event.status}`);
  console.log(`Tx: ${event.txHash}`);
});

getIncomingBridgeEvents

Fetches incoming bridge events (EVM → Solana) for the vault.
getIncomingBridgeEvents(options: {
  batchSize?: number;
  commitment?: Finality;
  txHashes?: string[];
  minSlot?: number;
}): Promise<CctpBridgeEvent[]>
options.batchSize
number
default:"1"
Batch size for RPC requests
options.commitment
Finality
default:"confirmed"
Solana finality level
options.txHashes
string[]
Specific transaction hashes to fetch
options.minSlot
number
default:"0"
Minimum slot to search from
Example
const incomingEvents = await glamClient.cctp.getIncomingBridgeEvents({
  minSlot: 250000000
});

incomingEvents.forEach(event => {
  console.log(`Received ${event.uiAmount} USDC from domain ${event.sourceDomain}`);
  console.log(`Slot: ${event.slot}`);
});

Types

CctpBridgeEvent

class CctpBridgeEvent {
  readonly uiAmount: number;
  slot?: number;
  
  constructor(
    readonly amount: BN,
    readonly sourceDomain: number,
    readonly sourceAddress: string,
    readonly destinationDomain: number,
    readonly destinationCaller: string,
    readonly destinationAddress: string,
    readonly attestation: string,
    readonly nonce: string,
    readonly status: string,
    readonly txHash: string
  );
}

CCTP domains

ChainDomain
Ethereum0
Avalanche1
OP Mainnet2
Arbitrum3
Solana5
Base6
Polygon7

Complete bridge example

import { BN } from "@coral-xyz/anchor";
import { PublicKey } from "@solana/web3.js";

// 1. Bridge USDC from Solana to Base
const evmAddress = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb";
const evmPubkey = new PublicKey(Buffer.from(evmAddress.slice(2), "hex"));

const bridgeTx = await glamClient.cctp.bridgeUsdc(
  new BN(50_000000), // 50 USDC
  6, // Base domain
  evmPubkey,
  {
    maxFee: new BN(1000),
    minFinalityThreshold: 1
  }
);

console.log("Bridged to Base:", bridgeTx);

// 2. Wait for attestation (typically 10-20 minutes)
// ...

// 3. On another instance, receive USDC from Base
const receiveTx = await glamClient.cctp.receiveUsdc(
  6, // Base domain
  {
    txHash: bridgeTx
  }
);

console.log("Received from Base:", receiveTx);

// 4. Query bridge history
const outgoing = await glamClient.cctp.getOutgoingBridgeEvents({});
const incoming = await glamClient.cctp.getIncomingBridgeEvents({});

console.log(`Outgoing transfers: ${outgoing.length}`);
console.log(`Incoming transfers: ${incoming.length}`);

Build docs developers (and LLMs) love