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 →.
Celo (Uniswap V2)
Price Quote
Get expected output amounts for a swap.
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"{
"from": "USDT",
"to": "IMC",
"amountIn": "1000000",
"amounts": [
"1000000",
"1998000"
],
"amountOut": "1998000"
}Query Parameters:
| Param | Type | Description |
|---|---|---|
from | string | Input token symbol (e.g. "USDT") |
to | string | Output token symbol (e.g. "IMC") |
amountIn | string | Input amount in base units |
Execute Swap
Swaps an exact amount of input tokens for output tokens. Gas is relayed by the platform.
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"
}'{
"status": "success",
"from": "USDT",
"to": "IMC",
"amountIn": "1000000",
"txHash": "0x...",
"chainId": 11142220
}Request Fields:
| Field | Type | Required | Description |
|---|---|---|---|
tenantSlug | string | No | Tenant (defaults to "comet") |
userId | number | Yes | User whose wallet to swap from |
from | string | Yes | Input token symbol |
to | string | Yes | Output token symbol |
amountIn | string | Yes | Input 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).
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"{
"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
| Pool | Fee |
|---|---|
| 0.3% | |
| 0.3% | |
| USDC/IMC | 0.3% |
How It Works
- User's wallet must hold the input token
- User approves the Uniswap V2 Router to spend input tokens
- Engine calls
swapExactTokensForTokens()on the Router - Router executes the swap on-chain
- Output tokens arrive in the user's wallet
- 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
🔒 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"
}'{
"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
🔒 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"
}'{
"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
🔒 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"
}'{
"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
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"{
"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
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"{
"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
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"
}'{
"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.