Skip to main content

Stellar

Two things about Stellar leak into your integration, and neither has an equivalent on any other chain here. An account must be created before it can be paid, and an asset must be opted into before it can be received. Both are properties of the ledger, not of ChainOS, and both are surfaced rather than hidden.

Slugxlm
Address formatStrKey — base32, G…, 56 characters, checksummed
Account pathm/44'/148'/{i}' (SEP-0005)
Cloud storesAn address pool. No extended public key exists
DerivationEdge only
Confirmations1, deterministic — a closed ledger does not reorganise
TokensUSDC and EURC, and any classic asset — trustline required
Base reserve0.5 XLM per entry; 1 XLM minimum for a bare account
Explorerhttps://stellar.expert/explorer/public/tx/{txid}

Why there is a pool

The same reason Solana has one. Ed25519 has no public-key-only child derivation, so there is no such thing as a Stellar extended public key and Cloud cannot derive Stellar addresses at all. The Edge derives them and ships the public halves; Cloud allocates from that pool with SELECT … FOR UPDATE SKIP LOCKED.

The two chains keep independent pools, index sequences and low-water thresholds. They sit at different BIP-44 coin types — 148 against Solana's 501 — and their path shapes differ as well: Solana's index carries a trailing change segment, SEP-0005 stops at the account. Index 7 on one chain has nothing to do with index 7 on the other.

If the Stellar pool is exhausted and no Edge is connected, POST /v1/addresses for xlm fails with 423 EDGE_OFFLINE, exactly as Solana does, and for the same reason: issuing an address nobody holds a key for would take a customer's deposit into a void.

The first deposit creates the account

A Stellar address that has never been funded does not exist on the ledger. A payment operation to it fails with op_no_destination regardless of how much it carries; the sender has to use create_account instead, with at least the 1 XLM minimum balance.

This is stricter than XRP, which is the closest comparison. An XRP payment of at least the base reserve creates the account by itself; a Stellar payment never does.

Publish a minimum first deposit

POST /v1/addresses returns reserveDrops for xlm — 10,000,000 stroops, which is 1 XLM. Treat it as the published minimum for a first deposit to a new address, the way an exchange does.

Nearly every wallet and exchange detects an unfunded destination and sends create_account automatically, so in practice this is invisible. A sender that issues a bare payment will see it fail, and the funds stay with them — nothing is lost, but the deposit does not arrive.

Every subsequent deposit, of any size, is an ordinary payment and succeeds.

ChainOS does not pre-fund addresses. Doing so would immobilise 1 XLM per issued address indefinitely, and the depositor's own payment does the job for free.

The minimum balance is reserved, not spendable

An account cannot be drained below (2 + subentries) × 0.5 XLM; the ledger refuses it. GET /v1/balances reports that floor as reserved and nets it out of available, the same treatment XRP's base reserve and Solana's rent-exempt minimum already get.

That is what makes the withdrawal pre-flight correct without branching on the chain: it compares against available, which already has the reserve deducted. A send that would breach the floor is refused with 422 INSUFFICIENT_RESERVE rather than being assembled and rejected on-ledger at your cost.

Each trustline is a subentry, so establishing one raises the account's own minimum by another 0.5 XLM.

USDC needs a trustline first

A Stellar account cannot hold a non-native asset until it has signed a changeTrust operation for it. This is the one place Stellar differs from Solana in a way that reaches your integration code:

  • On Solana, the sender creates the recipient's associated token account and pays its rent. ChainOS quotes that rent as its own fee line item, and a send to a recipient with no token account simply works.
  • On Stellar, only the destination can establish its own trustline. A sender cannot do it, and a USDC payment to an account without one fails with op_no_trust.

So ChainOS pre-flights it. A USDC withdrawal to a Stellar address with no USDC trustline is refused before a transaction is assembled:

{
"error": {
"code": "DESTINATION_OPT_IN_REQUIRED",
"message": "G… cannot receive USDC until it establishes a Stellar trustline for it. Only the holder of that account can do so.",
"details": { "chain": "xlm", "asset": "USDC", "destination": "G…" }
}
}

Its own error code because its remedy is unlike every other funding failure: the sender can add funds, add gas, or wait out a reserve, but cannot act on someone else's account.

Establishing a trustline on an address you issued

POST /v1/addresses/{address}/trustlines
Content-Type: application/json

{ "chain": "xlm", "asset": "USDC" }

Returns an unsigned payload that travels the ordinary signing path — your Edge signs it, Cloud broadcasts it. Nothing new is introduced: it is a reason to build a transaction, not a new way to sign one.

Three preconditions, each of which says so plainly when it is not met:

ConditionResponse
The account does not exist on-ledger yet422 CHAIN_REJECTED — fund it first
It holds less than 1.5 XLM plus the fee422 INSUFFICIENT_RESERVE
The trustline already exists409 CONFLICT

The 1.5 XLM is (2 + 1) × 0.5 — the new subentry raises the minimum. An account funded with exactly the 1 XLM minimum cannot establish a trustline until it receives a little more, which is why this is a deliberate call you make after funding rather than something ChainOS does at issuance.

GET /v1/balances for an address with no trustline returns a zero balance with accountExists: false, the same shape a missing Solana token account returns — not an error.

USDC is identified by code and issuer

A Stellar asset has no contract address. It is a CODE:ISSUER pair, and that pair goes wherever another chain would take a contract:

GET /v1/balances/xlm/{address}/tokens/USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN

Circle's EURC is carried the same way, as EURC:GDHU6WRG4IEQXM5NZ4BMPKOXHW76MZM4Y2IEMFDVXBSDP6SJY4ITNPP2, and needs its own trustline — one for USDC does not cover it.

The issuer is part of the asset's identity, not decoration. USDC from a different issuer is a different asset that happens to share a code, and a trustline to one grants nothing against the other — which is why the pair is carried whole rather than being abbreviated to the code anywhere it matters.

Balances are reported with 7 decimals, Stellar's fixed-point precision, rather than the 6 that USDC carries on the EVM chains and Solana. Read decimals from the response instead of assuming a USDC is a USDC across chains.

No memo is issued

ChainOS issues a unique address per customer on Stellar, as it does everywhere else, and emits no memo. The alternative — one funded account with memo IDs distinguishing customers — is the Stellar ecosystem norm, and it was considered and rejected for the same reasons it was rejected for XRP: it couples every customer's deposits to one key, makes a per-customer sweep impossible without moving everyone's funds through one account, and puts the burden of sending a correct memo on the depositor. An unmemoed deposit into a shared account is unattributable, which is a support problem with real money attached.

A memo ChainOS did not ask for is a memo a depositor can safely omit.

Availability

Stellar appears in the portal's chain lists only while it is enabled for your account. A chain that is not enabled is absent from those lists entirely rather than appearing and failing at call time, so you never offer a customer a deposit address ChainOS cannot monitor.

Ziklag operates the chain infrastructure behind Stellar; there is nothing for you to configure or pay for separately. If balances return 503 CHAIN_UNAVAILABLE while derivation keeps working, that is ours to fix — see Incident response.

Operational notes

  • The pool is the thing to watch. Orange readiness means it is under 20%. It replenishes automatically while a leader replica is connected, and only the leader derives — see High availability. Stellar's pool is counted and replenished separately from Solana's, so one can be healthy while the other is not.
  • Addresses are StrKey and canonically uppercase. Validation is checksummed, so a typo is usually caught before it reaches the ledger. ChainOS compares addresses verbatim rather than case-folding them, because a lower-cased G… is not an address the ledger would accept.
  • A trustline's reserve stays locked while the trustline exists. Removing one would release 0.5 XLM, but ChainOS establishes trustlines and does not remove them. Budget the 0.5 XLM per asset per address as permanent, and account for it in sweeps — see Sweeps.
  • address_activated fires when the first payment creates the account, which on Stellar is a real on-chain state change rather than an informational first-use signal. Only XRP has the same property.
  • Soroban is not used. ChainOS issues no contracts. Payments, balances and trustlines are all classic Stellar operations, so nothing on the Stellar side of ChainOS touches Soroban.