Trade Solana at the source. One Solana wallet, every market — spot memes, prediction markets, and perps from a single interface. No manual bridging. Built on bags.fm.
BAGS Terminal is a chain-abstracted trading terminal on Solana. The model is simple: you keep one Solana wallet and one interface, while solver infrastructure routes execution across markets. Spot trading and token launches on bags.fm are live today; prediction markets and perps are coming soon (currently placeholder pages in testing). On-chain spot trade execution is wired but deferred — it activates only once a buy/sell backend is configured (see Environment Variables).
- Production: www.bagsterminal.fm (apex
bagsterminal.fm307-redirects towww) - Repo: PJharwal/Bags-Terminal
Honesty note: this README documents what the product does right now. Features are tagged
Live,Coming soon, orDeferred. The/pitchdeck is an investor-facing narrative with forward-looking and aspirational claims (volume, traction, awards) — those are not current product capabilities and are not repeated here.
| Market | Route | Status |
|---|---|---|
| Spot / memecoins | /pulse, /trending, /terminal/[tokenId] |
Live (market data + live feed). On-chain trade execution deferred — see below. |
| Token launches | /launch |
Live (signed by your connected wallet) |
| Creator fee sharing & claims | /creator |
Live (signed by your connected wallet) |
| Prediction markets | /prediction |
Coming soon (placeholder, "In Testing") |
| Perps | /perps |
Coming soon (placeholder, "In Testing") |
The header navigation exposes seven items: HOME, PULSE, TRENDING, PERPS, PREDICTION, LAUNCH, CREATOR. The per-token terminal index (/terminal), /analyze, and /deployers exist and are reachable by direct URL or in-app links, but are intentionally not linked in the header nav.
/pulse
- Axiom-style three-column virtualized layout: New Pairs, Final Stretch, Migrated, rendered with
@tanstack/react-virtual. Columns and filtered lists are memoized so the trade firehose doesn't churn the virtualizer. - Filter dropdown (functional): market-cap tier (
All/High≥ $500K /Mid$100K–$500K /Low< $100K) plus a Hide risky tokens toggle (drops tokens with a critical risk flag). Active-filter count badge and reset. - Refresh: clears and re-fetches the three columns from the backend (
/api/tokenswithnew,status=graduating&hours=6,status=migrated). A separate silent 30s reconcile re-syncs Final Stretch + Migrated without skeletons. - Quick-buy button (
0.1 SOL): routes to/terminal/{tokenId}?buy=0.1, prefilling the terminal trade panel. - Logo backfill: a
metadata_updatedsocket event fills in logo / name / holders for tokens created before their off-chain metadata was indexed; cards retry a fresh logo when the URL changes. - Wrapped-SOL noise filtering: incoming trade events on wSOL (the quote side, the majority of the firehose) are dropped before any store update.
- Per-frame trade coalescing: trades are queued and flushed at most once per animation frame; additive fields (volume, tx count) accumulate, and only changed columns get new array references.
- Feed status is derived honestly:
liveif a real socket event arrived in the last 30s, elsepollingif REST data is fresh, elseoffline— shown as a colored dot with the visible (post-filter) token count.
/trending
- Reuses the Pulse store (live socket + DexScreener seed), sorted by market cap.
- Filters: state, market cap, with-fees; plus a leaders view ranked by lifetime fees on BAGS and a bags view. Grid and table layouts. Cards link to
/terminal/{tokenId}.
/terminal/[tokenId]
- GeckoTerminal chart embedded via iframe (pool resolved from DexScreener).
- Live trades table fed by the socket; empty state reads "No live trades — trade feed unavailable".
- Bottom tabs: Holders, Top Traders, Fees, Dev Tokens.
- Dynamic Open Graph / Twitter card per token (see Share cards).
- Trade panel: full buy/sell UI is present — Buy/Sell tabs, presets (
0.1 / 0.5 / 1 / 5SOL), Zero-Config, slippage / priority / Jito controls, and the?buy=<amount>deep link from Pulse quick-buy. - On-chain trade execution is deferred. The trade socket only connects when
NEXT_PUBLIC_BUYSELL_SERVER_URLis set. With it empty (the default), the trade panel shows "Trading not configured" and the trade button is disabled. Configure the buy/sell backend to enable execution.
/terminal with no token id renders a token picker (trending + recent Pulse tokens + manual mint-address input). It does not redirect.
/launch
- No-code token creation through the official Bags SDK / public API v3: image → IPFS, create config, build the launch transaction. The launch transaction is signed by your connected wallet (
sendTransaction) — independent of the buy/sell backend. - Fee sharing: up to 100 claimers, configured at launch.
- Referral attribution:
?ref=partner key, with a generated referral Open Graph card. - On success, shows a shareable token card and an "Open in Terminal" link.
/creator
- Loads your created tokens, claimable fees, claim history, and partner config (requires a connected wallet).
- Fee claims and partner claims build transactions via the Bags public API v3 (
/creator/claim,/partner/claim,claim-txs/v3) and are signed by your connected wallet.
/analyze— deep token analysis: risk audit, holders table, top-traders table./deployers— deployer intelligence aggregated from live token data: launch counts, success rate, risk flags.
Both work and are reachable by URL or in-app links; they are not in the header nav.
/api/og (server-rendered with next/og)
- Per-token card (
?mint=): fetches token data from DexScreener and renders a 1200×630 card with symbol, name, price, 24h change, MCAP / Vol / Liquidity; falls back to the default card if token data is unavailable. Terminal pages set their OG / Twitter images to this card. - Referral card (
?ref=): "REFERRED BY {short}… launch with built-in fee sharing", using the official Bags logo./launch?ref=wires this into its metadata. - Default card (no params): headline "One Terminal. Every Market." with sub-copy "Spot memes, prediction markets & perps on Solana — built on bags.fm." and a "LIVE · SOLANA MAINNET" tag.
/prediction — placeholder page (ComingSoonMarket): tagline "Cross-Chain Prediction Markets", title "POLYMARKET, ZERO BRIDGING", status badge "In Testing", "Coming Soon" CTA, "Back to Pulse" link. No trading logic.
/perps — placeholder page (ComingSoonMarket): tagline "Cross-Chain Perps", title "PERPS WITHOUT LEAVING SOLANA", status badge "In Testing", "Coming Soon" CTA, "Back to Pulse" link. No trading logic.
| Layer | Technology |
|---|---|
| Framework | Next.js 16.1.3 (App Router, Turbopack default) |
| Language | TypeScript 5 |
| UI runtime | React 19.2.3 / React DOM 19.2.3 |
| Styling | Tailwind CSS v4 (@tailwindcss/postcss) |
| State | Zustand 5 (10 stores: pulse, socket, terminal, wallet, referral, creator, launch, omnera, selection, social) |
| Real-time | socket.io-client 4.8 |
| Virtualization | @tanstack/react-virtual 3 |
| Animation | Framer Motion 12 |
| Icons | @remixicon/react, lucide-react |
| Components | HeroUI, Radix UI (Dialog, Popover, Tooltip) |
| Fonts | @fontsource — Space Mono, Syncopate |
| Share cards | next/og (ImageResponse); html-to-image / html2canvas for in-app cards |
| Blockchain | @solana/web3.js 1.98, wallet-adapter (react / react-ui / wallets), @solana/spl-token; bs58, bip39, ed25519-hd-key |
| Embedded wallet | Turnkey (@turnkey/sdk-server, @turnkey/crypto) — server-side keys |
| Token launches | @bagsfm/bags-sdk 1.3.4 |
| Lint | ESLint 9 (eslint-config-next 16.1.3) |
No test framework is currently installed; there is no test or typecheck npm script.
| Source | How it's used | Status |
|---|---|---|
| DexScreener | Primary market data: token detail, search, trending / boosted lists, and the per-token OG card. Called directly at api.dexscreener.com and also proxied at /api/dexscreener. |
Live |
| Bags API | Token launches, fee sharing, creator / partner claims. Proxied server-side at /api/bags to api.bags.fm and public-api-v2.bags.fm/api/v1. Server-side API key, never exposed to the client. 60 req/min in-memory rate limit per IP. |
Live |
| solshift backend | Pulse feed: REST /api/tokens (supports status=graduating / migrated, limit, hours) plus the socket.io firehose. Default https://backend.solshift.fun. |
Live |
| Solana RPC | Wallet provider + wallet store. Public mainnet endpoint by default (api.mainnet-beta.solana.com). |
Live |
| Turnkey | Embedded-wallet support; API keys are server-side only. Export route returns HTTP 503 when the buy/sell backend isn't configured. | Live (gated by buy/sell backend) |
| GMGN | Optional analytics. The /api/gmgn proxy first tries a local server (GMGN_LOCAL_URL, default http://localhost:8000, 1.5s timeout) then public gmgn.ai (3s timeout). |
Effectively dead — public GMGN is Cloudflare-blocked, so without a local server it returns 502 and the app falls back to DexScreener. |
| Buy/sell backend | On-chain spot trade execution + Turnkey export. NEXT_PUBLIC_BUYSELL_SERVER_URL. |
Deferred — empty by default; trade panel shows "Trading not configured" until set. |
The live feed connects to config.baseServerUrl over the websocket transport and subscribes to: new_tokens:all, trades:all, migrations:all, metadata:all.
- Node.js 18+ (the repo declares no
engines/packageManager;@types/nodeis v20) - A package manager:
bun,npm, orpnpm
git clone https://github.com/PJharwal/Bags-Terminal.git
cd Bags-Terminal
bun install # or: npm install / pnpm install
bun dev # or: npm run dev / pnpm devOpen http://localhost:3000.
bun dev # next dev (Turbopack)
bun build # next build
bun start # next start
bun lint # eslint(Use npm run <script> / pnpm <script> if you prefer.) There is no test or typecheck script defined.
Create .env.local. The app runs with sensible defaults for most public vars; some features stay disabled until their backend is configured.
| Variable | Default | Purpose |
|---|---|---|
NEXT_PUBLIC_BASE_SERVER_URL |
https://backend.solshift.fun |
solshift backend for the Pulse feed (REST + socket.io). |
NEXT_PUBLIC_SOLANA_RPC_URL |
https://api.mainnet-beta.solana.com |
Solana RPC for the wallet provider / wallet store. |
NEXT_PUBLIC_BUYSELL_SERVER_URL |
(empty) | Buy/sell backend for on-chain trade execution + Turnkey export. Empty = trading disabled ("Trading not configured"). |
NEXT_PUBLIC_BASE_GMGN_URL |
(empty) | GMGN proxy base. Empty logs a client-side config warning; GMGN is effectively dead, app falls back to DexScreener. |
NEXT_PUBLIC_TURNKEY_ORGANIZATION_ID |
(empty) | Turnkey organization id (public). |
NEXT_PUBLIC_TURNKEY_API_BASE_URL |
https://api.turnkey.com |
Turnkey API base URL. |
NEXT_PUBLIC_SITE_URL |
https://www.bagsterminal.fm |
Canonical site URL for metadata / absolute OG image URLs. |
NEXT_PUBLIC_BAGS_API_KEY |
(optional) | Public Bags API key, if used client-side. |
NEXT_PUBLIC_SOLANA_NETWORK |
(optional) | Solana network label (e.g. mainnet-beta). |
| Variable | Default | Purpose |
|---|---|---|
BAGS_API_KEY_SERVER |
(required for Bags features) | Bags API key injected by the /api/bags proxy. |
GMGN_LOCAL_URL |
http://localhost:8000 |
Local GMGN server tried first by the /api/gmgn proxy. |
TURNKEY_API_KEY |
(empty) | Turnkey server API key. |
TURNKEY_API_SECRET |
(empty) | Turnkey server API secret. |
TURNKEY_ORG_ID |
(empty) | Turnkey server organization id. |
TURNKEY_API_BASE_URL |
https://api.turnkey.com |
Turnkey server API base URL. |
src/
├── app/
│ ├── page.tsx # Home (hero + live tokens; connected: wallet stats, referral, quick actions)
│ ├── pulse/page.tsx # Live token feed (Axiom 3-column virtualized)
│ ├── trending/page.tsx # Trending + fee leaders
│ ├── terminal/page.tsx # Token picker index (no redirect)
│ ├── terminal/[tokenId]/page.tsx # Per-token terminal (chart, trades, trade panel)
│ ├── launch/page.tsx # Token launch (live, wallet-signed)
│ ├── creator/page.tsx # Creator dashboard (fees, claims, partner)
│ ├── analyze/page.tsx # Token analysis (unlinked)
│ ├── deployers/page.tsx # Deployer intelligence (unlinked)
│ ├── perps/page.tsx # Coming soon (ComingSoonMarket)
│ ├── prediction/page.tsx # Coming soon (ComingSoonMarket)
│ ├── pitch/page.tsx # Standalone investor deck (direct URL only)
│ ├── layout.tsx # Global metadata + providers
│ └── api/
│ ├── og/route.tsx # Open Graph / share / referral cards
│ ├── bags/[...path]/ # Bags proxy (server key, 60 req/min)
│ ├── dexscreener/[...path]/ # DexScreener proxy
│ ├── gmgn/[...path]/ # GMGN proxy (local → public fallback)
│ └── turnkey/ # Turnkey routes (export gated on buy/sell backend)
├── components/
│ ├── TopBar.tsx # Header nav (7 links) + search + UTC clock + wallet
│ ├── ComingSoonMarket.tsx # Placeholder used by /perps and /prediction
│ ├── pulse/ # Axiom feed (columns, cards, toolbar)
│ ├── terminal/ # Chart, trade panel, bottom tabs
│ └── ...
├── store/ # Zustand stores (10)
├── services/ # bags / dexscreener / gmgn / token services
├── hooks/ # useTradeSocket, useWallet, etc.
├── lib/ # turnkey, types, formatting, utils
└── config/env.ts # Data-source + env configuration
- Bags API key is server-side only (
BAGS_API_KEY_SERVER), injected by the/api/bagsproxy and never exposed to the client. Turnkey API secrets are server-side only. /api/bagsproxy applies a 60 req/min in-memory rate limit per IP, with periodic cleanup.- Global security headers (set in
next.config.ts):X-Frame-Options: DENY,X-Content-Type-Options: nosniff,Referrer-Policy: strict-origin-when-cross-origin, and aPermissions-Policydisabling camera / microphone / geolocation. - Wallet integration uses the official Solana wallet-adapter.
MIT