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 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.
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
mkdir amm-trade && cd amm-trade && npm init -y && npm install dotenv.env:
COMET_BASE_URL=http://localhost:8100
COMET_API_KEY=your-tenant-api-key
COMET_API_SECRET=your-tenant-api-secretCreate trade.js with this shared helper at the top — every step below adds to the same file:
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
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"
}'{
"amount": 10,
"chain": "celo",
"symbol": "USDT",
"txHash": "0x..."
}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"{
"from": "USDT",
"to": "IMC",
"amountIn": "10000000",
"amounts": [
"10000000",
"9970000"
]
}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"
}'{
"txHash": "0x...",
"chain": "celo",
"symbol": "USDT"
}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"
}'{
"status": "success",
"from": "USDT",
"to": "IMC",
"txHash": "0x...",
"chainId": 11142220
}Step 2: Swap IMC → USDT (the reverse direction)
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"{
"from": "IMC",
"to": "USDT",
"amountIn": "5000000",
"amounts": [
"5000000",
"4985000"
]
}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"
}'{
"txHash": "0x...",
"chain": "celo",
"symbol": "IMC"
}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"
}'{
"status": "success",
"from": "IMC",
"to": "USDT",
"txHash": "0x...",
"chainId": 11142220
}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
Supported pools:
| Pool | Fee |
|---|---|
| USDT/USDC | 0.3% |
| USDT/IMC | 0.3% |
| USDC/IMC | 0.3% |
Step 3: Run both directions as one script
Add to trade.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); });node trade.jsWhat 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.