--- name: cross-chain description: Cross-chain interoperability for Stellar. Entry point with a rail-selection decision table and shared pitfalls, routing to three companion files — cctp.md (Circle CCTP V2, native USDC burn-and-mint between Stellar and EVM/Solana chains, domain 27, the CctpForwarder requirement for Stellar recipients), axelar.md (Axelar GMP for Soroban contracts calling contracts on other chains, and the Interchain Token Service for multichain tokens), and layerzero.md (LayerZero V2 OApp messaging with configurable DVN security, OFT omnichain tokens, and USDT0 — native USDT on Stellar). Also covers NEAR Intents (intent-based cross-chain swaps into XLM or Stellar USDC) at the routing level. Use when bridging USDC or USDT to or from Stellar, sending messages between a Stellar contract and another blockchain, making a token exist on multiple chains, or adding cross-chain swaps to an app. user-invocable: true argument-hint: "[cross-chain task]" --- # Cross-Chain on Stellar Stellar connects to other blockchains over several production rails, each built for a different job. Picking the wrong rail wastes engineering effort; picking the right one is a routing decision, not a research project. This file routes; the deep dives live alongside it — **read the file that matches the task**: | You want to | Use | Where | |---|---|---| | Move **native USDC** between Stellar and an EVM chain or Solana (no wrapped assets, no liquidity pools) | Circle CCTP V2 | [cctp.md](cctp.md) | | Move **native USDT** between Stellar and an EVM chain | USDT0, a LayerZero OFT | [layerzero.md](layerzero.md#usdt0-native-usdt-on-stellar) | | Have a Stellar contract **call a contract on another chain**, or receive calls from one (arbitrary payloads) | Axelar GMP or LayerZero OApp | [axelar.md](axelar.md), [layerzero.md](layerzero.md) | | Make a token — new or an existing Stellar asset — **exist on multiple chains** | Axelar ITS or LayerZero OFT | [axelar.md](axelar.md), [layerzero.md](layerzero.md) | | **Swap any asset cross-chain** (BTC, ETH, SOL, … → XLM or Stellar USDC) without integrating a bridge yourself | NEAR Intents | [below](#near-intents-intent-based-swaps) | Rules of thumb: if the asset is USDC and both ends are CCTP chains, CCTP is the cheapest and most direct (it burns and mints Circle-native USDC — nothing wrapped, nothing pooled). If the asset is USDT, the answer is USDT0 over LayerZero OFT — a different rail, and one whose shape changes per leg: Stellar burns and mints, while the Ethereum leg locks and unlocks canonical USDT through an OFT Adapter, so check the route's OFT mode before you describe the flow. If you need logic, not just value, on the far chain, that is message passing — two rails do it, and the [comparison at the end of layerzero.md](layerzero.md#choosing-between-axelar-gmp-and-layerzero-oapp) helps you pick between Axelar's shared validator security and LayerZero's app-configured DVN sets. If you control a token and want it multichain, that is ITS or OFT on the same split. If the user just wants "turn my X on chain A into Y on Stellar" and you don't want bridge plumbing at all, quote it through NEAR Intents. ## When to use this skill - Bridging USDC between Stellar and Ethereum, Base, Arbitrum, Solana, or another CCTP-supported chain - Bridging USDT between Stellar and an EVM chain (USDT0 over LayerZero OFT) - Receiving bridged USDC into a Stellar account or contract (and not bricking the funds — see the forwarder warning below) - Writing a Stellar smart contract that sends messages to or receives messages from contracts on other chains - Deploying an interchain token, or connecting an existing Stellar asset to other ecosystems - Adding a "deposit from any chain" or cross-chain swap flow to a wallet or dapp ## Related skills - Trustlines, SAC deployment, asset anatomy → `../assets/SKILL.md` - Writing the Stellar smart contracts that send/receive messages → `../smart-contracts/SKILL.md` - Frontend transaction building, Freighter signing, RPC submission → `../dapp/SKILL.md` - Watching for the destination-side mint or contract events → `../data/SKILL.md` - Paying AI agents (x402/MPP) rather than bridging → `../agentic-payments/SKILL.md` ## Pitfalls shared by every rail These bite regardless of which rail you pick. Each companion file adds rail-specific ones. 1. **Address formats do not translate.** Stellar addresses are `strkey` strings (`G…` accounts, `C…` contracts, `M…` muxed); EVM uses 20-byte hex; Solana uses base58. Every rail defines its own encoding for foreign addresses (CCTP: raw 32-byte payloads; Axelar: strings + bytes payloads). Never paste an address from one chain into a field meant for another — encode it the way the rail specifies, and validate with the SDK (`StrKey.isValidEd25519PublicKey` / `isValidContract`) before encoding. 2. **Decimals differ.** Classic Stellar assets and their SACs use 7 decimals, but other Stellar token contracts (ITS-deployed tokens included) declare their own — call `decimals()` instead of assuming. USDC is 6 on every supported chain except Stellar (which uses 7); EVM tokens are commonly 18; CCTP messages are always 6-decimal. Convert at every boundary and test with amounts that exercise the last digit (see the worked decimal examples in [cctp.md](cctp.md#usdc-precision-7-decimals-vs-6)). LayerZero OFTs add a second layer: an OFT declares `shared_decimals` (6 for USDT0) alongside the token's local decimals, and **silently drops the remainder below that precision on send** — quote with `quote_oft` and trust its `OFTReceipt`, not your input amount (see [layerzero.md](layerzero.md#decimals-7-local-6-shared)). 3. **Classic Stellar recipients need a trustline first.** A `G…` account cannot receive an issued asset (USDC included) without a trustline to that asset. Bridged funds destined for an account without one will not land. Check and provision before starting the transfer — see `../assets/SKILL.md`. 4. **Cross-chain is asynchronous.** Every rail has a wait: CCTP waits for finality plus Circle's attestation (seconds to ~15 minutes depending on chain and finality threshold), Axelar waits for validator confirmation, intents wait for a market maker. Build UIs and agents around polling a status, never around "submit and assume". 5. **Testnet first, always.** Do the full round-trip on testnet before touching mainnet — cross-chain mistakes are frequently unrecoverable by design (burns are final, and some misencodings permanently strand funds). Two rails cannot be rehearsed that way: NEAR Intents is filled by real market makers, and **USDT0 publishes no testnet deployment** even though LayerZero's testnet endpoint exists. For both, the rehearsal path is read-only quotes (`quote_oft` and `quote_send` for USDT0, dry quotes for intents) followed by a dust-sized real transfer. LayerZero also has a testnet endpoint (EID `40600`) you can point your own OApp or OFT at, but confirm one testnet send actually delivers before you rely on it — see the [status notes](layerzero.md#limitations-and-status-notes). ## NEAR Intents (intent-based swaps) > **Status-sensitive.** Chain and asset support changes frequently — verify Stellar's current status in the [NEAR Intents docs](https://docs.near-intents.org/) before building. [NEAR Intents](https://docs.near-intents.org/) is an intent protocol: the user states an outcome ("swap 0.1 BTC to USDC on Stellar"), market makers compete to execute it. Stellar (XLM and USDC) is a supported destination and source, which makes this the shortest path to "deposit from any chain" UX — there is no bridge contract to integrate on the Stellar side at all. Integration is the [1Click API](https://docs.near-intents.org/near-intents/integration/distribution-channels/1click-api): `POST /v0/quote` with the asset pair returns a price and a **deposit address**; send funds there and market makers carry out the swap. `GET /v0/tokens` lists supported assets (the Stellar entries: XLM and USDC, 7-decimal), and a quote with `"dry": true` returns pricing and an ETA without creating a deposit commitment — probe pairs freely, commit later. Official SDKs exist for TypeScript, Go, and Rust. Three Stellar-specific facts (verified against the live API): - **Stellar deposits are MEMO mode only, and the API enforces it.** A Stellar-origin quote must set `"depositMode": "MEMO"` — without it `/v0/quote` rejects with `Incorrect depositMode for originAsset from stellar chain`. The quote then returns a deposit address **plus a memo** (`depositAddress` + `depositMemo`), and the memo is what routes your funds to your swap. A deposit without the memo is not credited — treat the memo as part of the address, and refuse to display one without the other. - **There is no testnet.** Intents are filled by real market makers with real liquidity, so the rehearsal path is dry quotes followed by a dust-sized real swap — not a testnet round trip. - Registering for an API key via the partners portal removes the default integrator fee; anonymous use works but is surcharged (it shows up in the quoted spread). For anything deeper (quote parameters, slippage, refund handling), work from the live 1Click API docs rather than this file — the protocol iterates quickly.