The first permissionless social perpetuals exchange on Solana. Copy the best traders, auto-mirror their positions, trade with up to 50Γ leverage β no KYC, no intermediaries.
Live: liqour-fi.vercel.app Β |Β API: liqour-fi.onrender.com
| Existing Product | What it does | What it lacks |
|---|---|---|
| Drift / Zeta Markets | On-chain perps on Solana | Zero social layer |
| eToro | Copy trading | Centralized, KYC, no Solana |
| Liqour | Perps + Copy trading | Nothing β this is the gap |
Liqour combines GMX-style perpetual trading with eToro-style social copy trading, built on a Rust backend with a Tokio multi-threaded matching engine. Traders can go long or short on SOL, BTC, and ETH at up to 50Γ leverage, follow top performers on the leaderboard, and auto-mirror their positions atomically.
Pyth Oracle (1s polling)
β
βΌ
Rust Engine (Tokio) βββββ mpsc channel βββββ HTTP Handlers (Axum)
BTreeMap orderbook β
Balance HashMap βΌ
Position HashMap NeonDB (PostgreSQL)
Copy trade logic users, orders, fills, positions,
Liquidation checker follows, trader_stats
β
βΌ broadcast channel
WebSocket clients (per-connection)
price:{market} Β· orderbook:{market}
positions Β· leaderboard
β
βΌ
Next.js 14 Frontend
TradingView Lightweight Charts Β· Phantom Wallet Connect
Leaderboard + Copy Trade UI Β· Live Portfolio
Design decisions:
- Stateless backend β crash-safe, horizontally scalable
- In-memory engine β sub-millisecond order matching
- DB + snapshot recovery β no data loss on restart
- WebSocket fan-out β zero polling on the frontend
| Layer | Tech | Why |
|---|---|---|
| Backend language | Rust | 10β100Γ faster than JS, memory safe, zero GC pauses |
| Web framework | Axum 0.7 | Tower-based, async, built-in WebSocket support |
| Async runtime | Tokio | Same runtime powering Solana itself |
| Database | PostgreSQL (NeonDB) | Serverless Postgres, free tier, auto-scales |
| ORM | SQLx | Async, compile-time checked queries |
| Price feed | Pyth Network | Solana-native oracle, sub-second updates, free |
| Frontend | Next.js 14 + TypeScript | App Router, SSR, full type safety |
| Styling | Tailwind CSS | Binance dark theme, utility-first |
| Charts | Lightweight Charts | TradingView library, free, no API key |
| Wallet | Solana Wallet Adapter | Phantom + Backpack support |
| Auth | JWT + ed25519-dalek | Stateless JWT on Solana's signature curve β no passwords |
| Concurrency | Tokio broadcast + mpsc | Fan-out to WS clients + engine commands |
- Full Axum server β all routes implemented
- JWT auth via Solana wallet signature (ed25519) β no passwords, no KYC
- NeonDB schema β 9 tables (users, orders, fills, positions, follows, trader_stats, markets, candles, snapshots)
- In-memory BTreeMap orderbook β O(log n) price-time priority matching
- Limit order + market order matching with best-price sweep
- Copy trade engine β atomic inside engine task (zero race conditions)
- Liquidation checker fires on every Pyth price update
- Crash recovery β full state loaded from DB on startup
- Snapshot scheduler every 5 minutes
- Pyth price feed β 1s polling, OHLCV candle builder
- WebSocket server with per-channel subscriptions
- Trader stats (PnL, win rate, follower count, volume)
- Paper money β 1000 USDC on signup for instant demo
- Postman collection for all 22 endpoints
- Binance-style dark UI with brand color system
- Phantom wallet animated connect flow (3 steps: choose β sign β done)
- Markets overview β live Pyth prices
- Full trading page β chart + live orderbook + order form + open positions
- TradingView Lightweight Charts β OHLCV candles, live updates
- Live orderbook with bid/ask depth visualization
- Order form β Long/Short, Limit/Market, 1β50Γ leverage slider
- Open positions table with unrealized PnL
- Leaderboard β 4 sort modes (PnL, win rate, volume, followers)
- Trader profile cards with open positions visible
- Copy trade modal β set allocation, auto-mirrors all positions
- Portfolio page β positions + trade history + who you're copying
- WebSocket live updates across all pages
1. User A places Long SOL 10Γ β engine receives via mpsc channel
2. Engine matches order β fill created
3. Engine queries DB: "does anyone follow User A?"
4. For each follower with copy_amount set:
follower_qty = (copy_amount / leader_position_value) Γ leader_qty
5. Engine places copy order for follower β same matching loop (ATOMIC)
6. Both users receive POSITION_UPDATE via WebSocket
Why atomic? Copy trade runs inside the engine task β the single mpsc consumer. If it ran in the HTTP handler, there's a race: leader fills, price moves, follower gets a worse entry. Inside the engine, both orders are processed in the same tick β same price, same moment.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /auth/nonce |
No | Get message to sign |
| POST | /auth/login |
No | Verify wallet signature, get JWT |
| PUT | /auth/username |
Yes | Set display name |
| GET | /markets |
No | All markets with live prices |
| GET | /markets/:m/candles |
No | OHLCV for chart |
| GET | /markets/:m/trades |
No | Recent fills |
| POST | /orders |
Yes | Place order |
| DELETE | /orders/:id |
Yes | Cancel order |
| GET | /orders |
Yes | My order history |
| GET | /positions |
Yes | Open positions + PnL |
| GET | /positions/history |
Yes | Trade history |
| GET | /leaderboard |
No | Top traders |
| GET | /leaderboard/:id |
No | Trader profile |
| POST | /follow |
Yes | Start copy trading |
| DELETE | /follow/:id |
Yes | Stop copy trading |
| GET | /follow/following |
Yes | Who I'm copying |
| GET | /follow/followers |
Yes | My followers |
| WS | /ws |
No | Real-time stream |
Import Liqour.postman_collection.json to test all endpoints locally.
const ws = new WebSocket('wss://liqour-fi.onrender.com/ws')
// Auth for personal events
ws.send(JSON.stringify({ type: 'AUTH', userId: 'your-user-id' }))
// Subscribe to channels
ws.send(JSON.stringify({ type: 'SUBSCRIBE', channel: 'price:SOL' }))
ws.send(JSON.stringify({ type: 'SUBSCRIBE', channel: 'orderbook:SOL' }))
ws.send(JSON.stringify({ type: 'SUBSCRIBE', channel: 'leaderboard' }))
ws.send(JSON.stringify({ type: 'SUBSCRIBE', channel: 'positions' }))
// Events you'll receive:
// PRICE_UPDATE, ORDERBOOK_UPDATE, FILL, POSITION_UPDATE, LEADERBOARD_UPDATEThis is a 1-week solo build. The following limitations are documented transparently.
- Anchor smart contract β No on-chain settlement. All trades are off-chain (CEX-style). Real Solana perps need an Anchor program for vault + position management
- On-chain USDC deposits β Users get paper money (1000 USDC on signup). SPL token deposits not implemented
- Real funding rate β Currently hardcoded at 0.01%. Real formula:
(mark_price - index_price) / index_priceper hour
- SQLx build β
sqlx::query!fails at compile-time without a live DB connection. Fix: runcargo sqlx prepareto generate.sqlx/cache, then setSQLX_OFFLINE=true - Rate limiting β No request throttling (Tower middleware needed)
- Orderbook persistence β Open limit orders lost on crash; only DB-recovered orders are replayed at startup
- Mobile responsive UI (desktop-first)
- PnL shareable card (OG image generation)
- Options layer (calls/puts)
- Trader reputation NFT (on-chain history)
- Governance token (LIQR)
- Cross-margin (vs current isolated margin per position)
- Redis Streams for horizontal engine scaling
- Mobile app (React Native)
- Rust (stable) β
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - Bun β
curl -fsSL https://bun.sh/install | bash - NeonDB account (free tier works)
cd liqour-rust
# 1. Configure environment
cp .env.example .env
# Fill in:
# DATABASE_URL=postgres://... (from NeonDB dashboard)
# JWT_SECRET=<any 64+ character random string>
# 2. Run DB schema
psql $DATABASE_URL -f schema.sql
# 3. Generate SQLx offline cache (required for cargo build)
DATABASE_URL=your_neon_url cargo sqlx prepare
# 4. Build and run
SQLX_OFFLINE=true cargo run --release
# Server starts at http://localhost:3000If
cargo sqlx preparefails: addSQLX_OFFLINE = "true"to.cargo/config.tomlunder[env], or run against a local Postgres instance first.
cd liqour-frontend
cp .env.example .env.local
# NEXT_PUBLIC_API_URL=http://localhost:3000
# NEXT_PUBLIC_WS_URL=ws://localhost:3000/ws
bun install
bun run dev
# Opens at http://localhost:3001Backend on Render:
- Connect GitHub repo
- Build command:
cargo build --release - Start command:
./target/release/liqour - Environment variables:
DATABASE_URL,JWT_SECRET,SQLX_OFFLINE=true - Run
schema.sqlon NeonDB before first deploy
Frontend on Vercel:
- Connect GitHub repo β Next.js auto-detected
- Environment variables:
NEXT_PUBLIC_API_URL,NEXT_PUBLIC_WS_URL - Deploy
MIT