StateSet CCTP Module
Overview
The StateSet CCTP Module is a native Cosmos SDK module that implements Circle’s Cross-Chain Transfer Protocol directly on StateSet Commerce Network. Unlike EVM chains where CCTP is implemented as smart contracts, StateSet’s implementation is built into the blockchain itself, providing superior performance, security, and integration with other StateSet modules.
Architecture
Module State
Core State Objects
// Owner - Controls module administration
type Owner struct {
Address string
}
// PendingOwner - Two-phase ownership transfer
type PendingOwner struct {
Address string
}
// AttesterManager - Manages attestation validators
type AttesterManager struct {
Address string
}
// Pauser - Can pause module operations
type Pauser struct {
Address string
}
// TokenController - Manages token configurations
type TokenController struct {
Address string
}
Operational State
// Paused states
type PausedState struct {
SendingAndReceivingPaused bool
BurningAndMintingPaused bool
}
// Message tracking
type MessageState struct {
NextAvailableNonce uint64
UsedNonces map[uint64]bool
MaxMessageBodySize uint64
}
// Attestation configuration
type AttestationConfig struct {
SignatureThreshold uint32
Attesters []Attester
}
// Token configuration
type TokenConfig struct {
TokenPairs []TokenPair
PerMessageBurnLimit map[string]sdk.Int
RemoteTokenMessengers map[uint32]string
}
Core Messages
1. DepositForBurn
Burns USDC on StateSet and initiates a cross-chain transfer.
type MsgDepositForBurn struct {
From string
Amount sdk.Coin
DestinationDomain uint32
MintRecipient []byte // 32 bytes
BurnToken string
}
// Example usage
msg := &MsgDepositForBurn{
From: "stateset1abc...",
Amount: sdk.NewCoin("usdc", sdk.NewInt(1000000000)), // 1000 USDC
DestinationDomain: 0, // Ethereum
MintRecipient: ethAddress32Bytes,
BurnToken: "usdc",
}
2. DepositForBurnWithCaller
Burns USDC with a specific authorized caller on the destination chain.
type MsgDepositForBurnWithCaller struct {
From string
Amount sdk.Coin
DestinationDomain uint32
MintRecipient []byte // 32 bytes
BurnToken string
DestinationCaller []byte // 32 bytes
}
3. ReceiveMessage
Processes incoming CCTP messages and mints USDC.
type MsgReceiveMessage struct {
From string
Message []byte
Attestation []byte
}
// Message format
type Message struct {
Version uint32
SourceDomain uint32
DestinationDomain uint32
Nonce uint64
Sender []byte // 32 bytes
Recipient []byte // 32 bytes
DestinationCaller []byte // 32 bytes
MessageBody []byte
}
4. ReplaceDepositForBurn
Replaces a pending burn message before attestation.
type MsgReplaceDepositForBurn struct {
From string
OriginalMessage []byte
OriginalAttestation []byte
NewMintRecipient []byte // 32 bytes, optional
NewDestinationCaller []byte // 32 bytes, optional
}
5. SendMessage
Sends a generic message to another domain.
type MsgSendMessage struct {
From string
DestinationDomain uint32
Recipient []byte // 32 bytes
MessageBody []byte
}
6. SendMessageWithCaller
Sends a message with a specific authorized caller.
type MsgSendMessageWithCaller struct {
From string
DestinationDomain uint32
Recipient []byte // 32 bytes
MessageBody []byte
DestinationCaller []byte // 32 bytes
}
Administrative Messages
Ownership Management
// Initiate ownership transfer
type MsgUpdateOwner struct {
From string
NewOwner string
}
// Accept ownership transfer
type MsgAcceptOwner struct {
From string
}
Attester Management
// Enable an attester
type MsgEnableAttester struct {
From string
Attester string
}
// Disable an attester
type MsgDisableAttester struct {
From string
Attester string
}
// Update signature threshold
type MsgUpdateSignatureThreshold struct {
From string
Amount uint32
}
Token Management
// Link a token pair
type MsgLinkTokenPair struct {
From string
LocalToken string
RemoteToken []byte // 32 bytes
RemoteDomain uint32
}
// Set burn limit per message
type MsgSetMaxBurnAmountPerMessage struct {
From string
LocalToken string
Amount sdk.Int
}
Pause Controls
// Pause operations
type MsgPauseBurningAndMinting struct {
From string
}
type MsgPauseSendingAndReceivingMessages struct {
From string
}
// Unpause operations
type MsgUnpauseBurningAndMinting struct {
From string
}
type MsgUnpauseSendingAndReceivingMessages struct {
From string
}
Integration with StateSet Modules
Orders Module Integration
// Automatic CCTP payment for cross-chain orders
func (k Keeper) PayOrderCrossChain(ctx sdk.Context, order Order, paymentChain uint32) error {
if paymentChain == STATESET_DOMAIN {
// Local payment
return k.processLocalPayment(ctx, order)
}
// Cross-chain payment via CCTP
msg := &MsgDepositForBurn{
From: order.CustomerAddress,
Amount: order.TotalAmount,
DestinationDomain: STATESET_DOMAIN,
MintRecipient: k.GetMerchantAddress(ctx),
BurnToken: "usdc",
}
// Process burn
burnResult, err := k.cctpKeeper.DepositForBurn(ctx, msg)
if err != nil {
return err
}
// Update order with cross-chain payment info
order.PaymentInfo = PaymentInfo{
Type: "cross_chain",
SourceChain: paymentChain,
MessageHash: burnResult.MessageHash,
AttestationPending: true,
}
return k.SetOrder(ctx, order)
}
Finance Module Integration
// Cross-chain invoice factoring
func (k Keeper) FactorInvoiceCrossChain(
ctx sdk.Context,
invoice Invoice,
factorChain uint32,
) error {
// Tokenize invoice as NFT
nft := k.TokenizeInvoice(ctx, invoice)
// Prepare cross-chain message
factorMsg := FactorInvoiceMessage{
InvoiceID: invoice.ID,
NFTMetadata: nft.Metadata,
Amount: invoice.Amount,
DiscountRate: invoice.FactoringRate,
}
// Send via CCTP
msg := &MsgSendMessageWithCaller{
From: invoice.SellerAddress,
DestinationDomain: factorChain,
Recipient: factorContractAddress,
MessageBody: factorMsg.Bytes(),
DestinationCaller: factorContractAddress, // Only factor contract can process
}
return k.cctpKeeper.SendMessageWithCaller(ctx, msg)
}
Agent Module Integration
// Enable agents to execute cross-chain transactions
func (k Keeper) AgentCrossChainTransfer(
ctx sdk.Context,
agent Agent,
transfer CrossChainTransfer,
) error {
// Verify agent permissions
if !k.CanAgentTransferCrossChain(ctx, agent, transfer) {
return ErrAgentNotAuthorized
}
// Check daily limits
if err := k.CheckAgentDailyLimit(ctx, agent, transfer.Amount); err != nil {
return err
}
// Execute CCTP transfer
msg := &MsgDepositForBurn{
From: agent.WalletAddress,
Amount: transfer.Amount,
DestinationDomain: transfer.DestinationChain,
MintRecipient: transfer.Recipient,
BurnToken: "usdc",
}
return k.cctpKeeper.DepositForBurn(ctx, msg)
}
Events
Core Events
// Emitted when tokens are burned for cross-chain transfer
type EventDepositForBurn struct {
Nonce uint64
BurnToken string
Amount sdk.Int
Depositor string
MintRecipient []byte
DestinationDomain uint32
DestinationCaller []byte
}
// Emitted when a message is sent
type EventMessageSent struct {
Message []byte
}
// Emitted when a message is received
type EventMessageReceived struct {
Caller string
SourceDomain uint32
Nonce uint64
Sender []byte
MessageBody []byte
}
// Emitted when tokens are minted
type EventMintAndWithdraw struct {
MintRecipient string
Amount sdk.Int
MintToken string
}
Attestation System
Attestation Verification
func (k Keeper) ValidateAttestation(
ctx sdk.Context,
message []byte,
attestation []byte,
) error {
// Get current attesters and threshold
attesters := k.GetEnabledAttesters(ctx)
threshold := k.GetSignatureThreshold(ctx)
// Verify attestation length
expectedLen := 65 * threshold // 65 bytes per signature
if len(attestation) != int(expectedLen) {
return ErrInvalidAttestationLength
}
// Extract and verify signatures
messageHash := crypto.Keccak256(message)
signers := make([]string, 0, threshold)
for i := 0; i < int(threshold); i++ {
sig := attestation[i*65 : (i+1)*65]
signer, err := RecoverSigner(messageHash, sig)
if err != nil {
return err
}
signers = append(signers, signer)
}
// Verify signers are enabled attesters in order
return k.VerifyAttesters(ctx, signers, attesters)
}
Query Interface
gRPC Queries
service Query {
// Get module roles
rpc Owner(QueryOwnerRequest) returns (QueryOwnerResponse);
rpc AttesterManager(QueryAttesterManagerRequest) returns (QueryAttesterManagerResponse);
rpc Pauser(QueryPauserRequest) returns (QueryPauserResponse);
rpc TokenController(QueryTokenControllerRequest) returns (QueryTokenControllerResponse);
// Get configuration
rpc SignatureThreshold(QuerySignatureThresholdRequest) returns (QuerySignatureThresholdResponse);
rpc MaxMessageBodySize(QueryMaxMessageBodySizeRequest) returns (QueryMaxMessageBodySizeResponse);
rpc NextAvailableNonce(QueryNextAvailableNonceRequest) returns (QueryNextAvailableNonceResponse);
// Get attesters
rpc Attester(QueryAttesterRequest) returns (QueryAttesterResponse);
rpc Attesters(QueryAttestersRequest) returns (QueryAttestersResponse);
// Get token configuration
rpc TokenPair(QueryTokenPairRequest) returns (QueryTokenPairResponse);
rpc TokenPairs(QueryTokenPairsRequest) returns (QueryTokenPairsResponse);
rpc PerMessageBurnLimit(QueryPerMessageBurnLimitRequest) returns (QueryPerMessageBurnLimitResponse);
// Get paused states
rpc BurningAndMintingPaused(QueryBurningAndMintingPausedRequest) returns (QueryBurningAndMintingPausedResponse);
rpc SendingAndReceivingPaused(QuerySendingAndReceivingPausedRequest) returns (QuerySendingAndReceivingPausedResponse);
}
CLI Commands
Transaction Commands
# Burn USDC for cross-chain transfer
stateset tx cctp deposit-for-burn \
--amount 1000usdc \
--destination-domain 0 \
--mint-recipient 0x742d35Cc6634C0532925a3b844Bc9e7595f6E321 \
--from mykey
# Receive message with attestation
stateset tx cctp receive-message \
--message-hex 0x... \
--attestation-hex 0x... \
--from mykey
# Administrative commands
stateset tx cctp enable-attester stateset1attester... --from owner
stateset tx cctp link-token-pair usdc 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 0 --from token-controller
stateset tx cctp set-burn-limit usdc 1000000000000 --from token-controller
Query Commands
# Query configuration
stateset query cctp owner
stateset query cctp signature-threshold
stateset query cctp attesters
stateset query cctp token-pairs
# Query specific token pair
stateset query cctp token-pair 0 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
# Query paused states
stateset query cctp burning-minting-paused
stateset query cctp sending-receiving-paused
Genesis State
{
"owner": "stateset1owner...",
"attester_manager": "stateset1attestermanager...",
"pauser": "stateset1pauser...",
"token_controller": "stateset1tokencontroller...",
"signature_threshold": 2,
"max_message_body_size": "10000",
"next_available_nonce": "1",
"attesters": [
{
"address": "stateset1attester1...",
"enabled": true
},
{
"address": "stateset1attester2...",
"enabled": true
}
],
"token_pairs": [
{
"remote_domain": 0,
"remote_token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"local_token": "usdc"
}
],
"per_message_burn_limits": [
{
"token": "usdc",
"limit": "1000000000000"
}
],
"burning_and_minting_paused": false,
"sending_and_receiving_messages_paused": false
}
Security Considerations
Access Control
- Role-based permissions: Only authorized addresses can perform administrative actions
- Two-phase ownership transfer: Prevents accidental loss of ownership
- Pause mechanisms: Emergency circuit breakers for security incidents
Message Security
- Nonce tracking: Prevents replay attacks
- Domain validation: Ensures messages are for the correct chain
- Attestation verification: Multi-signature validation from trusted attesters
- Message size limits: Prevents DoS through large messages
Token Security
- Burn limits: Per-message caps to limit exposure
- Token pair validation: Only configured tokens can be transferred
- Atomic operations: Burns and mints are atomic within their domains
Migration Guide
// StateSet domain: 7
// Update your domain references
const (
STATESET_DOMAIN = 7
)
// Update module addresses
const (
// New StateSet address
CCTP_ADDRESS = "stateset1cctp7m4ugfz4m6dd73yysz477jszqnfughxvkss5"
)
Best Practices
1. Message Construction
// Always validate addresses before creating messages
func PrepareRecipientAddress(address string, chain string) ([]byte, error) {
switch chain {
case "ethereum", "arbitrum", "optimism", "base":
return ConvertEVMAddress(address)
case "stateset":
return ConvertCosmosAddress(address)
default:
return nil, ErrUnsupportedChain
}
}
2. Attestation Handling
// Implement retry logic for attestation service
func WaitForAttestation(messageHash []byte, maxRetries int) ([]byte, error) {
for i := 0; i < maxRetries; i++ {
attestation, err := FetchAttestation(messageHash)
if err == nil {
return attestation, nil
}
if i < maxRetries-1 {
time.Sleep(time.Second * time.Duration(math.Pow(2, float64(i))))
}
}
return nil, ErrAttestationTimeout
}
3. Error Handling
// Comprehensive error handling for CCTP operations
func HandleCCTPTransfer(transfer CCTPTransfer) error {
// Pre-flight checks
if err := ValidateTransfer(transfer); err != nil {
return fmt.Errorf("validation failed: %w", err)
}
// Execute transfer
result, err := ExecuteTransfer(transfer)
if err != nil {
// Check if error is retryable
if IsRetryableError(err) {
return RetryTransfer(transfer)
}
return fmt.Errorf("transfer failed: %w", err)
}
// Monitor attestation
go MonitorAttestation(result.MessageHash)
return nil
}
Resources
Support
For CCTP module support:
Responses are generated using AI and may contain mistakes.