Tokenization API
Comet Engine tokenizes real-world assets by minting/burning on-chain tokens backed by real inventory. The engine enforces proof-of-reserve: airtime-backed tokens can only be minted/burned against registered airtime assets, and every operation is double-entry journaled.
How Tokenization Works
Real World On Chain
────────── ────────
User pays KES ──────────► Mint IMC (1:1 with KES)
│ │
│ ImpalaPay delivers │ On-chain supply = real float
│ airtime to phone │
│ │
▼ ▼
Airtime received ◄──────── Burn IMC (release backing)Key invariant: on-chain IMC supply = real airtime float remaining
When IMC is minted, the engine debits the airtime inventory account. When IMC is burned, it credits it. The journal always balances.
Tokenize Airtime
Mints the chain's airtime-backed asset (IMC) to a user's custodial wallet, drawing from the platform's real airtime inventory (ImpalaPay float).
curl -X POST "http://localhost:8100/api/v1/tokenize/airtime" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE" \
-d '{"userId": 12345, "amountBase": "10000000", "externalId": "airtime-001", "chain": "celo"}'{ "status": "success", "asset": "IMC", "type": "airtime", "chain": "celo", "userId": 12345, "to": "0xe3f53fe54f8e1b5e8c86f070e4c9c2c7fa641d0d", "amountBase": "10000000", "txHash": "0x..." }Request Fields:
| Field | Type | Required | Description |
|---|---|---|---|
userId | number | Yes | Recipient user |
amountBase | string | Yes | Amount in base units (6 decimals: 10000000 = 10 IMC) |
externalId | string | Yes | Idempotency key (prevents double-mint) |
chain | string | No | Target chain (defaults to celo) |
Proof-of-reserve
The caller (typically app-core-backend) must verify the ImpalaPay float balance before calling this endpoint. Comet Engine only mints assets registered with type: airtime or backing: airtime.
Burn on Redemption
When airtime is actually delivered, burn the on-chain mirror to keep supply pegged:
curl -X POST "http://localhost:8100/api/v1/assets/imc/burn-by-holder" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE" \
-d '{"userId": 12345, "amountBase": 10000000, "externalId": "airtime-delivery-001"}'{ "txHash": "0x...", "chain": "celo", "symbol": "IMC", "amount": 10 }Idempotency
Use the same externalId for the tokenize and burn calls of the same transaction. This prevents double-minting or double-burning if a request is retried.
Check Status
curl -X GET "http://localhost:8100/api/v1/tokenize/status/airtime-001" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE"{ "id": "airtime-001", "status": "confirmed", "txHash": "0x...", "amount": 10000000 }Delegated Endpoints
| Endpoint | Status | Notes |
|---|---|---|
POST /api/v1/tokenize/deposit | Delegates | Handled by app-core-backend |
GET /api/v1/tokenize/status/:id | Delegates | Proxied to app-core-backend |
End-to-End Example
Buy Airtime (KES 50)
# Step 1: User pays KES via M-Pesa (handled by app-core-backend)
# Step 2: Tokenize → mints 50 IMC to user's wallet
curl -X POST http://localhost:8100/api/v1/tokenize/airtime \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN" \
-d '{"userId": 24, "amountBase": "50000000", "externalId": "airtime-tx-001"}'
# => {"status":"success","asset":"IMC","txHash":"0x..."}
# Step 3: Deliver airtime via ImpalaPay (handled by app-core-backend)
# Step 4: Burn IMC to remove on-chain mirror
curl -X POST http://localhost:8100/api/v1/assets/imc/burn-by-holder \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN" \
-d '{"userId": 24, "amountBase": 50000000, "externalId": "airtime-tx-001"}'
# => {"txHash":"0x...","symbol":"IMC","amount":50}Journal entries:
| Entry | Debit | Credit | Amount |
|---|---|---|---|
| Tokenize | airtime_inventory::comet | chain::celo::imc | +50 IMC |
| Burn | chain::celo::imc | airtime_inventory::comet | -50 IMC |
Net effect: journal balanced, on-chain supply unchanged after full cycle.
Buy ARTM via AMM
# 1. Mint USDT to user (after KES deposit)
curl -X POST http://localhost:8100/api/v1/assets/usdt/mint \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN" \
-d '{"userId": 24, "amountBase": "10000000", "externalId": "buy-001"}'
# 2. Approve Uniswap router
curl -X POST http://localhost:8100/api/v1/assets/usdt/approve \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN" \
-d '{"userId": 24, "amountBase": "10000000"}'
# 3. Swap USDT → IMC
curl -X POST http://localhost:8100/api/v1/swap/tokens \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN" \
-d '{"userId": 24, "from": "USDT", "to": "IMC", "amountIn": "10000000"}'
# => ~9970000 IMC (10 USDT minus 0.3% fee)Sell ARTM via AMM
# 1. Get quote
curl "http://localhost:8100/api/v1/swap/quote?from=IMC&to=USDT&amountIn=5000000" \
-H "X-Service-Token: YOUR_TOKEN"
# => {"amountOut": "4985000", ...}
# 2. Execute swap
curl -X POST http://localhost:8100/api/v1/swap/tokens \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN" \
-d '{"userId": 24, "from": "IMC", "to": "USDT", "amountIn": "5000000"}'Cross-Border Payment
# Sender (Kenya): deposit KES → receive USDC
curl -X POST http://localhost:8100/api/v1/assets/usdc/mint \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN" \
-d '{"userId": 24, "amountBase": "50000000", "externalId": "cross-001"}'
# Sender: send USDC to recipient
curl -X POST http://localhost:8100/api/v1/assets/usdc/send \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN" \
-d '{"userId": 24, "to": "0xRecipientAddress", "amountBase": "50000000"}'
# Recipient (Nigeria): backend burns USDC → credits NGN
curl -X POST http://localhost:8100/api/v1/assets/usdc/burn-from \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN" \
-d '{"userId": 56789, "amountBase": "50000000", "externalId": "cross-001"}'Custom Loyalty Token
# 1. Deploy ERC-20, register in comet-engine
curl -X POST http://localhost:8100/api/v1/admin/assets/POINTS/register?chain=celo \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN" \
-d '{"contract": "0x...", "decimals": 18, "name": "FusionPoints", "type": "utility", "backing": "none"}'
# 2. Mint 1000 points to user
curl -X POST http://localhost:8100/api/v1/assets/points/mint \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN" \
-d '{"userId": 24, "amountBase": "1000000000000000000000", "externalId": "reward-001"}'
# 3. User redeems 500 points
curl -X POST http://localhost:8100/api/v1/assets/points/burn-by-holder \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN" \
-d '{"userId": 24, "amountBase": "500000000000000000000", "externalId": "redeem-001"}'Registering an Airtime Asset
Set env vars:
CELO_IMC_CONTRACT=0xF150E30B1C7Ab81938d650567D1454Ee0132d5A2
CELO_IMC_TYPE=airtime
CELO_IMC_BACKING=airtimeOr register at runtime:
curl -X POST "http://localhost:8100/api/v1/admin/assets/IMC/register?chain=celo" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE" \
-d '{"contract": "0xF150E30B1C7Ab81938d650567D1454Ee0132d5A2", "decimals": 6, "name": "ImpalaCoin", "type": "airtime", "backing": "airtime"}'{ "message": "asset IMC registered on celo" }