CCTP Module Integration Guide

Overview

This guide covers integrating with StateSet’s native CCTP module implementation. Unlike EVM chains where CCTP is implemented via smart contracts, StateSet has built CCTP directly into the blockchain as a Cosmos SDK module, providing superior performance, security, and integration capabilities.

Architecture Overview

Setting Up Your Development Environment

1. Install StateSet CLI

# 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

2. Create Test Accounts

# 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"]}'

3. Install SDKs

# JavaScript/TypeScript
npm install @stateset/cctp-sdk @stateset/client

# Go
go get github.com/stateset/stateset/x/cctp

# Python
pip install stateset-cctp

Basic Integration

JavaScript/TypeScript

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 };
}

Go Integration

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
}

Python Integration

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
    }

Advanced Integration Patterns

1. Cross-Chain Order Processing

// 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);
        }
      });
    });
  }
}

2. Multi-Signature CCTP Operations

// 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);
  }
}

3. Automated Cross-Chain Treasury Management

// 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;
  }
}

4. CCTP Rate Limiting and Compliance

// 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;
  }
}

Module Queries

CLI Queries

# 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

SDK Queries

// 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'
});

Event Monitoring

Subscribe to CCTP Events

// 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();
    }
  });

Webhook Integration

// 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');
});

Testing

Unit Tests

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);
  });
});

Integration Tests

// 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');
  });
});

Error Handling

Common Errors and Solutions

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;
    }
  }
}

Performance Optimization

1. Batch Processing

// 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)));
  }
}

2. Caching and Memoization

// 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;
  }
}

Security Best Practices

1. Input Validation

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');
  }
}

2. Rate Limiting

// 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');
    }
  }
}

Migration from Other CCTP Implementations

From Noble to StateSet

// 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'
    });
  }
}

Resources

Support

For integration support: