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

  1. Role-based permissions: Only authorized addresses can perform administrative actions
  2. Two-phase ownership transfer: Prevents accidental loss of ownership
  3. Pause mechanisms: Emergency circuit breakers for security incidents

Message Security

  1. Nonce tracking: Prevents replay attacks
  2. Domain validation: Ensures messages are for the correct chain
  3. Attestation verification: Multi-signature validation from trusted attesters
  4. Message size limits: Prevents DoS through large messages

Token Security

  1. Burn limits: Per-message caps to limit exposure
  2. Token pair validation: Only configured tokens can be transferred
  3. 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: