Bridging USDCx

Current work is underway to abstract this entire flow via Circle's Bridge Kit SDK.
Intro
USDCx on Stacks opens up stablecoin liquidity into its decentralized ecosystem via Circle's xReserve protocol. This enables asset transfers from Ethereum and enhances Stacks' DeFi offerings. Users access Stacks' DeFi, maintaining stable assets, increasing liquidity, and providing a reliable option for transactions and investments.
tl;dr
Bridging USDC onto Stacks enables stablecoin liquidity across the Stacks DeFi ecosystem.
Deposits are initiated on Ethereum and automatically minted on Stacks, while withdrawals are initiated on Stacks and settled on Ethereum.
The bridging process is powered by Circle’s xReserve and the Stacks attestation service.
Steps
Deposit
Setup USDC and xReserve's Solidity contract ABIs
Setup wallet and public clients to communicate with the Ethereum network
Check native ETH and USDC balances on a user's wallet.
Prepare deposit params before initiating deposit request.
Approve xReserve as a spender of your USDC.
Execute deposit to the remote chain Stacks.
Withdrawal
Prepare contract call arguments
Invoke
burnfunction from the.usdcx-v1contract
Key Tools To Use
viem - A Typescript-first library that interfaces with Ethereum.
stacks.js - A js library that helps developers build Stacks apps by handling transactions, wallet authentication, and smart contract interactions.
Circle Faucet - Get testnet USDC
Ethereum Sepolia faucet - Get testnet ETH
Complete Code
If you want to jump straight to the full implementation, the complete working code used in this guide is shown below.
This script bridges USDC from Ethereum Sepolia testnet to Stacks testnet by first approving the xReserve contract to spend USDC, then calling depositToRemote to initiate the cross-chain transfer. It encodes the Stacks recipient address into the bytes32 format required by the Ethereum contract and submits both transactions to the Sepolia network. The Stacks attestation service will receive this event and mint the equivalent amount to the specified Stacks address.
This code converts a Stacks address into a 32-byte hex string format required by Ethereum contracts for cross-chain messaging. It encodes the address by left-padding 11 zero bytes, then adding the version byte and 20-byte hash160 from the Stacks address, resulting in a bytes32 value that Ethereum can process.
Walkthrough (Deposit)
Before beginning, make sure you:
Create a wallet on Ethereum Sepolia.
Create a Stacks testnet wallet.
Configure environment variables
In the project root, create a .env file and set your Ethereum Sepolia wallet private key by replacing <YOUR_PRIVATE_KEY>. If you're working on the frontend with a browser wallet extension, this step won't be needed.
Warning: This is strictly for testing purposes. Never share your private key.
Import dependencies and define config constants
In this step, you’ll import the required dependencies and define the script’s configuration constants.
Create a file for the main script and import the below dependencies.
Configuration constants define the RPC endpoint, private key, contract addresses for the xReserve bridge and USDC token, and transfer parameters including the Stacks recipient address and deposit amount.
You can examine the USDC, xReserve, and the other contracts related to USDCx on Stacks here.
Replace YOUR_STACKS_TESTNET_ADDRESS with the wallet that should receive minted USDCx on Stacks testnet.
Stacks' domain id, used for the xReserve protocol, of 10003 is constant for all networks.
Prepare deposit params
Prepares the deposit parameters by converting USDC amounts to the correct decimal format (6 decimals), encoding the Stacks recipient address into bytes32 format, and setting empty hookData for the cross-chain transaction.
Stacks addresses need to be reformatted and encoded to bytes32 on the Ethereum side. Special helper methods are needed for this encoding.
Approve xReserve and execute deposit
Executes two sequential transactions: first approves the xReserve contract to spend the specified USDC amount and waits for confirmation, then calls the depositToRemote function to initiate the cross-chain bridge transfer to the Stacks recipient.
After some time, Stacks attestation service should receive the request and mint the equivalent value in USDCx on Stacks.
These are example transactions on testnet:
Ethereum: Depositing USDC
Stacks: Minting USDCx
Walkthrough (Withdrawal)
Limit: Up to 50 burn intents per request (max 10 per batch, max 5 batches). Submitting more than 50 intents in a single transaction request may lead to failed processing and risk of fund loss.
Prepare contract call arguments
Before invoking the burn function of the .usdcx-v1 contract, you'll need to determine the amount of USDCx to withdraw and the native recipient address that'll receive the USDC on the other chain.
An Ethereum address is technically only 20 bytes but the native-recipient needs to be a 32 byte buffer. Pad left the address to 32 bytes.
Execute withdrawal
Prepare the contract call transaction to invoke the burn function and broadcast the transaction payload.
The Stacks network's attestation service passes the burn intent message and signature to xReserve, managed by Circle. xReserve verifies the burn and issues a withdrawal attestation to release USDC to the user’s wallet.
Additional Insights
What can developers expect regarding timing, fees, and minimum amounts?
Mainnet
Peg-in
~15 minutes
~ETH gas
10 USDC
Peg-out
~60 minutes
~STX fees + $4.80
4.80 USDCx
Testnet
Peg-in
~15 minutes
~ETH gas
1 USDC
Peg-out
~25 minutes
~STX fees + $4.80
4.80 USDCx
Additional Resources
[StacksDevs Livestream] A technical breakdown by the main builder behind Stacks' USDCx
Last updated
Was this helpful?