View a
reference contract
that mints and burns USDC-backed stablecoins in the
Circle xReserve Contracts
GitHub repository.
Deployment requirements
USDC-backed stablecoins are backed 1:1 by an equivalent amount of USDC held in Circle’s xReserve contract. Your contract mints them after verifying a deposit attestation, and users burn them when they want to withdraw USDC or other USDC-backed stablecoins on a supported blockchain. When implementing your USDC-backed stablecoin contract, ensure that you:- Follow the decimal precision of native USDC, which is 6 decimal places. If your blockchain token standard uses a different decimal precision, you must handle the decimal conversion in the onchain contract so that it still aligns with 6-decimal USDC.
- Use
uint256for balances and amounts by default. If your runtime forces a smaller width such asuint128oruint64, select the equivalent integer type and implement overflow and underflow safeguards for that width.
State and configuration requirements
The following table lists the variables your token contract should track and the invariants that must remain true after every call that changes state.| Variable | Description | Required Invariant |
|---|---|---|
uint32 domain | Circle-assigned identifier for your blockchain. | Value must match the domain ID that Circle assigned to your blockchain. It must be immutable after deployment. |
mapping(bytes32 => uint256) balances | Tracks balances for each USDC-backed stablecoin account. | Must be non-negative. |
mapping(bytes32 => bool) usedNonces | Tracks consumed deposit intents (for replay protection). | Must be false before mint and set true atomically during mint. |
mapping(address => bool) xReserveAttesters | Allowlist of xReserve attester addresses (in bytes20 form). | Only the owner can configure this list. |
uint256 minBurnSize | Minimum burn amount for withdrawals. | The owner must implement and configure this amount. |
uint256 totalSupply (optional onchain) | Aggregate minted minus burned balance. | Must equal the sum of all account balances. Can be omitted if the complete USDC-backed token supply is indexed and exposed offchain through an API. |
Core contract functions
Your USDC-backed stablecoin contract should expose these external functions.mint(bytes depositIntent, bytes depositAttestation, uint256 feeAmount)
Mints USDC-backed stablecoins after verifying a deposit attestation.
For the canonical DepositIntent message format, see the
Technical Guide.
Preconditions
The following conditions must be met before you move state or the call will
revert:
ECDSA.recover(hash(payload), signature)must resolve to an address whosebytes20istruein thexReserveAttestersallowlist.depositIntent.magicmust be0x5a2e0acd.depositIntent.versionmust be1.depositIntent.amountmust be greater than zero.depositIntent.remoteDomainmust match this USDC-backed stablecoin contract’sdomain.depositIntent.remoteTokenmust match the identifier for this USDC-backed stablecoin contract.depositIntent.localTokenanddepositIntent.localDepositormust not be zero addresses.depositIntent.amountmust be at leastdepositIntent.maxFee.depositIntent.maxFeemust be greater than or equal to thefeeAmountpassed in.usedNonces[depositIntent.nonce]must befalse.
- Set
usedNonces[depositIntent.nonce]=true. - Add
depositIntent.amount - feeAmountto the recipient’s balance. - Add
feeAmountto the relayer’s balance if a relayer is present. - Increase
totalSupplybydepositIntent.amount.
totalSupplyequals exactly the previous supply +depositIntent.amount.- The sum of balances increased by exactly
depositIntent.amount. - Your contract emits a mint event:
mint(recipient, relayer, amount, feeAmount, remoteDomain, remoteToken).
burn(uint256 amount, uint32 destinationDomain, bytes32 destinationRecipient)
Burns USDC-backed stablecoins so xReserve can release USDC on the destination.
For information on withdrawals, burn intents, and burn intent signatures, see
the Technical Guide.
Preconditions:
The following conditions must be met before you move state or the call will
revert:
amountmust be greater than zero.- The caller (
msg.sender) must hold at leastamount. amountmust meet or exceedminBurnSize.
- Decrement the caller’s balance by
amount. - Decrement
totalSupplybyamount.
- Your contract emits a burn event:
burn(depositor, domain, amount, destinationDomain, destinationRecipient).
Administrative functions
Access to these administrative functions should be restricted to the contract owner or an equivalent privileged role controlled by your blockchain team.setAttester(bytes32 attester, bool enabled)
Enables or disables an attester address in the allowlist. Set enabled to
true to approve an
xReserve attester. Configure
more than one approved attester so xReserve can rotate attester keys without
downtime.