Circle’s Cross-Chain Transfer Protocol (CCTP) has just been integrated with Stellar. It let’s us move assets between chains at 1:1. Burn on one chain, mint native USDC on the other. No bridge pool, no synthetic token, no liquidity overhead.
I’ve put together a demo at: https://github.com/jamesbachini/CCTP-Stellar
The repo has two working examples:
bridgeout.js– Burn testnet Stellar USDC, mint native USDC on EVM (Base Sepolia testnet).bridgein.js– Burn EVM USDC, mint and forward USDC to a Stellar account.
The scripts are small and simple by design to show the moving parts: amount conversion, approvals, burn calls, attestation polling, final mint.
How CCTP Works
- Step 1: USDC is burned on the source chain through Circle’s contracts.
- Step 2: Circle’s Iris attestation service detects the burn and signs a message confirming it.
- Step 3: Someone (the app, a relayer, or a user with a terminal) submits the message and attestation on the destination chain.
- Step 4: The destination contract verifies the signature and mints native USDC.
For Stellar inbound, the EVM burn targets CctpForwarder instead of a user address. The final Stellar recipient is packed into hookData. The forwarder handles the rest.
The Decimal Trap
Note that Stellar USDC has 7 decimals. EVM USDC has 6. CCTP amount fields use 6.
If you send 1 USDC from Stellar, that is 10000000 (7 decimals). The script converts it to 1000000 (6 decimals) before burning. Get it wrong and you either get Amount has more than 7 decimal places or Amount has more than 6 decimal places.
This is the most common error. Do not hide this conversion in your UI code.
Running The Scripts
NodeJS @ https://nodejs.org/
Stellar side: An account with testnet XLM, a USDC trustline (issuer GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5), and testnet USDC from Circle’s faucet.
EVM side: A wallet on Base Sepolia or Ethereum Sepolia with native gas and testnet USDC from faucet.circle.com.
Set STELLAR_CLIENT_PRIVATE_KEY, DESTINATION_CHAIN, SOURCE_CHAIN, AMOUNT, and STELLAR_RECIPIENT in .env. Run node bridgeout.js or node bridgein.js.
If you bridge EVM -> Stellar and want the forwarder path, STELLAR_RECIPIENT is the final Stellar account, not the forwarder contract address. The script handles the encoding.
Get testnet funds for base at https://docs.base.org/base-chain/network-information/network-faucets
Get testnet funds for stellar at https://lab.stellar.org/account/fund
Once we have .env setup we can run the scripts via node.

Note the bridgein.js script takes longer to run while you wait for Circle attestation. There is a fast option as well if you want to pay a ~14bps fee.
Notes On Building With CCTP
CCTP is going to enable dApp builders seamless onboarding of funds from across various blockchain networks.
By enabling CCTP deposits and withdrawals we can make it as easy as possible for users to move USDC in and out of the Stellar network.
Note the scripts in this repo are basic examples, not a production relayer. Production needs job persistence, retry queues, idempotency, monitoring, rate-limit handling, and key management.
- Persist the burn hash before polling. If your process crashes, resume from the hash instead of burning again.
bridgein.jsdemonstrates this withSOURCE_TX_HASH. - Separate user transfer from relay. Anyone can submit the attested message on the destination chain. Let the user burn, let a backend watch Iris and complete.
- Use least-privilege allowances. Approve only what you need. Larger approvals are a product and security decision.
- Plan for replays. Destination
mint_and_forwardfails once a message is consumed. Treat that as a terminal state, not an error. - Don’t hardcode mainnet values. Contract addresses, domains, and fees change. Pull from Circle’s current docs or your own config registry.


