A full-featured Solana trading terminal powered by Jupiter, Meteora, and Flash Trade. Swap tokens, earn yield, trade perpetual futures, place limit orders, run DCA strategies, provide liquidity, browse trending tokens, and send tokens via invite codes — all from a single self-hosted app.
| Feature | Description |
|---|---|
| Swap | Best-route token swaps via Jupiter Ultra v2 (DEX + RFQ) with wallet token selector |
| Trending | Jupiter organic score leaderboard — bot-filtered token rankings with 5m/1h/6h/24h intervals, price, change, volume, and one-click buy |
| Lend / Earn | Deposit & withdraw into Jupiter lending products — markets and positions unified in one view, deposited amount shown per market card |
| Perps | Long/short perpetual futures on crypto and equities via Flash Trade — live preview, SL/TP orders, and collateral management |
| Limit Orders | Place and cancel trigger-based limit orders |
| DCA | Set up recurring dollar-cost-averaging purchases |
| Liquidity | Provide liquidity to Meteora DLMM pools — Spot, Curve, and Bid-Ask strategies |
| Portfolio | Token balances + Jupiter DeFi positions across all platforms |
| Send | Send tokens via claimable invite codes (clawback supported) |
| History | App transactions + live on-chain signature history via Solana RPC |
| Price Charts | Live OHLCV charts (1H / 4H / 1D / 1W / 1M) via Pyth Network |
| Token Search | Global search bar — find any Solana token, click to swap |
| Auto Trader | Automated meme coin sniper — configurable entry/exit rules, trailing stop, take profit, max hold time, rebuy cooldown, organic score filter, candidate sorting by score, and live P&L chart |
| Logs | Unified troubleshooting view — backend stdout/stderr, frontend console + window errors, outbound network/RPC calls (Jupiter / OpenAI / Solana / Telegram), and AI Decisions tail — with search, level/source filter, per-tab clear, and Copy/Download export |
| Settings | RPC endpoint, cluster, slippage tolerance, priority fee |
- React 18 + Vite + TypeScript
- Tailwind CSS v4 — dark theme UI
- Zustand — wallet, settings, and UI state
- TanStack Query — server state + caching
- lightweight-charts — TradingView-style price charts
- @solana/web3.js + @solana/spl-token — on-chain interactions
- @meteora-ag/dlmm + @coral-xyz/anchor — Meteora DLMM liquidity SDK
- Express.js + TypeScript — Jupiter, Meteora & Flash Trade API proxy
- express-rate-limit — 200 req / 10s global limit
- CORS — configurable frontend origin whitelist
SupaSOL/
├── frontend/ # React + Vite SPA
│ ├── src/
│ │ ├── pages/ # 12 route pages
│ │ ├── components/ # UI, charts, layout, liquidity, wallet modals
│ │ ├── hooks/ # Custom React hooks
│ │ ├── api/ # Jupiter, Meteora & Flash Trade client functions
│ │ ├── store/ # Zustand stores
│ │ └── config/ # Constants & token mints
│ ├── Dockerfile # Multi-stage: Node builder → nginx
│ └── nginx.conf # SPA routing + asset caching
│
├── backend/ # Express API proxy
│ ├── src/
│ │ ├── routes/ # Route modules (swap, lend, trigger, dlmm, perps, tokens…)
│ │ └── lib/ # Jupiter client + trigger JWT auth
│ └── Dockerfile # Multi-stage: Node builder → slim runtime
│
├── docker-compose.yml # Frontend + backend services
├── install.sh # Bare-metal Ubuntu deployment script
└── .env.example # Environment variable template
- Node.js 20+
- npm 10+
- A Jupiter API key — get one at portal.jup.ag
- A Helius API key (recommended) — get one at helius.dev for reliable RPC
git clone https://github.com/fphxgallery/SupaSOL.git
cd SupaSOL
npm installcp .env.example .envEdit .env:
JUPITER_API_KEY=your_key_here
VITE_API_BASE=http://localhost:4000
VITE_RPC_URL=https://mainnet.helius-rpc.com/?api-key=your_helius_key
VITE_CLUSTER=mainnet-beta
FRONTEND_ORIGIN=http://localhost:5173
PORT=4000npm run dev- Frontend: http://localhost:5173
- Backend: http://localhost:4000
cp .env.example .env
# Edit .env with your JUPITER_API_KEY and production URLs
docker compose up --build -d- Frontend (nginx): http://localhost:3000
- Backend (API): http://localhost:4000
The frontend container waits for the backend health check before starting.
| Arg | Default | Description |
|---|---|---|
VITE_API_BASE |
http://localhost:4000 |
Backend API URL |
VITE_RPC_URL |
https://api.mainnet-beta.solana.com |
Solana RPC endpoint |
VITE_CLUSTER |
mainnet-beta |
Solana cluster |
For VPS/dedicated server deployment with systemd + nginx:
chmod +x install.sh
./install.shThis script will:
- Install Node.js 20 and nginx (if not present)
- Build the frontend and copy to
/var/www/supasol - Configure nginx with SPA routing and
/api/proxy - Create and enable a
supasol-backendsystemd service
After install:
- Frontend:
http://your-server-ip - Backend health:
http://your-server-ip/api/health - Logs:
sudo journalctl -u supasol-backend -f
| Variable | Required | Default | Description |
|---|---|---|---|
JUPITER_API_KEY |
✅ | — | Jupiter API key from portal.jup.ag |
PORT |
— | 4000 |
Backend server port |
NODE_ENV |
— | development |
development or production |
FRONTEND_ORIGIN |
— | http://localhost:5173 |
CORS allowed origin |
VITE_API_BASE |
✅ | — | Backend URL (used at build time) |
VITE_RPC_URL |
— | mainnet-beta RPC | Solana RPC endpoint (Helius recommended) |
VITE_CLUSTER |
— | mainnet-beta |
Solana cluster name |
SupaSOL includes a built-in non-custodial wallet:
- Create — generates a new keypair with a 24-word seed phrase. The backup screen locks until you confirm you've saved the phrase.
- Import — paste an existing mnemonic or base58 private key
⚠️ Your private key is stored only in your browser'slocalStorage. Never share your seed phrase. This app does not transmit private keys to any server.
All backend routes proxy to Jupiter, Meteora, or Flash Trade with your API key injected server-side.
| Path | Description |
|---|---|
GET /health |
Health check |
GET /api/swap/* |
Quote + execute token swaps |
GET /api/lend/* |
Lending positions, deposit, withdraw |
GET /api/trigger/* |
Limit orders (create, cancel, list) |
GET /api/recurring/* |
DCA orders |
GET /api/tokens/* |
Token search, metadata, trending rankings |
GET /api/price/* |
Live prices |
GET /api/portfolio/* |
Portfolio positions |
POST /api/send/* |
Craft send / clawback invite |
GET /api/dlmm/pairs |
Meteora DLMM pool list |
GET /api/dlmm/pair/:address |
Meteora pool metadata |
GET /api/perps/pool-data |
Flash Trade markets + custody stats |
GET /api/perps/prices |
Flash Trade mark prices (live) |
GET /api/perps/positions/:wallet |
Open perp positions with PnL |
GET /api/perps/orders/:wallet |
Open SL/TP trigger orders |
POST /api/perps/open |
Build open position transaction |
POST /api/perps/close |
Build close position transaction |
POST /api/perps/add-collateral |
Build add collateral transaction |
POST /api/perps/remove-collateral |
Build remove collateral transaction |
POST /api/perps/trigger |
Build stop loss / take profit transaction |
POST /api/perps/cancel-trigger |
Cancel SL/TP order |
npm run dev # Start frontend + backend in watch mode
npm run build # Build both frontend and backend
npm run start # Start production build- Loss-streak circuit breaker — new "Risk Controls" card on Auto Trader. After N consecutive losses (default 3), entries freeze for a cooldown (default 30m), or earlier if 5m net-buyer breadth ≥ 6h breadth (regime no longer weakening). Open positions still managed normally; only new buys pause. Tunable via
lossStreakBreakerEnabled/lossStreakThreshold/lossStreakCooldownMinutes(engine.ts, BotPage.tsx) - Per-exit-reason avg pnl — bot performance snapshot now tracks avg pnl per top exit reason, surfacing which exit type is bleeding vs cutting. AI prompt block now reads
top exits: trailing stop (n=6, avg -8.2%), AI sell @80% (n=4, avg -3.1%). AI Decisions panel shows the figures with green/red color. Tells you when to retune the trail or AI gate (marketSentiment.ts, AiDecisionsPanel.tsx)
- Net-buyer breadth divergence (5m vs 6h) — old
6h net-buyer breadthwas structurally pegged near 100% because the sample is pre-filtered to high-organic-score trending tokens (selection bias = leaders are by definition being accumulated). Now also computes 5m breadth over the same cohort and surfaces the delta (5m − 6h). Negative delta = leaders losing short-term flow → exhaustion / rotation tell. Sentiment chip score replaces centered-6h with clipped-divergence term (±30pp). System prompt explicitly tells the AI to read the delta, not the level. Backwards-compatible: 5m field optional; old persisted decision logs render fine (marketSentiment.ts, aiAdvisor.ts, MarketSentimentChip.tsx, AiDecisionsPanel.tsx) - Multi-origin CORS —
FRONTEND_ORIGINnow accepts a comma-separated list; default covers bothhttp://localhost:5173(vite dev) andhttp://localhost:3000(docker nginx) so dev + docker work without env-flipping (backend/src/config.ts, .env.example)
- Dependency hygiene — drop unused
@solana/wallet-adapter-*—@solana/wallet-adapter-base/react/react-ui/walletswere declared in frontend/package.json but never imported (app uses its own zustandwalletStore+ rawKeypair). Removing them eliminates the entire particle / torus / walletconnect / reown transitive chain. - Result — 640 packages removed;
node_modules810M → 186M (-77%);npm audit56 vulns (8 critical, 5 high) → 17 (0 critical, 4 high); frontend build 5.26s → 2.00s; rollup PURE-comment warnings gone. Deadwalletentry also removed from vite.config.tsmanualChunks.
- Market sentiment — token age floor raised 6h → 48h — macro sample now drawn from tokens established for at least 2 days, further insulating the regime signal from new-launch volatility (marketSentiment.ts)
- Market sentiment — 6h token age filter —
computeMarketSentimentnow also requirescreatedAtknown and ≥ 6h, on top ofmcap ≥ $200kandorganicScore ≥ 70. Stops brand-new launches from skewing the macro 6h/24h price-change avg and 6h net-buyer breadth. Constraint surfaced in the prompt header line so AI sees the cap (marketSentiment.ts)
- Fix Jupiter price 400s when no open positions — Auto Trader was calling
usePrice([''])as a placeholder, producingGET /price/v3?ids=(emptyids) every 10s and returning HTTP 400. Now passes[]directly;fetchPricesalready short-circuits to{}(BotPage.tsx)
- Market sentiment chip in Auto Trader header — single composite glance (
0.4·avg6h + 0.3·avg24h + 0.3·breadth, clipped) labeled Bullish / Neutral / Bearish at ±0.15 thresholds. Tooltip exposes raw avg 6h/24h price change, 6h net-buyer breadth, and sample size. NewGET /api/bot/market-sentimentendpoint, polled every 30s while bot runs (MarketSentimentChip.tsx, routes/bot.ts, useMarketSentiment.ts) - AI Decisions — copy-as-JSON per row — small
JSONbutton copies the fullAiDecisionLogEntry(inputs + outcome + sentiment + perf snapshots) to clipboard for prompt debugging. Confirms with ✓ (AiDecisionsPanel.tsx) - AI Decisions — Δ vs prev (same mint) — expanded panel now shows changed fields (action, outcome, conf, composite, pnl, reason) between current decision and the previous one for the same mint, so you can see what the AI changed its mind on. Header shows
Δ{n}count
- Market sentiment context for AI — new marketSentiment.ts computes a per-tick macro snapshot from the trending list (top 25 by organicScore, filtered to mcap ≥ $200k & organicScore ≥ 70). Tracks avg 6h + 24h price change, 6h net-buyer breadth %, avg organicScore, day-of-week (UTC) and weekend flag. 15min TTL.
- Bot recent performance context for AI — same module derives last-20-closed stats: win/loss split, win-rate, avg pnl, avg held minutes, consecutive same-sign streak from newest, top exit reasons.
- Both blocks injected into entry + exit prompts — system prompt updated to treat them as advisory (broad-red regime → tighter entries; losing streak → bias skip on borderline). Will not hard-veto on macro alone.
- AI Decisions log captures snapshots —
AiDecisionLogEntrygainsmarketSentimentandbotPerformancefields, written byrecordDecisionLog(aiAdvisor.ts). - AI Decisions panel — expandable rows — rows with context show ▸/▾ indicator; click to reveal market regime + bot performance snapshot the AI saw at decision time (AiDecisionsPanel.tsx)
- Logs tab — Copy + Download — every log tab (Backend / Frontend / Network) gains export buttons that emit the currently filtered rows as ISO-timestamped plaintext, ready to paste into a bug report or save as
.log(LogsPage.tsx) - AI Decisions search + Action filter — new search box matches mint, symbol, reason, and action; new Action filter chips (buy / sell / hold / skip) sit alongside the existing Kind and Outcome filters (AiDecisionsPanel.tsx)
- AI Decisions panel relocated — moved from the Auto Trader page into the Logs tab as a 4th sub-tab, consolidating all troubleshooting surfaces in one place (LogsPage.tsx, BotPage.tsx)
- Logs tab — new
/logsroute with three sub-tabs (Backend / Frontend / Network) for end-to-end troubleshooting without tailing journalctl or opening DevTools - Backend log ring — logRing.ts keeps last 2000 entries; logger.ts
emit()patched to push every leveled log into the ring (stdout/stderr writes unchanged) - Network log ring + global fetch interceptor — networkRing.ts monkey-patches
globalThis.fetchonce at boot; captures method, URL, status, duration, error for all outbound calls. Auto-classifies source (Jupiter / OpenAI / Solana / Telegram / Other). Redactsapi_key,token,secretquery params. Last 1000 entries. - Frontend log capture — captureFrontendLogs.ts wraps
console.{log,info,warn,error}and hookswindow.error+unhandledrejection; entries land in frontendLogStore.ts (Zustand, in-memory, last 1000). Real console output preserved. - API surface —
GET|DELETE /api/logs/backend,GET|DELETE /api/logs/network(routes/logs.ts); polled every 3s by LogsPage.tsx with text search + level/source filter + clear button per tab
- AI-unavailable sell threshold raised 3 → 5 —
aiUnavailableStreakmust hit 5 consecutive failed AI exit calls before force-sell (AI unavailable x5). Combined with the 90s per-mint AI call floor, this gives ~7.5 minutes of AI-provider downtime tolerance before a position is dumped on transient errors. Streak still resets on any successful AI response. (engine.ts)
- Auto Trader responsive layout — outer grid now
grid-cols-1 xl:grid-cols-[minmax(0,1fr)_560px]. Below thexlbreakpoint (1280px) the right column stacks under the left so Active Positions table no longer gets squeezed on mid-size screens. At ≥1280px, side-by-side with right column fixed at 560px (down from 640px). (BotPage.tsx)
- Width-cap pass continued — Portfolio, Dashboard, and Perps capped at 1200px; Limit Orders, DCA, and Send capped at 1080px; all centered with
mx-auto w-full. Swap intentionally left full-width. (PortfolioPage.tsx, DashboardPage.tsx, PerpsPage.tsx, TriggerPage.tsx, RecurringPage.tsx, SendPage.tsx)
- Auto Trader layout reflow — settings cards (Entry, Exit, AI Advisor) moved to a fixed 640px right column on the bot page; PnL History now sits on the same row as Entry, with Active Positions + Activity Log side-by-side under PnL on the left. AI Decisions Panel stays full-width below. Right column no longer stretches on wide displays. (BotPage.tsx)
- Centered, width-capped page containers — Lend/Earn, Transaction History, Trending Tokens, and DLMM pages now cap content at 1080–1200px and center horizontally so cards/tables stay readable on ultrawide monitors instead of stretching edge-to-edge. (LendPage.tsx, HistoryPage.tsx, TrendingPage.tsx, LiquidityPage.tsx)
- Sell on 3rd consecutive AI-unavailable — exit engine now tracks
aiUnavailableStreakper position; when AI exit gate is open and the API call fails 3 times in a row, force-sell with reasonAI unavailable x3. Streak resets on any successful AI call. Previously a stuck-unavailable position would only exit at max-hold cap. Fields: types.ts, increment/reset in engine.ts, hydrate default in state.ts - Sidebar branding — replaced ⚡ glyph with the official Solana mark (inline SVG, green→purple gradient) in both desktop and mobile sidebar headers (Sidebar.tsx)
- Per-mint AI call floor (90s) — fixes log spam where 4 SELL@70% calls fired in 1 minute on the same blocked-by-gate position
- Root cause: exit cache key in aiAdvisor.ts includes
dhLen(decisionHistory length); each fresh call records a snapshot, so the next key always misses → every poll = new OpenAI call - Fix:
lastCallByMintmap withMIN_CALL_INTERVAL_MS = 90_000. On cache miss, if last fresh call for this mint was <90s ago, return last decision ascached: true— engine skips logging cached decisions and gate math (effectiveMinConf) decays locally without burning OpenAI calls - Cleared via
resetAdvisorState()on bot stop, alongside existing cache/rejections/history
- Structured backend logger — new logger.ts with leveled output (
debug|info|warn|error), ISO timestamps, scoped prefixes,LOG_LEVELenv gate (defaults toinfoin prod,debugotherwise); errors/warnings route to stderr - All 17
console.*call sites in backend prod paths swapped to scoped loggers: index.ts, config.ts, routes/perps.ts, routes/price.ts, routes/vault.ts, bot/state.ts, lib/notifier.ts - Noisy
[trigger] tx hexdump demoted/removed;tx byteskept atdebuglevel - notifier.ts
notify()fire-and-forget hardened — delegates to explicitasync deliver()withvoidmarker; all errors captured internally, no unhandled rejection path
- Fix HTTP 400 on Run Bot in Background after v1.10.4 — Zustand persist
mergein botStore.ts was preserving legacytakeProfitPct/tieredTpEnabledkeys from localStorage; backendvalidateConfigrejects unknown fields - Config hydration now whitelists to
DEFAULT_CONFIGkeys, so removed fields are dropped on load apiFetchin client.ts also readsbody.error(not justerr/message), matching backend's error shape
- Fix
[object Object]error in Run Bot in Background modal —apiFetchin client.ts now throws a realApiError extends Errorinstead of a plain object literal - Consumers using
err instanceof Error ? err.message : String(err)(UnlockBotModal, useTradingBot, AddLiquidityModal) now get the actual backend message (e.g. "Wrong password") instead of theObject.prototype.toStringfallback withRetrystill reads.retryable— unchanged
- Exit card consolidated, tiered TP always-on — merged Exit + Tiered Take-Profit into one card; removed non-tiered
takeProfitPctandtieredTpEnabledfrom config - Backend types.ts, validateConfig.ts, engine.ts, aiAdvisor.ts drop both fields; tiered sell path runs unconditionally; AI exit context no longer carries
takeProfitPct - Frontend botStore.ts, useTradingBot.ts, BotPage.tsx same strip; single Exit card lays trailing/maxhold/rebuy above T1/T2 with AI-gated + AfterT1 controls below
- Zustand persist will drop the legacy keys on next load; no migration needed
- Min token age filter — new
minTokenAgeHoursconfig (default 0 = off) skips tokens younger than X hours during entry evaluation - Backend engine.ts + frontend useTradingBot.ts entry loops drop candidates where
(now - createdAt) < minTokenAgeHours - Uses Jupiter top-level
createdAtfromtokens/v2/toptrendingresponse; exposed asTrendingToken.createdAt(ms epoch) - UI: "Min token age" input on BotPage under Min org buyers
- Max-hold AI-gated — new
maxHoldAiGatedtoggle (default on). When AI Advisor enabled, hitting the max-hold cap triggers an AI consult instead of force-selling; strongholdverdicts let winners run past the cap - engine.ts defers the max-hold exit reason when gated and opens the AI exit gate even outside loss/gain zones
- Prompt hint added to exit context: cap-reached positions lean SELL unless 5m+1h signals clearly bullish
- Exit cache key includes
capbucket so cap-hit calls don't reuse a pre-cap cached decision - Safety net: if AI unavailable while gated, cap is honored and position force-sells
- UI: new checkbox under Max Hold with conditional explainer
- AI decision history depth 10 → 20 — aiAdvisor.ts
MAX_DECISION_SNAPSHOTSbumped so exit prompt sees a longer HOLD-streak window on slow bleeders - Doubles per-call token footprint of the decision-history block on positions with >10 prior snapshots; trade-off for better streak detection
- Notis coverage fix — tiered take-profit sells (T1/T2) and close-all sells now fire Telegram notifications
doTierSell()in engine.ts routes throughnotify('bot.exit', …)on success andbot.erroron failurecloseAllPositions()same treatment- Previously only the full trailing-stop / take-profit / max-hold exit path notified, so partial tier exits were silent on Telegram
- Notis tab — Telegram notifications — new
/notispage with BotFather-driven setup flow - Backend notifier.ts: persistent config at
notis.json, token masking, per-event toggles (entry / exit / veto / start / stop / error),sendMessagevia Telegram Bot API,getUpdateschat discovery - Routes notis.ts:
GET|PUT /api/notis/config,POST /api/notis/telegram/chats,POST /api/notis/telegram/test(rate-limited) - Engine hooks in engine.ts fire
notify()on buy / sell / AI veto / bot start / stop / errors — fire-and-forget, never blocks trade logic - Frontend NotisPage.tsx: collapsible BotFather guide, token save, fetch-chats button, chat picker, event checkboxes, send-test button, last-send status
- Disabled by default; stored token is never round-tripped to the frontend (masked as
****last4)
- AI Decisions viewer on Auto Trader page — ring-buffered log (200 cap) of AI entry/exit calls with score, confidence, outcome, reason
- Backend ring log in aiAdvisor.ts; routes
GET|DELETE /api/bot/ai-decisions - Frontend
AiDecisionsPanelpolls every 5s, filterable by kind + outcome; Background mode only, in-memory
- AI exit prompt guards — fixes spurious SELL verdicts that cited fabricated "prior losses" on first exit evaluation
- Hallucination guard: system prompt forbids citing prior trades, HOLDs, or losses unless a history block is actually present in the user message; exit prompt now explicitly says "No prior AI calls…" / "No prior bot trades…" when absent so model cannot invent history
- Score-sign guard: if blended composite score ≥ 0 (neutral/bullish), SELL requires confidence ≥75 AND the reason MUST explicitly cite a specific metric contradicting the positive composite — otherwise default HOLD
- Grounding rule: narrated reasons must match cited numbers (e.g. claim of "negative 1h" requires 1h price/holder change to actually be negative)
- Dynamic composite-sign hint appended to exit user prompt per call — model sees which regime applies before responding
- AI exit memory + weighted composite score — fixes loop where losing positions got stuck on repeated
HOLD@65% - Per-position decision history (last 10 snapshots) now injected into exit prompt: prior action, confidence, pnl, composite score, age — AI sees its own HOLD-streak and can break out
- Weighted composite signal score [-1,+1] across 5m/1h in aiAdvisor.ts:
holderChange/numNetBuyers1.2,priceChange/organic buy-sell ratio 1.0,liquidityChange0.8,volumeChange0.5, total vol ratio 0.4, trade-count ratio 0.3; blended 40% 5m / 60% 1h - System prompt adds HOLD-streak failure-mode rubric — MUST bias SELL when composite trends negative across prior HOLDs with deteriorating pnl; score ≤ -0.3 on 1h = confirmed reversal
- Dynamic sell gate:
effectiveMinConf = max(25, aiMinConfidence − min(20, heldMin/10) − min(15, |lossPct|))— a -10%, 100-min position drops sell gate from 60 to 25, letting moderate-confidence SELL calls fire - Cache TTL decay: 2 consecutive HOLDs → cache shrinks to 5 min, 3+ → 2 min (was fixed at
aiCacheMinutes, typically 10) - Cache key now includes composite score bucket → cache auto-invalidates when signals shift, not just pnl
- Exit log line now shows
[gate=X, score=±Y]for visibility into why SELL did/didn't fire
- Codebase sanity pass — hardened Docker deployment + fixed AI exit gate edge case
docker-compose.ymlnow passes throughOPENAI_API_KEYandSOLANA_RPC_URL(were silently dropped, disabling AI and falling back to rate-limited public RPC in container)- Persistent state: new
BOT_STATE_DIRenv (defaultcwd, Docker:/data) honored by state.ts, vault.ts, bot.ts — named volumebot-statepreventsbot-state.json+vault.jsonwipe on container restart VITE_API_BASEno longer hardcoded in compose; overridable via env- AI exit loss-side gate decoupled from tier completion (engine.ts:245) — losing trades that never hit T1/T2 can now trigger
aiExitLossPctinstead of being silently skipped whentieredTpEnabled - Local browser bot now logs a one-time warning when
aiEnabledis on — AI advisor is backend-only, prevents silent no-AI runs
tsc -bbuild fix: narrowed tier union type after guard to satisfy strict TS indoTierSellcall
- 2-tier scale-out take-profit — sell fraction at T1, second fraction at T2, let remainder ride on trail
- Config:
tieredTpEnabled,tp1Pct/tp1SellPct,tp2Pct/tp2SellPct,afterT1Mode(tightentrail vsbreakevenfloor),tightTrailPct - Position state extends with
tokenAmountRemaining,tiersHit,breakevenFloor - Closed positions gain
tierfield (1/2) for partial exits; full close keeps existing semantics - Available in both backend vault bot and browser local bot
- HOLD-specific confidence rubric in system prompt — scores how certain the hold call is, not bullishness
- Prevents HOLD decisions from inheriting buy/sell confidence semantics
- Removed prompt-side instruction that forced
holdwhen confidence <50 — was redundant with exit HOLD default and risked converting real low-conf sell signals to hold - Confidence gating now handled purely by
aiMinConfidencethreshold in engine (single source of truth)
- AI confidence now explicitly defined as action certainty, not price prediction — fixes semantic ambiguity where model rated "likelihood of dump" instead of "certainty skip is correct"
- Hard floor added: 2+ bearish signals + skip/sell decision → confidence MUST be 80+
- Low-confidence band (<50) now instructs model to pick
holdinstead of acting with weak conviction
- AI advisor system prompt gains explicit confidence calibration rubric — 90-100 for multi-signal alignment, 80-89 for 2 strong signals, 70-79 for single signal, etc.
- Explicit instruction to avoid clustering around 70-80% when reason cites multiple confirming timeframes/signals
- Fixes observed miscalibration where strong multi-timeframe veto reasons ("negative holder change and declining volume across multiple timeframes") rendered at only 75% confidence
- Codebase sanity pass: hardened
.envload in backend/src/config.ts to be__dirname-relative (no longer brittle toprocess.cwd()) - Warn log emitted at startup when
OPENAI_API_KEYis missing — AI exit advisor silently no-ops before docker-compose.ymlFRONTEND_ORIGINnow overridable via env (${FRONTEND_ORIGIN:-http://localhost:3000}) for hybrid docker-backend + dev-frontend setups.env.exampledocuments optionalOPENAI_API_KEY
- AI exit prompt rewritten to bias toward HOLD as default, countering observed sell-happy behavior
- System prompt gains exit-specific guidance: ignore single-candle 5m noise, require sustained reversal across 5m AND 1h, small green P&L is not a sell signal
- Exit context now includes
trailingStopPct,takeProfitPct, anddrawdownFromPeak— AI can see the guardrails that already exist - User message reframes: "Default action is HOLD unless clear reversal" / "Recommend SELL only on clear sustained reversal"
- AI exit decisions now gated by P&L bands — AI only consulted when
pnlPct <= -aiExitLossPctorpnlPct >= aiExitGainPct - Middle band (small green/red) handled by trailing stop + max-hold only, preventing premature exits on minor wobble
- Two new config knobs:
aiExitLossPct(default 10%) andaiExitGainPct(default 30%), surfaced in AI Advisor card - Fixes issue where AI sold positions at +1% "good exit point" despite 100% TP target — AI was unaware of user strategy
- AI Advisor gains per-mint rejection memory — last 3 AI skip/veto/no-confirm decisions on the same mint injected into entry prompts
- Each rejection line: action, confidence, reason quote, age (e.g.
skip @80% — "sell-dominated volume" (3m ago)) - System prompt updated: if flagged concerns persist in current stats, keep skipping; if conditions materially improved, fresh look OK
- In-memory only (cleared on bot stop), capped at 3 per mint; cache key includes rejection count
- AI Advisor now has per-mint trade memory — last 5 closed trades on the same mint injected into both entry and exit prompts
- Each history line: PnL%, hold duration, exit reason, age (e.g.
+45.2% (held 12m, exit=take profit +45%, 2h ago)) - System prompt updated: model factors prior bot outcomes when judging repeat signals
- Cache key includes history length so new closes invalidate stale decisions
- No behavior change on first trade of a mint; token overhead ~200/call max
- AI Advisor system prompt retuned for memecoin context — removed "conservative/skeptical/ignore hype" framing that caused AI to veto nearly every trending token
- New prompt treats memecoins as momentum plays, weighs signals on balance, and only skips on clear red flags (dumping liquidity, sell-dominated volume, collapsing holders)
- AI Advisor prompts now feed full Jupiter market context to the model
- Entry prompt: all 13 stat fields (priceChange, holderChange, liquidityChange, volumeChange, buy/sell volume, organic volumes, num buys/sells/traders/organic buyers/net buyers) across 5m/1h/6h/24h intervals
- Exit prompt: fetches live token stats via
/tokens/v2/searchbefore each AI call and includes 5m + 1h intervals alongside entry/current/peak price, PnL, and hold time - Field names match Jupiter API exactly (no abbreviations) for self-documenting prompts
- Added
fetchTokenStats(mint)helper injupiterApi.ts; exportedIntervalStatsinterface
- Entry loop now tracks mints the AI rejected (veto or no-confirm) and silently skips re-evaluating them for
aiCacheMinutes - Eliminates repeated skip logs for the same trending tokens and saves redundant OpenAI calls
- Rejection list cleared when bot stops
- AI Advisor toggle label now always reads "Enabled" (checkbox state indicates on/off) — previous "Disabled" label was misleading
- Activity log no longer spams cached AI decisions — exit-loop advisor only logs on fresh calls (not cache hits), cutting log volume dramatically for held positions
- Backend now loads
.envfrom repo root instead ofbackend/.env - Single source of truth for env vars across the monorepo (matches existing
.envconvention used by this project) - No migration needed if you already keep
.envat the repo root
- Fix: AI Advisor numeric fields (min confidence, max calls/hr, cache TTL) appeared blank for users upgrading from ≤1.7.8
- Root cause: Zustand persist shallow-merged old storage over fresh defaults, wiping fields that didn't exist in prior versions
- Fix: custom
mergedeep-merges persistedconfigoverDEFAULT_CONFIG, so any new field added later shows its default instead of undefined
- Auto Trader: optional AI advisor via OpenAI API (opt-in,
OPENAI_API_KEYenv var) - Three modes: Veto (AI can block buys it disapproves), Confirm (AI must explicitly approve buys), Advisory (log-only, no action)
- Entry gate: runs after Jupiter filters, before swap — decision + confidence + reason logged to Activity Log
- Exit signals: AI can trigger early sell when no rule-based exit fires. Rule-based stops (trailing, take profit, max hold) always override — AI never blocks safety exits
- Cost control: per-token decision cache (configurable TTL) + hourly rate cap; model selection between
gpt-4o-mini(cheap) andgpt-4o - New UI card in Bot page for all AI config; defaults disabled so existing deploys unchanged
- Backend security hardening pass
- Brute-force guard on
POST /api/bot/unlock— 5 failed attempts per 15 min per IP (successful unlocks skip the limit) - Global error handler now logs full stack +
X-Request-Idper request; clients get therequestIdin the error body for traceability - New
validateBotConfigPatch()— strict field-level validation onPATCH /api/bot/configand theconfigpayload of/unlock. Blocks negativebuyAmountSol, out-of-rangeslippageBps, unknown fields, NaN, wrong types, andmcapMin > mcapMax
- Auto Trader: clear stale Active Positions that aren't in the wallet anymore (sold externally, failed exits, etc.)
- Per-row × button on each active position — removes from state without selling
- "Prune Ghosts" button scans wallet via
getParsedTokenAccountsByOwnerand auto-removes positions with 0 on-chain balance - Works with background bot (uses vault pubkey, no unlock required to prune)
- Portfolio → Token Holdings: SOL row now uses the official Solana icon via
TokenLogo, matching the style of SPL token rows (was a purple ◎ glyph)
- Backend hardening pass from codebase sanity check
- Path param validation added to proxy routes — base58 regex for wallet/mint/pool addresses (dlmm, tokens/icon, portfolio), whitelist for Jupiter token category/interval, safe charset for trigger orderId
- Atomic writes (tmp+rename) for
bot-state.jsonandvault.json— prevents corruption on crash mid-write - Icon cache now LRU-bounded at 5000 entries to prevent unbounded memory growth
- Backend RPC URL moved from frontend-scoped
VITE_RPC_URLto dedicatedSOLANA_RPC_URLenv var - Spot chart bars flattened; auto-fill toggle now renders proportional distribution (from v1.7.4)
- Lend / Earn page redesigned — Available Markets and My Positions merged into a single unified view
- Deposited amount and USD value now shown bottom-left of each market card inline
- Total deposited and position count moved to compact inline header strip
- Deposit/Withdraw mode toggle moved inline with input to reduce vertical space
- 2-column grid on large screens eliminates wasted whitespace
- Auto Trader:
rebuyCooldownMinutesadded as an independent config field (default: 60m) - Cooldown is now separate from max hold time — both can be tuned independently
- UI input added to Exit card; set to 0 to disable cooldown entirely
- Auto Trader: trending token candidates sorted by organic score (desc) before buying — best signals get position slots first when capacity is limited
- Auto Trader: rebuy cooldown blocks re-entering a token that was just stopped out within the cooldown window
- DLMM pool browser overhauled with smarter filtering and scanning
- TVL filter replaced with min/max range text inputs (supports
25k,1metc.) — defaults to $25K min - Bin step filter added — filters pools with step ≥ value, defaults to 100
- Scan button — filters and sort are committed on click, not on every keystroke
- Sort buttons (Fee/TVL, Volume, TVL) trigger a fresh server fetch on click
- APR sort removed — replaced by Fee/TVL (
fee_tvl_ratio_24h:desc) which is the correct metric - Fixed Meteora API TVL filter: now uses
filter_by=tvl>VALUE(was sending unsupportedmin_tvl) - "Other…" wallet button renamed to "Import"
- TVL filter replaced with min/max range text inputs (supports
- DLMM token icons now load correctly — icon lookups proxied through backend to fix CORS errors
- Backend caches token icons 24h in-memory, eliminating repeat Jupiter API calls and 429s
- Auto Trader runs as a background systemd process — continues trading without browser open
update.shskips backend rebuild when no backend files changed (faster redeployment)
- Auto Trader entry fields: suffixes/units now displayed inside input fields; all fields uniform width
- Activity Log card height capped to match other dashboard cards
- Auto Trader: price change filter replaced with a min/max Price Chg Range (was single min value)
- HTTPS support added to bare-metal install — self-signed TLS cert generated automatically
install.shnow auto-detects and setsFRONTEND_ORIGINto the server IP- Fixed relative API base path in production builds
- Auto Trader — automated meme coin sniper bot
- Configurable entry rules: signal interval, poll rate, buy amount, max positions, min organic score, price change range, price impact, mcap range, slippage, and skip filters (suspicious, mintable, freezable)
- Configurable exit rules: trailing stop %, take profit %, max hold time
- Live Activity Log with buy/sell events and Solana Explorer links
- Cumulative P&L chart with per-trade history
- Encrypted private key vault — key never leaves the browser unencrypted
- Close empty token accounts utility
- Portfolio tab: token detail panel with quick-sell on row click
- Portfolio tab: 24h price change column on token holdings
- Close empty token accounts button (reclaim rent)
- Fixed stale chart on token switch
- Trending tab: organic buyers column, interval filters, and one-click quick buy buttons
- DLMM tab redesigned with Trending-style layout
- Trending tab — Jupiter organic score token leaderboard
- 5m / 1h / 6h / 24h interval selector with per-interval price change and volume
- Rank, logo, name/symbol, price, % change, volume, organic score, and audit warnings per row
- Click any row to expand a full TokenInfoPanel with price, liquidity, market cap, and safety audit
- Buy button navigates directly to Swap with the token pre-selected
- 30s auto-refresh; data pulled directly from Jupiter toptrending API (no extra price calls)
- Perps SL/TP fixes
- Fixed error 6039 "Exponent Mismatch" for equity trigger orders (SPY etc.) — Flash Trade API hard-codes exponent -6 but equity oracles use -5; now patches transaction instruction bytes post-build
- SL/TP orders now visible on position cards — added
/orders/owner/:walletendpoint and merged trigger data into position display - Switched RPC to Helius for reliable blockhash fetching (avoids public endpoint 403s)
- Meteora DLMM Add Liquidity modal redesigned to match Meteora UI
- Fixed error 6040 "Invalid position width" — removed ±1 bin padding that exceeded 70-bin limit; default range targets ~30 bins using bin-step math
- Swap page wallet token selector — "You Pay" token dropdown shows your wallet balances at the top
- Dashboard price card shows BTC, SOL, and SPY via Flash Trade API; clicking a ticker switches the chart
- Swap page redesigned to single centered column with Jupiter-style TokenInfoPanel for output token (price, liquidity, market cap, audit flags)
- Wallet balance displayed for all tokens in "You Pay" field (not just SOL)
- Perps tab — perpetual futures trading via Flash Trade
- Long/short on SOL, BTC, ETH, JitoSOL, JUP, BONK, WIF, PYTH, JTO, RAY, plus equities (SPY, NVDA, TSLA, AAPL, AMD, AMZN) and commodities (XAU, XAG)
- 1×–10× leverage with presets and slider
- Live entry price + liquidation price preview
- Stop Loss / Take Profit orders with on-chain validation
- Add / Remove collateral to adjust leverage
- TradingView-style price chart above the trade form via Pyth Network OHLCV
- Live mark prices polled every 5s; positions refreshed every 15s
- Meteora DLMM Liquidity — full integration with the Meteora DLMM SDK
- View all active positions across every pool with live APR, bin range, and claimable fees
- Claim swap fees and LM rewards per position or all at once
- Remove 100% of liquidity and close a position in one action
- Pool browser with search, sorted by Fee/TVL, Volume, or APR
- Add liquidity with Spot, Curve, and Bid-Ask strategy selection
- Token search icons, SOL price display, real token logos via Jupiter CDN
- Mobile layout: slide-over sidebar with hamburger, auto-close on navigation
- Wallet backup flow, swap transaction feedback, on-chain history
- Initial release
MIT — use freely, build on top, ship your own terminal.
Built with ⚡ on Solana · Powered by Jupiter, Meteora & Flash Trade