Skip to main content

App & API Architecture - Prototype

1. Summary

This document outlines the pages and API endpoints for the initial prototype of the perpetual futures exchange.


2. App Pages

ElementDescription
LogoLink to home/trade
LinksTrade, Portfolio, Vaults, Leaderboard
WalletConnect wallet / Wallet Dropdown

Wallet Dropdown

OptionDescription
AccountsList all trading accounts (primary + sub-accounts) with balance, allow switching
Deposit / WithdrawQuick access to deposit/withdraw margin
TransferTransfer between trading accounts
DisconnectDisconnect wallet

3. Pages

Trade Page

Market Stats Bar

Displayed at the top of the trade page for the active market.

FieldSource
Mark PriceFundingState PDA (oracle-derived) - used for PnL and liquidations
Oracle PricePyth oracle - raw spot price
24H Change %Computed from mark price 24h ago vs now
24H Volumemarket_stats DB (sum of last 24 hourly buckets)
Open Interestmarket_stats DB (latest row)
Funding RateFundingState PDA - current rate and next settlement time

Components

ComponentDescription
Market SelectorSwitch between the available markets without navigating away
ChartCandlestick chart built from fill prices (candles table). Displays mark price line overlay
Order BookLive bids and asks from the OrderBook slab via WebSocket
Recent TradesLast fills from trades table via WebSocket
Order FormPlace market or limit orders (see below)
User TabsOpen Orders, Positions, Trade History, Funding History

Order Form

FieldDescription
DirectionLong / Short
Order TypeMarket / Limit
LeverageSlider (1x-max market leverage)
SizePosition size in USDC
PriceLimit price (limit orders only)
PreviewEstimated liquidation price, margin required, order value, fees

The order form calls the API to build an unsigned transaction. The API fetches a fresh Pyth VAA and constructs the place_order instruction. The VAA is required by the on-chain program to validate that the user has sufficient margin at the current mark price - it does not affect the fill price, which is always the best available order book price. The client signs and submits to Solana RPC directly.

User Tabs

TabSource
Open OrdersOrderBook slab on-chain (via WebSocket orders:{wallet})
PositionsPosition PDAs on-chain (via WebSocket positions:{wallet})
Trade Historypositions_history DB (closed + liquidated positions)
Funding Historypositions_history.funding_paid DB

Portfolio Page

ComponentDescription
SummaryTotal equity, available margin, margin used, unrealized PnL, 30D PnL
PositionsAll open positions across all markets (from chain)
Open OrdersAll open limit orders (from chain)
Trade Historypositions_history - closed and liquidated positions
Funding Historypositions_history.funding_paid per position
Deposits / Withdrawalsaccount_transactions - deposit, withdrawal, transfer records

Vaults Page

ComponentDescription
Vault ListAll active vaults with: name, TVL, share price (NAV), 30D return, performance fee, lockup period

Vault Detail Page

ComponentSource
Vault InfoName, description, manager, performance fee, lockup period - from Vault PDA + vaults.description
Share Price Chartvault_stats.nav_per_share over time
TVL Chartvault_stats.tvl over time
Open PositionsPosition PDAs on-chain for the vault's UserAccount
Trade Historypositions_history for vault account
Transactionsvault_transactions - deposits and withdrawals
Deposit / WithdrawBuilds unsigned vault_deposit / vault_withdraw transaction, client signs
Vault Activity LogAdmin events (freeze, unfreeze, deprecate) - read from on-chain transaction history

Leaderboard Page

ComponentDescription
TableRank, trader address, equity, all-time PnL, realized PnL, volume, trades, funding, win rate, Sharpe, max drawdown
SortDefault by all-time PnL descending. Client-side sort on other columns

Data source: leaderboard_stats table - precomputed every X minutes by the Indexer.


Faucet Page (devnet only)

ComponentDescription
ClaimRequest test USDC. One claim per wallet per 24h

4. API Endpoints

Auth

EndpointMethodAuthDescription
/auth/verifyPOST-Verify wallet signature, receive 24h JWT

Body: { address, message, signature } - message is a signed timestamp string, signature is base58 tweetnacl ed25519.


Market Data

EndpointMethodAuthDescription
/marketsGET-List all markets (identity only - symbol, address). Live config (fees, margin %, status) served from Redis/RPC
/candlesGET-OHLCV candles from candles table. Query: symbol, interval, limit, start_at, end_at
/markets/:symbol/tradesGET-Recent fills from trades table
/markets/:symbol/orderbookGET-Current order book snapshot (from Redis/RPC)

Orders

EndpointMethodAuthDescription
/ordersPOSTBearerBuild unsigned place_order transaction (fetches Pyth VAA, returns serialized tx for client to sign)
/orders/:id/cancelPOSTBearerBuild unsigned cancel_order transaction
/ordersGETBearerOrder history from orders_history (filled + canceled). Query: status, market, limit, cursor

Open orders are not stored in the DB. They come via the orders:{wallet} WebSocket channel.


Positions

EndpointMethodAuthDescription
/positionsGETBearerOpen positions - read from Position PDAs on-chain (via Redis cache)
/positions/historyGETBearerClosed + liquidated positions from positions_history DB

Account

EndpointMethodAuthDescription
/accountsGETBearerList all accounts for the wallet (primary + sub-accounts)
/accountsPOSTBearerCreate a new sub-account (DB row only - PDA initialized on first on-chain use)
/accounts/:idGETBearerGet account details
/accounts/:idPATCHBearerUpdate account name
/accounts/depositPOSTBearerBuild unsigned deposit_margin transaction
/accounts/withdrawPOSTBearerBuild unsigned withdraw_margin transaction
/accounts/transferPOSTBearerBuild unsigned transfer_margin transaction
/accounts/:id/transactionsGETBearerDeposit/withdrawal/transfer history from account_transactions
/accounts/:id/snapshotsGETBearerEquity + PnL snapshots from account_snapshots (for portfolio chart)

Leaderboard

EndpointMethodAuthDescription
/leaderboardGET-All rows from leaderboard_stats. Query: sort (column name, default all_time_pnl), order (asc/desc, default desc), search (wallet address prefix). Public

Vaults

EndpointMethodAuthDescription
/vaultsGET-List all vaults (identity from DB, live state from Redis/RPC)
/vaults/:idGET-Vault details
/vaults/:id/positionsGET-Open positions for the vault's account (from chain)
/vaults/:id/historyGET-positions_history for vault account
/vaults/:id/transactionsGET-vault_transactions - deposits and withdrawals
/vaults/:id/statsGET-vault_stats for TVL + share price chart
/vaults/:id/depositPOSTBearerBuild unsigned vault_deposit transaction
/vaults/:id/withdrawPOSTBearerBuild unsigned vault_withdraw transaction
/vaults/:id/descriptionPATCHBearerUpdate vault description (manager only, stored in DB)

Faucet

EndpointMethodAuthDescription
/faucet/claimPOSTBearerAirdrop test USDC to wallet. Devnet only. Rate limited per wallet (24h cooldown)

5. Real-time (WebSocket)

Channels

ChannelTriggerSubscriber
market:{symbol}Oracle price update (1s)Trade page - mark price, oracle price
candles:{symbol}:{interval}New fill (candle upsert)Trade page - chart updates
orderbook:{symbol}Order placed / filled / canceledTrade page - order book
trades:{symbol}Fill eventTrade page - recent trades
account:{id}Balance changeWallet dropdown, portfolio
positions:{wallet}Position opened / closed / liquidatedUser tabs, portfolio
orders:{wallet}Order placed / filled / canceledUser tabs
vault:{id}Vault TVL / NAV updateVault list, vault detail

Page → Channel mapping

EventAction
Trade page loadSubscribe market:{symbol}, orderbook:{symbol}, trades:{symbol}, candles:{symbol}:{interval}
Switch marketUnsubscribe old market channels, subscribe new
Change chart intervalUnsubscribe candles:{old}, subscribe candles:{new}
Wallet connectedSubscribe account:{id}, positions:{wallet}, orders:{wallet}
Vault detail openSubscribe vault:{id}