# PoolScout v0.4.0 PRD

| Field | Value |
|---|---|
| Version | Draft 1.5 |
| Date | 12 November 2026 (UTC+7) |
| Owner | King (Fomo), product owner |
| Author | LP Hunter |
| Status | Draft for review. Build starts only on King's explicit go-ahead. |
| Baseline | Backend v0.3 (Node 22, TypeScript, Hono, SQLite) on King's Windows PC; Chrome MV3 side-panel extension v0.3.1 (React, TypeScript, Vite), deployed. |
| Scope | Meteora only: DLMM and DAMM v2 on Solana. PumpSwap stays disabled. |

## 1. Summary, goals and non-goals

PoolScout v0.4.0 turns the v0.3 radar and copy tool into a complete, self-custodial LP workstation for Meteora DLMM and DAMM v2. It closes the feature gap with LP Agent (Discover, token safety, create pool, a full open-positions view, portfolio stats, copy-quality controls, share cards) and doubles down on what LP Agent does not offer: keys that never leave the user's PC, Telegram and WhatsApp alerts, a demo mode with virtual SOL, a named wallet tracker and new-pool alerts. It also adds a web app that pairs with the installed extension, and a mobile app (Expo, Android and iOS) that pairs with the user's backend, so all signing stays local and the key never lives on a phone or a hosted server.

### Goals

1. Reach parity with LP Agent on the core LP loop: find a safe token, pick or create its pool, open, manage and close a position, and share the result.
2. Make self-custody the visible product promise: private-key-only onboarding, key encrypted on the user's PC, no hosted signer, explicit arming of copy.
3. Raise copy quality: fee-checked leaders, Meme Protection, fund-loss pause, per-wallet limits and Copy Logs that explain every skip.
4. Give a full open-positions view: range, composition, fees, PnL breakdown, bins, activity, and claim, add and close actions.
5. Lower the learning cost with demo mode (virtual SOL, real prices) and keep safe defaults (copy master OFF, dry run ON).
6. Ship Telegram and WhatsApp alerts for position, copy and new-pool events.
7. Offer a web app that pairs with the extension, plus public no-login pages, without moving keys or signing to a server.
8. Offer a mobile app (Expo, Android and iOS from one codebase) that is UI only: it pairs with the user's local backend or self-hosted VPS, reuses the approved compact design system, and never holds a private key.

### Non-goals

- PumpSwap, Raydium, Orca or any non-Meteora venue (PumpSwap is postponed).
- Other chains (LP Agent's Robinhood Chain and Elysium are not PoolScout's market).
- A hosted or cloud signer, custodial wallet, or server-side automation.
- Public API, CLI or MCP server.
- Paid referral rewards before the fee model is decided (v0.4.0 ships referral UI and tracking only).
- PoolScout-run servers for users: 24/7 operation comes only from the user's own VPS (self-hosted always-on mode), never from a PoolScout cloud.
- Story-format (9:16) share cards.
- Seed-phrase onboarding or PIN unlock.

## 2. Positioning vs competitors

| Capability | PoolScout v0.4.0 | LP Agent | Krystal / OnlyFarm |
|---|---|---|---|
| Custody | Key encrypted on the user's PC, never sent to a server | Turnkey agent wallet in the cloud; restricted automation key | OnlyFarm: a vault per agent (vault program) plus an operator key that can trade but cannot withdraw; only the user's wallet withdraws. Krystal: wallet-connect plus EIP-712 orders executed by a Krystal Operator |
| Runs when the PC is off | PC mode: no. Self-hosted always-on mode (planned, P0): yes, on the user's own VPS | Yes. Copy, TP/SL, rebalance and compound run 24/7 | Yes. Operator and agents run in the cloud; OnlyFarm checks exit rules every 30 s |
| Copy landing speed | About 1 s detection via Helius stream; Jito bundles with dynamic priority fees planned (P0), normal RPC fallback | Claims 90% of copies land within 3.5 s via Jito bundles | No latency claim found. OnlyFarm agents act on a heartbeat (10 or 30 min) plus a 30 s exit loop; Krystal Solana zaps use Jito by default |
| Venues | Meteora DLMM and DAMM v2 | Meteora DLMM and DAMM v2; Uniswap v3/v4 on Robinhood Chain | OnlyFarm: Solana, Meteora DLMM and DAMM v2 only (PumpSwap listed for research). Main Krystal app: 10 EVM chains; current marketing no longer lists Solana |
| Notifications | Telegram and WhatsApp (WAHA) | Browser/PWA push and in-app chimes; no Telegram or WhatsApp documented | Krystal Telegram bot and community groups; possible web push; no WhatsApp found |
| Demo / paper mode | Yes, virtual SOL with real prices (planned) | Not found in docs | OnlyFarm dry run (one heartbeat, nothing sent) and 7-day rule backtest; no virtual-balance mode |
| Named wallet tracker | Yes, custom names with separate notify and copy toggles (planned) | Partial: any-wallet lookup and copy labels | Not found. Krystal Top Wallets lookup; OnlyFarm Smart Follow uses up to 3 wallets as signals |
| New-pool alerts | Yes, filtered, to Telegram and WhatsApp (planned) | Partial: New and No Pool lists; no new-pool notifications | Partial: agents can screen new pools; no new-pool push channel documented |
| Safe defaults | Copy master OFF, dry run ON | Meme Protection and fund protection on by default | OnlyFarm: missing data never passes screening; copied agents start paused; non-refundable bin-array rent off by default |
| Discover, safety, create pool | Planned in v0.4.0 | Shipped | Hot Pools with filters, fee and IL simulation, 2 to 4 pool compare; OnlyFarm safety from Jupiter, RugCheck and DexScreener with rugged-token auto-exit; create pool not found |
| Fee-checked Smart LP | Planned in v0.4.0 | Shipped (fees under 10% of profit are hidden) | Top Wallets and strategy leaderboard; OnlyFarm Smart LP list with a 30-day record; no fee-checked filter documented |
| Pricing | Free today; fee model open (King's decision) | 8% of LP fees on automated close/claim; 0.1% on manual add/remove; no subscription | OnlyFarm: 10% of claimed fees and rewards (9% with a referral code), no deposit, withdrawal or swap cut, pays agent network fees. Krystal: 10% of vault rewards, 2% auto-compound/harvest, 0.01 to 0.25% action fees, 0.1% swap |
| API / MCP | No | Paid API ($20 lifetime, $40 and $80 per 30 days), CLI and MCP | Krystal Cloud API (paid units), CLI and MCP; OnlyFarm agent API |

> **Krystal / OnlyFarm:** OnlyFarm is the Krystal team's Solana product for Meteora DLMM and DAMM v2 (public beta since 10 Nov 2026) and is the direct peer. The main Krystal app no longer lists Solana.

Competitor facts come from `/workspace/lpagent-research/report.md`, `/workspace/lpagent-research/krystal-report.md` and the screenshots in `/workspace/lpagent-research/screens/` (research date 12 November 2026). Competitor figures are company claims.

- Position PoolScout as the self-custody LP terminal for Meteora: the key stays on your computer, nothing signs in the cloud, and copy is armed explicitly after a dry run.
- Be precise about custody: Krystal and OnlyFarm also call themselves non-custodial, but OnlyFarm runs each agent from a vault program with an operator key (it cannot withdraw), and Krystal uses a privileged Operator with signed orders. PoolScout has no vault, no operator key and no cloud signer: the key stays encrypted on the user's PC.
- Lead with chat alerts (Telegram and WhatsApp), demo mode and the named wallet tracker: LP Agent does not document any of them, and Krystal / OnlyFarm document no WhatsApp, no virtual-balance demo and no named tracker.
- Create pool and Terminal-style share cards are also openings against Krystal / OnlyFarm, whose docs show neither.
- Be honest about today's gaps: in PC mode automation stops when the PC or backend is off, and copies do not use Jito yet. v0.4.0 closes both with self-hosted always-on mode and Jito landing (see Competitive strategy).
- Do not compete on multi-chain, API or MCP in v0.4.0.

### 2a. Competitive strategy

> **Approved by King.** Sequencing: items 1, 2 and 7 first to reach parity (M1), then items 5 and 6 to win (M2). Item 3 follows in M3; item 4 stays King's decision.

| Strategy | Priority | Phase |
|---|---|---|
| 1. Close the 24/7 gap without giving up self-custody | P0 | Parity (M1) |
| 2. Landing speed | P0 | Parity (M1) |
| 3. Self-custody messaging and trust | P1 | Depth (M3) |
| 4. Pricing strategy | Decision | Launch, then Gated |
| 5. Ship-first differentiators | P0 | Win (M2) |
| 6. Go-to-market: own the Indonesian Meteora community first | P1 | Win (M2) |
| 7. Copy-quality parity | P0 | Parity (M1) |

#### 1. Close the 24/7 gap without giving up self-custody

- Self-hosted always-on: a one-click installer runs the PoolScout backend on the user's own VPS (about $5 per month, an estimate), for example Ubuntu with Docker or a Node service.
- The keystore stays encrypted on the user's server; the extension and web app pair to it over HTTPS or a secure tunnel with an auth token.
- Health check and heartbeat alerts to Telegram and WhatsApp when the backend goes down.
- PC mode stays supported.

Wired into: Feature spec Self-hosted always-on mode (P0, M1); NFR Reliability and Security; Risks.

#### 2. Landing speed

- Jito bundles plus dynamic priority fees for copy open, close and claim.
- Target p50 from leader tx to our landed tx: 2 s or less (an estimate to validate in M1).
- Fallback to normal RPC send; tip cap and daily tip budget settings.

Wired into: Feature spec Jito landing and priority fees (P0, M1); Success metrics.

#### 3. Self-custody messaging and trust

- Tagline idea: Your key never leaves your machine. No vault, no operator key.
- Security page in the extension and on the web.
- Open-sourcing the backend is King's decision; trade-offs are listed in the spec.

Wired into: Feature spec Trust: security page and open-source decision (P1, M3); Open questions.

#### 4. Pricing strategy

- Free during launch to win users.
- Then about 3 to 5% of claimed fees on automation only, or a flat Pro subscription.
- Both stay under LP Agent's 8% and OnlyFarm's 10%. King makes the final decision.

Wired into: Section 5 Monetization; Open questions; Gated milestone.

#### 5. Ship-first differentiators

- Demo mode.
- Telegram and WhatsApp alerts (already shipped; extended by New pool alerts).
- Named Wallet Tracker.
- Create pool.
- Terminal-style PnL share cards, built to travel on X.

Wired into: Demo mode, Wallet Tracker, Create pool and PnL card v2 move to M2 as P0; New pool alerts in M2.

#### 6. Go-to-market: own the Indonesian Meteora community first

- Indonesian UI language option and Indonesian guides; project copy still defaults to English and Indonesian is an added locale.
- An Indonesian PoolScout Telegram group.
- Referral once the fee model exists.

Wired into: Feature spec Indonesian locale and community (P1, M2); Referral (P2, M4).

#### 7. Copy-quality parity

- Fee-checked leader filter (from Smart LP leaderboard upgrade, pulled into M1).
- Meme Protection, fund-loss pause and Copy Logs (Copy quality).
- Rugged-token exit (Auto Range and TP/SL, pulled into M1).

Wired into: Copy quality (P0, M1); fee-checked filter and rugged-token exit ship in M1.

## 3. Personas and key journeys

### Personas

| Persona | Context | Needs |
|---|---|---|
| King, the operator | Runs the backend on a Windows PC in Indonesia (UTC+7). Farms Meteora DLMM daily, follows top LPs, wants fast alerts on his phone and full control of his key. | Reliable copy with clear skip reasons, positions at a glance, Telegram and WhatsApp alerts, share cards for his community. |
| The learner | New to DLMM bins, strategies and Auto Range. Afraid of losing SOL while learning. | Demo mode with virtual SOL, plain-language safety checks, dry run before anything real. |
| The follower | Wants to copy proven LPs with small size and strict limits. | Fee-checked leaderboard, named tracker, per-wallet limits, Meme Protection, fund-loss pause. |
| The early LP | Hunts new tokens with volume but no Meteora pool yet. | New-pool and No Pool alerts, safety check, one-tap Create pool with clear rent costs. |

### Key journeys

#### First run to first live position

1. Install extension and start backend (one-click pairing)
2. Try demo mode with virtual SOL, or import a private key
3. Quick setup: Telegram/WhatsApp, dry run ON, SOL per position
4. Fund a small amount via the Deposit sheet (QR)
5. Open a position in dry run, review the result
6. Turn dry run OFF in Risk settings and open live

#### New token to own pool

1. New-pool or No Pool alert arrives on Telegram
2. Open the token in Discover and read the safety sheet
3. No Meteora pool yet: tap Create pool
4. Choose DLMM or DAMM v2, fee and bin step, review rent
5. Create with optional seed liquidity, signed locally
6. Turn on Auto range and share a PnL card later

#### Leader to copy

1. Open Smart LP with the fee-checked filter on
2. Compare win rate, EV, average age and monthly PnL
3. Track the wallet with a custom name, notify ON
4. Enable copy for that wallet in dry run with per-wallet limits
5. Read Copy Logs for would-copy and skip reasons
6. Arm live copy when satisfied

#### Position out of range

1. Out-of-range alert arrives
2. Open the position: range view, composition and PnL breakdown
3. Let Auto range rebalance, or claim fees and close
4. Share the closed result as a Landscape or Square card

#### Web app with the extension

1. Open the PoolScout web app and browse public pages without login
2. Tap Connect extension; a pairing code and QR appear
3. Approve the code in the extension; the extension signs the challenge with the hot wallet
4. Use portfolio and position screens on the web; every action is confirmed in the extension
5. Unpair from either side, or let the session expire

#### Mobile app with the user's backend

1. Install PoolScout from the store or an EAS build
2. Scan the pairing QR shown in the extension or on the backend (VPS HTTPS or a secure tunnel)
3. Browse positions and portfolio on the phone; the key stays on the PC or VPS
4. Receive a push when a position is out of range, hits TP/SL or is flagged rugged
5. Confirm a claim or close in the app; the backend signs; unpair from Paired devices

## 4. Feature specifications

| Feature | Priority | Milestone |
|---|---|---|
| Create pool (DLMM and DAMM v2) | P0 | M2 |
| Discover upgrade | P1 | M3 |
| Pools filters | P1 | M3 |
| Token safety check | P0 | M1 |
| New pool alerts | P1 | M2 |
| Wallet Tracker | P0 | M2 |
| Smart LP leaderboard upgrade | P1 | M3 (fee-checked filter in M1) |
| Portfolio stats, PnL/Fee chart and calendar | P1 | M3 |
| Open positions UI | P0 | M1 |
| Copy quality | P0 | M1 |
| Auto Range and TP/SL (refine) | P1 | M3 (rugged-token exit in M1) |
| Demo mode | P0 | M2 |
| PnL card v2 editor and share | P0 | M2 |
| Referral (UI and tracking only) | P2 | M4 |
| Self-hosted always-on mode (VPS) | P0 | M1 |
| Jito landing and priority fees | P0 | M1 |
| Trust: security page and open-source decision | P1 | M3 |
| Indonesian locale and community | P1 | M2 |
| Pre-open simulation | P1 | M2 |
| Daily recap via Telegram and WhatsApp | P1 | M2 |
| Token watchlist | P1 | M2 |
| Copy backtest | P1 | M2 |
| Web app paired with the extension | P1 | M4 |
| Mobile app (Expo, Android and iOS) | P1 | M5 |

> **API gaps for the positions UI** (found earlier): Composition split (SOL and token amounts and percentages) per position. IL breakdown (fees vs price change vs network and rent). Bin distribution with the active bin (DLMM). Activity timeline (opened, claimed, rebalanced, added, closed) with tx links. All four are covered by the new `GET /positions/:id/detail` endpoint (see 4.9).

### 4.1 Create pool (DLMM and DAMM v2) (P0, M2)

Create a Meteora DLMM or DAMM v2 pool for any token pair, with optional seed liquidity, signed locally with the hot wallet.

**Mockups:** /workspace/poolscout-mockup/create-pool/ (0-entry-pools, a-type, b0-no-pool, b0b-invalid-mint, b-pair, c-dlmm-settings, d-damm-settings, e-liquidity, f-review, g-success)

**User stories**

- As an early LP, I want to create a pool when a token has volume but no Meteora pool, so I earn fees first.
- As a user, I want to see the full rent cost and what is refundable before I sign.
- As a user, I want to be sent to the existing pool if the same pair, bin step and fee already exist.

**Functional requirements and UI**

1. Entry points: Pools tab button, Discover No Pool row, token page empty state (b0-no-pool).
2. Pair step: paste or search base mint; quote SOL or USDC; validate mint (b0b-invalid-mint); run the token safety check inline.
3. DLMM: preset list from `@meteora-ag/dlmm` (bin step + base fee); create with `createLbPair2` or `createCustomizablePermissionlessLbPair2`; optional activation time; dynamic fee flag.
4. DAMM v2: fee tiers 0.25, 0.3, 1, 2, 4 and 6%; optional fee scheduler (linear over 24 h or exponential over 2 h); create with `createPool` or `createCustomPool`.
5. Initial price defaults to the market price (Jupiter); warn when more than 5% away, block when more than 20% away.
6. Duplicate check: derive the pool address; if it exists, show Open existing pool instead of Create.
7. Seed liquidity (optional): SOL only, token only or both; strategy Spot, Curve or Bid-Ask; range presets ±5/10/20%; max 69 bins per position.
8. Review shows cost lines: pool accounts (non-refundable), bin arrays (non-refundable), position rent (refundable), network and priority fee, total, and the number of transactions.
9. Rent preview: every new bin array the seed position needs is listed with its cost and marked non-refundable before signing; refundable and non-refundable totals are shown separately.
10. Dry run simulates every transaction and shows the would-be pool address; demo mode creates a virtual pool.
11. Success screen with pool address, Solscan and Meteora links, Add liquidity, Turn on Auto range.

**Backend API**

- `GET /pools/presets?type=dlmm|damm` returns bin step and fee presets
- `POST /pools/create/quote` validates input, derives the address, checks duplicates, simulates and returns the cost breakdown
- `POST /pools/create` signs and sends (respects dry run, reserve and risk limits); returns tx signatures and pool address

**Data model (SQLite)**

- `created_pools` (id, pool_address, type, base_mint, quote_mint, bin_step, base_fee_bps, fee_scheduler, init_price, activation_ts, seed_position_id, rent_lamports, txs_json, dry_run, created_at)

**Edge and error states**

- Invalid or non-SPL mint: inline message, Continue disabled.
- Pool already exists: Open existing pool.
- Balance below total cost plus the 0.05 SOL reserve: disabled button with a Deposit link.
- Second transaction fails after the pool is created: show the pool as created without liquidity and offer Add liquidity.
- Token flagged Risky: allow, but require an extra confirm.
- RPC or Jupiter price unavailable: ask for a manual price and show the warning.

**Acceptance criteria**

- A DLMM pool with seed liquidity is created on mainnet with a small amount and appears in Meteora.
- Review cost matches the simulation within 5%.
- Duplicate pair, bin step and fee never produce a create transaction.
- Dry run and demo mode never send a transaction.
- Rent shown as estimates: DLMM pool about 0.012 SOL, each bin array about 0.071 SOL (non-refundable), position about 0.057 SOL (refundable); DAMM v2 about 0.02 to 0.03 SOL (estimate).

### 4.2 Discover upgrade (P1, M3)

A token-first Discover view with preset tabs, safety, and Open pool or Create pool on every row.

**Mockups:** /workspace/poolscout-mockup/copy/radar.png (current Radar, v0.3); /workspace/poolscout-mockup/create-pool/b0-no-pool.png (No Pool state); New mockup needed for the tab strip and token rows

**User stories**

- As a user, I want to scan trending and new tokens with their pools in one list.
- As an early LP, I want a No Pool tab for tokens with volume and no Meteora pool.
- As a cautious user, I want to hide risky tokens.

**Functional requirements and UI**

1. Tabs: Trending, New, Graduated, Top Fee/TVL, Rising fees, Smart money in, No Pool.
2. Time window: 5M, 1H, 6H, 24H.
3. Row: circular token logo, symbol, age, price and 24h change, market cap, holders, volume, liquidity, Fee/TVL, safety badge, pool count.
4. Row action: Open pool (best pool by Fee/TVL) or Create pool when the token has no Meteora pool.
5. Hide risky toggle (32x18) hides Risky and Unknown-safety tokens.
6. Smart money in uses PoolScout's own watched and tracked wallets (holdings, events).
7. Data cached in SQLite and refreshed every 60 s; the list shows Updated N s ago.

**Backend API**

- `GET /discover?tab=trending|new|graduated|top_fee_tvl|rising_fees|smart_money|no_pool&window=5m|1h|6h|24h&hideRisky=1`

**Data model (SQLite)**

- `tokens` (mint, symbol, name, logo, decimals, created_at, launchpad, graduated_at, mcap, holders, updated_at)
- `token_stats` (mint, window, volume, price_change, fee_tvl, liquidity, pool_count, ts)

**Edge and error states**

- Upstream source down: keep the last cached list with its age; never show an empty table.
- Token logo unverifiable or offensive: show a letter badge.
- Same token in many pools: one row, pools on expand.

**Acceptance criteria**

- Each tab loads under 1 s from cache.
- No Pool rows always offer Create pool and never Open pool.
- Hide risky removes Unknown as well as Risky tokens.

### 4.3 Pools filters (P1, M3)

Filter the Pools table by protocol, base fee, bin step, TVL, Fee/TVL, volume and organic score.

**Mockups:** /workspace/poolscout-mockup/copy/pools-open.png (current Pools); /workspace/poolscout-mockup/safety/s1-pools-safety-chip.png; New mockup needed for the filter sheet

**User stories**

- As a user, I want only DLMM pools with bin step 80 to 100 and fee at least 1%.
- As a user, I want to hide pools whose token has a low organic score.

**Functional requirements and UI**

1. Filter sheet: protocol (DLMM, DAMM v2), quote (SOL, USDC), base fee range, bin step list, min TVL, min Fee/TVL by window, min 24h volume, min Jupiter organic score, safety level.
2. Sort: Fee/TVL (default), volume, TVL, age.
3. Active filters shown as soft chips; Reset clears all.
4. Filters persist per device.

**Backend API**

- `GET /pools?protocol=&quote=&feeMin=&feeMax=&binStep=&tvlMin=&feeTvlMin=&window=&volMin=&organicMin=&safety=&sort=`

**Data model (SQLite)**

- `ui_prefs` (key, value_json, updated_at) for saved filters

**Edge and error states**

- Filters return nothing: show the closest matches with a note instead of an empty list.
- Organic score unavailable: treat as unknown and exclude only when the filter is set.

**Acceptance criteria**

- Every filter combination returns in under 500 ms from cache.
- Saved filters survive a side-panel reload.

### 4.4 Token safety check (P0, M1)

A three-level safety result (Safe, Caution, Risky, plus Unknown) on every token, used in Pools, Discover, open, create pool and copy filters.

**Mockups:** /workspace/poolscout-mockup/safety/ (s1-pools-safety-chip, s2-open-position-safe, s3-safety-sheet-safe, s4-safety-sheet-risky, s5-open-position-risky, s6-copy-settings-skip-risky)

**User stories**

- As a user, I want to see red flags before I put SOL into a pool.
- As a follower, I want copy to skip risky tokens automatically.

**Functional requirements and UI**

1. Checks: mint authority, freeze authority, Token-2022 extensions (transfer fee, permanent delegate, transfer hook), top-10 holders excluding pools and burn, liquidity depth, RugCheck report, Jupiter verified and organic tags.
2. Level rules: any critical flag gives Risky; any warning gives Caution; Safe only when every source answered and no flag fired.
3. Unknown is its own state and is never shown as Safe; a source that is down shows as unknown in the sheet.
4. Missing data never passes: any safety check, pool filter or copy token filter whose data is missing or stale counts as not passed (the token or pool is skipped and the reason is logged).
5. Rugged-token monitor: tokens held in open positions are re-checked on RugCheck every 5 min (proposal); a rugged result triggers the auto-exit rule in Auto Range and TP/SL.
6. Sheet lists each check with its value, the sources and the check age; View on RugCheck link.
7. Open and create pool on a Risky token need an extra confirm; copy settings has Skip Risky (default ON) and Skip Caution (default OFF).
8. Results cached 10 min; mint and freeze authority re-checked before every live open.

**Backend API**

- `GET /tokens/:mint/safety` (level, flags[], checks[], sources[], checkedAt)
- `POST /tokens/safety/batch` for list views

**Data model (SQLite)**

- `token_safety` (mint, level, flags_json, checks_json, sources_json, checked_at)

**Edge and error states**

- RugCheck or Jupiter down: level becomes Unknown unless RPC alone already found a critical flag (then Risky).
- Token-2022 extension not recognised: Caution with the extension name.
- Rate limit: serve cache with age.

**Acceptance criteria**

- No token with an unanswered source ever shows Safe.
- A filter with missing input data never passes in unit tests.
- Copy with Skip Risky ON never opens on a Risky token (verified in Copy Logs).
- Sheet opens in under 300 ms from cache and under 3 s fresh.

### 4.5 New pool alerts (P1, M2)

Filtered alerts for new Meteora pools, volume spikes and high Fee/TVL, in a Radar list and on Telegram and WhatsApp.

**Mockups:** /workspace/poolscout-mockup/alerts/ (a1-radar-new, a2-alert-filters, a3-telegram-alert)

**User stories**

- As an early LP, I want a Telegram ping when a new pool matches my filters.
- As a user, I do not want alert spam at night.

**Functional requirements and UI**

1. Sources: Meteora pool creation for DLMM and DAMM v2 (Helius logs subscription on program IDs, with an API poll fallback).
2. Filters: min TVL, min Fee/TVL over the last hour, pool types, quote token, exclude Risky (default ON), exclude Caution.
3. Also alert on: 1h volume at least 5x the 6h average; existing pools above 2%/h Fee/TVL.
4. Delivery: max alerts per hour, extra alerts grouped into one summary; channels Telegram and WhatsApp; quiet hours in UTC+7; Send test alert.
5. Message: pair, protocol, fee and bin step, TVL, Fee/TVL, safety level, links to Meteora and PoolScout.
6. The same detection pipeline powers the Token watchlist (kind `watchlist`), which bypasses these filters for watched mints.

**Backend API**

- `GET/PUT /alerts/settings`
- `GET /alerts/feed?cursor=`
- `POST /alerts/test`

**Data model (SQLite)**

- `pool_alert_settings` (single row, filters_json, channels_json, quiet_hours, max_per_hour)
- `pool_alerts` (id, pool_address, kind, payload_json, sent_channels, created_at)

**Edge and error states**

- Helius key exhausted: fall back to polling and show Polling 60s in the header.
- WhatsApp session logged out: mark the channel as disconnected and send to Telegram only.
- Burst of launches: grouping kicks in at the hourly cap.

**Acceptance criteria**

- A new matching pool alerts within 10 s with a healthy Helius key and within 70 s on polling.
- Quiet hours suppress chat delivery but keep the in-app list.
- Risky tokens never alert when excluded.

### 4.6 Wallet Tracker (P0, M2)

Paste any wallet, give it a custom name, and choose to be notified and/or to copy it; rename or remove anytime.

**Mockups:** /workspace/poolscout-mockup/wallet-tracker/ (h-tracked-list, h2-leaderboard-star, i-add-wallet, i2-add-duplicate, i3-add-invalid, j-edit-wallet, k-tracked-profile)

**User stories**

- As a user, I want to track a wallet under a name I choose.
- As a user, I want to get notified on its opens and closes without copying it.
- As a user, I want to star a wallet from the leaderboard.

**Functional requirements and UI**

1. Wallets tab segments: Leaderboard and Tracked.
2. Add: paste address, validate, show Meteora positions in the last 30 days, name up to 24 characters, Notify toggle, Copy toggle (uses Risk settings and dry run).
3. Edit: rename, toggles, remove with confirm; removing stops copy of new positions but does not close open copies.
4. Tracked list: initials avatar, name, short address, tier if ranked, open count, 24h PnL, copying tag.
5. Live activity feed limited to tracked wallets.
6. Profile page reuses the wallet profile with calendar.
7. Simulate copy (Copy backtest) is offered on add, edit and the first time Copy is turned on.

**Backend API**

- `GET /tracked`, `POST /tracked`, `PATCH /tracked/:addr`, `DELETE /tracked/:addr`
- Existing `GET /wallets/:addr/profile` and `/wallets/:addr/calendar`

**Data model (SQLite)**

- `tracked_wallets` (address, name, notify, copy, source, created_at, updated_at); `copy` mirrors into existing `copy_wallets`

**Edge and error states**

- Duplicate address: show the existing entry (i2-add-duplicate).
- Invalid address: inline message (i3-add-invalid).
- Wallet with no Meteora history: allow with a note.
- Name collision: allowed, address disambiguates.

**Acceptance criteria**

- Rename takes effect in list, feed and notifications without restart.
- Notify-only wallets never trigger a copy.
- Tracked wallets are never removed by the daily ranking run.

### 4.7 Smart LP leaderboard upgrade (P1, M3 (fee-checked filter in M1))

Add a fee-checked filter and richer stats so leaders are chosen for LP skill, not token pumps.

**Mockups:** /workspace/poolscout-mockup/wallet-tracker/h2-leaderboard-star.png; /workspace/poolscout-mockup/copy/wallet-profile.png; New mockup needed for the stat columns and filter sheet

**User stories**

- As a follower, I want to hide wallets whose profit comes mostly from their own token.
- As a follower, I want to compare win rate, EV and hold time over 7D, 30D and ALL.

**Functional requirements and UI**

1. Fee-checked filter (default ON): hide wallets whose LP fees are under 10% of profit (threshold editable). Ships early in M1 as part of copy-quality parity; the copy engine also refuses leaders that fail it unless the user overrides.
2. Columns: PnL, win rate, EV per position, average age, average invested, monthly PnL, fees, last active; period switch 7D, 30D, ALL.
3. Warning badge when the average hold is under 18 minutes.
4. Default filters: active in the last 7 days, minimum positions, minimum win rate (editable).
5. Keep existing tiers S, A, B and the score; show the score breakdown.
6. Star to track (Wallet Tracker).
7. Simulate copy on each card opens the Copy backtest for 7D or 30D.

**Backend API**

- `GET /wallets/leaderboard?period=7d|30d|all&feeChecked=1&minWin=&minPositions=&activeDays=`

**Data model (SQLite)**

- Extend `wallet_scores` with ev_sol, avg_age_min, avg_invested_sol, monthly_pnl_sol, fee_share, period

**Edge and error states**

- ALL period needs history beyond 30 days: compute progressively and mark partial data.
- Wallet with too few positions: show but grey out stats.

**Acceptance criteria**

- Fee-checked hides every wallet below the threshold.
- Stats match a manual calculation on three sample wallets.

### 4.8 Portfolio stats, PnL/Fee chart and calendar (P1, M3)

Headline stats, a PnL or Fee chart by day, week or month, and the calendar, for the user's wallet and any tracked wallet.

**Mockups:** /workspace/poolscout-mockup/copy/pnl-calendar.png; /workspace/poolscout-mockup/demo/d4-demo-portfolio.png; /workspace/poolscout-mockup/pnl-card/e3-entry-my-pnl-month.png

**User stories**

- As a user, I want win rate, EV, average invested and average monthly profit at a glance.
- As a user, I want to see which days were green or red.

**Functional requirements and UI**

1. Stats: net worth, closed positions, win rate, average invested, fees, total profit, average monthly profit, EV.
2. Chart: PnL or Fee; Daily, Weekly, Monthly; SOL or USD.
3. Calendar: green and red days; tap a day for its positions and a share entry.
4. Filters: protocol, Manual vs Copy vs Auto, Hide small PnL.
5. CSV export of closed positions.

**Backend API**

- `GET /portfolio/stats?wallet=&period=`
- `GET /portfolio/series?metric=pnl|fee&bucket=day|week|month&unit=sol|usd`
- Existing `/copy/calendar` and `/wallets/:addr/calendar`
- `GET /portfolio/export.csv`

**Data model (SQLite)**

- `pnl_daily` (wallet, date, pnl_sol, pnl_usd, fees_sol, positions_closed) materialised from `copy_positions` and upstream data

**Edge and error states**

- Other wallets' PnL is indexed and may miss external swaps: label as estimated.
- Empty history: show the demo prompt instead of an empty chart.

**Acceptance criteria**

- Own-wallet totals reconcile with `copy_trades` within 1%.
- Calendar day sums equal chart daily values.

### 4.9 Open positions UI (P0, M1)

A full list and detail view for open positions with range, composition, fees, PnL breakdown, bins, automation and activity, plus claim, add and close.

**Mockups:** /workspace/poolscout-mockup/positions/ (p1-positions-list, p2-detail-in-range, p3-detail-out-of-range, p4-close-sheet, p5-add-liquidity-sheet, p6a-claim-confirm, p6b-claim-success-toast, p7-empty)

**User stories**

- As a user, I want to see at once which positions are out of range.
- As a user, I want to know how much of my PnL is fees versus price change.
- As a user, I want to claim, add or close without leaving the panel.

**Functional requirements and UI**

1. List: pair with circular logos, plain-text type label (DLMM or DAMM v2), source (Manual, Copy of name, Auto), value, PnL in green or red, in-range state, age.
2. Range view modes: Price, Percent to edges, and 24h chart with the range band.
3. Liquidity view: bin distribution with the active bin marked (DLMM).
4. Composition: SOL and token amounts with the percentage split.
5. Fees: Fee/TVL rate, unclaimed, claimed, total earned; daily percentage rate (DPR) estimate.
6. PnL breakdown: fees, price change (IL), network and rent, net; labelled Estimated.
7. Automation summary with Edit (Auto range, TP, SL, claim interval).
8. Activity timeline: opened, fees claimed, rebalanced, added, closed, with tx links.
9. Actions: Claim fees (confirm and success toast), Add liquidity sheet, Close sheet (claim fees, swap back to SOL option).
10. Rent preview on open, add and rebalance: refundable position rent and any new non-refundable bin arrays are shown before signing, with the bins that need them.

**Backend API**

- Existing `GET /positions`, `GET /positions/:id`, `POST /positions/:id/claim`, `POST /positions/:id/close`, `PUT /positions/:id/automation`
- New `GET /positions/:id/detail` returning composition, pnlBreakdown, bins[], dpr, activity[] (fills the API gaps)
- New `POST /positions/:id/add` and `POST /positions/:id/add/quote`
- New `GET /positions/:id/chart?window=24h`

**Data model (SQLite)**

- `position_events` (id, position_id, kind, amount_sol, amount_token, tx, meta_json, ts)
- `position_snapshots` (position_id, ts, value_sol, fees_unclaimed_sol, active_bin, in_range) every 60 s for charts and IL

**Edge and error states**

- Position closed elsewhere (e.g. directly on Meteora): mark Closed externally and reconcile.
- Claim with zero fees: button disabled with a hint.
- Close fails mid-way (claim succeeded, remove failed): show partial state and Retry.
- No positions: empty state with Open position and Try demo (p7-empty).

**Acceptance criteria**

- All four API gaps (composition split, IL breakdown, bin distribution, activity timeline) are served by the backend.
- Out-of-range positions sort first and are marked.
- Claim, add and close work in dry run, demo and live.

### 4.10 Copy quality (P0, M1)

Controls that stop bad copies: Meme Protection, fund-loss pause, per-wallet limits, token filters and Copy Logs with skip reasons.

**Mockups:** /workspace/poolscout-mockup/copy/copy-settings.png (v0.3 settings); /workspace/poolscout-mockup/safety/s6-copy-settings-skip-risky.png; /workspace/poolscout-mockup/copy/copy-feed.png; New mockup needed for Copy Logs and per-wallet limits

**User stories**

- As a follower, I want to skip copies that would put almost all my deposit in a meme token.
- As a follower, I want copy to pause by itself after a set loss.
- As a follower, I want to know why a copy was skipped and fix it in one tap.

**Functional requirements and UI**

1. Meme Protection (default ON): skip when more than 95% of the deposit would sit in the non-SOL token.
2. Fund-loss pause (default ON, 20% of starting copy capital, editable): pause new opens and adds when closed-copy losses from peak hit the limit; exits still follow the leader; notify.
3. Per-wallet limits: copy %, max SOL per position, max open positions, daily SOL cap, strategy filter (Spot, Curve, Bid-Ask, DAMM v2).
4. Token filters: min organic score, min market cap, min 24h volume, min holders, skip Risky, blacklist; missing data never passes a filter.
5. Min leader hold-time warning (under 18 minutes average).
6. Copy Logs (7 days): every detected leader action with result Copied, Would copy (dry run), Skipped or Failed, the reason, latency, and a fix button (e.g. Raise limit, Allow strategy).
7. Low balance pause: pause opens when the balance is below the next position plus reserve.
8. Copy-quality parity bundle for M1 (Competitive strategy item 7): this spec plus the fee-checked filter (Smart LP leaderboard upgrade), rugged-token exit (Auto Range and TP/SL) and Jito landing.

**Backend API**

- Existing `GET/PUT /copy/settings`, `GET /copy/trades`
- New `GET /copy/logs?wallet=&result=&since=`
- New `PUT /copy/wallets/:addr/limits`
- New `POST /copy/resume` after a fund-loss pause

**Data model (SQLite)**

- `copy_logs` (id, leader, pool, action, result, reason_code, reason_text, latency_ms, tx, ts)
- Extend `copy_wallets` with limits_json
- Extend `copy_settings` with meme_protection, fund_loss_pct, token_filters_json, paused_reason

**Edge and error states**

- Leader action seen but pool data unavailable: Failed with a retry.
- Fund-loss pause active and the user opens manually: allowed, not counted as copy.
- Clock skew between detection and send: latency still logged.

**Acceptance criteria**

- Every leader action produces exactly one Copy Log row with a reason code.
- Meme Protection blocks a 96% one-sided deposit in tests.
- Fund-loss pause triggers at the threshold in a replay test and sends a notification.

### 4.11 Auto Range and TP/SL (refine) (P1, M3 (rugged-token exit in M1))

Refine the shipped automation: clearer editor, USD or SOL targets, per-position overrides and a visible rebalance history.

**Mockups:** /workspace/poolscout-mockup/positions/p2-detail-in-range.png (Automation block); /workspace/poolscout-mockup/copy/open-position-advanced.png

**User stories**

- As a user, I want to set TP and SL in SOL or USD.
- As a user, I want to see how many rebalances happened today and why.

**Functional requirements and UI**

1. Keep the 15 s monitor; Auto range after N minutes out of range (default 10), direction up, down or both, max 8 rebalances per day, min gap 30 min.
2. Fee-coverage gate for DLMM Auto range (default ON, 100%): rebalance only when fees earned over the recent window cover the estimated swap cost plus any new non-refundable bin-array rent; otherwise hold and log Not covered.
3. Rugged-token auto-exit (default ON): when the rugged-token monitor flags a token in an open position, close the position, claim fees and swap back to SOL, then notify; exit wins over rebalance when both fire. Ships early in M1 as part of copy-quality parity.
4. TP and SL in percent, SOL or USD, measured on value plus fees against entry.
5. Defaults for new manual positions (off by default) and for copies.
6. Rebalance and exit events appear in the activity timeline and in notifications.
7. Show a banner when the backend was offline and automation could not run.

**Backend API**

- Existing `PUT /positions/:id/automation`
- New `GET /automation/defaults`, `PUT /automation/defaults`

**Data model (SQLite)**

- Reuse `copy_positions` automation columns; log to `position_events`
- Extend `copy_positions` with fee_coverage_pct, rug_exit (bool) and last_rebalance_skip_reason

**Edge and error states**

- Price gaps through SL while offline: act on next run, notify with the actual result.
- Rebalance cap reached: notify and keep the position as is.

**Acceptance criteria**

- TP/SL triggers within one monitor cycle in replay tests.
- No rebalance exceeds the daily cap or the min gap.
- With the fee-coverage gate ON, no rebalance fires when recent fees are below swap cost plus new bin-array rent (replay test).
- A rugged flag closes the position within one monitor cycle and sends a notification.

### 4.12 Demo mode (P0, M2)

A separate practice account with virtual SOL and real prices. Nothing is signed or sent.

**Mockups:** /workspace/poolscout-mockup/demo/ (d1-welcome-try-demo, d2-demo-settings, d3-copy-demo, d4-demo-portfolio, d5-reset-demo)

**User stories**

- As a learner, I want to try opening, copying and creating pools without risking SOL.
- As a user, I want to reset the demo at any time.

**Functional requirements and UI**

1. Entry from onboarding (Try demo) and More.
2. Persistent DEMO badge in the header; all actions route to the simulator.
3. Virtual starting balance set by the user (default 10 SOL, a proposal).
4. Simulated: open and close, auto-copy, create pool, Auto range, TP/SL; fees and PnL estimated from live pool data, the same way as dry run.
5. Separate positions, PnL and calendar; Reset closes every demo position and restores the balance.
6. Real wallet and dry-run history are never touched.

**Backend API**

- `GET /demo/status`, `POST /demo/start`, `POST /demo/reset`, `POST /demo/stop`; existing position endpoints accept an `account=demo` scope

**Data model (SQLite)**

- `demo_account` (balance_lamports, start_balance, started_at)
- `account` column added to `copy_positions`, `copy_trades` and `position_events` (live, dry, demo)

**Edge and error states**

- User switches to live with demo positions open: they stay in the demo account.
- Pool data missing for a demo position: freeze its value and mark it stale.

**Acceptance criteria**

- No RPC send call happens in demo mode (verified by test).
- Reset restores the starting balance and clears demo positions only.

### 4.13 PnL card v2 editor and share (P0, M2)

Terminal-style share cards in Landscape 1600x900 and Square 1080x1080, customizable, shared via clipboard and X or Telegram intents.

**Mockups:** /workspace/poolscout-mockup/pnl-card-v2/ (v1-share-sheet-landscape, v2-customize-background, v3-customize-columns-details, v4-customize-loss-square, v5-share-to-x-with-referral, cards/)

**User stories**

- As a user, I want to share a closed position or a period as an image.
- As a user, I want to choose what the card shows.

**Functional requirements and UI**

1. Formats: Landscape 1600x900 and Square 1080x1080 only.
2. Layout: black background, brand lockup top left, referral top right, big token name, large SOL profit (green for profit, red for loss), Invested / Position / PnL columns with USD in brackets, light-streak art.
3. Customize: background, columns (deposited, withdrawn, current value, PnL %, fees), details (pool line, date and time in UTC+7, wallet address), referral link; choices saved as the default.
4. Sources: open position, closed position, day, month.
5. Share: copy PNG to clipboard, save PNG, open X or Telegram intent with prefilled text (intents cannot carry images).
6. Unverifiable token logos render as a letter badge.

**Backend API**

- Rendered client-side in the extension (canvas); `GET /share/data?positionId=|date=|month=` supplies numbers

**Data model (SQLite)**

- `ui_prefs` key `pnl_card_defaults`

**Edge and error states**

- Clipboard permission denied: fall back to Save PNG.
- Very long token names: shrink to fit, then truncate.

**Acceptance criteria**

- Exports are exactly 1600x900 and 1080x1080 px.
- Numbers on the card equal the position detail values.

### 4.14 Referral (UI and tracking only) (P2, M4)

Referral code, link, friends list and history. Rewards stay disabled until a fee model exists.

**Mockups:** /workspace/poolscout-mockup/referral/ (r1-referral-home, r2-referral-friends, r3-reward-history, r4-claim-sheet, r5a-enter-code-applied, r5b-enter-code-own-code). Rates in the mockups are sample terms only.

**User stories**

- As a user, I want my own code and link to share.
- As a new user, I want to enter a friend's code.

**Functional requirements and UI**

1. Code identity bound to a wallet signature; one code per wallet; one applied code per wallet within 7 days of install.
2. Link format `<domain>/r/<CODE>` (domain to be decided).
3. Friends list with joined and active counts; history list.
4. Claim UI hidden or disabled with a note until rewards launch.
5. Self-referral blocked (r5b-enter-code-own-code).
6. Optional referral line on PnL cards.

**Backend API**

- Hosted referral service (not the local backend): `POST /ref/code`, `POST /ref/apply`, `GET /ref/me` with wallet-signed requests

**Data model (SQLite)**

- Hosted store: `ref_codes` (code, wallet, created_at), `ref_links` (referrer, referee, applied_at), `ref_events` (referee, tx, fee_lamports, confirmed_at). Fee events only once a fee model exists.

**Edge and error states**

- Code taken: suggest alternatives.
- Referral service down: hide the page section, never block trading.

**Acceptance criteria**

- A code can be created, applied once, and shown in the referrer's friends list.
- No reward amount appears anywhere until King decides the fee model.

### 4.15 Self-hosted always-on mode (VPS) (P0, M1)

Run the same PoolScout backend 24/7 on the user's own VPS, installed in one step. The key stays encrypted on the user's server; PoolScout runs no servers for it. PC mode stays supported.

**Mockups:** New mockups needed: Backend mode chooser (PC or Server), server pairing screen, backend health card, offline banner

**User stories**

- As King, I want copy, Auto range and TP/SL to keep running when my PC is off, without handing my key to anyone.
- As a user, I want a Telegram or WhatsApp alert if my backend stops.
- As a user, I want to switch between PC mode and server mode.

**Functional requirements and UI**

1. Installer: one command on a fresh Ubuntu 22.04 or 24.04 VPS (about $5 per month, 1 vCPU and 1 GB RAM, an estimate) that installs Docker (or a Node 22 systemd service), the backend, a reverse proxy and the watchdog.
2. Keystore: the private key is generated on the server or imported in the server terminal (`poolscout key import`, read from stdin, never echoed); it is encrypted with the server's master key exactly as on the PC. Extension key import is disabled for remote backends by default.
3. Transport: HTTPS with an automatic TLS certificate on the user's own (sub)domain, or a secure tunnel; the backend binds to localhost behind the proxy.
4. Auth: the installer prints a pairing token and QR once; the extension and web app pair with it (`X-PoolScout-Token`), and the token can be rotated with `poolscout token rotate`.
5. Firewall: only 443 open (plus SSH with key-only login); fail2ban or equivalent recommended; automatic security updates on.
6. Health: `GET /health` with uptime, RPC status, stream status, last copy, version; the extension shows a Backend health card.
7. Heartbeat and down alerts: Docker or systemd restart policy; a watchdog sends Telegram and WhatsApp alerts on crash or restart; the extension alerts when `/health` misses 3 checks; an optional user-owned external uptime monitor can poll `/health`.
8. Mode chooser in More › Backend: PC (localhost) or Server (URL plus token); one active backend per wallet so two backends never sign for the same key.
9. Updates: `poolscout update` pulls a signed release and restarts with a health check and rollback on failure.
10. Restart reconciliation as in NFR Reliability: missed exits first, then automation, then a summary alert.

**Backend API**

- `GET /health` (public fields only without a token; full detail with a token)
- `GET /backend/info` (mode, version, host, uptime)
- `POST /backend/token/rotate`
- Installer and CLI: `poolscout install`, `poolscout key import`, `poolscout token rotate`, `poolscout update`, `poolscout status`

**Data model (SQLite)**

- `backend_heartbeats` (ts, uptime_s, rpc_ok, stream_ok, last_copy_ts)
- `backend_events` (id, kind start|stop|crash|update|rollback, detail, ts)

**Edge and error states**

- VPS out of memory or disk: watchdog alerts before the backend dies (thresholds 90%).
- TLS certificate renewal fails: alert 7 days before expiry.
- Two backends loaded with the same key: the second refuses to start automation and alerts.
- Server unreachable from the extension: offline banner; nothing is queued for later signing.
- Clock drift on the VPS: NTP enforced by the installer.

**Acceptance criteria**

- A fresh VPS goes from zero to paired in under 15 min following the guide (target).
- The private key never appears in installer output, logs, shell history or any network request (verified by test).
- Killing the backend process produces a Telegram alert within 2 min and an automatic restart.
- Copy and TP/SL keep running for 72 h with the PC off in a dry-run soak test.
- PC mode keeps working unchanged.

### 4.16 Jito landing and priority fees (P0, M1)

Land copy opens, closes and claims faster with Jito bundles and dynamic priority fees, with a normal RPC fallback and user-set tip caps.

**Mockups:** /workspace/poolscout-mockup/copy/copy-settings.png (settings home; new Landing rows needed); New mockup needed for the latency breakdown in Copy Logs

**User stories**

- As a follower, I want my copy to land close to the leader's transaction.
- As a user, I want to cap how much I spend on tips.

**Functional requirements and UI**

1. Send copy open, close and claim as Jito bundles (transaction plus tip) through a Jito block engine; manual actions can opt in.
2. Dynamic priority fee from recent prioritization fees (Helius priority-fee estimate), clamped by a max priority fee setting.
3. Settings: Landing mode (Jito with RPC fallback, RPC only), tip per bundle cap (default 0.0001 SOL, a proposal), daily tip budget, max priority fee.
4. Fallback: when a bundle has not landed within a few slots or the block engine errors, resend through normal RPC once; never double-execute (same blockhash and idempotency check).
5. Record each stage in Copy Logs: leader slot and time, detected, built, sent, landed, path (Jito or RPC), tip and priority fee.
6. Target p50 from leader tx to our landed tx: 2 s or less (an estimate to validate in M1 on real data).

**Backend API**

- `GET/PUT /copy/landing` (mode, tipCapLamports, dailyTipBudgetLamports, maxPriorityFee)
- Copy Logs entries extended with stage timings

**Data model (SQLite)**

- Extend `copy_logs` with leader_slot, detect_ms, build_ms, send_ms, land_ms, landing_path, tip_lamports, priority_fee_micro_lamports
- `landing_daily` (date, tips_lamports, bundles_sent, bundles_landed, rpc_fallbacks)

**Edge and error states**

- Daily tip budget used up: switch to RPC only and notify.
- Bundle lands and the RPC fallback also lands: prevented by the idempotency check; if detected, log and alert.
- Block engine region unreachable from the PC or VPS: RPC only with a warning.

**Acceptance criteria**

- p50 and p90 latency are reported from Copy Logs for every 7-day window.
- Tips never exceed the cap or the daily budget.
- Fallback lands the transaction when Jito is disabled or failing, without double execution.

### 4.17 Trust: security page and open-source decision (P1, M3)

Make the self-custody promise visible and checkable: a plain-language security page, the tagline, and a decision on open-sourcing the backend.

**Mockups:** New mockups needed: More › Security page, onboarding trust line, web security page

**User stories**

- As a new user, I want to know exactly where my key lives and what PoolScout can and cannot do.
- As a skeptic, I want to verify the claims.

**Functional requirements and UI**

1. Tagline idea: Your key never leaves your machine. No vault, no operator key.
2. Security page (extension More › Security and a public web page): where the key lives (PC or own VPS), encryption, what the backend can and cannot do, what data leaves the machine, safe defaults, how to remove the wallet.
3. Onboarding shows the tagline and links the page.
4. Open-source decision (King): options are fully open source, source-available (read-only license), or closed with a published security note.
5. Trade-offs, pro: users and auditors can verify no key leaves the machine; stronger trust than vault and operator-key competitors; community fixes.
6. Trade-offs, con: easier forks and clones; fee logic can be stripped from forks if a fee model ships; more support load; secrets hygiene in the repo.

**Backend API**

- Static content in the extension and web app; no new backend endpoints

**Data model (SQLite)**

- None

**Edge and error states**

- Claims on the page must match the code; review at each release.

**Acceptance criteria**

- Every claim on the page maps to a test or a code reference.
- King records the open-source decision before M3 ends.

### 4.18 Indonesian locale and community (P1, M2)

Win the Indonesian Meteora community first: an Indonesian UI option, Indonesian guides and a Telegram group. English stays the default.

**Mockups:** New mockup needed: language setting in More

**User stories**

- As an Indonesian LP, I want the app and guides in Bahasa Indonesia.
- As a user, I want a community group to ask questions.

**Functional requirements and UI**

1. i18n layer in the extension and web app; English is the default and source locale; Indonesian is an added locale chosen in More › Language.
2. All strings, error messages and notifications externalised; Telegram and WhatsApp messages follow the chosen locale.
3. Guides in Indonesian: setup (PC and VPS), demo mode, copy safety, create pool; English versions remain canonical.
4. An Indonesian PoolScout Telegram group linked from More.
5. Referral launches here once the fee model exists.

**Backend API**

- `GET/PUT /settings/locale` (used for notification language)

**Data model (SQLite)**

- `ui_prefs` key `locale`

**Edge and error states**

- Missing translation: fall back to English per string.
- Number and date formats follow the locale; times stay UTC+7 labelled.

**Acceptance criteria**

- Switching to Indonesian changes every screen without reload and without truncated text at the 13 px body size.
- Notifications arrive in the selected language.

### 4.19 Pre-open simulation (P1, M2)

Before opening, estimate daily fees and IL risk for the chosen pool, amount, range and strategy from pool volume and volatility. Every figure is labelled Estimate.

**Mockups:** Basis: /workspace/poolscout-mockup/copy/open-position.png, copy/open-position-advanced.png, copy/calculator-open.png; new mockup needed for the simulation panel

**User stories**

- As a user, I want to see what a range would likely earn per day before I put SOL in.
- As a learner, I want to understand the trade-off between a narrow and a wide range.
- As an early LP, I want an estimate for the seed liquidity of a pool I create.

**Functional requirements and UI**

1. Inputs: pool, amount, strategy (Spot, Curve, Bid-Ask), range (bins or price); DAMM v2 uses its own price range or full range.
2. Data: 24h and 7d volume, base and dynamic fee, liquidity per bin around the active bin (DLMM), TVL, and realised volatility from 24h and 7d price history.
3. Outputs: estimated fees per day (low, mid, high), daily percentage rate, probability of staying in range over 24 h, estimated IL at each range edge and at a one-sigma move, break-even days, and rent (refundable and non-refundable).
4. Every output carries an Estimate label and a one-line method note; nothing is shown as a promise.
5. Updates live as the user drags the range or changes the amount; a narrow vs wide comparison row.
6. Shown in Open position, Add liquidity, Create pool seed step and Demo mode.
7. Confidence level Low when the pool has under 24 h of data or volume is concentrated in a few trades.

**Backend API**

- `POST /positions/simulate` (pool, amount, strategy, range) returns feesPerDay {low, mid, high}, dpr, inRangeProb24h, ilAtEdges, ilOneSigma, breakEvenDays, rent, confidence

**Data model (SQLite)**

- `pool_stats_cache` (pool, window, volume_usd, fees_usd, volatility, bins_json, ts)
- `simulations` (id, pool, input_json, output_json, position_id nullable, created_at) to compare estimates with real results later

**Edge and error states**

- Pool younger than 24 h: wide band, confidence Low.
- Price history unavailable: hide IL figures and say why; never show zero IL.
- Extreme volatility: cap the displayed range of outcomes and warn.

**Acceptance criteria**

- Every simulation figure is labelled Estimate in the UI and in exports.
- For closed positions with a stored simulation, the median error of fees per day is reported in the backend log.
- Simulation responds in under 500 ms from cache.

### 4.20 Daily recap via Telegram and WhatsApp (P1, M2)

A daily summary message: the day's PnL, fees claimed, positions out of range and automation actions, sent at a time the user chooses, in the user's language.

**Mockups:** Basis: /workspace/poolscout-mockup/alerts/a3-telegram-alert.png (message style); new mockups needed for recap settings and the recap message preview

**User stories**

- As King, I want one message each evening that tells me how the day went.
- As an Indonesian user, I want the recap in Bahasa Indonesia.

**Functional requirements and UI**

1. Content: realised PnL and change in unrealised PnL for the day (SOL and USD), fees claimed, open positions and which are out of range, automation actions (rebalances, TP and SL hits, rug exits, copies opened, closed and skipped), wallet balance, and a link to share the day as a PnL card.
2. Settings: on or off, send time (default 21:00 UTC+7, a proposal), channels (Telegram, WhatsApp), sections to include, skip when there was no activity.
3. Language follows the locale setting (English default, Indonesian optional).
4. Demo mode and dry run get their own clearly labelled recap when active.
5. Preview in Settings and Send test.

**Backend API**

- `GET/PUT /recap/settings`
- `POST /recap/preview` returns the rendered text
- `POST /recap/test`

**Data model (SQLite)**

- `recap_settings` (single row: enabled, send_time, channels_json, sections_json, skip_empty)
- `recap_log` (date, account, payload_json, sent_channels, sent_at, late)

**Edge and error states**

- Backend off at send time: send on the next start marked Late if within 6 h, otherwise skip that day.
- WhatsApp disconnected: send to Telegram and note it in the app.
- Very busy day: cap lists at 5 items with a count of the rest.

**Acceptance criteria**

- Sent within 2 min of the set time while the backend is running.
- Numbers equal the Portfolio values for that day.
- With locale Indonesian, the recap is in Indonesian.

### 4.21 Token watchlist (P1, M2)

Add a token mint and get notified as soon as a new Meteora DLMM or DAMM v2 pool appears for it, with a safety summary. Uses the New pool alerts pipeline.

**Mockups:** New mockups needed: watchlist list, add token sheet, watchlist alert message

**User stories**

- As an early LP, I want to know the moment a token I follow gets its first Meteora pool.
- As a user, I want the safety level in the alert so I can decide quickly.

**Functional requirements and UI**

1. Add by pasting a mint or from Discover and token pages (star); optional label; per-token notify toggle and channels.
2. Detection reuses New pool alerts (pool creation logs for DLMM and DAMM v2, poll fallback); watchlist matches bypass the global TVL and Fee/TVL filters because the user asked for this token.
3. Alert content: pair, protocol, bin step and fee, TVL, creator if known, safety level with the top flags, links to Open pool and Meteora.
4. Safety summary follows the safety rules: Unknown is never shown as Safe; missing data never passes.
5. List shows each token with its current Meteora pool count and last alert time; if pools already exist, they are listed and new ones still alert.
6. Burst protection: several pools for the same token within 10 min are grouped into one alert.

**Backend API**

- `GET /watchlist`, `POST /watchlist`, `PATCH /watchlist/:mint`, `DELETE /watchlist/:mint`
- Alerts flow through the existing `pool_alerts` pipeline with kind `watchlist`

**Data model (SQLite)**

- `token_watchlist` (mint, label, notify, channels_json, created_at, last_alert_at)

**Edge and error states**

- Invalid mint: inline message.
- Mint already watched: open the existing entry.
- Token flagged Risky: alert still sent, with the Risky label first.
- Detection degraded to polling: alert still sent, marked delayed.

**Acceptance criteria**

- A new pool for a watched mint alerts within 10 s on Helius and within 70 s on polling.
- Every alert includes a safety level, and none shows Safe with missing data.
- Removing a token stops alerts immediately.

### 4.22 Copy backtest (P1, M2)

Before enabling copy, simulate what would have happened if you had copied a wallet for the last 7 or 30 days with your sizing and filters. Clearly marked as a simulation.

**Mockups:** New mockups needed: Simulate copy entry on wallet cards and profile, backtest result screen

**User stories**

- As a follower, I want to see how copying a wallet would have worked for me before I risk SOL.
- As a user, I want to see which of its positions my filters would have skipped.

**Functional requirements and UI**

1. Entry points: Smart LP cards, wallet profile, Wallet Tracker add and edit sheets, and the first time the Copy toggle is turned on for a wallet.
2. Period 7D or 30D; uses the leader's historical positions (open and close times, pool, range, strategy) from Meteora data.
3. Applies the user's current settings: copy % and max per position, total cap, max open positions, token filters, safety rules, Meme Protection, fund-loss pause, min TVL; editable what-if overrides.
4. Model: fees scaled from the leader's fees by size share; entry and exit priced with a latency assumption from Copy Logs (or a default); rent and network fees included.
5. Results: positions copied and skipped (with the same reason codes as Copy Logs), simulated PnL in SOL and USD, fees, win rate, max drawdown, peak capital needed, rent, and a daily PnL chart.
6. A persistent Simulation label and a note that past results do not predict future results.
7. Historical safety is approximated with current safety data and flagged.

**Backend API**

- `POST /copy/backtest` (wallet, period, overrides) starts a job
- `GET /copy/backtest/:id` returns progress and result

**Data model (SQLite)**

- `backtest_runs` (id, wallet, period, settings_json, result_json, status, created_at)

**Edge and error states**

- Incomplete leader history: show coverage (for example 82% of positions) and mark the result partial.
- Positions still open at the end: marked to market and labelled.
- Rate limits on large wallets: run as a background job with progress.

**Acceptance criteria**

- The same inputs give the same result.
- Skip reasons use the Copy Logs reason codes.
- The Simulation label is visible on every result view and in any share.
- Turning Copy on for a wallet for the first time offers the backtest before arming.

### 4.23 Web app paired with the extension (P1, M4)

A PoolScout web app with public no-login pages, plus a paired mode where the installed extension is the signer and bridge to the local backend. Decided by King.

**Mockups:** New mockups needed: public pages, Connect extension screen, pairing code and QR, extension approval sheet, per-action confirm sheet

**User stories**

- As a visitor, I want to browse Discover, pools, the leaderboard and public wallet pages without logging in.
- As a user, I want to manage my positions on a big screen while my key stays on my PC.
- As a user, I want to approve every action in my extension.

**Functional requirements and UI**

1. Public pages (no login): Discover, pool pages, Smart LP leaderboard, public wallet portfolio by address, shared PnL card landing, referral landing.
2. Paired pages: my portfolio, positions, copy settings and logs, tracker, alerts; read through the extension.
3. The web app holds no keys and runs no signer; the server only serves public data and the referral service.
4. Pairing: web shows a short pairing code and QR; the user approves it in the extension, which shows the requesting origin; the extension asks the backend to sign a challenge (nonce, origin, expiry) with the hot wallet; web verifies the signature and gets a session.
5. Session: opaque session ID on the web, session key in the extension; idle expiry 12 h and absolute expiry 7 days (proposal); re-pair after expiry.
6. Unpair: from the web (Disconnect) or the extension (Paired sites list, Revoke); revoking kills the session at once.
7. Offline behaviour: when the extension, backend or PC is off, public pages work and paired pages show read-only cached or public data with actions disabled and a clear status.
8. Every state-changing action (open, add, close, claim, create pool, copy toggles, settings) is confirmed in the extension with full details; reads need no confirm.

**Backend API**

- Web to extension: Chrome `externally_connectable` with the PoolScout web origin allowlisted; `chrome.runtime.sendMessage(EXTENSION_ID, msg)` from the page.
- Extension service worker forwards allowed calls to `http://localhost:8787` with `X-PoolScout-Token`; the page never talks to localhost and never sees the token.
- New backend routes: `POST /web/pair/challenge`, `POST /web/pair/sign`, `GET /web/sessions`, `DELETE /web/sessions/:id`
- Fallback when `externally_connectable` is not available: a content script on the allowlisted origin with `window.postMessage`, checking `event.origin` and `event.source`.

**Data model (SQLite)**

- Backend: `web_sessions` (id, origin, wallet, session_key_hash, created_at, last_seen, expires_at, revoked_at)
- Backend: `web_nonces` (nonce, session_id, ts) for replay protection

**Edge and error states**

- Extension not installed: show install link; public pages only.
- Extension installed but backend down: status Backend offline; actions disabled.
- Pairing code expired (after 2 min): generate a new one.
- Request from a non-allowlisted origin: rejected and logged.
- User declines the confirm sheet: action cancelled, nothing sent.

**Acceptance criteria**

- No private key, token or session key ever reaches the web origin (verified by message logging).
- Replayed or stale (over 60 s) action messages are rejected.
- Revoking a session in the extension blocks the next web action.
- All public pages load with the extension absent.

### 4.24 Mobile app (Expo, Android and iOS) (P1, M5)

A PoolScout mobile app, built with Expo (React Native and TypeScript), Android and iOS from one codebase. Decided by King: the app is UI only and pairs with the user's local backend or self-hosted VPS, under the same custody model as the web app (PRD 4a). The private key never lives on the phone. All signing and automation stay on the backend.

**Mockups:** New mockups needed: mobile pairing (scan QR), paired home and positions, per-action confirm, push permission, paired-devices and unpair, offline and backend-unreachable, optional later MWA connect

**User stories**

- As a user, I want to watch my positions and get alerts on my phone while my key stays on my PC or VPS.
- As a user, I want to pair my phone the same way I pair the web app, with a code and a QR.
- As a user, I want a push when a position is out of range, hits take profit or stop loss, or is flagged rugged.

**Functional requirements and UI**

1. Stack: Expo (React Native and TypeScript); one codebase for Android and iOS; EAS Build for store binaries; OTA JavaScript updates with expo-updates (native changes need a new binary).
2. Monorepo: pnpm workspaces with Turborepo (a suggestion). Shared packages for types, the API client, Meteora math and formatting, and design tokens. Apps: extension, web, mobile, backend. Introduce the monorepo when M4 (web) starts so M5 can reuse the packages. Do not restructure the repo in M1 unless the owner asks.
3. Custody, decided: same as PRD 4a. The mobile app holds no keys, runs no signer and has no hosted signer. It pairs with the user's local backend (through a secure tunnel or HTTPS) or self-hosted VPS. All signing and automation stay there. The private key never lives on the phone, not even in the secure enclave or a Keychain item.
4. Pairing: the extension or backend shows a short code and QR (backend URL plus a 2 min code); the user scans it in the app; the backend signs a challenge (nonce, origin, device, expiry) with the hot wallet; the app verifies the signature and stores an opaque session. TLS is required. First-pair is trust on first use, pinned to that host.
5. Session: idle and absolute expiry follow the web proposal (12 h idle, 7 days absolute) unless the owner chooses otherwise; re-pair after expiry. Unpair from the app, or from a Paired devices list in the extension or backend, which kills the session at once.
6. Every state-changing action is confirmed in the mobile app with full details (pool, amount, fees, dry run). The request carries a session ID, nonce and timestamp, signed with the session key, and is rejected if replayed or older than 60 s. Reads need no confirm.
7. Least privilege: the phone cannot export the private key, remove the wallet, rotate the backend token, or change the keystore. Arming copy and turning dry run off from the phone still need the same confirmations as on the web, and may also notify the extension.
8. Offline: when the backend, tunnel or VPS is unreachable, the app shows cached or public data as read-only, with actions disabled and a clear status. Automation keeps running only if the backend itself is up (for example on the VPS).
9. Push notifications with expo-notifications for position, take-profit, stop-loss, rugged-token and out-of-range alerts (and optionally copy events). Delivery needs the backend to be running. Payloads must not include secrets; prefer a data push that the app uses to fetch details from the paired backend. Telegram and WhatsApp alerts stay available.
10. UI: reuse the approved compact design system (title 17 px, body 13 px, numbers 15 px, meta 11 px, tabs 10 px, max weight 600, one accent, green and red only for PnL, bottom tab bar, 32x18 toggles, circular logos, plain-text pool types).
11. Optional future, not in M5 unless the owner approves: direct signing with the user's external wallet through Solana Mobile Wallet Adapter. That requires an Expo development build, not Expo Go, and is Android-first. Still no key storage in the app. Scope is an open question.
12. Expo Go may be used for UI development only. Any native module (MWA, some push or update behaviour, background tasks) is verified on a development or EAS build.

**Backend API**

- Reuse and generalise the web session model: `POST /mobile/pair/challenge`, `POST /mobile/pair/sign`, `GET /mobile/sessions`, `DELETE /mobile/sessions/:id`, `POST /mobile/push/register` (device push token, no secrets).
- The app talks only to the user's backend over HTTPS (VPS or tunnel), never to a PoolScout-hosted signer. It never receives `X-PoolScout-Token` or the private key.
- Push dispatch from the backend on the same events as notifications (PRD 6).

**Data model (SQLite)**

- `device_sessions` (id, platform web|ios|android, device_name, origin, wallet, session_key_hash, push_token_hash, created_at, last_seen, expires_at, revoked_at), or an extension of `web_sessions` with platform and push columns
- Reuse `web_nonces` (or `device_nonces`) for replay protection

**Edge and error states**

- Backend or tunnel unreachable: read-only cache, actions disabled, status shown.
- Pairing code expired (after 2 min): generate a new one.
- Push permission denied: the app still works; Telegram and WhatsApp remain the fallback.
- Two devices paired: both work; each has its own session and can be revoked separately.
- Expo Go: pairing and UI work where the JS APIs allow; MWA and some native features are unavailable and are labelled as needing a development build.
- User declines a confirm: action cancelled, nothing signed.

**Acceptance criteria**

- No private key, keystore or `X-PoolScout-Token` ever reaches the device (verified by logging and a review of storage).
- Replayed or stale (over 60 s) action messages are rejected.
- Revoking a session blocks the next action from that device.
- A push is registered for out-of-range, TP, SL and rugged-token events when permission is granted and the backend is online.
- Android and iOS are built from the same codebase with EAS.
- MWA is not required for M5 and is not offered inside Expo Go.

### 4a. Decided: web app pairs with the extension

> **Decided.** Decided by King: the web app pairs with the user's installed PoolScout extension. The extension is the signer and bridge to the local backend on the user's PC. All signing and automation stay in the extension and local backend. The server holds no keys and runs no hosted signer. Public no-login pages stay.

Architecture: Web app (UI only, no keys) → Extension (bridge and confirm, via `externally_connectable`) → Local backend (keystore, signing, automation; localhost + `X-PoolScout-Token`) → Solana RPC. Public pages read from a read-only public data server.

**Pairing flow**

1. **Request:** Web shows Connect extension; it generates a pairing code (6 characters, 2 min expiry) and a QR.
2. **Approve:** The extension receives the request via `externally_connectable`, shows the origin and code; the user confirms the code matches.
3. **Sign:** The backend signs a challenge (nonce, origin, wallet, expiry) with the hot wallet; the key never leaves the backend.
4. **Session:** Web verifies the signature against the wallet address and stores an opaque session ID; the extension keeps the session key.
5. **Use:** Reads pass through; every write opens a confirm sheet in the extension before the backend acts.
6. **End:** Unpair from either side, idle expiry 12 h, absolute expiry 7 days (proposal), or wallet removal ends every session.

**Security considerations**

- Origin allowlist: only the PoolScout web origin in `externally_connectable` and the content-script match; everything else is rejected.
- No localhost exposure: the page never calls the backend directly; only the extension holds `X-PoolScout-Token`.
- Per-action confirmation in the extension with full details (pool, amount, fees, dry run state).
- Replay protection: each action carries session ID, a unique nonce and a timestamp, signed with the session key; nonces stored and messages older than 60 s rejected.
- Least privilege: an allowlist of message types; no export of the private key, no change of the backend token, no wallet removal from the web.
- Dry run and copy master defaults are unchanged by pairing; arming copy from the web still needs the extension confirm.
- Session list with Revoke in the extension; all sessions revoked on wallet removal or backend token change.

**What works when the extension or PC is offline**

| Capability | Paired and online | Extension or PC offline |
|---|---|---|
| Public pages (Discover, pools, leaderboard, public wallets) | Work | Work |
| Own portfolio and positions | Live via extension | Public on-chain view only |
| Open, add, close, claim, create pool | Work, with extension confirm | Disabled |
| Copy and automation | Run in the local backend | Stopped until the PC and backend are back |
| Alerts (Telegram, WhatsApp) | Sent by the local backend | Not sent |

### 4b. Design coverage

Mockup paths are relative to `/workspace/poolscout-mockup/`; `compact/` is `/workspace/poolscout/extension/screens/compact/`. Status as of 12 Nov 2026.

| Screen or state | Milestone | Mockup | Status |
|---|---|---|---|
| Token safety (chip, sheet Safe and Risky, open warnings, copy skip) | M1 | safety/s1 to s6 | Covered |
| Open positions list, detail, close, add, claim, empty | M1 | positions/p1 to p7; bin-step/bs07, bs08, bs08b | Covered |
| Range view modes (Price, Percent, 24h chart) and bin chart toggle | M1 | positions/p2 shows price range only | Partial |
| Rent preview on open and add (non-refundable bin arrays) | M1 | create-pool/f-review only | Partial |
| Backend mode chooser (PC or Server) and server pairing | M1 | extension compact/backend.png (localhost only) | Partial |
| VPS backend health card and heartbeat status | M1 | None | Missing |
| Backend offline banner and down alert message | M1 | None | Missing |
| VPS setup guide flow (install, key import, pair) | M1 | None | Missing |
| Jito landing settings (mode, tip cap, daily budget, max priority fee) | M1 | None | Missing |
| Copy Logs list with skip reasons, fix buttons and latency breakdown | M1 | copy/copy-feed.png shows Copied and Would copy only | Partial |
| Meme Protection and fund-loss pause settings | M1 | copy/copy-settings.png has risk limits only | Missing |
| Fund-loss paused state and Resume | M1 | None | Missing |
| Per-wallet copy limits sheet | M1 | wallet-tracker/j-edit-wallet.png has the copy toggle only | Partial |
| Copy token filters (organic score, mcap, volume, holders) | M1 | safety/s6-copy-settings-skip-risky.png | Partial |
| Fee-checked filter on the leaderboard | M1 | wallet-tracker/h2-leaderboard-star.png has no filter | Missing |
| Rugged-token exit setting and alert | M1 | None | Missing |
| Onboarding (private key only, no PIN, no seed) | M1 | extension compact/onb-welcome, onb-import, onb-prefs; copy/onboarding set-pin and create-seed are outdated | Covered |
| Demo mode | M2 | demo/d1 to d5 | Covered |
| Wallet Tracker | M2 | wallet-tracker/h to k | Covered |
| Create pool | M2 | create-pool/0 to g | Covered |
| PnL card v2 editor and share | M2 | pnl-card-v2/v1 to v5 | Covered |
| New pool alerts (radar list, filters, Telegram message) | M2 | alerts/a1 to a3 | Covered |
| Pre-open simulation panel | M2 | copy/calculator-open.png and compact/calculator.png are the basis | Missing |
| Daily recap settings | M2 | None | Missing |
| Daily recap message preview (Telegram and WhatsApp) | M2 | alerts/a3-telegram-alert.png sets the style | Missing |
| Token watchlist list, add sheet and alert | M2 | None | Missing |
| Copy backtest entry and result | M2 | None | Missing |
| Notifications settings with new events (recap, watchlist, rug exit, backend down) | M2 | copy/notifications.png; compact/notifications.png | Partial |
| Language setting and Indonesian screens | M2 | None | Missing |
| Discover tabs and token rows with Open or Create pool | M3 | copy/radar.png and bin-step/bs11 are Radar; create-pool/b0-no-pool covers one row state | Missing |
| Pools filter sheet | M3 | bin-step/bs02 to bs03 (type, bin step, base fee, TVL) | Partial |
| Smart LP cards and filter sheet (EV, avg age, avg invested, monthly PnL, ALL) | M3 | wallet-tracker/h2 and copy/wallet-profile.png show 7D/30D, PnL and win rate | Partial |
| Portfolio stats header, PnL/Fee chart (D/W/M) and calendar | M3 | copy/wallet-profile.png, bin-step/bs09, copy/pnl-calendar.png, compact/my-pnl-calendar.png | Partial |
| Automation editor (fee-coverage gate, TP/SL in SOL or USD) | M3 | copy/open-position-advanced.png; positions/p2 automation block | Partial |
| Settings hub with new entries (Backend mode, Landing, Language, Security, Recap) | M3 | copy/more.png; compact/more.png, settings-toggles.png | Partial |
| Security page and onboarding trust line | M3 | None | Missing |
| Web public pages (Discover, pools, leaderboard, wallet, card landing) | M4 | None | Missing |
| Web Connect extension with pairing code and QR | M4 | None | Missing |
| Extension pairing approval sheet | M4 | None | Missing |
| Extension per-action confirm sheet | M4 | None | Missing |
| Paired sites list and Revoke | M4 | None | Missing |
| Web offline and read-only states | M4 | None | Missing |
| Referral | M4 | referral/r1 to r5b | Covered |
| Mobile pairing (scan QR, code, approve) | M5 | None | Missing |
| Mobile home, positions and portfolio | M5 | Extension and positions mockups are the visual basis | Missing |
| Mobile per-action confirm sheet | M5 | None | Missing |
| Mobile push permission and notification settings | M5 | None | Missing |
| Mobile paired-devices list and unpair | M5 | None | Missing |
| Mobile offline and backend-unreachable states | M5 | None | Missing |
| Optional Mobile Wallet Adapter connect (future) | M5 | None | Missing |

## 5. Monetization (open decision)

> **King's decision.** PoolScout is free today. Referral rewards cannot be real until a fee model is chosen.

| Option | Mechanism | Pros | Cons |
|---|---|---|---|
| A. Stay free | No fee | Simplest; strongest growth story; no referral rewards possible | No revenue; referral stays cosmetic |
| B. Share of automation fees | For example 3 to 5% of LP fees on automated close or claim (under LP Agent's 8%) | Directly comparable and cheaper than LP Agent; funds referrals | Charged even on losing positions; needs a fee transfer in locally signed transactions |
| C. Share of realized profit | For example 5 to 10% of profit on automated exits, only when profitable | Fair; easy to market | Harder to compute exactly; disputes on PnL basis |
| D. Flat fee per automated open | For example 0.001 SOL per copy or auto open | Predictable; easy to implement | Hurts small positions; does not scale with value |
| E. Optional Pro tier | Monthly subscription for Pro features, core stays free | No fee on trades | Payments, licensing and enforcement in a self-hosted app |

- This is King's decision and is open. All figures above are options, not recommendations of exact rates.
- LP Agent reference: 8% of LP fees when automation closes, reduces or claims; 0.1% on manual add and remove; 0% on copy opens; no subscription.
- OnlyFarm reference: 10% of LP fees and rewards claimed (9% for life with a referral code), nothing on deposit, withdrawal or swaps, and OnlyFarm pays the agent's network fees; the rate is capped on-chain at 10%.
- Avoid charging on the user's own manual size (LP Agent's 0.1%), to keep the self-custody story clean.
- Proposed strategy (Competitive strategy item 4): free during launch to win users, then about 3 to 5% of claimed fees on automation only, or a flat Pro subscription; both under LP Agent's 8% and OnlyFarm's 10%. King makes the final decision.
- Any fee must be visible in the review line of each transaction and recorded locally, and is a prerequisite for real referral rewards.

## 6. Non-functional requirements

### Security

- Private-key-only onboarding; key encrypted with AES-256-GCM using a machine master key; no PIN; never logged, never sent to a server, never returned by an endpoint except once after Create new wallet.
- Extension sees only the public address; all signing happens in the local backend.
- Pairing trust on first use; `X-PoolScout-Token` required for writes; CORS locked to the extension origin in production.
- Helius keys and `COPY_API_TOKEN` are redacted from logs, errors and the UI.
- Safe defaults: copy master OFF, dry run ON, 0.05 SOL reserve; panic close-all.
- Web pairing as in the decided model: origin allowlist, per-action confirm, replay protection.
- Self-hosted mode: key generated or imported only in the server terminal, encrypted at rest; TLS only; firewall with 443 and key-only SSH; pairing token rotatable; PoolScout never sees the key or the server.

### Performance

- Side panel first paint under 1 s from cache.
- Copy detection about 1 s with a healthy Helius stream; with Jito landing, target p50 from leader tx to our landed tx of 2 s or less (an estimate to validate in M1).
- Lists of 500 rows scroll at 60 fps (virtualised).
- Backend idle CPU under 5% and memory under 500 MB on King's PC (target).

### RPC and data

- A fresh Helius key is required for realtime copying, discovery and new-pool alerts; the key pool rotates on 429 and falls back to the public RPC with a warning.
- Meteora data APIs with a proper User-Agent and throttling under the documented 30 rps.
- Jupiter for prices and swaps; RugCheck for safety; failures degrade to Unknown, never to Safe.

### Reliability when the PC is off

- Copy, Auto range, TP/SL and alerts do not run while the backend is off; this is stated in onboarding and Settings.
- On restart: reconcile every open position on-chain, process missed leader exits first, then run automation, and send one Backend was offline for N min summary.
- Self-hosted always-on mode (P0, M1) runs the backend 24/7 on the user's own VPS with heartbeat and down alerts; PC mode stays supported.

### Privacy

- No telemetry by default.
- Data leaves the PC only to RPC, Meteora, Jupiter, RugCheck, Telegram and WAHA, and (if used) the referral service, web origin and (M5, open question) a push provider. Push payloads must not include secrets or keys.
- Referral service stores wallet addresses and codes only; fee events only after a fee model exists.

### UI rules

- Compact native-mobile layout with bottom tabs.
- Type scale: title 17 px, body 13 px, numbers 15 px (hero 22 px), meta 11 px, tabs 10 px; max weight 600.
- One accent colour; green and red only for PnL; fewer borders; soft borderless badges.
- Circular token logos; plain-text pool type labels; 32x18 toggles.
- Friendly mapped error messages, no raw backend text.
- All UI copy and docs in professional English; Indonesian is an added UI locale (English stays the default).

## 7. Release plan

| Milestone | Scope | Exit criteria | Duration |
|---|---|---|---|
| M0 Prerequisites | Fresh Helius key; small live smoke test of open, claim and close with dry run OFF; backup and keystore check. | One live position opened and closed cleanly. | 2 to 3 days (estimate) |
| M1 Parity (P0) | Strategy items 1, 2 and 7: Self-hosted always-on mode, Jito landing and priority fees, Copy quality with the fee-checked filter and rugged-token exit, Token safety, Open positions UI with the detail endpoint. | P0 parity acceptance criteria pass; 72 h always-on soak test in dry run; p50 latency measured. | 3 to 4 weeks (estimate) |
| M2 Win (P0/P1) | Strategy items 5 and 6: Demo mode, Wallet Tracker, Create pool, PnL card v2, New pool alerts on Telegram and WhatsApp, Indonesian locale, guides and Telegram group; plus Pre-open simulation, Daily recap, Token watchlist and Copy backtest. | Acceptance criteria pass; King signs off on screenshots; launch free. | 2 to 3 weeks (estimate) |
| M3 Depth (P1) | Discover upgrade, Pools filters, Smart LP upgrade (rest), Portfolio stats and chart, Auto Range/TP-SL refine (rest), Trust: security page and open-source decision. | P1 acceptance criteria pass. | 2 to 3 weeks (estimate) |
| M4 Reach (P1/P2) | Web app paired with the extension and public pages; Referral UI and tracking. Introduce the pnpm and Turborepo monorepo so web, extension and backend share packages. | Pairing security tests pass; referral codes work end to end without rewards. | 2 weeks (estimate), needs a domain |
| M5 Mobile (P1) | Expo mobile app (Android and iOS): pairing with the user's backend or VPS, positions and portfolio UI, per-action confirm, expo-notifications for TP/SL, rug and out-of-range, EAS Build and OTA. Optional Mobile Wallet Adapter is out of scope until the owner approves it. | Custody tests pass (no key on device); pairing, unpair and replay protection pass on a development build; push delivered in a dry-run test; King signs off on screenshots. | 3 to 4 weeks (estimate); starts only after King approves M5, and needs store accounts |
| Gated | Referral rewards and any fee collection after the free launch; open-source release if King chooses it. | King decides the fee model, terms and open-source option. | Blocked |

Durations are estimates. Build starts only after King says build; nothing ships without his screenshot sign-off.

## 8. Risks and open questions

| Risk | Impact | Mitigation |
|---|---|---|
| Fee model undecided | Referral rewards and revenue blocked | Decision section in this PRD; ship referral as UI and tracking only |
| No domain | No referral links or web app | Pick and register a domain before M3 |
| No fresh Helius key | Slow copies, no discovery, slow alerts | Get a key in M0; polling fallback with visible status |
| Live trading untested | Real losses from bugs | M0 smoke test with small size; dry run default ON |
| WhatsApp (WAHA) ban | Linked number could be banned for automated messages | Use a secondary number; rate limits; Telegram as the primary channel |
| PC off or sleeping | Missed exits and TP/SL | Restart reconciliation, offline summary, clear docs |
| Upstream API changes or limits | Broken lists or stale data | Caching, health checks, friendly fallbacks |
| Web pairing attack surface | Unauthorized actions | Origin allowlist, per-action confirm, replay protection, revocation |
| Competitor pace | LP Agent and OnlyFarm already ship much of v0.4.0 | Parity first (M1), then differentiators (M2): self-custody, chat alerts, demo mode, tracker, create pool, share cards |
| VPS misconfiguration | Exposed backend or stolen keystore on the user's server | Installer hardens by default (TLS, firewall, key-only SSH, localhost bind); security page; token rotation |
| Jito tip cost and failures | Tips eat small positions; bundles fail to land | Tip cap and daily budget; RPC fallback; latency and cost in Copy Logs |
| Open-sourcing | Forks strip any future fee | King's decision with trade-offs in the Trust spec |
| App Store or Play review | Delay or rejection of the mobile app | Start accounts and review notes before M5; follow store rules; no key handling in the app |
| Push through a third party | Device tokens or alert metadata leave the user's machines | Open question on the provider; minimal payloads; fetch details from the paired backend |

### Open questions

1. Fee model (options A to E) and the referral rates and terms.
2. Domain for referral links and the web app.
3. Who hosts the web app and the referral service, and where.
4. Demo default starting balance (proposal: 10 SOL).
5. Fee-checked threshold default (proposal: 10%, as LP Agent).
6. Pricing after the free launch: about 3 to 5% of claimed fees on automation only, or a flat Pro subscription, and when the free launch ends.
7. Open-source the backend: fully open, source-available, or closed with a security note, and which license.
8. Default Jito tip cap and daily tip budget (proposal: 0.0001 SOL per bundle).
9. Which VPS providers and setup (Docker or Node service, domain or tunnel) to document first.
10. Who runs and moderates the Indonesian Telegram group.
11. Daily recap default send time (proposal: 21:00 UTC+7).
12. Web session expiry values (proposal: 12 h idle, 7 days absolute).
13. Apple Developer and Google Play accounts, and who submits the app for review.
14. Push provider for the mobile app (Expo Push Service or another) and the privacy of device tokens.
15. Scope and timing of optional Solana Mobile Wallet Adapter signing (Android-first, development build only, not Expo Go).
16. iOS signing: certificates, provisioning, and whether a personal or organization Apple Developer account is used.

## 9. Success metrics

| Metric | Target | Source |
|---|---|---|
| Copy decision coverage | 100% of leader actions have a Copy Log row with a reason | Copy Logs |
| Copy latency | p50 from leader tx to our landed tx of 2 s or less with Jito (an estimate to validate in M1) | Copy Logs stage timings |
| Always-on uptime | At least 99% backend uptime in self-hosted mode (target) | backend_heartbeats |
| Down alert speed | Telegram or WhatsApp alert within 2 min of a backend stop | backend_events and notify_log |
| Simulation accuracy | Median error of estimated fees per day reported for closed positions (no target until measured) | simulations vs copy_positions |
| Recap delivery | Daily recap sent within 2 min of the set time on running days | recap_log |
| Indonesian community | Telegram group members and Indonesian-locale users after M2 | Group count; local counters, opt-in |
| Safety correctness | 0 tokens shown Safe with an unanswered source | Tests and audit of token_safety |
| Live action success | At least 98% of live opens, claims and closes confirm (target) | copy_trades |
| Alert speed | New-pool alert within 10 s on Helius | pool_alerts timestamps |
| Uptime awareness | Every restart sends an offline summary | notify_log |
| Demo to live | Share of demo users who later fund a wallet (once distributed) | Local counters, opt-in |
| Sharing | Cards shared per week | Local counters |
| Web pairing safety | 0 actions executed without an extension confirm | web_sessions and action log |
| Mobile pairing safety | 0 private keys or backend tokens on the device; 0 actions without an in-app confirm | device_sessions and action log |
