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.
| Slug | xlm |
| Address format | StrKey — base32, G…, 56 characters, checksummed |
| Account path | m/44'/148'/{i}' (SEP-0005) |
| Cloud stores | An address pool. No extended public key exists |
| Derivation | ❌ Edge only |
| Confirmations | 1, deterministic — a closed ledger does not reorganise |
| Tokens | USDC and EURC, and any classic asset — trustline required |
| Base reserve | 0.5 XLM per entry; 1 XLM minimum for a bare account |
| Explorer | https://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.
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:
| Condition | Response |
|---|---|
| The account does not exist on-ledger yet | 422 CHAIN_REJECTED — fund it first |
| It holds less than 1.5 XLM plus the fee | 422 INSUFFICIENT_RESERVE |
| The trustline already exists | 409 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_activatedfires 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.