Skip to content

AMM (Swaps) API ​

On-chain token-to-token swaps: Uniswap V2 on Celo, and Stellar's own protocol-level AMM, side by side on this page.

A third swap mechanism exists too

Both AMMs below price off real, on-chain pool reserves. For a chain-agnostic swap priced off an admin-set rate table instead (no pool required), see IMM →.

▸API ConfigurationNot configured

Celo (Uniswap V2) ​

Price Quote ​

Get expected output amounts for a swap.

Swap Quote
curl -X GET "http://localhost:8100/api/v1/swap/quote?from=USDT&to=IMC&amountIn=1000000" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-API-Secret: YOUR_API_SECRET"
Response200
{
  "from": "USDT",
  "to": "IMC",
  "amountIn": "1000000",
  "amounts": [
    "1000000",
    "1998000"
  ],
  "amountOut": "1998000"
}

Query Parameters:

ParamTypeDescription
fromstringInput token symbol (e.g. "USDT")
tostringOutput token symbol (e.g. "IMC")
amountInstringInput amount in base units

Execute Swap ​

Swaps an exact amount of input tokens for output tokens. Gas is relayed by the platform.

Execute Swap
curl -X POST "http://localhost:8100/api/v1/swap/tokens" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-API-Secret: YOUR_API_SECRET" \
  -d '{
  "tenantSlug": "comet",
  "userId": 12345,
  "from": "USDT",
  "to": "IMC",
  "amountIn": "1000000"
}'
Response200
{
  "status": "success",
  "from": "USDT",
  "to": "IMC",
  "amountIn": "1000000",
  "txHash": "0x...",
  "chainId": 11142220
}

Request Fields:

FieldTypeRequiredDescription
tenantSlugstringNoTenant (defaults to "comet")
userIdnumberYesUser whose wallet to swap from
fromstringYesInput token symbol
tostringYesOutput token symbol
amountInstringYesInput amount in base units

WARNING

The user's wallet must hold at least amountIn of the from token and have approved the router for the swap. The amountOutMin is set to 0 (accepts any output) for testnet simplicity.


List Pools ​

Returns all AMM pool information (pairs and reserves).

List Pools
curl -X GET "http://localhost:8100/api/v1/swap/pools" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-API-Secret: YOUR_API_SECRET"
Response200
{
  "pools": [
    {
      "token0": "0x3C8E...DDDF",
      "token1": "0xF150...d5A2",
      "pair": "0xfE6A...4082",
      "reserve0": "500000000000",
      "reserve1": "1000000000000"
    }
  ],
  "count": 3
}

Full worked example

See Examples: AMM Trading for a complete swap walkthrough in both directions — mint, quote, approve, execute.


Supported Pools ​

PoolFee
USDT logo USDT/USDC0.3%
USDT logo USDT/IMC0.3%
USDC/IMC0.3%

How It Works ​

  1. User's wallet must hold the input token
  2. User approves the Uniswap V2 Router to spend input tokens
  3. Engine calls swapExactTokensForTokens() on the Router
  4. Router executes the swap on-chain
  5. Output tokens arrive in the user's wallet
  6. Gas is paid by the platform relayer

Stellar Native AMM ​

Separate from the Celo Uniswap V2 AMM above — different chain, different protocol primitive. Stellar has had a protocol-level AMM since CAP-38 (2021): constant-product liquidity pools enforced by network validators, not a deployed smart contract. These endpoints are Stellar-only and don't take a ?chain= parameter (see also Stellar Assets & AMM → for the underlying model — there's no contract address here to look up, because there's no contract).

Create a Pool ​

Create Pool

🔒 Admin operation — always requires a Service Token, regardless of the panel above.

curl -X POST "http://localhost:8100/api/v1/admin/stellar/pools/create" \
  -H "Content-Type: application/json" \
  -H "X-Service-Token: YOUR_SERVICE_TOKEN" \
  -d '{
  "assetA": "PTEST",
  "assetB": "native"
}'
Response200
{
  "assetA": "PTEST",
  "assetB": "native",
  "status": "success",
  "poolId": "d4b757960775b71542a966f674c84c3ddabae7fa9089803bcb6514f06246d6b0"
}

Stellar pools have no separate "create" operation — a pool comes into existence implicitly on first deposit. This step opts the relayer into the pool-share trustline so it can hold shares before depositing, and returns the deterministic pool ID. Use "native" (or "XLM") for XLM itself; any other symbol must already be registered via POST /admin/assets/:symbol/register.

Deposit Liquidity ​

Deposit Liquidity

🔒 Admin operation — always requires a Service Token, regardless of the panel above.

curl -X POST "http://localhost:8100/api/v1/admin/stellar/pools/deposit" \
  -H "Content-Type: application/json" \
  -H "X-Service-Token: YOUR_SERVICE_TOKEN" \
  -d '{
  "assetA": "PTEST",
  "assetB": "native",
  "maxAmountA": "1000",
  "maxAmountB": "500"
}'
Response200
{
  "assetA": "PTEST",
  "assetB": "native",
  "status": "success",
  "txHash": "2d6cb9028fa6b1dca3d7dbca58670681a20b909a4671d9ad39de390c92f4bb95"
}

Funds the pool from the relayer's own balance (relayer-owned LP position — the Stellar counterpart of Celo's admin/pools/fund). minPriceN/D and maxPriceN/D are optional slippage bounds; omit all four for a wide-open band, appropriate for a brand-new pool's first deposit.

An asset's issuer can't fund its own pool

Stellar forbids an issuing account from holding a trustline to its own asset — so if the relayer is also an asset's issuer (the normal case for anything registered via this engine, see Stellar Assets & AMM →), the relayer can never hold a balance of that asset to seed a pool with. PTEST above is issued by a separate account specifically so the relayer could hold and deposit it. There is currently no supported way to seed a pool with a relayer-issued asset.

Withdraw Liquidity ​

Withdraw Liquidity

🔒 Admin operation — always requires a Service Token, regardless of the panel above.

curl -X POST "http://localhost:8100/api/v1/admin/stellar/pools/withdraw" \
  -H "Content-Type: application/json" \
  -H "X-Service-Token: YOUR_SERVICE_TOKEN" \
  -d '{
  "assetA": "PTEST",
  "assetB": "native",
  "shareAmount": "50",
  "minAmountA": "1",
  "minAmountB": "1"
}'
Response200
{
  "assetA": "PTEST",
  "assetB": "native",
  "status": "success",
  "txHash": "30791694fce451e8f20249e29612bb5c73112b65e5c11615a84556032b07fd73"
}

Redeems relayer pool shares back into the underlying assets. minAmountA/minAmountB are the caller's slippage floor.

Get Pool Reserves ​

Get Pool Reserves
curl -X GET "http://localhost:8100/api/v1/stellar/pools?assetA=PTEST&assetB=native" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-API-Secret: YOUR_API_SECRET"
Response200
{
  "pool": {
    "poolId": "d4b757960775b71542a966f674c84c3ddabae7fa9089803bcb6514f06246d6b0",
    "feeBp": 30,
    "assetA": "native",
    "reserveA": "500.0000000",
    "assetB": "PTEST:GBUQZ7LZC7F42FFGR7JFKVV4WTAK7IKSSUQMGMUFYLPVIDCYAZYJB4Z2",
    "reserveB": "1000.0000000",
    "totalShares": "707.1067811"
  }
}

Unlike Celo's listPools (which enumerates every pair from the factory), Stellar has no pool registry to enumerate from this SDK version — you ask for a pair you already know about. Note feeBp: 30 (0.3%), matching the Celo AMM's fee.

Get a Swap Quote ​

Get Swap Quote
curl -X GET "http://localhost:8100/api/v1/stellar/swap/quote?from=native&to=PTEST&amountIn=1" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-API-Secret: YOUR_API_SECRET"
Response200
{
  "from": "native",
  "to": "PTEST",
  "amountIn": "1",
  "amountOut": "1.9900318",
  "feeBp": 30
}

Derives expected output from the pool's live reserves using the same constant-product formula the network enforces on-chain, and the pool's own configured fee — not a hardcoded assumption. Amounts are Stellar decimal strings (up to 7 fractional digits), not this engine's base units.

Execute a Swap ​

Execute Swap
curl -X POST "http://localhost:8100/api/v1/stellar/swap/execute" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-API-Secret: YOUR_API_SECRET" \
  -d '{
  "externalUserId": "bekana-buyer-1",
  "from": "native",
  "to": "PTEST",
  "amountIn": "1",
  "destMin": "1.9",
  "externalId": "amm-swap-001"
}'
Response200
{
  "amountIn": "1",
  "chain": "stellar",
  "from": "native",
  "status": "success",
  "to": "PTEST",
  "txHash": "ca5898c372ae205581d52ba1b041cf5eb20a2cccc3dd107ed85abe1334251ec8"
}

Verified

Confirmed on Stellar testnet: the resulting path_payment_strict_send operation paid exactly 1.9900318 PTEST to the user — matching the quote above to the last digit.

Executes a user-signed PathPaymentStrictSend — Stellar's "path payment to self" swap pattern. Unlike Celo's swap path, this writes a ledger.Record of type "swap" on every execution (see Journal API) — a deliberate improvement over the existing Celo precedent, not a Stellar-specific requirement.

New wallet, immediate swap

A freshly-created custodial wallet's account may not have propagated through Horizon yet when a swap immediately follows wallet creation in the same request flow — this can fail with tx_bad_seq. A short retry (a few seconds) resolves it; this is a Horizon replication-lag race, not a bug in the swap logic itself.

Released under the MIT License.