A Clarity implementation of USDCx (USD Coin Cross-Chain) for the Stacks blockchain, designed as an educational project to demonstrate cross-chain token bridging.
- Overview
- Architecture
- How Cross-Chain Bridging Works
- Key Components
- Token Contract Details
- Bridge Contract Details
- Setup and Installation
- Testing
- Contract Deployment
- Educational Value
- Differences from Production USDCx
- Contributing
- License
- Resources
USDCxBL implements a cross-chain stablecoin protocol that allows USDC to be bridged between Stacks and other blockchain networks. This project is based on the official USDCx implementation and serves as a learning resource for understanding:
- SIP-010 fungible token standards
- Cross-chain bridging mechanisms
- Role-based access control in smart contracts
- Deposit intent validation and signature recovery
- Protocol pausing and governance
The project consists of two main contracts:
The core USDCx token implementing the SIP-010 standard with additional protocol features:
- SIP-010 Compliance: Standard fungible token interface
- Role-Based Access Control: Governance, mint, and pause roles
- Protocol Functions: Minting, burning, and transferring with protocol controls
- Pausable Protocol: Emergency stop functionality
Key Features:
- Token name: USDCxBradleyLearn
- Symbol: USDCxBL
- Decimals: 6
- Total supply: Dynamic (minted/burned as needed)
graph TD
A[User/Contract] --> B[SIP-010 Interface]
B --> C[get-name]
B --> D[get-symbol]
B --> E[get-decimals]
B --> F[get-balance]
B --> G[get-total-supply]
B --> H[transfer]
A --> I[Protocol Interface]
I --> J[validate-protocol-caller]
I --> K[is-protocol-paused]
I --> L[protocol-transfer]
I --> M[protocol-mint]
I --> N[protocol-burn]
I --> O[protocol-mint-many]
A --> P[Governance Interface]
P --> Q[set-active-protocol-caller]
P --> R[protocol-set-name]
P --> S[protocol-set-symbol]
P --> T[protocol-set-token-uri]
A --> U[Pause Interface]
U --> V[pause]
U --> W[unpause]
J --> X[Role Validation]
K --> Y[Pause Check]
M --> X
N --> X
O --> X
Q --> X
R --> X
S --> X
T --> X
V --> X
W --> X
The cross-chain bridge implementation handling deposits and withdrawals:
- Deposit Processing: Validates and processes cross-chain deposits
- Signature Verification: Uses Circle attestors for deposit validation
- Minting Logic: Mints USDCx tokens upon successful deposit verification
- Burning Logic: Burns tokens for cross-chain withdrawals
- Nonce Management: Prevents replay attacks
graph TD
A[External Chain] --> B[Deposit Intent Created]
B --> C[Circle Attestor Signs]
C --> D[Deposit Intent + Signature]
D --> E[Bridge Contract: mint]
E --> F[parse-and-validate-deposit-intent]
F --> G[verify-deposit-intent-signature]
G --> H[Check Nonce Used]
H --> I[Protocol Mint USDCx]
I --> J[Mark Nonce Used]
K[Stacks User] --> L[Bridge Contract: burn]
L --> M[Check Min Withdrawal]
M --> N[Check Native Domain]
N --> O[Protocol Burn USDCx]
O --> P[Emit Burn Event]
F --> Q[Parse Deposit Intent]
Q --> R[Validate Magic/Version]
R --> S[Validate Amounts]
S --> T[Validate Domains/Tokens]
T --> U[Get Remote Recipient]
G --> V[Recover PK from Sig]
V --> W[Check Attestor Map]
- Deposit Intent Creation: User creates a deposit intent on the source chain
- Attestation: Circle attestors sign the deposit intent
- Validation: Bridge contract verifies the signature and intent parameters
- Minting: USDCx tokens are minted to the recipient on Stacks
- Burn Request: User calls burn function with withdrawal details
- Token Burning: USDCx tokens are burned from user's balance
- Event Emission: Burn event is emitted for off-chain processing
- Cross-Chain Transfer: External system processes the withdrawal on destination chain
{
magic: 0x5a2e0acd,
version: u1,
amount: uint,
remote-domain: uint,
remote-token: (buff 32),
remote-recipient: (buff 32),
local-token: (buff 32),
local-depositor: (buff 32),
max-fee: uint,
nonce: (buff 32),
hook-data: (buff 80)
}- Governance (0x00): Contract updates, role management
- Mint (0x01): Token minting and burning operations
- Pause (0x02): Protocol pause/unpause controls
- Signature Verification: ECDSA signature recovery using secp256k1
- Nonce Tracking: Prevents deposit replay attacks
- Fee Management: Configurable fees for cross-chain operations
- Minimum Withdrawal Amounts: Prevents dust transactions
transfer(amount, sender, recipient, memo): Transfers tokens between accounts. Validates ownership and emits memo if provided.get-name(): Returns the token name ("USDCxBradleyLearn").get-symbol(): Returns the token symbol ("USDCxBL").get-decimals(): Returns the decimal places (6).get-balance(owner): Returns the token balance of an account.get-total-supply(): Returns the total supply of tokens.get-token-uri(): Returns the optional token metadata URI.
validate-protocol-caller(role, caller): Checks if a caller has the required role.is-protocol-paused(): Returns whether the protocol is paused.validate-protocol-active(): Ensures the protocol is not paused.protocol-transfer(amount, sender, recipient): Transfers tokens with protocol validation (mint role required).protocol-mint(amount, recipient): Mints new tokens (mint role required).protocol-burn(amount, owner): Burns tokens from an account (mint role required).protocol-mint-many(recipients): Mints tokens to multiple recipients (mint role required).
set-active-protocol-caller(caller, role, enabled): Adds or removes protocol roles (governance role required).protocol-set-name(new-name): Updates token name (governance role required).protocol-set-symbol(new-symbol): Updates token symbol (governance role required).protocol-set-token-uri(new-uri): Updates token URI (governance role required).
pause(): Pauses the protocol (pause role required).unpause(): Unpauses the protocol (pause role required).
parse-deposit-intent(deposit-intent): Parses raw bytes into a structured deposit intent.recover-deposit-intent-pk(deposit-intent, signature): Recovers the public key from a signature.verify-deposit-intent-signature(deposit-intent, signature): Verifies signature against known attestors.get-remote-recipient(remote-recipient-bytes, hook-data): Converts bytes to principal, supporting contract recipients.parse-and-validate-deposit-intent(deposit-intent): Full validation including Stacks-specific checks.mint(deposit-intent, signature, fee-amount): Main minting function - validates and mints USDCx.
set-min-withdrawal-amount(new-min): Sets minimum withdrawal amount (custom role 0x04 required).get-min-withdrawal-amount(): Returns the current minimum withdrawal amount.burn(amount, native-domain, native-recipient): Burns tokens for withdrawal, emits burn event.
add-or-remove-circle-attestor(public-key, enabled): Adds/removes Circle attestors (governance role required).
- Clarinet - Stacks development environment
- Node.js (for testing)
- npm or yarn
- Clone the repository:
git clone <repository-url>
cd USDCx- Install dependencies:
npm install- Check contracts:
clarinet checkRun the test suite:
npm testWatch mode for development:
npm run test:watchThe contracts are configured in Clarinet.toml for local development. For mainnet deployment:
- Update contract names and parameters
- Set appropriate Circle attestor public keys
- Configure domain identifiers
- Deploy token contract first, then bridge contract
This implementation demonstrates:
- Smart Contract Architecture: Modular design with separate token and bridge contracts
- Cross-Chain Communication: Handling deposits from external chains
- Cryptographic Operations: Signature verification and public key recovery
- State Management: Maps for attestors, nonces, and protocol state
- Error Handling: Comprehensive error codes and validation
- Event Logging: Print statements for off-chain monitoring
This educational version includes:
- Simplified attestor management
- Learning-focused naming and comments
- Test-friendly configurations
- Additional logging for debugging
This is an educational project. For production use, refer to the official Circle USDCx implementation.
ISC License