> ## Documentation Index
> Fetch the complete documentation index at: https://docs.etherfuse.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Stellar

> Asset identifiers, trustlines, XLM gas, claimable-balance onramps, and transaction expiry on Stellar.

export const ChainAssets = ({chain}) => {
  const {useState, useEffect} = React;
  const [rows, setRows] = useState(null);
  const [error, setError] = useState(null);
  const EXPLORER = {
    solana: id => 'https://solscan.io/token/' + id,
    stellar: id => 'https://stellar.expert/explorer/public/asset/' + id,
    base: id => 'https://basescan.org/token/' + id,
    polygon: id => 'https://polygonscan.com/token/' + id,
    monad: id => 'https://monadvision.com/token/' + id,
    avalanche: id => 'https://snowtrace.io/token/' + id
  };
  const CHAIN_LABEL = {
    stellar: 'Stellar',
    solana: 'Solana',
    monad: 'Monad',
    base: 'Base',
    polygon: 'Polygon',
    avalanche: 'Avalanche'
  };
  const tokenLogo = s => 'https://stablebonds.s3.us-west-2.amazonaws.com/stablebond/spl-' + String(s).toLowerCase() + '.png';
  const hideOnError = e => {
    e.currentTarget.style.display = 'none';
  };
  useEffect(() => {
    let cancelled = false;
    fetch('https://api.etherfuse.com/lookup/stablebonds').then(r => {
      if (!r.ok) throw new Error('HTTP ' + r.status);
      return r.json();
    }).then(data => {
      if (cancelled) return;
      setRows(data.stablebonds || []);
    }).catch(e => {
      if (!cancelled) setError(e.message || 'failed to load');
    });
    return () => {
      cancelled = true;
    };
  }, []);
  const border = '1px solid rgba(128,128,128,0.25)';
  const cell = {
    padding: '6px 12px',
    borderBottom: border,
    textAlign: 'left',
    verticalAlign: 'middle'
  };
  const head = {
    ...cell,
    fontWeight: 600
  };
  const mono = {
    fontFamily: 'monospace',
    fontSize: '0.85em',
    wordBreak: 'break-all'
  };
  const inline = {
    display: 'inline-flex',
    alignItems: 'center',
    gap: '6px'
  };
  if (error) return <p>
        Couldn't load the live list. See <code>GET /lookup/stablebonds</code>.
      </p>;
  if (!rows) return <p>Loading live addresses…</p>;
  const items = (rows || []).filter(b => b.symbol !== 'MEX').map(b => {
    const entry = (b.blockchains || []).find(c => c.blockchain === chain);
    return entry ? {
      symbol: b.symbol,
      currency: b.bondCurrency,
      id: entry.tokenIdentifier
    } : null;
  }).filter(Boolean).sort((a, z) => String(a.symbol).localeCompare(String(z.symbol)));
  if (!items.length) return <p>No stablebonds are currently live on {CHAIN_LABEL[chain] || chain}.</p>;
  return <div>
      <table style={{
    width: '100%',
    borderCollapse: 'collapse',
    fontSize: '0.95em'
  }}>
        <thead>
          <tr>
            <th style={{
    ...head,
    width: '160px'
  }}>Asset</th>
            <th style={head}>Address</th>
          </tr>
        </thead>
        <tbody>
          {items.map(it => {
    const url = EXPLORER[chain] ? EXPLORER[chain](it.id) : null;
    return <tr key={it.symbol}>
                <td style={cell}>
                  <span style={inline}>
                    <img src={tokenLogo(it.symbol)} alt="" width={20} height={20} onError={hideOnError} />
                    <strong>{it.symbol}</strong>
                    <span style={{
      opacity: 0.6
    }}>· {it.currency}</span>
                  </span>
                </td>
                <td style={{
      ...cell,
      ...mono
    }}>
                  {url ? <a href={url} target="_blank" rel="noreferrer">
                      {it.id}
                    </a> : it.id}
                </td>
              </tr>;
  })}
        </tbody>
      </table>
      <p style={{
    fontSize: '0.8em',
    opacity: 0.65,
    marginTop: '8px'
  }}>
        Live from <code>GET https://api.etherfuse.com/lookup/stablebonds</code>. Production addresses — when quoting,
        use the <code>identifier</code> from <code>GET /ramp/assets</code> (sandbox differs).
      </p>
    </div>;
};

<img src="https://mintcdn.com/etherfuse/XRSchVFGUgreQatD/img/chains/stellar.svg?fit=max&auto=format&n=XRSchVFGUgreQatD&q=85&s=f52097ff454516ca28cdfdfae0cb559d" alt="Stellar" width="64" noZoom style={{ float: "right", margin: "0 0 1rem 1.5rem" }} data-path="img/chains/stellar.svg" />

We support onramps, offramps and swaps on Stellar. Stellar needs more chain-specific setup than any other supported network: assets require **trustlines**, wallets need a small **XLM** balance for reserves and fees, and a prepared transaction goes stale when the wallet's **sequence number** moves. This page covers the Stellar-specific parts. The end-to-end flows are in the [Onramps](/v2/guides/testing-onramps), [Offramps](/v2/guides/testing-offramps) and [Swaps](/v2/guides/testing-swaps) guides.

## Supported operations

| Operation | Supported |
| - | - |
| Onramp (fiat → token) | ✓ |
| Offramp (token → fiat) | ✓ |
| Swap (token → token) | ✓ |

## Asset identifiers

Stellar assets use the **`CODE:ISSUER`** format, for example `CETES:GC3CW7EDYRTWQ635VDIGY6S4ZUF5L6TQ7AA4MWS7LEQDBLUSZXV7UPS4`.

<Warning>
  **Always read identifiers from [`GET /supported_assets`](/v2/api-reference/assets/list-supported-assets). Never hardcode the issuer.** The issuer account differs between **sandbox and production** and can change. A hardcoded issuer is the most common cause of "wrong asset" problems. Pass `chain=stellar` and use the token's `asset` as-is for `sourceAsset` or `destinationAsset` in your quote.
</Warning>

<Tip>
  **Getting testnet USDC in sandbox.** We have no faucet for sandbox USDC. To get some, **onramp CETES, then swap CETES → USDC**. You need a trustline for any asset you receive; see below.
</Tip>

## Stablebond addresses

Live asset issuers for every stablebond on Stellar, each linked to Stellar Expert:

<ChainAssets chain="stellar" />

## Wallet and gas requirements

Holding and receiving Stellar assets requires XLM for the account reserve and for each trustline. These are **Stellar network requirements, not Etherfuse fees**:

* **\~1 XLM** base reserve to create the account on-chain.
* **\~0.5 XLM** additional reserve per trustline (so \~1.5 XLM for an account holding one asset).
* A few **stroops** per transaction for the network fee. About 0.01 XLM on a wallet covers thousands of transactions.

<Info>
  **Self-custody wallets hold their own XLM.** A bring-your-own Stellar wallet pays its own reserves and network fees. The exception is an **onramp**: when the destination wallet has no account or no trustline, we price the account and trustline setup into the quote fee and deliver a claimable balance (see [Onramps: wallets without a trustline](#onramps-wallets-without-a-trustline) below).

  **Embedded wallets** work differently: the key holder signs an order `authorization` instead of signing XDR, and we build every transaction. See [Embedded Wallets](/v2/guides/embedded-wallets).
</Info>

### Trustlines

A wallet **must hold a trustline for any asset it receives.** For onramps, the claim transaction adds the trustline (see below). For **offramps and swaps**, the wallet must already have the trustline and enough XLM before you create the order.

## Register a Stellar wallet

Register the wallet as a payment account with `accountDetails` of kind `address` and `chain` `stellar`. Set `$ORGANIZATION_ID` to the organization that owns the wallet: your own organization or a customer organization. See [Organizations in the route](/v2/overview#organizations-in-the-route).

```bash theme={null}
curl -X POST https://api.sand.etherfuse.com/organization/$ORGANIZATION_ID/payment_account \
  -H "Authorization: <api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "wallet",
    "label": "Stellar wallet",
    "accountDetails": {
      "kind": "address",
      "address": "GDUKMGUGD3V6VXTU2RLAUM7A2FABLMHCPWTMDHKP7HHJ6FCZKEY4PVWL",
      "chain": "stellar"
    }
  }'
```

Use the returned `id` as the `source` or `destination` of a [quote](/v2/api-reference/quotes/create-quote). A mis-copied `G...` address fails its checksum and returns `400` `invalid-request`.

## Onramps: wallets without a trustline

You do not need to set up the wallet before an onramp. The wallet is the quote's destination, so we check it on-chain when you quote. If it has no account or no trustline for the asset, the quote fee includes a one-time cost for the XLM, and we deliver the asset as a **claimable balance**.

The order then moves to `claimable`, and `claimInstructions` describes the balance:

```json theme={null}
{
  "status": "claimable",
  "claimInstructions": {
    "kind": "stellarClaimableBalance",
    "balanceId": "00000000abc123...",
    "asset": "CETES:GC3CW7EDYRTWQ635VDIGY6S4ZUF5L6TQ7AA4MWS7LEQDBLUSZXV7UPS4",
    "amount": "100.0000000",
    "claimant": "GDUKMGUGD3V6VXTU2RLAUM7A2FABLMHCPWTMDHKP7HHJ6FCZKEY4PVWL",
    "expiresAt": null
  }
}
```

To claim, call [POST /organization/\{organizationId}/order/\{id}/claim](/v2/api-reference/orders/prepare-claim) with no body:

* A self-held wallet gets `kind` `stellarTransaction` with an `xdr`. The transaction adds the trustline and claims the balance in one step. The customer signs and submits it.
* An embedded wallet gets `kind` `authorization` with an `authorizationId`. The key holder signs the order's `authorization` and you submit it. See [Order authorization](/v2/guides/embedded-wallets#order-authorization).

This applies to **onramps only**. Offramps and swaps need the wallet to exist and be funded first. See [Onramps: Stellar wallets without a trustline](/v2/guides/testing-onramps#stellar-wallets-without-a-trustline) for the full flow.

## Offramps and swaps: fund the wallet first

<Warning>
  **The customer's wallet sends the funds.** For offramps and swaps from a self-held wallet, the wallet must exist on-chain, hold enough XLM for the network fee, and hold a trustline for the asset it sends. For a swap, it also needs a trustline for the asset it receives.

  Before creating the order, confirm the wallet exists on-chain, holds at least about 1.5 XLM, and has a trustline for the asset being sold. Use [`GET /supported_assets?chain=stellar`](/v2/api-reference/assets/list-supported-assets) to get the issuer for the trustline.
</Warning>

When the source is an [embedded wallet](/v2/guides/embedded-wallets), `fundingInstructions.destination` has no `address`. It carries an `authorizationId` instead, and the key holder approves the order's [authorization](/v2/guides/embedded-wallets#order-authorization) to fund the order.

For a self-held wallet on a Stellar offramp or swap, `fundingInstructions.destination` gives the `address` to pay and a `memo` when one is needed. Fund the order in one of two ways:

1. **Sign the prepared transaction.** When `preparedTransaction` is present, sign `transaction` (an unsigned base64 transaction envelope XDR) and submit it to Horizon. The customer's account is the transaction's source and pays the fee.
2. **Build the payment yourself.** Send a payment of the order's `source.amount` of `source.asset` to `address`, with `memo` when it is present. Wallets that support [SEP-24](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0024.md)-style withdrawals can use this path.

## Stale transactions and refresh

A prepared Stellar transaction stops being valid at the order's funding deadline, so `refreshAfter` is the same time as `fundingDeadline`. It also uses the wallet's sequence number at the time we built it, so it goes stale when the wallet sends another transaction first. Horizon then returns `tx_bad_seq`. Get a new one instead of retrying the stale one:

* **Offramp or swap:** call [`POST /organization/{organizationId}/order/{id}/funding_instructions/refresh`](/v2/api-reference/orders/refresh-funding-instructions). You can call it at any time while the order is `created`. It returns the order as it is. If the old transaction already landed, nothing changes and no webhook arrives, so the customer can't fund the order twice. The new funding instructions arrive a moment later in an `orderUpdated` webhook, and on [`GET .../order/{id}`](/v2/api-reference/orders/get-order).
* **Onramp claim:** the claim transaction uses the wallet's current sequence number, so another transaction from the wallet makes it stale. We build a new claim transaction on every call to [`POST /organization/{organizationId}/order/{id}/claim`](/v2/api-reference/orders/prepare-claim), so call it right before the customer signs.

<Tip>
  Get the transaction right before the customer signs it. If Horizon returns `tx_bad_seq`, refresh and have the customer sign the new one.
</Tip>

## Related

<CardGroup cols={3}>
  <Card title="Onramps" icon="arrow-up-right-dots" href="/v2/guides/testing-onramps" />

  <Card title="Offramps" icon="arrow-down" href="/v2/guides/testing-offramps" />

  <Card title="Swaps" icon="right-left" href="/v2/guides/testing-swaps" />
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.