You have to sell a token you never meant to own. Glidepath turns it into a dated selling calendar sized to the market's organic demand β so you are never the biggest seller on a day the pros are exiting.
It plans. It never trades. 12 credits per plan on EVM chains, 15 on solana/base, 0 on a cache hit β and the 13 recorded plans replay offline byte-for-byte with zero network calls and zero credits (npm run verify).
JUDGE.md Β· DEMO.md Β· SCORING.md Β· DX-REPORT.md Β· ARCHITECTURE.md
The PEPE plan, live on 2026-09-19 β 12 credits, 9 calls, 2.0 s; a red today (exchange net deposits +$736K) halves the first tranche β and every Nansen call streams into the rail on the right as it lands:
BONK on solana inside a red regime β today is green, but tgm/flow-intelligence 7d shows exchange net deposits of +$3.32M over the week and tgm/flows marks 1 of the last 13 days red; the costs are real trade/quote routes β and the rail shows all 12 calls that just landed, 15 credits, 5.3 s:
| provenance drawer beside the rail β the receipt and the live meter, same nine calls | mobile β the TURBO thin plan, 90 tranches, 30 % still unsold; the rail is the bar at the bottom |
|---|---|
![]() |
![]() |
![]() |
A nonprofit's finance officer receives a $40K memecoin donation. A freelancer is paid in a project's token. They have never used a DEX, and they must turn the tokens into dollars without crashing the price or competing with the funds on the day the funds are dumping.
Paste token Β· chain Β· amount held. Glidepath:
- Computes organic daily buy volume β DEX buys over the last 7 days by wallets that carry none of Nansen's Smart Money / Fund / Whale / Exchange / sniper-bot labels β via
tgm/who-bought-soldlabel filters andtgm/token-information. - Prints the dump-today line: position value, estimated impact, and what share of a full day's organic buying you would be.
- Sizes daily tranches to
kΓ organic/day, wherekslides from 10 % to 3 % as Nansen's peer-percentile risk indicators rise (tgm/indicators), capped at 1 % ofliquidity_usd. - Flags red days: today from
tgm/flow-intelligence(Smart Money net-selling, or net deposits to exchanges), the 13 complete days before it fromtgm/flowsdaily cohort history (a 14-day strip with today). A red today halves the first tranche; the observed red-day rate stretches the expected finish. - Shows dump today vs glidepath cost β constant-product from
liquidity_usd, or a real routedtrade/quoteon solana/base, labelled as such. - Exports the plan as ICS (one calendar event per tranche, with the go/no-go rule inside) and CSV, plus a share card with an OG image.
- Streams every Nansen call into the call rail on the right as it happens (endpoint Β· params Β· credits Β· ms Β· hash), then opens a provenance drawer: every call, the fields used, credits (from Nansen's response headers), cached or live, ms β and a "computed Ns ago" badge.
It plans. It never trades.
One plan is resolve β facts β computePlan β applyQuotes; the plan is a pure function of (facts, input, now), so a recorded fixture replays to the same calendar dates, the same red history and the same decision hash. Full module map and data flow: ARCHITECTURE.md.
Mermaid source β expand to see the diagram as text (renders on GitHub)
flowchart LR
IN["token Β· chain Β· amount"] --> R["resolve.ts<br/>address passes through Β· ticker β search/general (0 credits)"]
R --> F["facts.ts<br/>Promise.all over 8 calls, every failure lands in errors[term]"]
F <--> C["cache.ts<br/>key = sha256(method + endpoint + canonical body)<br/>hit = 0 credits Β· NANSEN_OFFLINE=1 never touches the network"]
C <--> N["Nansen API<br/>tgm/token-information Β· tgm/who-bought-sold Γ2<br/>tgm/flow-intelligence Γ2 Β· tgm/flows Γ2 Β· tgm/indicators"]
F --> P["plan.ts<br/>price β organic/day β risk dial k β tranches<br/>β red-day rule (today + 13 days) β impact β decision hash"]
P --> Q["impact.ts<br/>constant-product cost(V) = VΒ²/(L/2+V)<br/>or trade/quote Γ3 on solana/base"]
Q <--> N
P --> X["export.ts<br/>ICS (one VEVENT per tranche, go/no-go rule inside) Β· CSV"]
P --> CLI["packages/cli<br/>npm run glidepath -- <token> --chain --amount"]
P --> WEB["apps/web (Next.js 15)<br/>/api/plan?stream=1 β the call rail Β· /api/export Β· /api/og Β· /p share page"]
N -. "CallEvent start Β· end (the same Call objects)" .-> WEB
| layer | what | where |
|---|---|---|
| Engine | TypeScript. NansenClient: token bucket (8 rps), per-call timeout, 1 retry on 429/5xx/timeout honouring Retry-After, credits read from response headers; CachedNansenClient read-through cache (1 h TTL on the web, failures never cached) |
packages/core |
| CLI | npm run glidepath -- <token> --chain <chain> --amount <n> with --json --explain --ics --csv --no-cache --no-quotes |
packages/cli |
| Web | Next.js 15 App Router, plain CSS, server-side key; memory + /tmp disk cache on Vercel; OG card 1200Γ630 |
apps/web |
| Data | Nansen API β 7 endpoints Β· 11 calls per plan (table below) | packages/core/src/nansen.ts |
| Proof | 258 vitest tests (60,000 fast-check property cases on the planner, key-boundary tests) Β· 13 recorded fixtures replayed offline Β· 60 Playwright checks on the built app Β· bench.ts p50/p95 Β· check_submission_readiness.ts |
packages/core/test, apps/web/test, e2e/, fixtures/, scripts/ |
| CI | GitHub Actions, 7 stages, no key anywhere: quality (prettier, eslint, tsc Γ2, vitest + coverage, offline verify, readiness) β security (TruffleHog, npm audit, licenses) β build + bundle budget β Playwright E2E β Lighthouse β deploy gate β Vercel production deploy (prebuilt, main only); CodeQL, gitleaks (full history), Dependabot, semantic releases alongside |
.github/workflows/ |
The data drives the logic β every on-screen number traces to a named Nansen field.
| endpoint | credits | fields used | what it drives |
|---|---|---|---|
search/general |
0 | tokens[].symbol/name/chain/address/rank |
ticker β address on the chosen chain; "exists on other chains" when not |
tgm/token-information (7d) |
1 | market_cap_usd, circulating_supply, buy_volume_usd, unique_buyers, liquidity_usd, total_holders, total_supply |
price, total DEX buys, liquidity cap, impact model, sanity warnings |
tgm/who-bought-sold BUY 7d, include_smart_money_labels = pro labels |
1 | data[].bought_volume_usd, address_label |
the pro share subtracted from total buys β organic/day |
tgm/who-bought-sold BUY 7d, exclude_smart_money_labels, page 1 of 1000 |
1 | data[].bought_volume_usd |
single-buyer dependence (top organic buyer > 25 %) β k β 2 pts |
tgm/flow-intelligence 1d |
1 | smart_trader_net_flow_usd, exchange_net_flow_usd |
is today red (halve tranche 1) |
tgm/flow-intelligence 7d |
1 | same | 7-day regime β a week of net-selling past 3Γ the daily thresholds, shown beside today |
tgm/flows label smart_money, 14d |
1 | date, is_complete, price_usd, total_inflows_count, total_outflows_count |
daily Smart Money net flow β red-day history |
tgm/flows label exchange, 14d |
1 | same | daily exchange net deposits β red-day history and rate |
tgm/indicators |
5 | liquidity-risk, concentration-risk, btc-reflexivity scores |
the participation rate k (10 % β 3 %) |
trade/quote Γ3 (solana, base) |
1 each | toTokenDecimals, inUsdValue, outUsdValue, priceImpactPct |
real routed cost of one tranche and of the whole bag |
12 credits per plan on EVM chains, 15 on solana/base, 0 on a cache hit. Formulas with the real PEPE numbers: docs/SCORING.md.
You watch it happen. The right-hand Nansen call rail streams every call as the plan is computed β /api/plan?stream=1 emits an NDJSON line when a call leaves (a pending row) and one when it lands (the recorded Call: endpoint, params, credits from the response header, ms, sha256 of the body) β the same objects the provenance drawer prints afterwards, so the rail's counters and the drawer's totals always agree. On load the rail shows the recorded example's calls labelled replayed Β· 0 cr; the CLI prints the same rows with --explain.
"Organic demand" is a who, not a how much. An RPC node or an explorer gives you volume; it cannot tell you which buyers are funds, Smart Money, exchanges or sniper-bot users. On Nansen that split exists as a server-side label filter on tgm/who-bought-sold β and the spike showed the filter acts on labels the row's address_label does not even display, so there is no client-side substitute. The red-day rule needs cohort net flows (tgm/flows, tgm/flow-intelligence); the risk dial needs peer-percentile scores (tgm/indicators); the route quote needs an aggregator that returns decimals with the price (trade/quote). Remove Nansen and Glidepath degrades to a TWAP calculator on raw volume β exactly the number a forced seller must not trust.
The constant-product estimate treats liquidity_usd as one pool with the token on one side. It is optimistic for tokens whose depth sits in a single thin pool, and it ignores MEV and gas. On solana and base the routed trade/quote replaces it and the label changes to "route quote". Not financial advice.
| metric | value | how to reproduce |
|---|---|---|
| tests | 258 vitest tests, green β 18 of them regression tests named for the defect each pins | npm test |
| spend guard | every credit-spending route (/api/plan, /api/export, /p, /api/og) capped at 6 requests/min per address per route (429) and a shared 3,000 live credits/day (honest 503 past it, before any Nansen call) |
apps/web/lib/guard.ts, apps/web/test/guard.test.ts |
| property-based verification | 60,000 generated cases (fast-check, 6 properties Γ 10,000) on the tranche planner β found and fixed one real defect | npm test (plan.property.test.ts) |
| key boundary | the nsn_ key never reaches a client: engine JSON/ICS/CSV, the route handler, the built pages β all asserted key-free; validation runs before any fetch |
npm test + npm run e2e |
| E2E | 60 Playwright checks (30 tests Γ chromium + Pixel 7) against the production build with no key | npm run e2e |
| fixtures | 13 recorded, 13/13 reproduced offline β zero network calls, zero credits | npm run verify |
| bench, cold (fresh cache, live Nansen) | p50 3.5 s / p95 6.0 s | npm run bench (7 tokens Γ 3 runs) |
| bench, warm (second call, same cache) | p50 5 ms / p95 356 ms | same |
| credits / plan | 13.0 (bench average; 12 on EVM, 15 on solana/base, 0 on a cache hit) | same |
| calls / plan | 9.3 | same |
| who-bought-sold pages / plan | 2.0 | same |
| failed calls | 7 of 195 (6 deterministic: tgm/flows refuses stablecoins) |
same |
| readiness | npm run check β files, secrets, kitchen leaks, fixtures, verify, tests, README claims |
npm run check |
- 258 vitest tests (
npm test): tranche-sizing table, red-day rule, risk dial vs indicator scores, impact model, decision-hash stability, pagination cap (the 20,000-buyer case), cache and offline mode, client retries/timeouts/header credits, ICS/CSV, resolver, end-to-end on a fake Nansen, one replay test per fixture β and three high-signal categories:- 18 regression tests, each named for the defect it pins (
qa round β¦,regression (β¦)): the one-tranche "expect ~2 days" copy, the ICS\;no-op, the error body cut mid-word, the FLOKI empty symbol,liquidity_usd = 0, the 7-day regime that was fetched but never shown, the echoed-key redaction, the zero-token tranche; and from the 2026-09-23 audit: a non-JSON 200 recorded twice and cached for an hour, a socket error never retried, a pro list ending exactly on page 3 called truncated, a cross-model "savings" figure, an unbounded memory cache. - One property-based verification, 60,000 generated cases (
packages/core/test/plan.property.test.ts, fast-check, 6 properties Γ 10,000 runs): Ξ£ tranches + remainder = amount held; every tranche β€ k Γ organic/day and β€ 1 % of liquidity; a red today halves tranche 1 and only tranche 1; consecutive UTC dates, β€ 90 of them, expectedDays β₯ days β₯ 1; the decision hash is deterministic and invariant to timing and context fields. Run 1 found a real defect β a liquidity cap underflowing at a dust price produced 90 tranches of zero tokens β now a guarded, fully-unsoldthinplan with its own regression test. - Key-boundary tests (
packages/core/test/boundary.test.ts,apps/web/test/api-boundary.test.ts,e2e/): the server key travels in one request header and nowhere else β plan JSON, provenance, ICS/CSV, the route handler's 400/500 bodies and the built pages are asserted free ofnsn_; an upstream body that echoes the key is redacted;/api/planvalidates input before the key check and before anyfetch.
- 18 regression tests, each named for the defect it pins (
- 13 fixtures, 13/13 reproduced offline (
npm run verify): each stores the raw Nansen responses byte-for-byte, the plan and the clock; replay must match the hash with zero network calls and zero credits. - Bench (
npm run bench, 7 tokens Γ 3 runs): cold p50 3.5 s / p95 6.0 s, warm p50 5 ms / p95 356 ms, 13.0 credits/plan, 9.3 calls/plan, 2.0 who-bought-sold pages/plan, 7 of 195 calls failed (6 deterministic:tgm/flowsrefuses stablecoins). Full table and reproduce steps: DEMO.md.
Every number traces to a named Nansen field β the provenance drawer and --explain list them per call. Nothing is estimated silently; when a call fails, the term is null, the warning names the endpoint, and the plan degrades (no history strip, no route quote, k treated as medium) instead of inventing a value. Honest states: no organic demand (< $50/day or < 5 buyers) β "there is nobody to sell to at any pace", numbers shown, no calendar. Calendar beyond 90 days β "N % still unsold after 90 days". Ticker unknown on the chain β the chains where it exists.
- The impact model is an approximation β see the caveat: one pool, token on one side, optimistic for a single thin pool, no MEV or gas. Only solana/base get a real route quote.
- The pro share is small. On the 11 tokens tried, Smart Money / funds / whales / exchanges were 0β3.6 % of DEX buys β Glidepath shows that split rather than dramatising it (DX-REPORT.md).
- Future days cannot be known red or green. Today is live; the 13 complete days before it are real; each ICS event carries the rule to re-check on the morning.
- A well-formed but nonexistent address still spends 12 credits β the parallel fan-out in
facts.tshas no existence gate before it; the plan comes backno-pricewith no crash (independent review 2026-09-16, left as is: gating ontoken-informationwould serialise the hero path). - The 12 s organic-breadth timeout is the wall-time ceiling on a slow Nansen day β it timed out once during review (plan took 14.8 s, shown as a red "failed: timeout" row in provenance, "concentration not applied"); not a crash, but visible (independent review 2026-09-16).
- Bug found and fixed β
/pand/api/exporthad no try/catch and sat outside the spend guard. A missing server key turned a share link into a 500 page, and a loop on either GET URL could spend credits around the per-IP rate and the daily ceiling. Oneadmit()gate now fronts all four credit-spending routes; failures render a banner or a plain-text status (audit 2026-09-23). - Bug found and fixed β one-tranche plans said "fits in one day" beside "expect ~2 days at the 15 % red-day rate".
expectedDaysis now 1 when today's colour is already known; regression inpackages/core/test/plan.test.ts(independent review 2026-09-16). - Bug found and fixed β ICS escaping was a no-op (
"\;") so;was never escaped, and content lines were not folded at 75 octets (RFC 5545). Both fixed without splitting UTF-8; regression inpackages/core/test/export.test.ts(independent review 2026-09-16). - Bug found and fixed β Nansen JSON error bodies were cut mid-word on screen ("β¦is a st"). The body's
messageis now surfaced as a sentence ("β¦is a stablecoin. The TGM flows endpointβ¦") (independent review 2026-09-16). - Bug found and fixed β
seed --rederiveoverwrote the USDC fixture's live 422 reason with an offline-cache-miss string. The script now preserves recordederrors; the fixture was restored from the original recording (independent review 2026-09-16). - Bug found and fixed β the share-page "computed Ns ago" badge hydrated with a server clock (server/client mismatch), and CLI
--explaindid not label who-bought-sold rows as (pros)/(organic). Both fixed (independent review 2026-09-16). - Docs bug found and fixed β the history strip was described as "the last 14 days". Nansen's
tgm/flowsfromis exclusive, so it is 13 complete days + live today (14 cells); wording fixed here and in SCORING.md (independent review 2026-09-16).
- Node β₯ 20
NANSEN_API_KEYβ annsn_β¦key from https://app.nansen.ai/api. Only the live steps need it; without a key,npm run verifyreplays the 13 recorded plans offline.
Quickstart β runs in under 10 minutes:
git clone https://github.com/edycutjong/glidepath && cd glidepath # 0:10
npm install # 0:40 (Node β₯ 20)
export NANSEN_API_KEY=nsn_... # from https://app.nansen.ai/api
npm run glidepath -- PEPE --chain ethereum --amount 12000000000 # 0:05 β the plan, 12 credits
npm run dev # 0:20 β http://localhost:3000, click "PEPE Β· 12B"Timed on a clean clone (macOS, Node 22, warm npm cache, 2026-09-16): clone + npm install 8 s, first live plan 2 s, npm run verify 1 s, web build 12 s β under a minute; budget 10 minutes on a cold npm cache and a slow link. Without a key, npm run verify replays the 13 recorded plans offline.
CLI flags: --json (full plan + provenance) Β· --explain (every tranche and every call) Β· --ics plan.ics Β· --csv plan.csv Β· --no-cache Β· --no-quotes.
7-stage pipeline: Quality β Security β Build β E2E β Performance β Deploy gate β Production Deploy (prebuilt vercel deploy to glidepath.edycu.dev, main only, after every gate) β no Nansen key anywhere; the only secret is VERCEL_TOKEN. The product is only exercised live by a human with a key.
# ββ Code Quality ββββββββββββββββββββββββββββ
npm run lint # ESLint (flat config, TS + React hooks)
npm run format:check # Prettier
npm run typecheck # engine + CLI + scripts Β· npm run typecheck:web
npm test # 258 vitest tests incl. 60,000 property cases
npm run test:coverage # + v8 coverage (100% statements/branches/functions/lines, engine + API)
npm run verify # 13/13 recorded plans replay offline β no key, no network, 0 credits
npm run check # submission-readiness audit (files, secrets, kitchen leaks, fixtures, verify, tests, README claims)
npm run ci # audit + format + lint + typecheck Γ2 + coverage + verify + check
# ββ Advanced Testing ββββββββββββββββββββββββ
npm run e2e # Playwright, 4 suites Γ chromium + Pixel 7, built app, no key
npm run e2e:ui # Playwright interactive mode
npm run lighthouse # Lighthouse CI on / and /judge (a11y β₯ 0.9 is a hard gate)
npm run ci:full # ci + next build + e2e
# ββ Security / live βββββββββββββββββββββββββ
npm run audit # npm audit on production dependencies (high+)
npm run bench # live, ~270 credits for 7 tokens Γ 3 runs| Layer | Tool | Status |
|---|---|---|
| Code Quality | ESLint 9 (typescript-eslint, react-hooks) + Prettier + tsc (engine, web) | β |
| Unit Testing | vitest, 258 tests, v8 coverage 100% statements/branches/functions/lines | β |
| Property-based | fast-check β 60,000 generated cases on the tranche planner | β |
| Key boundary | engine + route handler + built pages asserted nsn_-free |
β |
| Offline proof | 13/13 fixtures replay byte-for-byte (verify) |
β |
| E2E Testing | Playwright β 5 suites (home, plan flow, responsive, /judge, call rail), 60 checks, no key | β |
| Security (SAST) | CodeQL (javascript-typescript) | β |
| Security (SCA) | Dependabot (4 npm manifests + actions, grouped monthly, no majors) + npm audit + license-checker | β |
| Secret Scanning | TruffleHog (verified only) + gitleaks (full history) + npm run check history grep |
β |
| Performance | Lighthouse CI (advisory), bundle budget 1.5/2 MB | β |
| Releases | Semantic versions from conventional commits (release.yml), v1.0.0 β v1.1.0 tagged by the workflow |
β |
| Community | CoC Β· Contributing Β· Security policy Β· issue + PR templates | β |
packages/core engine (client, cache, facts, plan, impact, export, fixtures) packages/cli the CLI
apps/web Next.js 15 planner (/api/plan, /api/export, /api/og, /p) scripts/ spike Β· seed Β· verify Β· bench Β· check
fixtures/ 13 recorded live runs docs/ SCORING.md Β· DX-REPORT.md Β· screenshots
e2e/ Playwright suites (no key) JUDGE.md the /judge page, mirrored for the repo reader
- Live: https://glidepath.edycu.dev β no wallet, no account; five example chips (PEPE Β· BONK Β· BRETT Β· TURBO Β· SHIB2), ICS/CSV export, share card with OG image.
- Reproduce every number: DEMO.md β the recorded PEPE plan, offline replay, tests, bench table, re-recording the fixtures, the day-one spike, and the credits spent.
- Formulas with the real PEPE numbers: docs/SCORING.md Β· API findings from the spike: docs/DX-REPORT.md.
MIT β see LICENSE. Built for the Nansen Meridian Buildathon (Sep 2026) by @edycutjong.





