Skip to main content
Circle’s chain-abstracted USDC balance. Deposit USDC into the GatewayWallet on one chain, then mint it on another chain through the GatewayMinter with a Circle-signed attestation.

In a Sauce program

Deposit the Pot’s USDC into Gateway for depositor, the user’s own address:
  • Whose balance it credits. depositFor(token, depositor, value) credits depositor. deposit(token, value) credits the caller, which in a Sauce program is the Pot: the Gateway balance is then the Pot’s, not the user’s. A balance credited to the Pot can be spent only by a burn intent signed by a delegate the Pot adds for itself with the GatewayWallet’s addDelegate, which the registry’s ABI does not declare. To let the user spend it through Gateway from their wallet, deposit for their address.
  • Approve exactly the amount for CircleGateway.GatewayWallet first, and deposit exactly what the Pot means to: a given amount, or the USDC a swap just delivered measured as a delta. On Arc never deposit pot.USDC whole, because the same balance is the Pot’s native coin, from which any native fee the program pays comes.
  • Read it back. totalBalance(USDC, depositor) and availableBalance(USDC, depositor) both rise by the deposit in the same transaction.
  • Where the program ends. The deposit is the on-chain step. Spending the balance on another chain is off-chain first: a burn intent signed for the depositor goes to Circle’s Gateway API, which returns an attestation, and CircleGateway.GatewayMinter.gatewayMint(attestationPayload, signature) on the destination is a transaction of its own.
  • isTokenSupported(USDC) says whether the chain’s Gateway takes USDC.

Notes

  • Gateway uses Circle domain ids, the same ids as CCTP (Arc is 26), not EVM chain ids
  • Spending a Gateway balance on another chain is off-chain first: sign a burn intent, submit it to Circle’s Gateway API, then call gatewayMint with the returned attestation
  • Both contracts are upgradeable (EIP-1967) proxies. Arc’s Minter runs a different, smaller implementation from the other chains’ that dispatches the same functions
  • Circle also runs Gateway on HyperEVM (domain 19) and World Chain (14), which are not SDK chains. It has no Solana mainnet deployment
  • On a testnet (Arc Testnet, Base Sepolia, Ethereum Sepolia) the Wallet and Minter are Circle’s testnet pair, not the mainnet addresses; CircleGateway.GatewayWallet and CircleGateway.GatewayMinter resolve to the chain’s own. Their burn intents go to Circle’s testnet Gateway API, https://gateway-api-testnet.circle.com

Its names in a program

On Ethereum, CircleGateway is a namespace: its contracts need no import.

Deployments

ABI

GatewayMinterABI

import GatewayMinterABI from "./protocols/circle-gateway/GatewayMinterABI.json"; Speaks for: gatewayMinter.
  • gatewayMint(bytes attestationPayload, bytes signature)
  • isTokenSupported(address token) view returns (bool)
  • domain() view returns (uint32)

GatewayWalletABI

import GatewayWalletABI from "./protocols/circle-gateway/GatewayWalletABI.json"; Speaks for: gatewayWallet.
  • deposit(address token, uint256 value)
  • depositFor(address token, address depositor, uint256 value)
  • totalBalance(address token, address depositor) view returns (uint256)
  • availableBalance(address token, address depositor) view returns (uint256)
  • isTokenSupported(address token) view returns (bool)
  • domain() view returns (uint32)

Next steps