# SimpleDEX — AI Agent Guide (v1.9 · 2026-04-08) > SimpleDEX is a non-custodial decentralised exchange and token launch > platform on XPR Network. This file tells AI agents how to read protocol > state, execute trades, and query analytics. > > **Testnet:** For testnet operations (different contract names, endpoints, > and chain ID), see: `https://testnet.dex.protonnz.com/llms-testnet.txt` > > **Important: This file is ~1,300 lines. Read the FULL document before > concluding something is missing. Sections 1-2 cover read-only analytics, > sections 3-6 cover trading, liquidity, launches, and token creation.** ## Quick reference | Item | Value | |------|-------| | DEX contract | `simpledex` | | Launch contract | `simplelaunch` | | Token contract | `simpletoken` | | XPR token | `eosio.token` (4,XPR) | | RPC endpoint | `https://api.protonnz.com` | | Explorer | `https://explorer.xprnetwork.org` | | Frontend | `https://dex.protonnz.com` | | Analytics API | `https://indexer.protonnz.com` | | Treasury | `dex.protonnz` | All token amounts are integers with 4 decimal places. `1.0000 XPR` = raw value `10000`. Multiply human amounts by 10000. --- ## 1 — Analytics API (no auth required) The indexer polls on-chain data every 5 minutes and serves analytics via a public REST API. All endpoints return JSON with CORS enabled. Base URL: `https://indexer.protonnz.com` **Agent efficiency guide** — choose the smallest endpoint for your task: - Quick market check: `GET /api/tokens?fields=compact&limit=5` (~400 bytes) - Latest launches: `GET /api/tokens?sort=newest&fields=compact&limit=5` (~400 bytes) - One token lookup: `GET /api/tokens?symbol=MARSH` (~300 bytes) - Platform stats only: `GET /api/stats` (~200 bytes, includes volume/fees) - Volume summary: `GET /api/volume` (~500 bytes, daily time-series + totals) - My position/PnL: `GET /api/portfolio/:account/pnl` (~1KB per token traded) - My recent trades: `GET /api/portfolio/:account/trades?limit=10` (~500 bytes) - Top traders: `GET /api/leaderboard/profit?limit=5` (~300 bytes) - All traders: `GET /api/traders?sort=volume&limit=50` (~3KB, paginated) - Full protocol snapshot: `GET /api/overview` (~15KB, use sparingly) ### 1.1 Full protocol overview (one call) ``` GET /api/overview ``` Returns everything in one response: protocol info, live stats, all tokens (with prices, images, creators), and all pools (with TVL and depth). Updated every 5 minutes. This is the best starting point for agents that need a complete picture of the platform. ### 1.3 Token prices #### v1 (flat) ``` GET /api/prices # default: flat array GET /api/prices?format=object # keyed by symbol (legacy) ``` Default returns a flat array of all tokens: ```json [ { "symbol": "PUMP", "price": 0.000123, "mcap": 123000, "contract": "simpletoken", "change24h": 5.2, "change7d": -2.1, "ath": 0.000456, "athTimestamp": 1740700000, "high24h": 0.00013, "low24h": 0.00011, "supply": 715614513.8, "maxSupply": 1000000000, "burned": 148375585.9, "circulatingSupply": 567238927.9 } ] ``` `?format=object` returns the same data keyed by symbol: `{ "PUMP": { ... } }`. #### v2 (paginated, sorted, nested) ``` GET /api/v2/prices GET /api/v2/prices?page=1&limit=50&sort=mcap&order=desc ``` Parameters: - `page` — page number, 1-based (default: 1) - `limit` — items per page (default: 100, max: 1000) - `sort` — field to sort by: `mcap`, `price`, `change24h`, `change7d`, `symbol` - `order` — `asc` or `desc` (default: desc) Response: ```json { "items": [ { "symbol": "PUMP", "contract": "simpletoken", "tokenId": 42, "precision": 4, "price": { "usd": 0.000123, "change24h": 5.2, "change7d": -2.1, "ath": 0.000456, "athTimestamp": 1740700000, "high24h": 0.00013, "low24h": 0.00011 }, "market": { "mcap": 123000, "supply": 715614513.8, "maxSupply": 1000000000, "circulatingSupply": 567238927.9, "burned": 148375585.9 } } ], "page": 1, "limit": 50, "total": 404, "pages": 9 } ``` Fields `change24h`, `change7d` are null until enough snapshots accumulate. Supply fields are null for non-graduated tokens (virtual curve, no on-chain supply). ### 1.3.1 Token price history (for charts) ``` GET /api/prices/MARSH/history # all history (up to 30 days) GET /api/prices/MARSH/history?since=TS # from unix timestamp ``` Returns time-series price data for charting: ```json { "symbol": "MARSH", "history": [ { "price": 0.0000211, "timestamp": 1740600000 }, { "price": 0.0000222, "timestamp": 1740600300 } ] } ``` Data points are ~5 minutes apart. Retained for 30 days. Useful `since` values: `now - 86400` (24h), `now - 604800` (7d). ### 1.4 Token details (for investigating launches) ``` GET /api/tokens # all tokens (full details) GET /api/tokens?symbol=MARSH # single token lookup GET /api/tokens?creator=westcoastred # tokens by launcher account GET /api/tokens?graduated=true # only graduated tokens GET /api/tokens?graduated=false # only tokens still on curve GET /api/tokens?sort=newest&limit=5 # 5 most recently created tokens GET /api/tokens?fields=compact # lightweight: no description/image/ATH GET /api/tokens?fields=compact&limit=10 # top 10 by mcap, minimal fields GET /api/tokens?sort=newest&fields=compact&limit=5 # latest launches, minimal ``` Full response (default): ```json { "tokens": [{ "tokenId": 5, "symbol": "MARSH", "name": "Marshall Coin", "description": "A homage to the Living Legend...", "imageUrl": "https://simplelaunch.mypinata.cloud/ipfs/QmRPc...", "creator": "westcoastred", "graduated": true, "dexPoolId": 6, "price": 0.0000222, "mcap": 22284, "change24h": 5.2, "change7d": -2.1, "ath": 0.0000456, "athTimestamp": 1740700000, "high24h": 0.000025, "low24h": 0.000020, "supply": 710382366.7, "maxSupply": 1000000000, "burned": null, "circulatingSupply": 710382366.7, "buys24h": 182, "sells24h": 175, "volume24h": 1200.50, "uniqueTraders24h": 45, "totalUnclaimed": 180000000, "unclaimedAccounts": 3 }], "count": 29 } ``` Compact response (`fields=compact`) — 8 fields per token instead of 16: ```json { "tokens": [{ "tokenId": 5, "symbol": "MARSH", "name": "Marshall Coin", "creator": "westcoastred", "graduated": true, "price": 0.0000222, "mcap": 22284, "change24h": 5.2 }], "count": 1 } ``` **Tip for agents**: Use `?fields=compact&limit=10` for a quick market overview, then `?symbol=X` to drill into a specific token. ### 1.5 Pool data with liquidity depth ``` GET /api/pools GET /api/pools?poolId=6 ``` Returns all pools with reserves, TVL, fee rate, volume, fees, APY, and max swap size at 5% and 10% price impact: ```json { "pools": [{ "poolId": 6, "tokenA": { "symbol": "XPR", "contract": "eosio.token" }, "tokenB": { "symbol": "MARSH", "contract": "simpletoken" }, "reserveA": "1700000000", "reserveB": "50000000000", "tvlUsd": 5136.52, "feeRate": 30, "depth5pct": { "tokenA": 135.2, "tokenB": 132.8 }, "depth10pct": { "tokenA": 285.6, "tokenB": 280.1 }, "volumeUsd24h": 1200.50, "volumeUsd7d": 8400.00, "feesUsd24h": 3.60, "apyBase": 12.5 }], "count": 27 } ``` `depth5pct.tokenA` = max USD you can sell of tokenA before 5% price impact. `apyBase` = annualized LP fee yield based on 7d volume and current TVL. ### 1.6 TVL history ``` GET /api/tvl GET /api/tvl?since=1740600000 ``` Aggregate TVL history and current platform stats: ```json { "history": [{ "totalTvlUsd": 50000, "timestamp": 1740600000 }], "current": { "totalTvlUsd": 14741, "totalMarketCapUsd": 48473, "poolCount": 27, "tokenCount": 29, "graduatedCount": 16 } } ``` ### 1.7 Pool TVL history ``` GET /api/pools/6/tvl GET /api/pools/6/tvl?since=1740600000 ``` ### 1.8 Volume and fees ``` GET /api/volume # aggregate daily volume time-series + summary GET /api/volume?since=1740600000 # from unix timestamp ``` ```json { "daily": [ { "day": 1740700000, "volume": 9020.50, "fees": 27.06, "trades": 1005 } ], "summary": { "volume24h": 9020.50, "volume7d": 42000.00, "volumeTotal": 142000.00, "fees24h": 27.06, "fees7d": 126.00, "trades24h": 1005, "trades7d": 7035 } } ``` Volume is indexed from Hyperion swap history into SQLite. Updated every 5 minutes. Daily buckets are UTC midnight-aligned. ### 1.8.1 Pool volume history ``` GET /api/pools/6/volume GET /api/pools/6/volume?since=1740600000 ``` ```json { "poolId": 6, "daily": [{ "day": 1740700000, "volume": 1200.50, "fees": 3.60, "trades": 150 }], "summary": { "volume24h": 1200.50, "volume7d": 8400.00, "fees24h": 3.60, "fees7d": 25.20 } } ``` ### 1.8.2 DefiLlama integration endpoints These endpoints serve pre-computed data in DefiLlama's expected format: ``` GET /api/defillama/summary # daily + total volume, fees, revenue GET /api/defillama/pools # per-pool TVL, APY, volume (yield format) GET /api/defillama/volume?timestamp=TS # historical daily volume for a specific day ``` Summary response: ```json { "dailyVolume": 9020.50, "totalVolume": 142000.00, "dailyFees": 27.06, "dailyUserFees": 27.06, "dailyRevenue": 9.02, "dailySupplySideRevenue": 18.04, "totalFees": 426.00, "totalRevenue": 142.00, "tvl": 22000.00, "timestamp": 1709251200 } ``` Fee breakdown: 0.3% swap fee → 50% protocol revenue, 50% to LPs. ### 1.9 Platform stats ``` GET /api/stats ``` ```json { "totalTvlUsd": 14741, "totalMarketCapUsd": 48473, "poolCount": 27, "tokenCount": 29, "graduatedCount": 16, "dailyVolumeUsd": 9020.50, "dailyFeesUsd": 27.06, "dailyTradeCount": 1005, "weeklyVolumeUsd": 42000.00, "weeklyFeesUsd": 126.00, "weeklyTradeCount": 7035, "timestamp": 1740700000 } ``` ### 1.10 Top movers ``` GET /api/movers GET /api/movers?limit=10 ``` Top gainers and losers by 24h % change: ```json { "gainers": [{ "symbol": "PUMP", "tokenId": 1, "price": 0.000123, "mcap": 123000, "change24h": 25.5 }], "losers": [{ "symbol": "DUMP", "tokenId": 2, "price": 0.000001, "mcap": 1000, "change24h": -40.2 }] } ``` ### 1.11 Graduation events ``` GET /api/events GET /api/events?limit=10&since=1740600000 ``` ```json { "graduations": [{ "tokenId": 5, "symbol": "PUMP", "name": "PumpCoin", "creator": "pumpmaker", "dexPoolId": 12, "detectedAt": 1740700000 }] } ``` ### 1.12 Trade history ``` GET /api/tokens/107/trades?limit=50&offset=0 # token trades (paginated) GET /api/tokens/107/trades?limit=50&offset=200 # page 5 of token trades GET /api/pools/107/trades?limit=50&offset=0 # pool trades from DB (paginated) GET /api/pools/107/trades?limit=5&live=1 # pool trades from live Hyperion (10s cache) GET /api/tokens/107/holders?limit=50 # top holders (pre-computed, zero RPC) ``` Query params for token/pool trades: - `limit` — results per page (default 50, max 200) - `offset` — skip first N results (default 0). Use for pagination. - `live` — (pool trades only) `1` for near-real-time Hyperion data instead of DB Token trade response format (graduated tokens from DB): ```json { "trades": [ { "type": "buy", "account": "trader1", "xprAmount": 1000.0, "tokenAmount": 45000000, "outputQuantity": null, "timestamp": "2026-03-14T03:37:43.000Z", "txId": "abc123..." }, { "type": "sell", "account": "trader2", "xprAmount": 500.0, "tokenAmount": 22000000, "outputQuantity": null, "timestamp": "2026-03-14T03:30:00.000Z", "txId": "def456..." } ], "total": 1270, "hasMore": true, "source": "indexer" } ``` Pool trade response format: ```json { "trades": [ { "type": "swap", "account": "trader1", "amountIn": "1000.0000 XPR", "amountOut": "45000.0000 AGENT", "timestamp": "2026-03-14T03:37:43.000Z", "txId": "abc123..." } ], "total": 1270, "hasMore": true, "source": "indexer" } ``` **Pagination example** — to get ALL trades for a token: ```bash # Page 1 curl -s 'https://indexer.protonnz.com/api/tokens/182/trades?limit=200&offset=0' # Page 2 curl -s 'https://indexer.protonnz.com/api/tokens/182/trades?limit=200&offset=200' # Continue until hasMore=false ``` **Data freshness guide for agents:** - **Non-graduated tokens**: Trades come from live Hyperion (10s cache). Brand new tokens (not yet in 5-min poll) are also served live. - **Graduated tokens**: Trades come from SQLite DB (synced every ~5 min). For real-time needs, use pool trades with `?live=1`. - **Holders**: Pre-computed LP-augmented balances (zero RPC at request time). Returns empty `[]` for non-graduated tokens (virtual curve, no on-chain balances). - **Prices/stats**: Updated every 5 min from on-chain poll. Use `/api/prices` for latest. ### 1.12.1 Rate limiting The indexer API has IP-based rate limits: | Endpoint type | Limit | |---------------|-------| | General (prices, stats, pools, tokens) | 120 requests/min | | Expensive (trades, holders, OG images, exports) | 50 requests/min | When scanning multiple tokens (e.g., all 200 holders endpoints), add a small delay between requests (~1.5s) to stay within limits. Or use `/api/overview` which returns all tokens and pools in one call. ### 1.13 Health check ``` GET https://indexer.protonnz.com/health ``` ```json { "status": "ok", "snapshots": 8352, "poolSnapshots": 7776, "lastPoll": 1740700000 } ``` ### 1.14 Portfolio & PnL (know your position) **Agent efficiency guide** — choose the right endpoint: - Quick position check: `GET /api/portfolio/:account/pnl` (per-token P&L, holdings, LP) - Trade history: `GET /api/portfolio/:account/trades?limit=20` (paginated) - Account summary: `GET /api/portfolio/:account` (volume, fees, trade counts) - LP activity: `GET /api/portfolio/:account/lp-events?limit=20` - LP fee earnings: `GET /api/portfolio/:account/lp-earnings` (estimated fees per pool) #### Account summary ``` GET /api/portfolio/youraccount ``` ```json { "account": "youraccount", "summary": { "totalTrades": 142, "totalVolumeUsd": 15230.50, "totalFeesPaidUsd": 45.69, "lastTrade": 1740700000 }, "byType": { "swap": { "count": 80, "volumeUsd": 10000.00 }, "buy": { "count": 30, "volumeUsd": 3000.00 }, "sell": { "count": 20, "volumeUsd": 2000.00 }, "addlp": { "count": 6, "volumeUsd": 200.00 }, "removelp": { "count": 6, "volumeUsd": 30.50 } }, "lpSummary": { "totalDepositsUsd": 200.00, "totalWithdrawalsUsd": 30.50, "eventCount": 12, "pools": [ { "poolId": 6, "depositsUsd": 200.00, "withdrawalsUsd": 30.50 } ] } } ``` #### Per-token PnL (cost basis + unrealized) ``` GET /api/portfolio/youraccount/pnl ``` ```json { "account": "youraccount", "summary": { "totalRealizedPnlUsd": 1250.00, "totalVolumeUsd": 15230.50, "winRate": 66.67, "tradeCount": 142, "profitableCount": 4, "totalTokensTraded": 6, "bestToken": "MARSH", "worstToken": "DUMP" }, "tokens": [ { "symbol": "MARSH", "totalBought": 50000000, "totalBoughtUsd": 1500.00, "totalSold": 20000000, "totalSoldUsd": 800.00, "lpSold": 5000000, "lpSoldUsd": 150.00, "realizedPnlUsd": 200.00, "avgBuyPriceUsd": 0.00003, "currentPriceUsd": 0.0000222, "stillHeld": 30000000, "unrealizedPnlUsd": -234.00, "inLp": 5000000, "inLpUsd": 111.00 } ] } ``` Cost basis method: average cost. `stillHeld` is the on-chain wallet balance. `inLp` is the amount locked in LP positions (resolved from DEX `lp` table). `totalSold`/`totalSoldUsd` = direct sells only (LP deposits excluded). `lpSold`/`lpSoldUsd` = tokens deposited into LP (tracked separately for tax exports). `realizedPnlUsd` = profit/loss from direct sells only (LP deposits excluded). `unrealizedPnlUsd = stillHeld * (currentPrice - avgBuyPrice)`. PnL is recomputed every ~50 minutes. Prices update every 5 minutes. #### Trade history (paginated) ``` GET /api/portfolio/youraccount/trades?limit=20&offset=0 GET /api/portfolio/youraccount/trades?type=swap&limit=50 GET /api/portfolio/youraccount/trades?since=1740600000 ``` Query params: - `limit` (default 50, max 200) - `offset` (default 0) - `type` — filter: `swap`, `buy`, `sell`, `claim`, `send`, `addlp`, `removelp` - `since` — unix timestamp, only trades on or after ```json { "trades": [ { "id": 1234, "trxId": "abc123...", "account": "youraccount", "tradeType": "swap", "poolId": 6, "tokenId": null, "symbol": "XPR", "tokenContract": "eosio.token", "amount": 1000000, "amountUsd": 30.00, "feeUsd": 0.09, "timestamp": 1740700000, "blockNum": 123456789, "outputAmount": 45000000, "outputSymbol": "MARSH", "tokenSymbol": "MARSH" } ], "total": 142, "hasMore": true } ``` **Critical: Understanding `tradeType` values for direction:** | `tradeType` | Source | Direction | |-------------|--------|-----------| | `buy` | Bonding curve buy | XPR → Token (on simplelaunch) | | `sell` | Bonding curve sell | Token → XPR (on simplelaunch) | | `swap` | DEX pool trade | **Either direction** — check `symbol` to determine: if `symbol` is `XPR` it's a buy, otherwise it's a sell | | `multihop` | Multi-pool routed swap | Same as `swap` — check `symbol` for direction | | `claim` | Post-graduation claim | Free token issuance | | `send` | Token transfer | Peer-to-peer send | | `addlp` | Add liquidity | LP deposit | | `removelp` | Remove liquidity | LP withdrawal | **Example: finding all DEX sells of AGENT by an account:** ```bash curl -s 'https://indexer.protonnz.com/api/portfolio/someaccount/trades?type=swap&limit=200' \ | jq '[.trades[] | select(.symbol == "AGENT")] | length' # symbol=AGENT means the user SENT Agent → this is a sell # symbol=XPR with outputSymbol=AGENT means the user SENT XPR → this is a buy ``` LP add/remove events are merged into the trade list (as `tradeType: "addlp"` / `"removelp"`) unless you filter by `type`. #### LP events ``` GET /api/portfolio/youraccount/lp-events?limit=20 GET /api/portfolio/youraccount/lp-events?poolId=6 ``` Query params: `limit` (default 50, max 200), `offset` (default 0), `poolId` (filter) ```json { "events": [ { "id": 56, "trxId": "def456...", "account": "youraccount", "eventType": "add", "poolId": 6, "amountA": 1000000, "symbolA": "XPR", "amountB": 45000000, "symbolB": "MARSH", "totalUsd": 60.00, "timestamp": 1740700000, "blockNum": 123456789 } ], "total": 12, "hasMore": false } ``` #### LP fee earnings (estimated) ``` GET /api/portfolio/youraccount/lp-earnings ``` Returns estimated fee earnings for each pool the account has provided liquidity to. Compares current LP value against deposited value and HODL value to estimate fees earned. ```json { "account": "youraccount", "totalDepositedUsd": 500.00, "totalCurrentValueUsd": 520.00, "totalHodlValueUsd": 510.00, "totalLpValueUsd": 520.00, "totalEarningsUsd": 10.00, "totalEstimatedFeesUsd": 15.50, "pools": [ { "poolId": 6, "pair": "XPR/SNIPS", "depositedUsd": 300.00, "withdrawnUsd": 0, "currentValueUsd": 315.00, "hodlValueUsd": 305.00, "earningsUsd": 10.00, "earningsPercent": 3.28, "sharePercent": 12.5, "lpTokens": 50000, "firstDeposit": "2026-02-15T10:00:00Z", "estimatedDailyFees": 0.35, "estimatedFeesEarned": 15.50, "daysInPool": 41 } ] } ``` #### Tax export (CSV) ``` GET /api/portfolio/youraccount/export?format=koinly&year=2026 GET /api/portfolio/youraccount/export?format=csv&year=2026 ``` Returns a CSV file download. Formats: `koinly` (Koinly-compatible labels) or `csv` (standard). **Requires payment.** If not paid, returns HTTP 402 with pricing and payment instructions: ```json { "error": "payment_required", "pricing": { "30_days": { "xpr": "10000.0000 XPR", "xmd": "20.000000 XMD" }, "365_days": { "xpr": "50000.0000 XPR", "xmd": "100.000000 XMD" }, "lifetime": { "xpr": "200000.0000 XPR", "xmd": "400.000000 XMD" } }, "instructions": { "to": "dex.protonnz", "memo": "taxreport:youraccount:DAYS", "example": "taxreport:youraccount:30", "note": "Send XPR (via eosio.token) or XMD (via xmd.token)" } } ``` Includes: all trades, LP events (staked/unstaked), creator fee income, token creation fees (deductible). Historical prices from on-chain snapshots. ### 1.15 Leaderboard ``` GET /api/leaderboard/profit GET /api/leaderboard/profit?limit=10 ``` Top traders by realized PnL: ```json { "traders": [ { "account": "toptrader", "realizedPnlUsd": 5000.00, "volumeUsd": 50000.00, "tradeCount": 500, "profitableCount": 8, "totalTokensTraded": 12, "winRate": 66.67, "lastTrade": 1740700000 } ] } ``` `limit` default 20, max 100. Ordered by realized PnL descending. ### 1.16 All traders ``` GET /api/traders GET /api/traders?sort=volume&order=desc&limit=50&offset=0 GET /api/traders?sort=pnl&min_trades=5 ``` Paginated list of all traders with stats. Sortable and filterable. Query params: - `sort`: `volume` (default), `pnl`, `trades`, `last_trade`, `tokens`, `win_rate` - `order`: `desc` (default), `asc` - `limit`: 1–200, default 50 - `offset`: default 0 - `min_trades`: minimum trade count filter (default 1) ```json { "traders": [ { "account": "sometrader", "tradeCount": 320, "volumeUsd": 45000.00, "realizedPnlUsd": 1200.50, "profitableCount": 6, "totalTokensTraded": 10, "winRate": 60.00, "lastTrade": 1740700000 } ], "total": 1500, "limit": 50, "offset": 0, "hasMore": true } ``` ### 1.17 Burns leaderboard ``` GET /api/leaderboard/burns GET /api/leaderboard/burns?limit=10 ``` Top tokens by percentage of supply burned: ```json { "tokens": [ { "symbol": "DOG", "tokenId": 42, "burned": 565897586, "supply": 733697075, "circulatingSupply": 167799489, "burnPercent": 77.13, "price": 0.0000012, "mcap": 201 } ] } ``` `limit` default 10, max 50. Ordered by burn percentage descending. `burned` = tokens sent to `token.burn` account. `supply` = total minted (includes burned). `circulatingSupply = supply - burned`. Only graduated tokens have burn data. --- ## 2 — Reading on-chain state (no auth required) All reads use the standard EOSIO `get_table_rows` RPC endpoint. ### 2.1 List all pools ```bash curl -s -X POST https://api.protonnz.com/v1/chain/get_table_rows \ -H 'Content-Type: application/json' \ -d '{"json":true,"code":"simpledex","scope":"simpledex","table":"pools","limit":100}' ``` Response fields per row: - `id` — pool ID (use this in swap/liquidity actions) - `tokenAContract`, `tokenASymbol` — first token - `tokenBContract`, `tokenBSymbol` — second token - `reserveA`, `reserveB` — current reserves (raw u64) - `totalLpTokens` — total LP supply - `feeRate` — basis points (30 = 0.3%) - `paused` — 1 if paused ### 2.2 Get a single pool Set `lower_bound` and `upper_bound` to the pool ID: ```bash curl -s -X POST https://api.protonnz.com/v1/chain/get_table_rows \ -d '{"json":true,"code":"simpledex","scope":"simpledex","table":"pools","lower_bound":"1","upper_bound":"1","limit":1}' ``` ### 2.3 Get your LP position Scope is your account name: ```bash curl -s -X POST https://api.protonnz.com/v1/chain/get_table_rows \ -d '{"json":true,"code":"simpledex","scope":"youraccount","table":"lp","limit":100}' ``` ### 2.4 Get token balance ```bash curl -s -X POST https://api.protonnz.com/v1/chain/get_table_rows \ -d '{"json":true,"code":"eosio.token","scope":"youraccount","table":"accounts","limit":100}' ``` For SimpleLaunch tokens use `"code":"simpletoken"`. ### 2.5 List bonding curves ```bash curl -s -X POST https://api.protonnz.com/v1/chain/get_table_rows \ -d '{"json":true,"code":"simplelaunch","scope":"simplelaunch","table":"curves","limit":100}' ``` Response fields per row: - `id` — token ID - `symbol` — e.g. `"4,COOK"` - `creator`, `name`, `description`, `imageUrl` - `virtualXpr`, `virtualTokens` — bonding curve reserves - `realXpr` — actual XPR collected (compare to graduation threshold) - `realTokensSold` - `graduated` — 1 if graduated to DEX - `dexPoolId` — DEX pool ID after graduation ### 2.6 Get launch config and fees ```bash # Graduation parameters curl -s -X POST https://api.protonnz.com/v1/chain/get_table_rows \ -d '{"json":true,"code":"simplelaunch","scope":"simplelaunch","table":"gradparams","limit":1}' # Fee config curl -s -X POST https://api.protonnz.com/v1/chain/get_table_rows \ -d '{"json":true,"code":"simplelaunch","scope":"simplelaunch","table":"fees","limit":1}' ``` ### 2.7 Get your bonding curve holdings ```bash curl -s -X POST https://api.protonnz.com/v1/chain/get_table_rows \ -d '{"json":true,"code":"simplelaunch","scope":"youraccount","table":"holdings","limit":100}' ``` ### 2.8 Contract table quick reference **simpledex tables** (scope: `simpledex` unless noted): | Table | Scope | Key fields | |-------|-------|------------| | `pools` | simpledex | id, tokenAContract, tokenASymbol, tokenBContract, tokenBSymbol, reserveA, reserveB, totalLpTokens, feeRate, paused | | `lp` | *user account* | poolId, lpTokens | | `deposits` | *user account* | poolId, amountA, amountB | | `protofee` | simpledex | enabled, feeShareBps, treasury | | `allowedctrs` | simpledex | contract (whitelisted token contracts) | **simplelaunch tables** (scope: `simplelaunch` unless noted): | Table | Scope | Key fields | |-------|-------|------------| | `curves` | simplelaunch | id, symbol, creator, name, description, imageUrl, virtualXpr, virtualTokens, realXpr, realTokensSold, graduated, dexPoolId, createdAt | | `holdings` | *user account* | tokenId, amount | | `gradparams` | simplelaunch | threshold, cooldownSec, dexFeeRate | | `fees` | simplelaunch | buyFeeBps, sellFeeBps, creatorShareBps | | `antisnipe` | simplelaunch | creatorOnlyPeriodSec, earlyPeriodSec, maxBuyPerTxEarly, maxBuyPerTx | | `community` | simplelaunch | tokenId, description, website, telegram, twitter, discord, bannerUrl | | `config` | simplelaunch | treasury, creationFee | | `blacklist` | simplelaunch | account, blocked | | `hidden` | simplelaunch | tokenId, hide | | `reserved` | simplelaunch | symbol, reserve | ### 2.9 Get protocol fee config ```bash curl -s -X POST https://api.protonnz.com/v1/chain/get_table_rows \ -d '{"json":true,"code":"simpledex","scope":"simpledex","table":"protofee","limit":1}' ``` ### 2.10 Get XPR/USD oracle price ```bash curl -s -X POST https://api.protonnz.com/v1/chain/get_table_rows \ -d '{"json":true,"code":"oracles","scope":"oracles","table":"data","lower_bound":"3","upper_bound":"3","limit":1}' ``` The `aggregate.d_double` field contains the USD price. Feed index 3 = XPR. --- ## 3 — Swap tokens (DEX) Swaps use a two-step deposit-then-execute pattern, but **memo-based swaps do both in a single transfer**. ### 3.1 Memo-based swap (recommended) Transfer the input token to the DEX contract with memo `swap:::`. ```bash # Swap 100 XPR for tokens in pool 1, require at least 950000 raw output proton action eosio.token transfer \ '["youraccount","simpledex","100.0000 XPR","swap:1:950000:true"]' \ youraccount@active ``` Memo format: `swap:POOL_ID:MIN_OUT:IS_TOKEN_A_IN` - `POOL_ID` — integer pool ID - `MIN_OUT` — minimum output amount (raw u64), for slippage protection - `IS_TOKEN_A_IN` — `true` if you're sending tokenA, `false` if tokenB ### 3.2 Calculate expected output Use the constant product formula: ``` amountOut = (reserveOut * amountIn * (10000 - feeRate)) / (reserveIn * 10000 + amountIn * (10000 - feeRate)) ``` Example with pool feeRate=30 (0.3%): - Sending 100.0000 XPR (raw: 1000000) into pool with reserveA=10000000, reserveB=5000000 - amountOut = (5000000 * 1000000 * 9970) / (10000000 * 10000 + 1000000 * 9970) - amountOut = 453636 Set `minAmountOut` to `amountOut * (1 - slippage)`. A 1% slippage tolerance means `minAmountOut = floor(453636 * 0.99) = 449099`. ### 3.3 Multi-hop swap For tokens without a direct pool, route through intermediate pools: ```bash # Deposit input token first proton action eosio.token transfer \ '["youraccount","simpledex","100.0000 XPR","deposit:1"]' \ youraccount@active # Execute multi-hop: pool 1 -> pool 3, minimum final output 9000 proton action simpledex multihopswap \ '["youraccount",[1,3],9000,[true,true]]' \ youraccount@active ``` Maximum 4 pools per multi-hop. Each `isTokenAIns` entry indicates whether the input to that hop is tokenA of that pool. ### 3.4 Limits - **Max swap size**: 50% of the input reserve per transaction - **Cooldown**: 1 second between `swap`/`multihopswap` actions (anti-MEV) - **Fee**: 0.3% default (configurable per pool), built into the constant product formula - **Fee distribution**: The 0.3% fee stays in pool reserves (growing LP token value). Then 50% of the fee is deducted from the swap output and sent to treasury (`dex.protonnz`). The remaining 50% stays in reserves as LP earnings. **Note**: Memo-based swaps via `onTransfer` are intentionally NOT rate-limited, allowing bots to chain multiple memo swaps in one tx. --- ## 4 — Liquidity (DEX) ### 4.1 Add liquidity Deposit both tokens, then execute: ```bash # Deposit token A proton action eosio.token transfer \ '["youraccount","simpledex","100.0000 XPR","deposit:1"]' \ youraccount@active # Deposit token B proton action simpletoken transfer \ '["youraccount","simpledex","50000.0000 COOK","deposit:1"]' \ youraccount@active # Execute — minLpTokens for slippage protection proton action simpledex execaddliq \ '["youraccount",1,0]' \ youraccount@active ``` First deposit to a pool locks 1000 LP tokens permanently (minimum liquidity). **Critical: Always bundle all 3 actions in a single atomic transaction.** If you send deposit transfers without `execaddliq`, or if `execaddliq` fails, stale deposits pile up. Maximum 10 pending deposits per account — after that, all liquidity operations are blocked until deposits are cleaned or withdrawn. ### 4.1.1 Clean stale deposits Deposits expire after 24 hours. Anyone can clean expired deposits (no auth required): ```bash # Clean up to 10 expired deposits for an account proton action simpledex cleandeposit \ '["targetaccount",10]' \ anyaccount@active ``` To withdraw non-expired deposits manually (requires account auth): ```bash # Withdraw a specific deposit by ID proton action simpledex withdrawdep \ '["youraccount",DEPOSIT_ID]' \ youraccount@active ``` Check pending deposits: ```bash proton table simpledex youraccount deposits ``` ### 4.2 Remove liquidity ```bash # Remove all LP tokens, accept any output amounts proton action simpledex remliquidity \ '["youraccount",1,500000,0,0]' \ youraccount@active ``` Parameters: `[account, poolId, lpTokens, minAmountA, minAmountB]` ### 4.3 LP token calculation ``` First add: lpTokens = sqrt(amountA * amountB) - 1000 Later adds: lpTokens = min(amountA * totalLP / reserveA, amountB * totalLP / reserveB) Withdrawal: amountA = lpTokens * reserveA / totalLP amountB = lpTokens * reserveB / totalLP ``` LP tokens are raw u64 integers (no decimals). **How LPs earn fees**: The full 0.3% swap fee stays in pool reserves, growing LP token value over time. The protocol fee (50% of the fee amount) is an additional deduction from the swap output — LPs keep the full 0.3%. When you withdraw, you get back more tokens than you deposited. There are no separate fee claim actions — earnings are realized on remove liquidity. --- ## 5 — Token launch (bonding curve) ### 5.1 Buy tokens on a curve Transfer XPR with memo `buy:`: ```bash # Buy tokens for token ID 1, require at least 5000000 raw tokens out proton action eosio.token transfer \ '["youraccount","simplelaunch","10.0000 XPR","buy:1:5000000"]' \ youraccount@active ``` Memo format: `buy:TOKEN_ID[:MIN_TOKENS_OUT]` ### 5.2 Sell tokens on a curve ```bash proton action simplelaunch sell \ '["youraccount",1,5000000,8000]' \ youraccount@active ``` Parameters: `[seller, tokenId, tokenAmount, minXprOut]` ### 5.3 Bonding curve price formula ``` Buy: tokensOut = (virtualTokens * xprAfterFee) / (virtualXpr + xprAfterFee) Sell: xprOut = (virtualXpr * tokensIn) / (virtualTokens + tokensIn) then subtract sell fee Buy fee: 1% default (deducted from XPR input before curve calc) Sell fee: 1% default (deducted from XPR output after curve calc) ``` ### 5.4 Graduation When `realXpr >= graduation threshold`, anyone can trigger graduation: ```bash proton action simplelaunch graduate '[1]' youraccount@active ``` This atomically: 1. Creates a DEX pool for the token 2. Seeds it with the collected XPR and 20% of total supply 3. Marks the curve as graduated After graduation, if you bought tokens on the curve, claim them as real tokens: ```bash proton action simplelaunch claim '["youraccount",1]' youraccount@active ``` Then trade on the DEX pool. **Note**: Token creators do NOT receive any token allocation. Creators earn 50% of trading fees while the token is on the bonding curve, but if they want to hold tokens they must buy on the curve like everyone else. ### 5.5 Anti-snipe protection New token launches have time-gated buying restrictions to prevent sniping: | Phase | Duration | Rule | |-------|----------|------| | Creator-only | 0–60s after creation | Only the token creator can buy | | Early bird (capped) | 60–360s after creation | Anyone can buy, max 5,000 XPR per transaction | | Open trading | 360s+ | No restrictions | The `createdAt` field on the curve record (unix timestamp) determines timing. Agents should check `createdAt` before attempting to buy a newly launched token. Read the current anti-snipe config: ```bash curl -s -X POST https://api.protonnz.com/v1/chain/get_table_rows \ -d '{"json":true,"code":"simplelaunch","scope":"simplelaunch","table":"antisnipe","limit":1}' ``` Response fields: - `creatorOnlyPeriodSec` — seconds only creator can buy (default 60) - `earlyPeriodSec` — seconds of capped buying after creator period (default 300) - `maxBuyPerTxEarly` — max XPR per buy during early period (raw, default 50000000 = 5,000 XPR) - `maxBuyPerTx` — global max per buy, 0 = no cap ### 5.6 Launch constants | Parameter | Value | |-----------|-------| | Max supply per token | 1,000,000,000 (1B) | | Curve allocation | 80% (800M tokens) | | DEX allocation | 20% (200M tokens) | | Graduation threshold | 50,000 XPR | | Buy fee | 1% | | Sell fee | 1% | | Creator fee share | 50% of fees | | Protocol fee share | 50% of fees to treasury | | Graduation cooldown | 5 minutes | | Creation fee | 40,000 XPR | | Creator-only window | 60 seconds | | Early bird cap | 5,000 XPR per tx for 5 minutes | ### 5.7 Token supply model Tokens on the bonding curve are **virtual** — no tokens are minted until graduation. At graduation, only the 200M DEX allocation is minted. Users then call `claim` to mint their individual holdings as real tokens. **Unsold curve tokens are never minted.** There is no admin mint function. The `simpletoken` contract only allows `simplelaunch` (the issuer) to call `issue()`, and `simplelaunch` only issues during `graduate` and `claim`. Once all holders have claimed, the remaining max_supply gap is permanently inaccessible. Most graduated tokens have a circulating supply of 700-750M (not 1B). Market cap is calculated as `price × circulatingSupply` (supply minus tokens sent to `token.burn`). Non-graduated tokens use FDV (`price × 1B`). **Burns:** Users can send tokens to the `token.burn` account. This does not call `retire()` (which requires issuer auth) — it simply removes tokens from circulation. The `burned` field in API responses reflects the `token.burn` account balance. To check supply on-chain: ```bash # Token supply (stat table scoped by symbol) curl -s -X POST https://api.protonnz.com/v1/chain/get_table_rows \ -d '{"json":true,"code":"simpletoken","scope":"MARSH","table":"stat","limit":1}' # Burn balances (all tokens in one call) curl -s -X POST https://api.protonnz.com/v1/chain/get_table_rows \ -d '{"json":true,"code":"simpletoken","scope":"token.burn","table":"accounts","limit":200}' ``` --- ## 6 — Create a new token ```bash # Step 1: deposit creation fee (40,000 XPR) proton action eosio.token transfer \ '["youraccount","simplelaunch","10000.0000 XPR","create"]' \ youraccount@active # Step 2: create the token proton action simplelaunch createtoken \ '["youraccount","4,MYTOKEN","My Token","A great token","https://your-ipfs-gateway/ipfs/QmHash"]' \ youraccount@active ``` - Symbol format: `"PRECISION,NAME"` e.g. `"4,MYTOKEN"` (max 7 chars, uppercase A-Z) - Creation fee: 40,000 XPR (returned if creation fails) - Image URL: must be IPFS. Upload via Pinata, Infura, or web3.storage. ### 6.1 Upload a token image to IPFS Before creating your token, upload a logo image to IPFS. You can use any IPFS pinning service (Pinata, Infura, web3.storage, etc). Example using Pinata: ```bash # Upload image to IPFS via Pinata curl -s -X POST https://api.pinata.cloud/pinning/pinFileToIPFS \ -H "Authorization: Bearer YOUR_PINATA_JWT" \ -F "file=@logo.png" \ | jq -r '"https://gateway.pinata.cloud/ipfs/" + .IpfsHash' ``` Or using the IPFS CLI: ```bash ipfs add logo.png # Returns: added QmHash logo.png # Use: https://ipfs.io/ipfs/QmHash ``` Recommended: square PNG or SVG, 256x256px or larger. The frontend displays logos at 40-64px with circular crop. **Important**: Use a dedicated IPFS pinning service for reliable image loading. Arbitrary URLs are not accepted — only IPFS uploads. ### 6.2 Update token metadata The token creator can update the name, description, and image URL at any time: ```bash proton action simplelaunch updatetoken \ '[2,"Charlie","An AI agent memecoin","https://gateway.pinata.cloud/ipfs/QmNewHash"]' \ charliebot@active ``` Parameters: `[tokenId, name, description, imageUrl]` Authorization: must be signed by the token's original creator account. ### 6.3 Community profiles (graduated tokens only) Graduated tokens can have a community profile with banner image, description, and social links. Data is stored on-chain in the `community` table. **Read a community profile:** ```bash curl -s -X POST https://api.protonnz.com/v1/chain/get_table_rows \ -d '{"json":true,"code":"simplelaunch","scope":"simplelaunch","table":"community","lower_bound":"TOKEN_ID","upper_bound":"TOKEN_ID","limit":1}' ``` Response fields: - `tokenId` — token ID (primary key) - `description` — up to 280 characters - `website`, `telegram`, `twitter`, `discord` — social links (up to 100 chars each) - `bannerUrl` — banner image URL (up to 150 chars, IPFS recommended) - `updatedAt` — unix timestamp of last update - `updatedBy` — account that last updated **Set community profile (creator only, free):** ```bash proton action simplelaunch setcommunit \ '[TOKEN_ID,"Token description","https://website.com","https://t.me/group","https://x.com/handle","https://discord.gg/invite","ipfs://QmBannerHash"]' \ creatoraccount@active ``` Parameters: `[tokenId, description, website, telegram, twitter, discord, bannerUrl]` **Update community profile (anyone, costs 100,000 XPR):** Non-creators can update a community profile by first depositing 100,000 XPR: ```bash # Step 1: deposit fee proton action eosio.token transfer \ '["youraccount","simplelaunch","100000.0000 XPR","community:TOKEN_ID"]' \ youraccount@active # Step 2: update profile proton action simplelaunch updcommunit \ '["youraccount",TOKEN_ID,"New description","https://website.com","","","",""]' \ youraccount@active ``` The 100,000 XPR fee goes to the protocol treasury (`dex.protonnz`). Creators should use `setcommunit` instead (free, no deposit needed). --- ## 7 — Protocol parameters | Parameter | Value | Description | |-----------|-------|-------------| | DEX swap fee | 0.3% (30 bps) | Built into AMM formula (fee stays in reserves) | | Protocol fee share | 50% (5000 bps) | Deducted from swap output, sent to treasury | | LP fee share | 50% | Accrues in pool reserves, increasing LP token value | | Max swap ratio | 50% | Single swap limited to 50% of reserves | | Minimum liquidity | 1,000 | Locked on first LP deposit | | Deposit expiry | 24 hours | Pending deposits expire after this | | Max multi-hop | 4 pools | Maximum route length | | Swap cooldown | 1 second | Per-account rate limit | | Launch buy fee | 1% (100 bps) | Fee on bonding curve buys | | Launch sell fee | 1% (100 bps) | Fee on bonding curve sells | | Creator fee share | 50% | Of launch buy/sell fees | | Graduation threshold | 50,000 XPR | XPR needed to graduate | | Creation fee | 40,000 XPR | To launch a new token | | Token precision | 4 decimals | All amounts are x10000 | --- ## 8 — Useful patterns for agents ### Get token price from the API (easiest) ```bash curl -s https://indexer.protonnz.com/api/prices \ | jq '.COOK' ``` ### Get platform overview ```bash curl -s https://indexer.protonnz.com/api/stats ``` ### Find the best pool to trade ```bash curl -s https://indexer.protonnz.com/api/pools \ | jq '.pools | sort_by(-.tvlUsd) | .[0:5] | .[] | {poolId, pair: (.tokenA.symbol + "/" + .tokenB.symbol), tvlUsd}' ``` ### Price check before swap (on-chain) ```bash # Get pool state POOL=$(curl -s -X POST https://api.protonnz.com/v1/chain/get_table_rows \ -d '{"json":true,"code":"simpledex","scope":"simpledex","table":"pools","lower_bound":"1","upper_bound":"1","limit":1}') # Extract reserves (use jq) RESERVE_A=$(echo $POOL | jq -r '.rows[0].reserveA') RESERVE_B=$(echo $POOL | jq -r '.rows[0].reserveB') FEE=$(echo $POOL | jq -r '.rows[0].feeRate') # Spot price: tokenB per tokenA echo "scale=8; $RESERVE_B / $RESERVE_A" | bc ``` ### Check if a token is graduated ```bash CURVE=$(curl -s -X POST https://api.protonnz.com/v1/chain/get_table_rows \ -d '{"json":true,"code":"simplelaunch","scope":"simplelaunch","table":"curves","lower_bound":"1","upper_bound":"1","limit":1}') GRADUATED=$(echo $CURVE | jq -r '.rows[0].graduated') POOL_ID=$(echo $CURVE | jq -r '.rows[0].dexPoolId') if [ "$GRADUATED" = "1" ]; then echo "Token graduated — trade on DEX pool $POOL_ID" else REAL_XPR=$(echo $CURVE | jq -r '.rows[0].realXpr') echo "Still on curve — $REAL_XPR / 500000000 XPR collected" fi ``` ### Monitor graduation progress ```bash curl -s -X POST https://api.protonnz.com/v1/chain/get_table_rows \ -d '{"json":true,"code":"simplelaunch","scope":"simplelaunch","table":"curves","limit":100}' \ | jq '.rows[] | select(.graduated == 0) | {id, name: .name, progress: ((.realXpr / 5000000) | tostring + "%")}' ``` ### Get recent graduations from the API ```bash curl -s https://indexer.protonnz.com/api/events \ | jq '.graduations' ``` ### Check liquidity depth before large trade ```bash # How much can you trade with <5% price impact? curl -s https://indexer.protonnz.com/api/pools?poolId=6 \ | jq '.pools[0] | {pair: (.tokenA.symbol + "/" + .tokenB.symbol), depth5pct, depth10pct}' ``` ### Top tokens by supply burned ```bash curl -s https://indexer.protonnz.com/api/leaderboard/burns?limit=10 \ | jq '.tokens[] | {symbol, burnPercent, burned, circulatingSupply}' ``` ### Check your P&L and holdings ```bash # Am I profitable? What do I hold? curl -s https://indexer.protonnz.com/api/portfolio/youraccount/pnl \ | jq '{realizedPnl: .summary.totalRealizedPnlUsd, winRate: .summary.winRate, tokens: [.tokens[] | {symbol, realizedPnlUsd, stillHeld, unrealizedPnlUsd, inLp}]}' ``` ### Get your recent trades ```bash curl -s 'https://indexer.protonnz.com/api/portfolio/youraccount/trades?limit=10' \ | jq '.trades[] | {type: .tradeType, symbol, amount, outputSymbol, outputAmount, usd: .amountUsd}' ``` ### Check your trading volume and fees paid ```bash curl -s https://indexer.protonnz.com/api/portfolio/youraccount \ | jq '{volume: .summary.totalVolumeUsd, fees: .summary.totalFeesPaidUsd, trades: .summary.totalTrades, byType: .byType}' ``` ### See top traders (leaderboard) ```bash curl -s 'https://indexer.protonnz.com/api/leaderboard/profit?limit=5' \ | jq '.traders[] | {account, pnl: .realizedPnlUsd, winRate, volume: .volumeUsd}' ``` --- ## 9 — Error handling | Error message | Cause | Fix | |--------------|-------|-----| | `Swap amount exceeds maximum` | Input > 50% of reserve | Split into smaller swaps | | `Slippage exceeded` | Price moved past minAmountOut | Increase slippage tolerance or retry | | `Pool is paused` | Admin paused pool | Wait or use a different pool | | `Insufficient balance` | Not enough tokens | Check balance first | | `Token must be graduated` | Trying DEX ops on curve token | Use buy/sell actions on launch contract instead | | `Swap cooldown active` | < 1 second since last swap action | Wait 1 second between swap/multihopswap calls | | `Deposit expired` | Deposit older than 24 hours | Withdraw and re-deposit | | `Maximum 10 deposits per user` | 10 stale deposits from incomplete add-liquidity attempts | Run `cleandeposit` to clear expired deposits, or `withdrawdep` for non-expired ones (see §4.1.1) | | `Pool already exists for this token pair` | Pair already has a pool | Use existing pool | | `insufficient ram` | Account needs more RAM to hold new token balances | Buy RAM via `eosio::buyrambytes` or at resources.xprnetwork.org/storage | --- ## 10 — About SimpleDEX is built by [Proton NZ](https://protonnz.com) on [XPR Network](https://xprnetwork.org). Smart contracts are open source. The protocol is non-custodial — no one can access your funds except through on-chain actions signed by your private key. Security: Audited Feb & Mar 2026. Guardian monitoring with auto-pause on anomalous pool drains. Circuit breakers and timelocked admin changes. 23 reserved symbols prevent impersonation of ecosystem tokens (XPR, METAL, SNIPS, all wrapped xtokens). Creator blacklist for moderation. Report bugs or security issues: https://protonnz.com