Complete guide to integrating with StateSet’s native CCTP module for cross-chain USDC transfers
# Download and install the StateSet CLI
curl -L https://get.stateset.network | bash
# Verify installation
stateset version
# Output: v1.0.0
# Configure for testnet
stateset config chain-id stateset-testnet-1
stateset config node https://rpc-test.stateset.network:443
# Create a new account
stateset keys add testaccount
# Get testnet USDC from faucet
curl -X POST https://faucet-test.stateset.network/credit \
-H "Content-Type: application/json" \
-d '{"address": "stateset1...", "coins": ["1000000usdc"]}'
# JavaScript/TypeScript
npm install @stateset/cctp-sdk @stateset/client
# Go
go get github.com/stateset/stateset/x/cctp
# Python
pip install stateset-cctp
import { StateSetClient } from '@stateset/client';
import { CCTPModule } from '@stateset/cctp-sdk';
// Initialize client
const client = await StateSetClient.connect('https://rpc.stateset.network');
const cctp = new CCTPModule(client);
// Set up signer
const signer = await client.getSigner('your-mnemonic-or-private-key');
// Example: Burn USDC to transfer to Ethereum
async function transferToEthereum() {
const burnMsg = {
from: signer.address,
amount: {
denom: 'usdc',
amount: '1000000000' // 1000 USDC (6 decimals)
},
destinationDomain: 0, // Ethereum
mintRecipient: ethers.utils.hexZeroPad('0x742d35...', 32),
burnToken: 'usdc'
};
// Execute burn
const tx = await cctp.depositForBurn(burnMsg);
console.log('Burn tx hash:', tx.transactionHash);
console.log('Message hash:', tx.messageHash);
// Wait for attestation from Circle
const attestation = await cctp.waitForAttestation(tx.messageHash);
console.log('Attestation received:', attestation);
// The attestation can now be used on Ethereum to mint the USDC
return { message: tx.message, attestation };
}
package main
import (
"context"
"fmt"
"github.com/cosmos/cosmos-sdk/client"
"github.com/stateset/stateset/x/cctp/types"
"github.com/stateset/stateset/x/cctp/client/cli"
)
func TransferUSDC(ctx context.Context, clientCtx client.Context) error {
// Create deposit for burn message
msg := &types.MsgDepositForBurn{
From: clientCtx.GetFromAddress().String(),
Amount: sdk.NewCoin("usdc", sdk.NewInt(1000000000)),
DestinationDomain: 0, // Ethereum
MintRecipient: hexutil.MustDecode("0x742d35Cc6634C0532925a3b844Bc9e7595f6E321"),
BurnToken: "usdc",
}
// Validate message
if err := msg.ValidateBasic(); err != nil {
return fmt.Errorf("invalid message: %w", err)
}
// Broadcast transaction
res, err := cli.BroadcastTx(clientCtx, msg)
if err != nil {
return fmt.Errorf("broadcast failed: %w", err)
}
// Extract message hash from events
messageHash := ExtractMessageHash(res.Events)
fmt.Printf("Message hash: %s\n", messageHash)
return nil
}
from stateset_cctp import CCTPClient, DepositForBurnMsg
from stateset_cctp.attestation import AttestationService
# Initialize client
client = CCTPClient(
rpc_endpoint="https://rpc.stateset.network",
private_key="your-private-key"
)
# Transfer USDC to Arbitrum
async def transfer_to_arbitrum():
# Create burn message
msg = DepositForBurnMsg(
from_address=client.address,
amount={"denom": "usdc", "amount": "500000000"}, # 500 USDC
destination_domain=3, # Arbitrum
mint_recipient=format_evm_address("0x742d35..."),
burn_token="usdc"
)
# Execute burn
result = await client.deposit_for_burn(msg)
print(f"Transaction hash: {result.tx_hash}")
print(f"Message hash: {result.message_hash}")
# Get attestation
attestation_service = AttestationService()
attestation = await attestation_service.get_attestation(
result.message_hash
)
return {
"message": result.message,
"attestation": attestation
}
// Process orders with payment on any supported chain
class CrossChainOrderProcessor {
constructor(
private cctp: CCTPModule,
private orderModule: OrderModule
) {}
async processOrderWithCrossChainPayment(
orderId: string,
paymentChain: number,
paymentTx: string
) {
// Verify payment on source chain
const payment = await this.verifyPayment(paymentChain, paymentTx);
if (paymentChain !== STATESET_DOMAIN) {
// Wait for CCTP transfer to complete
const cctpTransfer = await this.waitForCCTPTransfer(
payment.messageHash
);
// Update order with payment
await this.orderModule.updatePayment({
orderId,
paymentInfo: {
type: 'cross_chain_usdc',
sourceChain: paymentChain,
amount: cctpTransfer.amount,
transactionHash: cctpTransfer.txHash
}
});
}
// Fulfill order
await this.orderModule.fulfillOrder(orderId);
}
private async waitForCCTPTransfer(messageHash: string) {
// Monitor for receive message transaction
return new Promise((resolve) => {
const subscription = this.cctp.subscribeToEvents({
messageHash
}).subscribe(event => {
if (event.type === 'mint_and_withdraw') {
subscription.unsubscribe();
resolve(event);
}
});
});
}
}
// Require multiple signatures for large CCTP transfers
class MultiSigCCTP {
constructor(
private cctp: CCTPModule,
private multisig: MultisigModule
) {}
async proposeTransfer(params: CCTPTransferParams) {
// Create multisig proposal
const proposal = await this.multisig.createProposal({
messages: [{
type: 'cctp/MsgDepositForBurn',
value: {
from: this.multisig.address,
amount: params.amount,
destinationDomain: params.destinationDomain,
mintRecipient: params.recipient,
burnToken: 'usdc'
}
}],
metadata: {
title: `CCTP Transfer ${params.amount.amount} USDC to chain ${params.destinationDomain}`,
description: params.description
}
});
return proposal.id;
}
async executeTransfer(proposalId: string) {
// Check if proposal is approved
const proposal = await this.multisig.getProposal(proposalId);
if (!proposal.isApproved()) {
throw new Error('Proposal not approved');
}
// Execute the transfer
return await this.multisig.executeProposal(proposalId);
}
}
// Automatically rebalance treasury across chains
class TreasuryManager {
private readonly TARGET_BALANCE = 100000; // $100k USDC per chain
private readonly REBALANCE_THRESHOLD = 0.2; // 20% deviation
async rebalanceAllChains() {
const balances = await this.getAllChainBalances();
const transfers = this.calculateOptimalTransfers(balances);
// Execute transfers in parallel where possible
const transferPromises = transfers.map(transfer =>
this.executeCCTPTransfer(transfer)
);
const results = await Promise.allSettled(transferPromises);
// Log results
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Transfer ${index} completed:`, result.value);
} else {
console.error(`Transfer ${index} failed:`, result.reason);
}
});
}
private calculateOptimalTransfers(balances: ChainBalance[]): Transfer[] {
const totalBalance = balances.reduce((sum, b) => sum + b.amount, 0);
const targetPerChain = totalBalance / balances.length;
const transfers: Transfer[] = [];
const surplus = balances.filter(b => b.amount > targetPerChain * (1 + this.REBALANCE_THRESHOLD));
const deficit = balances.filter(b => b.amount < targetPerChain * (1 - this.REBALANCE_THRESHOLD));
// Match surplus with deficit chains
for (const source of surplus) {
for (const dest of deficit) {
const transferAmount = Math.min(
source.amount - targetPerChain,
targetPerChain - dest.amount
);
if (transferAmount > 1000) { // Min $1k transfer
transfers.push({
sourceChain: source.domain,
destChain: dest.domain,
amount: transferAmount
});
source.amount -= transferAmount;
dest.amount += transferAmount;
}
}
}
return transfers;
}
}
// Implement rate limiting and compliance checks
class ComplianceCCTP {
private dailyLimits = new Map<string, number>();
private readonly MAX_DAILY_LIMIT = 1000000; // $1M USDC
async transferWithCompliance(params: CCTPTransferParams) {
// Check daily limits
const dailyTotal = await this.getDailyTotal(params.from);
if (dailyTotal + parseInt(params.amount.amount) > this.MAX_DAILY_LIMIT) {
throw new Error('Daily transfer limit exceeded');
}
// Compliance checks
if (await this.isBlacklisted(params.mintRecipient)) {
throw new Error('Recipient address is blacklisted');
}
// Check destination chain restrictions
if (!await this.isDestinationAllowed(params.from, params.destinationDomain)) {
throw new Error('Transfers to this destination not allowed');
}
// Execute transfer with monitoring
const result = await this.cctp.depositForBurn(params);
// Record for compliance
await this.recordTransfer({
from: params.from,
amount: params.amount,
destination: params.destinationDomain,
recipient: params.mintRecipient,
timestamp: Date.now(),
messageHash: result.messageHash
});
return result;
}
}
# Query module configuration
stateset query cctp owner
stateset query cctp signature-threshold
stateset query cctp max-message-body-size
# Query attesters
stateset query cctp attesters
stateset query cctp attester stateset1attester...
# Query token pairs
stateset query cctp token-pairs
stateset query cctp token-pair 0 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
# Query burn limits
stateset query cctp per-message-burn-limit usdc
# Query paused state
stateset query cctp burning-minting-paused
stateset query cctp sending-receiving-paused
// Query module state
const owner = await cctp.query.owner();
const threshold = await cctp.query.signatureThreshold();
const attesters = await cctp.query.attesters();
// Query specific token pair
const tokenPair = await cctp.query.tokenPair({
remoteDomain: 0,
remoteToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'
});
// Query burn limits
const burnLimit = await cctp.query.perMessageBurnLimit({
token: 'usdc'
});
// Monitor all CCTP events
const subscription = cctp.subscribeToEvents()
.subscribe(event => {
switch(event.type) {
case 'deposit_for_burn':
console.log('Burn initiated:', event);
trackBurn(event);
break;
case 'message_sent':
console.log('Message sent:', event);
break;
case 'message_received':
console.log('Message received:', event);
trackReceive(event);
break;
case 'mint_and_withdraw':
console.log('USDC minted:', event);
completeTrasfer(event);
break;
}
});
// Monitor specific message
const messageSubscription = cctp.subscribeToMessage(messageHash)
.subscribe(update => {
console.log('Message status:', update.status);
if (update.status === 'completed') {
messageSubscription.unsubscribe();
}
});
// Set up webhooks for CCTP events
app.post('/webhooks/cctp', async (req, res) => {
const event = req.body;
// Verify webhook signature
if (!verifyWebhookSignature(req)) {
return res.status(401).send('Invalid signature');
}
// Process event
switch(event.type) {
case 'cctp.deposit_for_burn':
await handleBurnEvent(event);
break;
case 'cctp.mint_and_withdraw':
await handleMintEvent(event);
break;
case 'cctp.message_received':
await handleMessageReceived(event);
break;
}
res.status(200).send('OK');
});
import { MockCCTPModule } from '@stateset/cctp-sdk/testing';
describe('CCTP Integration', () => {
let cctp: MockCCTPModule;
beforeEach(() => {
cctp = new MockCCTPModule();
});
it('should burn USDC for cross-chain transfer', async () => {
const burnMsg = {
from: 'stateset1test...',
amount: { denom: 'usdc', amount: '1000000' },
destinationDomain: 0,
mintRecipient: '0x742d35...',
burnToken: 'usdc'
};
const result = await cctp.depositForBurn(burnMsg);
expect(result.transactionHash).toBeDefined();
expect(result.messageHash).toBeDefined();
expect(result.nonce).toBeGreaterThan(0);
});
});
// Test full cross-chain flow
describe('Cross-chain USDC Transfer', () => {
it('should complete transfer from StateSet to Ethereum', async () => {
// 1. Burn on StateSet
const burnResult = await statesetCCTP.depositForBurn({
amount: { denom: 'usdc', amount: '1000000000' },
destinationDomain: 0,
mintRecipient: ethRecipient,
burnToken: 'usdc'
});
// 2. Wait for attestation
const attestation = await waitForAttestation(burnResult.messageHash);
expect(attestation).toBeDefined();
// 3. Mint on Ethereum
const mintTx = await ethereumCCTP.receiveMessage(
burnResult.message,
attestation
);
// 4. Verify balance
const balance = await ethereumUSDC.balanceOf(ethRecipient);
expect(balance.toString()).toBe('1000000000');
});
});
class CCTPErrorHandler {
async handleCCTPOperation(operation: () => Promise<any>) {
try {
return await operation();
} catch (error) {
if (error.code === 'INSUFFICIENT_FUNDS') {
throw new Error('Insufficient USDC balance for transfer');
}
if (error.code === 'INVALID_RECIPIENT') {
throw new Error('Recipient address format is invalid');
}
if (error.code === 'PAUSED') {
throw new Error('CCTP operations are currently paused');
}
if (error.code === 'BURN_LIMIT_EXCEEDED') {
throw new Error('Transfer amount exceeds per-message burn limit');
}
if (error.code === 'NONCE_ALREADY_USED') {
throw new Error('This message has already been processed');
}
// Unknown error
console.error('CCTP operation failed:', error);
throw error;
}
}
}
// Process multiple transfers efficiently
class BatchCCTPProcessor {
async processBatch(transfers: Transfer[]) {
// Group by destination domain
const grouped = this.groupByDomain(transfers);
// Process each group in parallel
const results = await Promise.all(
Object.entries(grouped).map(([domain, transfers]) =>
this.processGroup(parseInt(domain), transfers)
)
);
return results.flat();
}
private async processGroup(domain: number, transfers: Transfer[]) {
// For same destination, can optimize with batching
if (transfers.length > 10) {
return this.processBatchMessage(domain, transfers);
}
// Otherwise process individually in parallel
return Promise.all(transfers.map(t => this.processSingle(t)));
}
}
// Cache attestations and frequently used data
class CachedCCTP {
private attestationCache = new Map<string, string>();
private tokenPairCache = new Map<string, TokenPair>();
async getAttestation(messageHash: string): Promise<string> {
// Check cache first
if (this.attestationCache.has(messageHash)) {
return this.attestationCache.get(messageHash)!;
}
// Fetch from service
const attestation = await this.attestationService.get(messageHash);
// Cache for future use
this.attestationCache.set(messageHash, attestation);
return attestation;
}
}
function validateCCTPTransfer(params: CCTPTransferParams) {
// Validate amount
const amount = parseInt(params.amount.amount);
if (isNaN(amount) || amount <= 0) {
throw new Error('Invalid amount');
}
// Validate destination domain
if (!SUPPORTED_DOMAINS.includes(params.destinationDomain)) {
throw new Error('Unsupported destination domain');
}
// Validate recipient address format
if (!isValidAddressFormat(params.mintRecipient, params.destinationDomain)) {
throw new Error('Invalid recipient address format');
}
// Validate sender has sufficient balance
if (!hasSufficientBalance(params.from, amount)) {
throw new Error('Insufficient balance');
}
}
// Implement rate limiting for CCTP operations
class RateLimitedCCTP {
private limits = new Map<string, RateLimit>();
async checkRateLimit(address: string, amount: number) {
const limit = this.limits.get(address) || this.createLimit(address);
// Check per-transaction limit
if (amount > limit.maxPerTransaction) {
throw new Error('Transaction exceeds maximum allowed amount');
}
// Check hourly limit
const hourlyTotal = await this.getHourlyTotal(address);
if (hourlyTotal + amount > limit.maxPerHour) {
throw new Error('Hourly transfer limit exceeded');
}
// Check daily limit
const dailyTotal = await this.getDailyTotal(address);
if (dailyTotal + amount > limit.maxPerDay) {
throw new Error('Daily transfer limit exceeded');
}
}
}
// Update your integration to use StateSet's native module
class CCTPMigration {
// Old Noble integration
async oldTransfer() {
await nobleClient.execute(
nobleContract,
{ deposit_for_burn: { ... } }
);
}
// New StateSet integration
async newTransfer() {
await statesetCCTP.depositForBurn({
from: signer.address,
amount: { denom: 'usdc', amount: '1000000' },
destinationDomain: 0,
mintRecipient: recipient,
burnToken: 'usdc'
});
}
}