Cross-Chain Transfer Protocol (CCTP) on StateSet
Overview
StateSet Commerce Network implements Circle’s Cross-Chain Transfer Protocol (CCTP) as a native Cosmos SDK module, enabling seamless USDC transfers between StateSet and other supported blockchains. This eliminates the need for wrapped tokens or custodial bridges, providing a secure and efficient way to move USDC across chains.
StateSet has implemented CCTP as a native blockchain module rather than smart contracts, providing superior performance and deeper integration with other StateSet features. For technical details, see the StateSet CCTP Module documentation.
How CCTP Works
Key Benefits
- Native USDC: No wrapped tokens - USDC is burned on source and minted on destination
- No Bridge Risk: Eliminates custodial bridge vulnerabilities
- Unified Liquidity: Same USDC across all supported chains
- Fast Transfers: Typically completes in minutes
- Low Cost: Only pay standard transaction fees
Supported Chains
Chain | Domain | Status |
---|
StateSet Commerce Network | 7 | ✅ Active |
Ethereum | 0 | ✅ Active |
Avalanche | 1 | ✅ Active |
Optimism | 2 | ✅ Active |
Arbitrum | 3 | ✅ Active |
Base | 6 | ✅ Active |
Polygon | 7 | ✅ Active |
Module Address
StateSet CCTP Module
- Mainnet:
stateset1cctp7m4ugfz4m6dd73yysz477jszqnfughxvkss5
- Testnet:
stateset1test9x8ugfz4m6dd73yysz477jszqnfughxvkss5
Quick Start
Transfer USDC from StateSet to Ethereum
import { StateSetCCTP } from '@stateset/cctp-sdk';
const cctp = new StateSetCCTP({
endpoint: 'https://api.stateset.network',
signer: wallet
});
// Burn USDC on StateSet
const burnTx = await cctp.depositForBurn({
amount: '1000.00', // 1000 USDC
destinationDomain: 0, // Ethereum
mintRecipient: '0x742d35Cc6634C0532925a3b844Bc9e7595f6E321',
burnToken: 'usdc'
});
// Get attestation from Circle
const attestation = await cctp.getAttestation(burnTx.messageHash);
// Mint on Ethereum (using ethers.js)
const mintTx = await ethereumCCTP.receiveMessage(
burnTx.message,
attestation
);
Core Functions
depositForBurn
Burns USDC on StateSet and initiates transfer to another chain.
interface DepositForBurnParams {
amount: string; // Amount of USDC to transfer
destinationDomain: number; // Target chain domain
mintRecipient: string; // Recipient address (32 bytes)
burnToken: string; // Token to burn (usually 'usdc')
}
const result = await cctp.depositForBurn({
amount: '500.00',
destinationDomain: 3, // Arbitrum
mintRecipient: ethers.utils.hexZeroPad(recipientAddress, 32),
burnToken: 'usdc'
});
// Returns
{
txHash: '0x...',
messageHash: '0x...',
message: '0x...',
nonce: 12345
}
depositForBurnWithCaller
Burns USDC with a specific caller authorized on destination.
interface DepositForBurnWithCallerParams {
amount: string;
destinationDomain: number;
mintRecipient: string;
burnToken: string;
destinationCaller: string; // Authorized caller (32 bytes)
}
// Only specified contract can receive on destination
const result = await cctp.depositForBurnWithCaller({
amount: '1000.00',
destinationDomain: 0,
mintRecipient: ethers.utils.hexZeroPad(recipientAddress, 32),
burnToken: 'usdc',
destinationCaller: ethers.utils.hexZeroPad(contractAddress, 32)
});
receiveMessage
Receives and processes a CCTP message to mint USDC.
interface ReceiveMessageParams {
message: string; // Message bytes from source chain
attestation: string; // Attestation from Circle
}
// Receive USDC from another chain
const result = await cctp.receiveMessage({
message: '0x...', // From burn transaction
attestation: '0x...' // From Circle API
});
// Returns
{
txHash: '0x...',
amount: '1000.00',
recipient: 'stateset1...'
}
replaceDepositForBurn
Replace a pending burn message (before attestation).
interface ReplaceDepositForBurnParams {
originalMessage: string;
originalAttestation: string;
newMintRecipient?: string;
newDestinationCaller?: string;
}
// Change recipient before message is processed
const result = await cctp.replaceDepositForBurn({
originalMessage: '0x...',
originalAttestation: '0x...',
newMintRecipient: ethers.utils.hexZeroPad(newRecipient, 32)
});
CCTP messages follow a standardized format across all chains:
interface CCTPMessage {
version: number; // Message version (currently 0)
sourceDomain: number; // Origin chain domain
destinationDomain: number; // Target chain domain
nonce: number; // Unique message identifier
sender: string; // Sender address (32 bytes)
recipient: string; // Recipient address (32 bytes)
destinationCaller: string;// Authorized caller (32 bytes)
messageBody: string; // Message payload
}
Integration Examples
E-Commerce Cross-Chain Payments
// Accept payment on any chain, settle on StateSet
class CrossChainPaymentProcessor {
async acceptPayment(
orderData: Order,
paymentChain: number,
customerAddress: string
) {
// Generate payment address on customer's chain
const paymentAddress = await this.getPaymentAddress(paymentChain);
// Monitor for payment
const payment = await this.waitForPayment(
paymentAddress,
orderData.total
);
// Transfer to StateSet for settlement
if (paymentChain !== STATESET_DOMAIN) {
const burnTx = await this.burnUSDC({
chain: paymentChain,
amount: payment.amount,
destinationDomain: STATESET_DOMAIN,
mintRecipient: this.settlementAddress
});
// Get attestation
const attestation = await this.getAttestation(burnTx.messageHash);
// Receive on StateSet
await this.stateset.cctp.receiveMessage({
message: burnTx.message,
attestation
});
}
// Process order
await this.fulfillOrder(orderData);
}
}
Multi-Chain Treasury Management
// Optimize treasury across chains
class MultiChainTreasury {
async rebalance() {
const balances = await this.getBalancesAllChains();
const optimal = this.calculateOptimalDistribution(balances);
for (const transfer of optimal.transfers) {
// Move funds between chains
await this.cctpTransfer({
fromChain: transfer.source,
toChain: transfer.destination,
amount: transfer.amount
});
}
}
async cctpTransfer({ fromChain, toChain, amount }) {
// Initiate burn on source chain
const burnTx = await this.clients[fromChain].depositForBurn({
amount,
destinationDomain: this.domains[toChain],
mintRecipient: this.addresses[toChain]
});
// Wait for attestation
const attestation = await this.waitForAttestation(
burnTx.messageHash
);
// Complete on destination
await this.clients[toChain].receiveMessage({
message: burnTx.message,
attestation
});
}
}
Arbitrage Across Chains
// Execute arbitrage using CCTP for instant settlement
class CrossChainArbitrage {
async executeArbitrage(opportunity: ArbOpportunity) {
// Buy on cheap chain
const buyTx = await this.dexes[opportunity.buyChain].swap({
from: 'USDC',
to: opportunity.token,
amount: opportunity.amount
});
// Transfer token to expensive chain (if supported)
// ... token bridge logic ...
// Sell on expensive chain
const sellTx = await this.dexes[opportunity.sellChain].swap({
from: opportunity.token,
to: 'USDC',
amount: buyTx.output
});
// Transfer profits back to StateSet
const profit = sellTx.output - opportunity.amount;
if (profit > 10) { // Min $10 profit
await this.transferViaProtocol({
amount: profit.toString(),
fromChain: opportunity.sellChain,
toChain: 'stateset'
});
}
}
}
Attestation Service
Circle provides attestations for CCTP messages. Integration example:
class AttestationService {
private baseUrl = 'https://iris-api.circle.com/attestations';
async getAttestation(messageHash: string): Promise<string> {
// Poll for attestation (usually ready in 10-30 seconds)
for (let i = 0; i < 60; i++) {
try {
const response = await fetch(
`${this.baseUrl}/${messageHash}`
);
if (response.status === 200) {
const data = await response.json();
return data.attestation;
}
} catch (error) {
// Continue polling
}
await new Promise(resolve => setTimeout(resolve, 2000));
}
throw new Error('Attestation timeout');
}
}
Gas Optimization
Batch Transfers
// Batch multiple transfers to save gas
class BatchCCTP {
async batchTransfer(transfers: Transfer[]) {
// Group by destination domain
const grouped = this.groupByDomain(transfers);
for (const [domain, domainTransfers] of grouped) {
// Create batch message
const batchMessage = this.encodeBatchTransfer(domainTransfers);
// Single burn for multiple recipients
await this.cctp.depositForBurnWithCaller({
amount: this.sumAmounts(domainTransfers),
destinationDomain: domain,
mintRecipient: this.batchProcessor[domain],
destinationCaller: this.batchProcessor[domain],
burnToken: 'usdc'
});
}
}
}
Security Considerations
Best Practices
- Verify Addresses: Always verify recipient addresses are correct format
- Check Domains: Ensure destination domain matches intended chain
- Monitor Attestations: Implement timeout handling for attestations
- Validate Messages: Verify message integrity before processing
- Rate Limiting: Implement transfer limits for security
// Convert addresses to 32-byte format
function formatAddress(address: string, chain: string): string {
switch(chain) {
case 'ethereum':
case 'arbitrum':
case 'optimism':
case 'base':
case 'avalanche':
case 'polygon':
// EVM addresses: pad to 32 bytes
return ethers.utils.hexZeroPad(address, 32);
case 'stateset':
// Cosmos addresses: convert and pad
const decoded = bech32.decode(address);
const bytes = bech32.fromWords(decoded.words);
return '0x' + Buffer.from(bytes).toString('hex').padStart(64, '0');
default:
throw new Error(`Unsupported chain: ${chain}`);
}
}
Error Handling
Common errors and solutions:
Error | Cause | Solution |
---|
Invalid domain | Unsupported destination chain | Check supported domains list |
Insufficient balance | Not enough USDC | Ensure adequate balance |
Invalid recipient | Malformed address | Use proper address formatting |
Attestation timeout | Circle service delay | Implement retry logic |
Nonce already used | Duplicate message | Use unique nonce |
Monitoring and Analytics
// Track CCTP transfers
class CCTPMonitor {
async trackTransfer(transfer: CCTPTransfer) {
// Log transfer initiation
await this.log({
type: 'cctp_burn',
chain: transfer.sourceChain,
amount: transfer.amount,
destination: transfer.destinationChain,
timestamp: Date.now()
});
// Monitor attestation
const attestationTime = await this.measureAttestationTime(
transfer.messageHash
);
// Track completion
const completion = await this.waitForMint(
transfer.destinationChain,
transfer.messageHash
);
// Record metrics
await this.metrics.record({
transferTime: completion.timestamp - transfer.timestamp,
attestationTime,
gasUsed: transfer.gasUsed + completion.gasUsed,
success: true
});
}
}
Native Module vs Smart Contracts
StateSet implements CCTP as a native Cosmos SDK module, providing several advantages:
- 10x faster: Direct blockchain integration vs contract execution
- Lower fees: No contract overhead, just base transaction costs
- Atomic operations: Module-level atomicity guarantees
Integration
- Deep integration: Direct access to other StateSet modules
- Native CLI: Built-in command-line interface
- Governance: Integrated with StateSet governance
Security
- No contract risk: Eliminate smart contract vulnerabilities
- Chain-level validation: Enhanced security at the consensus layer
- Upgrade path: Coordinated upgrades via governance
For detailed module documentation, see StateSet CCTP Module.
Resources
Support
For CCTP integration support: