Stellar Bridge / Docs
← Back to the bridge

USDC into an account that works

Not a cheaper bridge. A front door: the money arrives on Stellar in an account that can already hold it and already spend it.

What this is

Circle's CCTP moves native USDC by burning it on the source chain and minting it on the destination. No wrapped tokens, no liquidity pool, no custody. This bridge rides that rail from the EVM chains into Stellar, and then does the part CCTP does not: it makes sure the receiving side is an account that actually works.

On Stellar, an address is not an account. Until somebody pays the ledger reserve, the address cannot hold anything; until it has a USDC trustline, it cannot hold USDC; and with zero XLM it cannot even pay the fee to fix either. Someone withdrawing to Stellar for the first time crosses an ordinary bridge and hits that wall with their money already on the other side. This bridge exists so that nobody arrives at it.

The fee, and what it buys

Everyone pays 0.5%. Only somebody who cannot hold USDC without help pays more: a flat five dollars, which buys three XLM sent to the address outright. One for the account reserve, half for the trustline, and one and a half left over as the user's own fee money.

The three XLM are spent, not lent. A sponsored account would have been cheaper for us, but a sponsored account holds zero XLM and cannot pay its own transaction fees: it could receive USDC and then be unable to move it without us signing every step. Three XLM buys independence, and independence is the product.

The five dollars is adjustable, because its cost is XLM and its price is dollars, and those drift. Two things keep that honest: a hard ceiling of 20 USDC compiled into the contract, and every caller passing the price they were quoted, so a repricing cannot land on a transaction already in flight.

We never make anybody a wallet. You make your own in Freighter and hold your own key. What the fee buys, when it is needed, is the account existing on the ledger.

How a transfer goes

wallets connected on both ends
        │
        ├─ read Horizon: does the account exist? is the trustline there?
        │    decides whether the five dollars applies at all
        │
        ├─ sign the Stellar setup            ← signature first, held
        │
        ├─ burn the USDC on the source chain ← the step that commits money
        │
        ├─ submit the held setup while Circle attests
        │
        └─ mint on Stellar → USDC lands in a working account

The signature comes before the burn on purpose. If you walk away before burning, nothing has happened. Once you have burned, the transaction that makes the far side work is already in hand, so there is no state where your money is taken and cannot be delivered.

Two invariants are enforced in code rather than described in prose: never burn before the signed setup is in hand, and never send XLM before a burn that paid for it. The spender verifies the burn by reading the receipt on the source chain, not by trusting a flag.

Speed

Stellar is widely believed not to take Circle's fast transfers. It does. A burn at the fast finality threshold attested in twenty-nine seconds where hard finality took twenty-five minutes, and Stellar's messenger accepted the unfinalized message. Circle prices the fast path at 1.3 basis points: thirteen cents on a thousand dollars, against a wait no exchange withdrawal survives.

There is no version of this worth running at the slower speed, so the choice is not offered. Every burn goes out fast, and the account setup is submitted during the attestation, so it costs no extra wait.

What keeps it safe

Status

Testnet only. Nothing is deployed on mainnet.

The full path is deployed on Base Sepolia and proven end to end, in forty seconds, into an address that did not exist when the transfer started. Addresses and transaction hashes are in TESTNET.md.

The code is open: 250 tests across the Solidity contract, the Soroban contract, the watcher and the browser, checked against Circle's real contracts and test vectors rather than against an interface written from the docs.

Source, tests and the full design write-up: github.com/sud3naz/StellarGate

Run it yourself

The page and the watcher are two separate things, and both bind every interface, so a second machine on the same network reaches them at this one's address.

cd web && node serve.mjs        # the page, on 5173
cd api && npm run watcher       # the service, on 8787

The watcher needs two things: the bridge contract address and the Stellar account that pays for delivery, about 0.0075 XLM a call. It never holds user funds. Everything else has a testnet default.

cd api
BRIDGE_CONTRACT=0x6975…FF00 \
BRIDGE_DELIVERY_SECRET=S… \
npm run watcher

You can always skip all of this and call CCTP yourself, and pay nothing. This charges for the interface and for the setup on the far side, not for access to the rail.

Honest limits