Skip to content

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.

API ConfigurationNot configured

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).

Tokenize Airtime
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"}'
Response200
{ "status": "success", "asset": "IMC", "type": "airtime", "chain": "celo", "userId": 12345, "to": "0xe3f53fe54f8e1b5e8c86f070e4c9c2c7fa641d0d", "amountBase": "10000000", "txHash": "0x..." }

Request Fields:

FieldTypeRequiredDescription
userIdnumberYesRecipient user
amountBasestringYesAmount in base units (6 decimals: 10000000 = 10 IMC)
externalIdstringYesIdempotency key (prevents double-mint)
chainstringNoTarget 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:

Burn IMC
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"}'
Response200
{ "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

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"
Response200
{ "id": "airtime-001", "status": "confirmed", "txHash": "0x...", "amount": 10000000 }

Delegated Endpoints

EndpointStatusNotes
POST /api/v1/tokenize/depositDelegatesHandled by app-core-backend
GET /api/v1/tokenize/status/:idDelegatesProxied to app-core-backend

End-to-End Example

Buy Airtime (KES 50)

bash
# 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:

EntryDebitCreditAmount
Tokenizeairtime_inventory::cometchain::celo::imc+50 IMC
Burnchain::celo::imcairtime_inventory::comet-50 IMC

Net effect: journal balanced, on-chain supply unchanged after full cycle.

Buy ARTM via AMM

bash
# 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

bash
# 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

bash
# 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

bash
# 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:

env
CELO_IMC_CONTRACT=0xF150E30B1C7Ab81938d650567D1454Ee0132d5A2
CELO_IMC_TYPE=airtime
CELO_IMC_BACKING=airtime

Or register at runtime:

Register Airtime Asset
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"}'
Response200
{ "message": "asset IMC registered on celo" }

Released under the MIT License.