Quickstart
- SDK Installation & Setup Guide
- Platform Quickstarts
StateSet One
- Guides
StateSet Response
- Core Concepts
- Guides
Guides
CCTP Module Integration Guide
Complete guide to integrating with StateSet’s native CCTP module for cross-chain USDC transfers
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
Copy
Ask AI
# 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
Copy
Ask AI
# 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
Copy
Ask AI
# 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
Copy
Ask AI
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
Copy
Ask AI
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
Copy
Ask AI
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
Copy
Ask AI
// 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
Copy
Ask AI
// 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
Copy
Ask AI
// 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
Copy
Ask AI
// 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
Copy
Ask AI
# 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
Copy
Ask AI
// 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
Copy
Ask AI
// 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
Copy
Ask AI
// 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
Copy
Ask AI
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
Copy
Ask AI
// 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
Copy
Ask AI
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
Copy
Ask AI
// 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
Copy
Ask AI
// 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
Copy
Ask AI
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
Copy
Ask AI
// 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
Copy
Ask AI
// 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:
- Discord: #cctp-dev
- GitHub Issues: github.com/stateset/cctp-module/issues
- Email: developers@stateset.com
On this page
- CCTP Module Integration Guide
- Overview
- Architecture Overview
- Setting Up Your Development Environment
- 1. Install StateSet CLI
- 2. Create Test Accounts
- 3. Install SDKs
- Basic Integration
- JavaScript/TypeScript
- Go Integration
- Python Integration
- Advanced Integration Patterns
- 1. Cross-Chain Order Processing
- 2. Multi-Signature CCTP Operations
- 3. Automated Cross-Chain Treasury Management
- 4. CCTP Rate Limiting and Compliance
- Module Queries
- CLI Queries
- SDK Queries
- Event Monitoring
- Subscribe to CCTP Events
- Webhook Integration
- Testing
- Unit Tests
- Integration Tests
- Error Handling
- Common Errors and Solutions
- Performance Optimization
- 1. Batch Processing
- 2. Caching and Memoization
- Security Best Practices
- 1. Input Validation
- 2. Rate Limiting
- Migration from Other CCTP Implementations
- From Noble to StateSet
- Resources
- Support
Assistant
Responses are generated using AI and may contain mistakes.