Skip to content

Project: Build a Token Swap Trader ​

A complete, runnable project implementing a full round-trip through the Uniswap V2 AMM on Celo — minting the input token, pricing the trade, approving the router, and executing — in both directions. See the AMM API for the endpoint reference.

Celo logo Celo only

This is Celo's Uniswap V2 AMM specifically — no chain selector on this page, since Stellar's AMM (see Stellar AMM) is a separate native protocol-level mechanism with its own distinct API, not another chain for this same swap flow.

▸API ConfigurationNot configured

What you'll build ​

A trade.js script with one reusable swap() function that mints the input token, prices the trade, approves the router, and executes — run once for USDT → IMC and once for IMC → USDT.

Prerequisites ​

  • Node.js 18+ (for global fetch)
  • A running comet-engine with a tenant API key/secret — see Authentication
  • A funded USDT/IMC pool already created — see Liquidity Pool Setup

Project setup ​

bash
mkdir amm-trade && cd amm-trade && npm init -y && npm install dotenv

.env:

env
COMET_BASE_URL=http://localhost:8100
COMET_API_KEY=your-tenant-api-key
COMET_API_SECRET=your-tenant-api-secret

Create trade.js with this shared helper at the top — every step below adds to the same file:

js
import 'dotenv/config';

const base = process.env.COMET_BASE_URL;
const headers = {
  'Content-Type': 'application/json',
  'X-API-Key': process.env.COMET_API_KEY,
  'X-API-Secret': process.env.COMET_API_SECRET,
};

async function comet(method, path, body) {
  const res = await fetch(base + path, { method, headers, body: body ? JSON.stringify(body) : undefined });
  const data = await res.json();
  if (!res.ok) throw new Error(`${method} ${path} -> ${res.status}: ${JSON.stringify(data)}`);
  return data;
}

Step 1: Swap USDT → IMC ​

1. Mint USDT to user
curl -X POST "http://localhost:8100/api/v1/assets/usdt/mint" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-API-Secret: YOUR_API_SECRET" \
  -d '{
  "userId": 123,
  "amountBase": "10000000",
  "externalId": "swap-001"
}'
Response200
{
  "amount": 10,
  "chain": "celo",
  "symbol": "USDT",
  "txHash": "0x..."
}
2. Get quote
curl -X GET "http://localhost:8100/api/v1/swap/quote?from=USDT&to=IMC&amountIn=10000000" \
  -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": "10000000",
  "amounts": [
    "10000000",
    "9970000"
  ]
}
3. Approve router
curl -X POST "http://localhost:8100/api/v1/assets/usdt/approve" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-API-Secret: YOUR_API_SECRET" \
  -d '{
  "userId": 123,
  "amountBase": "10000000"
}'
Response200
{
  "txHash": "0x...",
  "chain": "celo",
  "symbol": "USDT"
}
4. 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 '{
  "userId": 123,
  "from": "USDT",
  "to": "IMC",
  "amountIn": "10000000"
}'
Response200
{
  "status": "success",
  "from": "USDT",
  "to": "IMC",
  "txHash": "0x...",
  "chainId": 11142220
}

Step 2: Swap IMC → USDT (the reverse direction) ​

1. Get quote
curl -X GET "http://localhost:8100/api/v1/swap/quote?from=IMC&to=USDT&amountIn=5000000" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-API-Secret: YOUR_API_SECRET"
Response200
{
  "from": "IMC",
  "to": "USDT",
  "amountIn": "5000000",
  "amounts": [
    "5000000",
    "4985000"
  ]
}
2. Approve router
curl -X POST "http://localhost:8100/api/v1/assets/imc/approve" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-API-Secret: YOUR_API_SECRET" \
  -d '{
  "userId": 123,
  "amountBase": "5000000"
}'
Response200
{
  "txHash": "0x...",
  "chain": "celo",
  "symbol": "IMC"
}
3. 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 '{
  "userId": 123,
  "from": "IMC",
  "to": "USDT",
  "amountIn": "5000000"
}'
Response200
{
  "status": "success",
  "from": "IMC",
  "to": "USDT",
  "txHash": "0x...",
  "chainId": 11142220
}

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

Supported pools:

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

Step 3: Run both directions as one script ​

Add to trade.js:

js
async function swap(userId, from, to, amountIn) {
  // 1. Mint the input token to the user (skip this in production — the
  // user's balance comes from wherever they actually acquired it)
  await comet('POST', `/api/v1/assets/${from.toLowerCase()}/mint`, { userId, amountBase: amountIn, externalId: `swap-mint-${Date.now()}` });

  // 2. Price it
  const quote = await comet('GET', `/api/v1/swap/quote?from=${from}&to=${to}&amountIn=${amountIn}`);
  console.log(`quote: ${amountIn} ${from} -> ${quote.amounts[1]} ${to}`);

  // 3. Approve the router to spend the input token
  await comet('POST', `/api/v1/assets/${from.toLowerCase()}/approve`, { userId, amountBase: amountIn });

  // 4. Execute
  const result = await comet('POST', '/api/v1/swap/tokens', { userId, from, to, amountIn });
  console.log('swapped:', result.txHash);
  return result;
}

async function main() {
  const userId = 123;
  await swap(userId, 'USDT', 'IMC', '10000000');   // USDT -> IMC
  await swap(userId, 'IMC', 'USDT', '5000000');    // IMC -> USDT
}

main().catch((e) => { console.error(e); process.exit(1); });
bash
node trade.js

What you built ​

  • A reusable swap() function covering mint → quote → approve → execute for any pair on the pool.
  • Both directions of a real USDT/IMC round-trip, gas paid by the platform relayer.

Next steps: replace the mint step with your own balance-acquisition flow, or see Liquidity Pool Setup if you need to create the pool this project trades against first.

Released under the MIT License.