Skip to content

Repository files navigation

πŸ₯ƒ Liqour β€” Social Copy Trading Perps on Solana

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.

Backend Frontend License Built with Rust Superteam Fellowship

Live: liqour-fi.vercel.app Β |Β  API: liqour-fi.onrender.com


What is Liqour?

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.


Architecture

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

image

Tech Stack

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

What's Built

Backend (Rust)

  • 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

Frontend (Next.js)

  • 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

Copy Trade Engine β€” How It Works

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. Uploading image.png…


API Reference

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.

WebSocket Usage

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_UPDATE

What's NOT Built (Honest)

This is a 1-week solo build. The following limitations are documented transparently.

Production Blockers

  • 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_price per hour

Known Issues

  • SQLx build β€” sqlx::query! fails at compile-time without a live DB connection. Fix: run cargo sqlx prepare to generate .sqlx/ cache, then set SQLX_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

Deferred

  • Mobile responsive UI (desktop-first)
  • PnL shareable card (OG image generation)

V2 Scope

  • 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)

Setup

Prerequisites

  • 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)

Backend

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:3000

If cargo sqlx prepare fails: add SQLX_OFFLINE = "true" to .cargo/config.toml under [env], or run against a local Postgres instance first.

Frontend

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:3001

Deploy

Backend on Render:

  1. Connect GitHub repo
  2. Build command: cargo build --release
  3. Start command: ./target/release/liqour
  4. Environment variables: DATABASE_URL, JWT_SECRET, SQLX_OFFLINE=true
  5. Run schema.sql on NeonDB before first deploy

Frontend on Vercel:

  1. Connect GitHub repo β€” Next.js auto-detected
  2. Environment variables: NEXT_PUBLIC_API_URL, NEXT_PUBLIC_WS_URL
  3. Deploy

Team


License

MIT

About

πŸ₯ƒ social copy-trading perps DEX on Solana. Trade SOL/BTC/ETH with up to 50x leverage, follow top traders and auto-mirror their positions. Built with Rust + Tokio matching engine, Axum, Pyth oracles, Next.js. Superteam India Fellowship Capstone.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages