The web app
Next.js 16 (App Router), bun, dev server on port 3000 (the Privy app allows only
localhost:3000 and the production domain). The app runs on Solana mainnet only; the RPC
URL is MAINNET_RPC_URL in .env.local (or the host's environment).
Transaction flow
- The client asks a server route to build a transaction for the connected wallet.
- The server builds it with the vendored tender SDK (
app/lib/tender, synced from~/tender/sdk/solanabyscripts/sync-tender-sdk.sh), simulates it, and returns base64. Where a fresh keypair must sign (a new NFT asset, a new mint), the server pre-signs. - The wallet signs (Privy
useSignTransaction, chainsolana:mainnet). - The client posts the signed transaction to a send route; the server relays and polls to confirmation.
Routes
| Route | Purpose |
|---|---|
GET /api/options/[mint] | options view: call ladder quotes per tenor, puts, balances, positions, listings |
POST /api/options/[mint]/tx | build: buyCall (tenor, amount, strikeMcapUsd), buyPut, exercise, cashOut, claim, deposit, withdraw, collect, listCall, updateListing, cancelListing, buyListing |
POST /api/options/send | relay |
GET /api/launch/build | stake options table (5% to 10% in 0.5% steps) from the pump.fun curve |
POST /api/launch/build | setup: backer pool and lookup table transactions for the mint (fresh from the browser, or "reserved" for the owner's server-held mint, owner wallet only), stake and dev-buy caps |
POST /api/launch/send | relay one setup transaction (waits a slot after the last so the table is live) |
POST /api/launch/final | the launch, open calls and the dev buy on a fresh blockhash (Jito tip included) |
POST /api/launch/bundle | send the three wallet-signed transactions: the launch first with preflight, then open calls and the dev buy (a Jito bundle only with POC_USE_JITO=1); waits for the launch |
POST /api/launch/metadata | upload image + metadata to pump.fun IPFS; the launch is refused if it fails |
GET /api/portfolio/[owner] | every position across all vaults with value, paid, P/L |
GET /api/markets | Established Tokens and every launch vault |
GET /api/vault/[mint] | put and launch vault state |
GET /api/swap/[mint], POST /api/swap/[mint], POST /api/swap/send | spot quotes and swaps: Jupiter; a coin Jupiter has not indexed yet trades on its pump.fun curve or PumpSwap pool |
GET /api/token/[mint]/activity, /candles | trades, holders, chart candles |
Metadata on IPFS
/api/launch/metadata forwards the image, name, symbol, description and links to
https://pump.fun/api/ipfs and puts the returned metadataUri on chain. The launch is refused
if the upload fails. Because ipfs.io no longer serves files to applications, the app
reads any /ipfs/<cid> link through pump.mypinata.cloud (ipfsGateway in
app/lib/options/server.ts).
Pages
/markets (Explore), /t/[mint] (token page with chart and options terminal), /launch,
/portfolio, /vault/[mint], /vaults, /marketplace, /watchlist. Shared header
(AppHeader, links from app/lib/site-nav.json): Docs, Twitter, Explore, Launch, Vaults,
Marketplace, Portfolio.
Data sources
Token data: POC API, then Dexscreener, then chain reads. GeckoTerminal serves candles for tokens not launched on POC (throttled).
Trade indexer (coins launched on POC)
Every coin launched on POC has its chart, trades tab and 24h figures from POC's own indexer
(app/lib/indexer/), not from any third party:
- What it reads: each swap on the coin's pump.fun bonding curve (the curve's token balance and its SOL) and, after graduation, on its PumpSwap pool (the pool's token and wSOL balances), straight from Solana transactions. Creation, migration and fee moves are not swaps and are skipped.
- Which coins: every launch vault of the launch program, found automatically every minute, with its whole history from the launch transaction on.
- Live: a background loop in the web server (
instrumentation.ts) reads new transactions every 3 seconds. - Storage: SQLite on the server at
.data/indexer.sqlite(one row per swap). - Dollars: each trade is priced with SOL/USD at its own minute (Coinbase SOL-USD 1-minute closes, Binance SOLUSDC as a fallback), stored alongside.
- Hosting: its own service at
https://indexer.poc.money(a small Ubuntu server; systemd restarts it on crash and reboot, Caddy serves HTTPS). The site on Vercel reads it throughINDEXER_URL. Deploy or update:SERVER=root@<ip> SSH_KEY=~/.ssh/poc_indexer DOMAIN=indexer.poc.money bash indexer/deploy/deploy.sh. Caches with last-good fallback protect against RPC rate limits.
