docs: rewrite README
Browse files
README.md
CHANGED
|
@@ -1,209 +1,224 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
#
|
| 11 |
|
| 12 |
-
|
| 13 |
|
| 14 |
-
|
| 15 |
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
```
|
| 19 |
-
|
| 20 |
-
β 4e8d7f02 churn_risk_high 3nRt8...Wk2p score=88 source=manual ACCEPTED
|
| 21 |
-
β 9a1c3b44 churn_risk_medium 5mQs1...Yx7r score=67 source=scan ACCEPTED
|
| 22 |
-
β 7f4e2d81 streak_maintained 2pLv6...Nj4t score=12 source=manual ACCEPTED
|
| 23 |
-
β 2b9a6c13 comeback_detected 8kHz9...Cs3u score=34 source=scan ACCEPTED
|
| 24 |
```
|
| 25 |
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
---
|
| 29 |
|
| 30 |
-
##
|
| 31 |
|
| 32 |
-
|
| 33 |
|
| 34 |
```
|
| 35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
```
|
| 37 |
|
|
|
|
|
|
|
| 38 |
---
|
| 39 |
|
| 40 |
-
## Torque
|
|
|
|
|
|
|
| 41 |
|
| 42 |
-
| Primitive | Campaign | Trigger |
|
| 43 |
-
|-----------|----------|---------|
|
| 44 |
-
| Leaderboard | Weekly Volume Champions β `SUM(swap_volume)` | `volume_milestone` |
|
| 45 |
-
| Raffle | Comeback Raffle β streak multiplier entry | `comeback_detected` |
|
| 46 |
-
| Gift / Bounty | Anti-Churn Gift Drop β
|
| 47 |
-
| Rebate / Trial | Streak Multiplier β 7+ day active users | `streak_maintained` |
|
| 48 |
|
| 49 |
-
|
| 50 |
|
| 51 |
```
|
| 52 |
-
churn_risk_high
|
| 53 |
-
churn_risk_medium
|
| 54 |
comeback_detected wallet returns after 7+ days silence
|
| 55 |
streak_maintained 7+ consecutive active days
|
| 56 |
-
volume_milestone
|
| 57 |
-
inactivity_detected wallet goes quiet β soft nudge
|
| 58 |
-
referral_from_saved
|
| 59 |
```
|
| 60 |
|
| 61 |
---
|
| 62 |
|
| 63 |
-
##
|
| 64 |
|
| 65 |
-
###
|
| 66 |
-
|
| 67 |
|
| 68 |
### Bulk Rescue
|
| 69 |
-
|
| 70 |
|
| 71 |
-
### Toast
|
| 72 |
-
Every
|
| 73 |
|
| 74 |
-
###
|
| 75 |
-
|
| 76 |
|
| 77 |
-
###
|
| 78 |
-
|
| 79 |
|
| 80 |
-
###
|
| 81 |
-
|
| 82 |
-
- Critical β "Send Gift" β `churn_risk_high`
|
| 83 |
-
- High β "Enter Raffle" β `churn_risk_high`
|
| 84 |
-
- Medium β "Activate Rebate" β `churn_risk_medium`
|
| 85 |
-
|
| 86 |
-
Fires to real Torque ingest, shows ingestion ID inline.
|
| 87 |
|
| 88 |
### Create Campaign Modal
|
| 89 |
-
Full campaign creation form wired to `POST /api/torque/campaigns`
|
| 90 |
|
| 91 |
-
###
|
| 92 |
-
|
| 93 |
|
| 94 |
---
|
| 95 |
|
| 96 |
## Architecture
|
| 97 |
|
| 98 |
```
|
| 99 |
-
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 100 |
-
β
|
| 101 |
-
β Dashboard Β· Wallets Β· Campaigns Β· Analytics Β· Agent
|
| 102 |
-
β
|
| 103 |
-
|
| 104 |
-
ββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββ
|
| 105 |
οΏ½οΏ½ Next.js API Routes
|
| 106 |
-
βββββββββββ΄ββββββββββ
|
| 107 |
-
β
|
| 108 |
-
ββββββΌβββββββ
|
| 109 |
-
β AI Engine β
|
| 110 |
-
β 5-signal β
|
| 111 |
-
β churn β
|
| 112 |
-
β scoring β
|
| 113 |
-
ββββββ¬βββββββ
|
| 114 |
-
βββββββββββ¬ββββββββββ
|
| 115 |
β
|
| 116 |
-
βββββββββββΌβββββββββββββ
|
| 117 |
-
β
|
| 118 |
-
β
|
| 119 |
-
β ingest.torque.so
|
| 120 |
-
β server.torque.so
|
| 121 |
-
ββββββββββββββββββββββββ
|
| 122 |
-
```
|
| 123 |
-
|
| 124 |
-
### Churn Scoring Model (5 signals)
|
| 125 |
-
|
| 126 |
-
```typescript
|
| 127 |
-
score += daysInactive >= 30 ? 40 : daysInactive >= 14 ? 25 : daysInactive >= 7 ? 15 : 0
|
| 128 |
-
score += volumeDropPct >= 80 ? 30 : volumeDropPct >= 50 ? 20 : volumeDropPct >= 25 ? 10 : 0
|
| 129 |
-
score += uniqueProtocols <= 1 ? 15 : uniqueProtocols <= 3 ? 8 : 0
|
| 130 |
-
score += currentStreak === 0 ? 10 : 0
|
| 131 |
-
score += hasLiquidation ? 5 : 0
|
| 132 |
-
// critical β₯80 high β₯60 medium β₯40 low β₯20 safe <20
|
| 133 |
```
|
| 134 |
|
| 135 |
---
|
| 136 |
|
| 137 |
## Pages
|
| 138 |
|
| 139 |
-
| Page |
|
| 140 |
-
|------|---------|
|
| 141 |
-
| Dashboard | Live events strip, Scan Now
|
| 142 |
-
| Wallets |
|
| 143 |
-
| Campaigns | Campaign cards + Create Campaign modal β real
|
| 144 |
-
| Leaderboard | Top-3 podium, sortable rankings, scoring formula
|
| 145 |
-
| Analytics | Retention cohort heatmap, custom event breakdown, KPI charts |
|
| 146 |
-
| AI Agent | Start/pause, live feed with real scan results,
|
| 147 |
|
| 148 |
---
|
| 149 |
|
| 150 |
## Run Locally
|
| 151 |
|
| 152 |
```bash
|
| 153 |
-
git clone https://github.com/
|
| 154 |
-
cd flowstate
|
| 155 |
-
|
| 156 |
cp .env.example .env.local
|
| 157 |
-
#
|
| 158 |
npm run dev
|
| 159 |
```
|
| 160 |
|
| 161 |
-
|
| 162 |
|
| 163 |
-
|
| 164 |
|
| 165 |
-
| Variable | Format |
|
| 166 |
|----------|--------|---------|
|
| 167 |
-
| `TORQUE_API_KEY` | `eyJ...` JWT | `server.torque.so`
|
| 168 |
-
| `TORQUE_INGEST_KEY` | `tq_...` | `ingest.torque.so`
|
| 169 |
|
| 170 |
---
|
| 171 |
|
| 172 |
-
##
|
| 173 |
|
| 174 |
-
Next.js 14 Β· TypeScript Β· Tailwind CSS Β· Recharts Β· Lucide Β· Torque MCP
|
| 175 |
|
| 176 |
---
|
| 177 |
|
| 178 |
## Friction Log
|
| 179 |
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
-
|
| 185 |
-
-
|
|
|
|
|
|
|
|
|
|
| 186 |
|
| 187 |
### What Could Be Better
|
| 188 |
|
| 189 |
**1. Two credentials, no joint setup docs**
|
| 190 |
-
JWT and `tq_` ingest key serve different endpoints
|
| 191 |
|
| 192 |
-
**2. Custom event schema
|
| 193 |
-
Events silently drop if the schema
|
| 194 |
|
| 195 |
-
**3. No batch ingest
|
| 196 |
-
Scanning 10,000 wallets = 10,000
|
| 197 |
|
| 198 |
**4. Campaign creation API undocumented**
|
| 199 |
-
`POST server.torque.so/campaigns`
|
| 200 |
|
| 201 |
-
**5. MCP auth is stateful
|
| 202 |
-
Must call `auth()` before
|
| 203 |
|
| 204 |
-
**6. No webhook callbacks
|
| 205 |
-
No way to know when a raffle
|
| 206 |
|
| 207 |
---
|
| 208 |
|
| 209 |
-
Built for the Torque Hackathon Β· [platform.torque.so](https://platform.torque.so)
|
|
|
|
| 1 |
+
# FlowState
|
| 2 |
+
|
| 3 |
+
> **The autonomous retention engine for Solana protocols.**
|
| 4 |
+
> Detect wallet churn before it happens. Fire the right incentive at the right moment. Watch users come back.
|
| 5 |
+
|
| 6 |
+
[](https://torque.so)
|
| 7 |
+
[](https://nextjs.org)
|
| 8 |
+
[](https://typescriptlang.org)
|
| 9 |
+
|
| 10 |
---
|
| 11 |
|
| 12 |
+
## The Problem
|
| 13 |
|
| 14 |
+
DeFi protocols bleed users silently. A wallet goes quiet for 10 days β nobody notices. By day 30, they're gone. There's no CRM, no lifecycle email, no second chance. Just an address that stopped showing up.
|
| 15 |
|
| 16 |
+
**FlowState fixes this.**
|
| 17 |
|
| 18 |
+
---
|
| 19 |
+
|
| 20 |
+
## How It Works
|
| 21 |
+
|
| 22 |
+
FlowState watches wallet behavior across Solana in real time. When it detects a churn signal β inactivity, volume drop, streak broken β it scores the risk, picks the optimal Torque incentive, and fires it. Automatically.
|
| 23 |
|
| 24 |
```
|
| 25 |
+
MONITOR wallets β SCORE churn risk β DECIDE incentive β EXECUTE via Torque β TRACK recovery
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
```
|
| 27 |
|
| 28 |
+
The AI engine runs a 5-signal model on every wallet:
|
| 29 |
+
|
| 30 |
+
```typescript
|
| 31 |
+
score += daysInactive >= 30 ? 40 : daysInactive >= 14 ? 25 : daysInactive >= 7 ? 15 : 0
|
| 32 |
+
score += volumeDropPct >= 80 ? 30 : volumeDropPct >= 50 ? 20 : volumeDropPct >= 25 ? 10 : 0
|
| 33 |
+
score += uniqueProtocols <= 1 ? 15 : uniqueProtocols <= 3 ? 8 : 0
|
| 34 |
+
score += currentStreak === 0 ? 10 : 0
|
| 35 |
+
score += hasLiquidation ? 5 : 0
|
| 36 |
+
// critical β₯80 Β· high β₯60 Β· medium β₯40 Β· low β₯20 Β· safe <20
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
Score determines the response:
|
| 40 |
+
|
| 41 |
+
| Risk | Action | Torque Primitive |
|
| 42 |
+
|------|--------|-----------------|
|
| 43 |
+
| Critical (β₯80) | Send a gift β keep them | Gift / Bounty |
|
| 44 |
+
| High (β₯60) | Enter them in a raffle | Raffle |
|
| 45 |
+
| Medium (β₯40) | Activate a rebate | Rebate / Trial |
|
| 46 |
+
| Returning | Welcome back reward | Comeback campaign |
|
| 47 |
+
| Streak active | Reinforce the habit | Leaderboard |
|
| 48 |
|
| 49 |
---
|
| 50 |
|
| 51 |
+
## Live Proof β Real Events Fired
|
| 52 |
|
| 53 |
+
These are real ingestion IDs returned by `ingest.torque.so` during development. Not mocked.
|
| 54 |
|
| 55 |
```
|
| 56 |
+
β b3f2a1c9 churn_risk_high 7xKp3...Bm9q score=91 ACCEPTED
|
| 57 |
+
β 4e8d7f02 churn_risk_high 3nRt8...Wk2p score=88 ACCEPTED
|
| 58 |
+
β 9a1c3b44 churn_risk_medium 5mQs1...Yx7r score=67 ACCEPTED
|
| 59 |
+
β 7f4e2d81 streak_maintained 2pLv6...Nj4t score=12 ACCEPTED
|
| 60 |
+
β 2b9a6c13 comeback_detected 8kHz9...Cs3u score=34 ACCEPTED
|
| 61 |
```
|
| 62 |
|
| 63 |
+
All 7 custom event schemas are live on Torque. Campaign and project created. Events verified on [platform.torque.so](https://platform.torque.so).
|
| 64 |
+
|
| 65 |
---
|
| 66 |
|
| 67 |
+
## Torque Integration
|
| 68 |
+
|
| 69 |
+
All 4 primitives wired to real trigger events:
|
| 70 |
|
| 71 |
+
| Primitive | Campaign | Trigger Event |
|
| 72 |
+
|-----------|----------|--------------|
|
| 73 |
+
| **Leaderboard** | Weekly Volume Champions β `SUM(swap_volume)` | `volume_milestone` |
|
| 74 |
+
| **Raffle** | Comeback Raffle β streak multiplier entry | `comeback_detected` |
|
| 75 |
+
| **Gift / Bounty** | Anti-Churn Gift Drop β score β₯80 wallets | `churn_risk_high` |
|
| 76 |
+
| **Rebate / Trial** | Streak Multiplier β 7+ day active users | `streak_maintained` |
|
| 77 |
|
| 78 |
+
**7 custom event schemas registered on Torque:**
|
| 79 |
|
| 80 |
```
|
| 81 |
+
churn_risk_high score β₯80, inactive β₯14d β triggers gift campaign
|
| 82 |
+
churn_risk_medium score β₯40 β triggers rebate activation
|
| 83 |
comeback_detected wallet returns after 7+ days silence
|
| 84 |
streak_maintained 7+ consecutive active days
|
| 85 |
+
volume_milestone crosses $10K / $50K / $100K lifetime volume
|
| 86 |
+
inactivity_detected wallet goes quiet β soft nudge
|
| 87 |
+
referral_from_saved retained wallet refers a new user
|
| 88 |
```
|
| 89 |
|
| 90 |
---
|
| 91 |
|
| 92 |
+
## Features
|
| 93 |
|
| 94 |
+
### Autonomous Scan Engine
|
| 95 |
+
Hit **Scan Now** or enable **Auto Mode** β FlowState scores every wallet and fires Torque events for at-risk users without any manual input. Auto mode runs every 30 seconds with a live countdown.
|
| 96 |
|
| 97 |
### Bulk Rescue
|
| 98 |
+
One button fires `churn_risk_high` for every critical wallet in parallel. Live progress counter: `3/7 fired`. Ingestion IDs appear inline next to each wallet address as confirmation.
|
| 99 |
|
| 100 |
+
### Toast Notifications
|
| 101 |
+
Every event that hits Torque surfaces as a bottom-right toast: event name, wallet address, first 10 chars of the ingestion ID. Visual proof, not just logs.
|
| 102 |
|
| 103 |
+
### Per-Wallet Intervention
|
| 104 |
+
Filter wallets by risk tier. Each critical/high/medium row has a contextual action button β Send Gift, Enter Raffle, Activate Rebate β that fires directly to Torque ingest and shows the returned ingestion ID.
|
| 105 |
|
| 106 |
+
### Live Event Feed
|
| 107 |
+
The dashboard AI Agent Feed injects real Torque events at the top with a LIVE badge and green border as they arrive, alongside ambient mock activity showing system scale.
|
| 108 |
|
| 109 |
+
### Real-Time Status
|
| 110 |
+
Topbar shows **TORQUE LIVE** (green pulse) or **TORQUE OFFLINE** based on actual credential check, polling every 8s. Sidebar shows live session event count refreshing every 5s.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
|
| 112 |
### Create Campaign Modal
|
| 113 |
+
Full campaign creation form β type, name, description, budget, trigger event β wired to `POST /api/torque/campaigns`. Returns campaign ID on success.
|
| 114 |
|
| 115 |
+
### Keyboard Shortcut
|
| 116 |
+
Press `S` on the Dashboard to trigger a scan instantly.
|
| 117 |
|
| 118 |
---
|
| 119 |
|
| 120 |
## Architecture
|
| 121 |
|
| 122 |
```
|
| 123 |
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 124 |
+
β FlowState UI β
|
| 125 |
+
β Dashboard Β· Wallets Β· Campaigns Β· Analytics Β· Agent β
|
| 126 |
+
β Auto-scan Β· Bulk Rescue Β· Toast Β· Keyboard shortcut β
|
| 127 |
+
ββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 128 |
οΏ½οΏ½ Next.js API Routes
|
| 129 |
+
βββββββββββ΄βββββββββββ
|
| 130 |
+
β β
|
| 131 |
+
ββββββΌβββββββ βββββββββββΌββββββββ
|
| 132 |
+
β AI Engine β β Event Store β
|
| 133 |
+
β 5-signal β β in-memory β
|
| 134 |
+
β churn β β tracks IDs β
|
| 135 |
+
β scoring β β session-wide β
|
| 136 |
+
ββββββ¬βββββββ βββββββββββ¬ββββββββ
|
| 137 |
+
βββββββββββ¬βββββββββββ
|
| 138 |
β
|
| 139 |
+
βββββββββββΌβββββββββββββββ
|
| 140 |
+
β torque-mcp.ts β
|
| 141 |
+
β β
|
| 142 |
+
β ingest.torque.so β β x-api-key: tq_...
|
| 143 |
+
β server.torque.so β β Authorization: Bearer JWT
|
| 144 |
+
ββββββββββββββββββββββββββ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
```
|
| 146 |
|
| 147 |
---
|
| 148 |
|
| 149 |
## Pages
|
| 150 |
|
| 151 |
+
| Page | What's There |
|
| 152 |
+
|------|-------------|
|
| 153 |
+
| **Dashboard** | Live events strip, Scan Now + Auto Mode, Torque status badge, agent feed with real LIVE entries |
|
| 154 |
+
| **Wallets** | Risk table, per-wallet Intervene buttons, Bulk Rescue All Critical, inline ingestion ID confirmation |
|
| 155 |
+
| **Campaigns** | Campaign cards + Create Campaign modal β real Torque API |
|
| 156 |
+
| **Leaderboard** | Top-3 podium, sortable rankings, scoring formula |
|
| 157 |
+
| **Analytics** | Retention cohort heatmap, custom event breakdown, KPI charts |
|
| 158 |
+
| **AI Agent** | Start/pause, live feed with real scan results, detection thresholds |
|
| 159 |
|
| 160 |
---
|
| 161 |
|
| 162 |
## Run Locally
|
| 163 |
|
| 164 |
```bash
|
| 165 |
+
git clone https://github.com/MUTHUKUMARAN-K-1/flowstate
|
| 166 |
+
cd flowstate
|
| 167 |
+
npm install
|
| 168 |
cp .env.example .env.local
|
| 169 |
+
# fill in your two Torque credentials
|
| 170 |
npm run dev
|
| 171 |
```
|
| 172 |
|
| 173 |
+
### Two Credentials β Both Required
|
| 174 |
|
| 175 |
+
Torque uses separate credentials for its REST API and event ingest. Get both from [platform.torque.so](https://platform.torque.so):
|
| 176 |
|
| 177 |
+
| Variable | Format | Used for |
|
| 178 |
|----------|--------|---------|
|
| 179 |
+
| `TORQUE_API_KEY` | `eyJ...` JWT | `server.torque.so` β campaign creation, leaderboards |
|
| 180 |
+
| `TORQUE_INGEST_KEY` | `tq_...` | `ingest.torque.so` β firing custom events |
|
| 181 |
|
| 182 |
---
|
| 183 |
|
| 184 |
+
## Stack
|
| 185 |
|
| 186 |
+
**Next.js 14** Β· **TypeScript** Β· **Tailwind CSS** Β· **Recharts** Β· **Lucide** Β· **Torque MCP**
|
| 187 |
|
| 188 |
---
|
| 189 |
|
| 190 |
## Friction Log
|
| 191 |
|
| 192 |
+
Honest account of what we hit building this. Torque's primitives are genuinely powerful β these are sharp edges worth filing down.
|
| 193 |
+
|
| 194 |
+
### What Worked
|
| 195 |
+
|
| 196 |
+
- **Custom events are the superpower.** Any signal β any campaign type. The mapping is clean and expressive.
|
| 197 |
+
- **Four primitives cover every DeFi retention play.** Nothing missing for the gift/raffle/rebate/leaderboard loop.
|
| 198 |
+
- **Formula engine is perfect for leaderboards.** `SUM(swap_volume)` just works.
|
| 199 |
+
- **Ingest endpoint is fast.** 10 test events all returned ACCEPTED in under 2 seconds.
|
| 200 |
+
- **MCP server makes campaign creation scriptable.** Great fit for AI agents.
|
| 201 |
|
| 202 |
### What Could Be Better
|
| 203 |
|
| 204 |
**1. Two credentials, no joint setup docs**
|
| 205 |
+
JWT and `tq_` ingest key serve different endpoints, obtained in different places. Discovered by testing, not docs. One unified setup guide listing both would save builders 30+ minutes.
|
| 206 |
|
| 207 |
+
**2. Custom event schema required before ingest β silent failure**
|
| 208 |
+
Events silently drop if the schema isn't pre-registered via MCP. No error message. A `422 Unprocessable: event schema not found` response would surface this in seconds instead of hours.
|
| 209 |
|
| 210 |
+
**3. No batch ingest**
|
| 211 |
+
Scanning 10,000 wallets = 10,000 HTTP calls. A `POST /events/batch` taking an array is table stakes for production retention at scale.
|
| 212 |
|
| 213 |
**4. Campaign creation API undocumented**
|
| 214 |
+
`POST server.torque.so/campaigns` works but has no public schema. Had to infer fields from MCP tool signatures. A REST reference would unblock builders immediately.
|
| 215 |
|
| 216 |
+
**5. MCP auth is stateful, no auto-retry**
|
| 217 |
+
Must call `auth()` before anything else. No automatic re-auth on token expiry. Silent failures until you realize the token is stale.
|
| 218 |
|
| 219 |
+
**6. No webhook callbacks**
|
| 220 |
+
No way to know when a raffle is drawn or a gift is claimed without polling. Webhooks would close the loop: detect churn β fire event β get callback when reward is claimed β mark user recovered.
|
| 221 |
|
| 222 |
---
|
| 223 |
|
| 224 |
+
*Built for the Torque Hackathon Β· [torque.so](https://torque.so) Β· [platform.torque.so](https://platform.torque.so)*
|