How the platform is built, how the paper engine works, and how funded capital is kept separate.
A single Next.js application serves the UI and API. All authoritative state lives in Postgres; the browser renders what the server returns.
browser ──► Next.js (RSC + client components)
│
├─► /api/* route handlers ──► Prisma ──► PostgreSQL
├─► market data provider (mock in DEMO)
└─► Solana RPC (wallet auth, token metrics)POST /api/paper/orders
{
"symbol": "BONK",
"side": "buy",
"type": "market", // or "limit" with "limitPrice"
"qty": 2500000,
"stopLoss": 0.0000182 // optional
}
// 200 → { account: PaperAccount, trade?: Trade }
// 422 → { error: "Insufficient paper balance" }Funded accounts are a distinct model (FundedAccount, kind: "funded") in separate tables, with their own execution path and risk engine. A paper account can never be promoted in place, and no simulated balance is ever converted to real funds.
type FundedStatus = "PENDING" | "ACTIVE" | "WARNING" | "PAUSED" | "REVOKED";
// risk engine (server) — evaluated on every fill and price tick
if (dailyLoss >= limits.daily || drawdown >= limits.total) status = "PAUSED";
else if (maxUsage >= 0.75) status = "WARNING";/api/market/tokensToken list with price, 24h change, liquidity and volume.
/api/market/candles?symbol=&tf=OHLCV candles for a symbol and timeframe (1m…1D).
/api/leaderboard?period=Ranked traders for 24H / 7D / 30D / ALL. Demo data in DEMO mode.
/api/programProgram config: requirements, funding tiers, risk rules, fees.
/api/auth/nonceOne-time nonce for wallet sign-in.
/api/auth/verifyVerifies the signed nonce and issues a session cookie.
/api/paper/ordersPlaces a paper order; server prices, fills and persists it. Session
/api/token/metrics$FUNDER metrics. Returns isSample: true until on-chain data is wired.
// GET /api/program
{
"requirements": { "minReturnPct": 100, "maxDrawdownPct": 80, "minTradingDays": 3, "minConsistency": 70 },
"tiers": [{ "id": "t1", "allocation": 10000, "label": "Starter", ... }],
"risk": { "maxDailyLossPct": 5, "maxTotalDrawdownPct": 10, ... }
}Only NEXT_PUBLIC_* flags ship to the browser. API keys and DB credentials stay server-side.
DATABASE_URL, provider keys and session secrets are injected per environment, never committed.
Users sign a one-time nonce; no transaction is requested and no private key ever leaves the wallet.
Auth, order and market endpoints are rate-limited per IP and per wallet.
Fills, balances, PnL and risk metrics are computed on the server from its own price feed.
Admin actions require role checks and are audit-logged.
# .env (server only — never prefix secrets with NEXT_PUBLIC_)
DATABASE_URL=postgresql://...
SESSION_SECRET=...
MARKET_PROVIDER_API_KEY=...
NEXT_PUBLIC_DEMO_MODE=true