Skip to content

Commit c140f89

Browse files
TokenBriceclaude
andcommitted
docs: fix 22 stale claims across 6 documentation files
- Endpoint count 22→23 (missing digest-snapshot), migration count 26→28 - Add priceConfidence, supplySource, circuits to API reference - Fix methodology version 1.0→3.1, remove phantom PSI freezes component - Document circuit breakers, dual-primary validation, CG supply fallback - Add full digest-snapshot endpoint documentation - Mark strategic audit items 2+3 as implemented Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent bd92beb commit c140f89

File tree

6 files changed

+167
-41
lines changed

6 files changed

+167
-41
lines changed

CLAUDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ src/components/ — UI components (ui/ = shadcn primitives, do not edit)
2525
src/hooks/ — TanStack Query hooks + shared state hooks
2626
src/lib/ — Types, stablecoin list, formatters, classification, peg logic
2727
worker/src/cron/ — Data sync crons
28-
worker/src/api/ — REST API handlers (22 endpoints)
28+
worker/src/api/ — REST API handlers (23 endpoints)
2929
worker/src/lib/ — DB helpers, constants, shared utilities
3030
```
3131

@@ -58,7 +58,7 @@ cd worker && npx tsc --noEmit # Worker type-check
5858
Read these when working on related code:
5959

6060
- **`docs/architecture.md`** — Full file tree, API endpoints
61-
- **`docs/api-reference.md`** — Full API reference: all endpoints (22 router handlers + inline admin), query params, response shapes, caching
61+
- **`docs/api-reference.md`** — Full API reference: all endpoints (23 router handlers + inline admin), query params, response shapes, caching
6262
- **`docs/classification.md`** — Classification system, peg currencies, gold/JPY/IDR stablecoins
6363
- **`docs/dex-liquidity.md`** — Liquidity score algorithm, quality multipliers
6464
- **`docs/stability-index.md`** — PSI formula, components, condition bands, calibration

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,9 @@ src/ Frontend (Next.js static export)
114114
worker/ Cloudflare Worker (API + cron jobs)
115115
├── src/
116116
│ ├── cron/ Scheduled data sync (sync-stablecoins, enrich-prices, detect-depegs, sync-dex-liquidity, etc.)
117-
│ ├── api/ REST endpoints (22 handlers, all wrapped with withErrorHandler)
118-
│ └── lib/ D1 helpers, shared constants, depeg types, API error handler
119-
└── migrations/ D1 SQL migrations (26 total)
117+
│ ├── api/ REST endpoints (23 handlers, all wrapped with withErrorHandler)
118+
│ └── lib/ D1 helpers, shared constants, depeg types, API error handler, circuit breaker
119+
└── migrations/ D1 SQL migrations (28 total)
120120
```
121121

122122
## Infrastructure
@@ -163,6 +163,7 @@ The data pipeline includes multiple guardrails designed for research-grade accur
163163
- **Freshness header**`/api/stablecoins` returns `X-Data-Updated-At` so consumers can detect stale data
164164
- **Atomic backfill** — depeg event backfills use transactional batch operations to prevent data loss on worker crashes
165165
- **Retry logic** — all external API fetches use exponential backoff with configurable 404 handling
166+
- **Circuit breakers** — per-source circuit breakers (3-strike open, 30-min probe) prevent hammering downed APIs; dual-primary price validation cross-checks DefiLlama and CoinGecko within 50 bps; CoinGecko supply fallback activates when DefiLlama is unavailable
166167

167168
## Deployment
168169

docs/api-reference.md

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ Full stablecoin list with current supply, price, chain breakdown, and FX rates.
8484
| `gecko_id` | `string \| null` | CoinGecko ID |
8585
| `pegType` | `string` | DefiLlama peg type (e.g. `"peggedUSD"`, `"peggedEUR"`) |
8686
| `pegMechanism` | `string` | `"fiat-backed"`, `"crypto-backed-algorithmic"`, etc. |
87-
| `priceSource` | `string` | Source of the current price (`"defillama"`, `"coingecko"`, `"dexscreener"`) |
87+
| `priceSource` | `string` | Source of the current price (`"defillama"`, `"coingecko"`, `"defillama+coingecko"`, `"dexscreener"`) |
88+
| `priceConfidence` | `string \| null` | Price confidence level: `"high"` (dual-source agreement), `"single-source"`, `"low"` (sources diverge), `"fallback"` (enrichment pipeline) |
89+
| `supplySource` | `string \| undefined` | Supply data source: `"defillama"` or `"coingecko-fallback"` |
8890
| `price` | `number \| null` | Current price in USD |
8991
| `circulating` | `Record<string, number>` | Current supply in USD, keyed by pegType (e.g. `{ "peggedUSD": 138000000 }`) |
9092
| `circulatingPrevDay` | `Record<string, number>` | Supply 24 h ago |
@@ -597,6 +599,42 @@ Each element uses `digestText` (note: differs from the singular `/api/daily-dige
597599

598600
---
599601

602+
### `GET /api/digest-snapshot`
603+
604+
Contextual data snapshot for a specific digest date — includes the digest's input data, active depeg events, and blacklist events for that day. Used by SSG builds for individual digest pages.
605+
606+
**Cache:** slow
607+
608+
**Required query parameter**
609+
610+
| Param | Type | Description |
611+
|-------|------|-------------|
612+
| `date` | `string` | Date in `YYYY-MM-DD` format (required) |
613+
614+
**Response**
615+
616+
```json
617+
{
618+
"date": "2026-02-27",
619+
"inputData": { "totalMcapUsd": 230000000000, "mcap7dDelta": 0.012, ... },
620+
"prevInputData": { ... },
621+
"depegEvents": [{ "stablecoinId": "42", "symbol": "FOO", "direction": "below", "peakDeviationBps": 150, ... }],
622+
"blacklistEvents": [{ "stablecoin": "USDT", "chainName": "Ethereum", "eventType": "blacklist", ... }]
623+
}
624+
```
625+
626+
| Field | Type | Description |
627+
|-------|------|-------------|
628+
| `date` | `string` | The requested date |
629+
| `inputData` | `object \| null` | Digest input data (mcap, depegs, supply changes, PSI) for this date |
630+
| `prevInputData` | `object \| null` | Previous day's input data for delta computation |
631+
| `depegEvents` | `array` | Up to 20 depeg events active on that date, ordered by severity |
632+
| `blacklistEvents` | `array` | Up to 50 blacklist events on that date |
633+
634+
**Error responses:** `400` for missing/invalid date, `404` if no digest exists for that date.
635+
636+
---
637+
600638
### `GET /api/health`
601639

602640
Worker health check. Reports cache freshness and blacklist table integrity. Not served from Cloudflare edge cache (`no-store`).
@@ -618,6 +656,10 @@ Worker health check. Reports cache freshness and blacklist table integrity. Not
618656
"blacklist": {
619657
"totalEvents": 13422,
620658
"missingAmounts": 0
659+
},
660+
"circuits": {
661+
"defillama-stablecoins": { "state": "closed", "consecutiveFailures": 0, "lastSuccessAt": 1772190029 },
662+
"coingecko-prices": { "state": "closed", "consecutiveFailures": 0, "lastSuccessAt": 1772190030 }
621663
}
622664
}
623665
```
@@ -629,6 +671,7 @@ Worker health check. Reports cache freshness and blacklist table integrity. Not
629671
| `caches` | `Record<string, CacheStatus>` | Per-cache freshness status |
630672
| `blacklist.totalEvents` | `number` | Total events in blacklist table |
631673
| `blacklist.missingAmounts` | `number` | Events where `amount` is null (should be 0) |
674+
| `circuits` | `Record<string, CircuitRecord>` | Per-source circuit breaker states. Keys: `defillama-stablecoins`, `defillama-coins`, `defillama-yields`, `defillama-protocols`, `coingecko-prices`, `coingecko-mcap`. Empty until first cron run |
632675

633676
**`CacheStatus`**
634677

@@ -639,8 +682,8 @@ Worker health check. Reports cache freshness and blacklist table integrity. Not
639682
| `healthy` | `boolean` | `true` when `ageSeconds / maxAge ≤ 1.5` |
640683

641684
**Overall status logic:**
642-
- `healthy` — worst cache ratio ≤ 1.5
643-
- `degraded` — worst ratio between 1.5 and 2
685+
- `healthy` — worst cache ratio ≤ 1.5 and no open circuits
686+
- `degraded` — worst ratio between 1.5 and 2, or any circuit is open
644687
- `stale` — worst ratio > 2
645688

646689
---
@@ -664,7 +707,7 @@ Daily Pharos Stability Index (PSI) scores. The PSI is a composite ecosystem heal
664707
"current": {
665708
"score": 81.1,
666709
"band": "STEADY",
667-
"components": { "severity": 4.59, "breadth": 15, "freezes": 0, "trend": 0.65 },
710+
"components": { "severity": 4.59, "breadth": 15, "trend": 0.65 },
668711
"computedAt": 1771977600
669712
},
670713
"history": [
@@ -678,7 +721,7 @@ Daily Pharos Stability Index (PSI) scores. The PSI is a composite ecosystem heal
678721
| `current` | `object \| null` | Latest PSI score and components. `null` if cron has not yet run |
679722
| `current.score` | `number` | PSI score 0–100 |
680723
| `current.band` | `string` | Condition band: `"BEDROCK"`, `"STEADY"`, `"TREMOR"`, `"FRACTURE"`, `"CRISIS"`, `"MELTDOWN"` |
681-
| `current.components` | `object` | Component breakdown: `severity`, `breadth`, `freezes`, `trend` |
724+
| `current.components` | `object` | Component breakdown: `severity`, `breadth`, `trend` |
682725
| `current.computedAt` | `number` | Unix seconds of computation |
683726
| `history` | `array` | Historical scores, newest first. With `detail=true`, each entry includes `components` |
684727

@@ -699,7 +742,7 @@ Stablecoin risk grade cards with dimension-level scores. Grades are computed fro
699742
"edges": [{ "from": "2", "to": "5" }, ...]
700743
},
701744
"methodology": {
702-
"version": "1.0",
745+
"version": "3.1",
703746
"weights": { "pegStability": 0.25, "liquidity": 0.20, "resilience": 0.20, "decentralization": 0.10, "dependencyRisk": 0.25 },
704747
"thresholds": [{ "grade": "A+", "min": 97 }, { "grade": "A", "min": 93 }, ...]
705748
},
@@ -737,7 +780,10 @@ Stablecoin risk grade cards with dimension-level scores. Grades are computed fro
737780
| `liquidityScore` | `number \| null` |
738781
| `concentrationHhi` | `number \| null` |
739782
| `bluechipGrade` | `BluechipGrade \| null` |
740-
| `canBeBlacklisted` | `boolean` |
783+
| `canBeBlacklisted` | `boolean \| "possible"` |
784+
| `chainRisk` | `ChainRisk` |
785+
| `collateralQuality` | `CollateralQuality` |
786+
| `custodyModel` | `CustodyModel` |
741787
| `governanceTier` | `GovernanceType` |
742788
| `dependencies` | `DependencyWeight[]` |
743789

docs/architecture.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
| `GET /api/supply-history` | Per-coin supply history (`?stablecoin=ID&days=N`) |
1818
| `GET /api/daily-digest` | AI-generated daily market summary (latest) |
1919
| `GET /api/digest-archive` | All daily digests, newest-first |
20-
| `GET /api/health` | Worker health check |
20+
| `GET /api/digest-snapshot` | Digest snapshot for SSG builds (returns latest digest at build time) |
21+
| `GET /api/health` | Worker health check (includes circuit breaker states) |
2122
| `GET /api/status` | Admin status dashboard (cron runs, cache freshness, data quality). Requires `X-Admin-Key` header |
2223
| `GET /api/stability-index` | Daily Pharos Stability Index scores, bands, and component breakdowns (`?detail=true` for full history) |
2324
| `GET /api/report-cards` | Stablecoin risk grade cards with dimension scores (peg, liquidity, resilience, decentralization, dependency) |
@@ -225,13 +226,13 @@ src/ # Next.js frontend (static export)
225226
226227
worker/ # Cloudflare Worker (API + cron jobs)
227228
├── wrangler.toml # Worker config, D1 binding, cron triggers
228-
├── migrations/ # D1 SQL migrations (26 total)
229+
├── migrations/ # D1 SQL migrations (28 total)
229230
└── src/
230231
├── index.ts # Entry: fetch + scheduled handlers, CORS
231232
├── router.ts # Route matching for API endpoints
232233
├── cron/
233234
│ ├── sync-stablecoins.ts # DefiLlama + CoinGecko gold → D1 (orchestrator, delegates to enrich-prices + detect-depegs)
234-
│ ├── enrich-prices.ts # 5-pass price enrichment pipeline (DefiLlama, CoinGecko, CoinMarketCap, DexScreener)
235+
│ ├── enrich-prices.ts # Dual-primary price validation + 5-pass enrichment pipeline (DefiLlama, CoinGecko, CoinMarketCap, DexScreener)
235236
│ ├── detect-depegs.ts # Depeg event detection + orphan event cleanup
236237
│ ├── sync-stablecoin-charts.ts # Historical chart data → D1
237238
│ ├── snapshot-supply.ts # Per-coin supply snapshots → D1 (daily, 8AM UTC)
@@ -256,6 +257,7 @@ worker/ # Cloudflare Worker (API + cron jobs)
256257
│ ├── bluechip.ts # GET /api/bluechip-ratings
257258
│ ├── daily-digest.ts # GET /api/daily-digest
258259
│ ├── digest-archive.ts # GET /api/digest-archive
260+
│ ├── digest-snapshot.ts # GET /api/digest-snapshot
259261
│ ├── dex-liquidity.ts # GET /api/dex-liquidity (includes HHI, trends)
260262
│ ├── dex-liquidity-history.ts # GET /api/dex-liquidity-history
261263
│ ├── health.ts # GET /api/health
@@ -270,7 +272,8 @@ worker/ # Cloudflare Worker (API + cron jobs)
270272
└── lib/
271273
├── db.ts # D1 read/write helpers (setCacheIfNewer CAS guard, batchExecute, buildPaginatedQuery, logCronRun with protected catch)
272274
├── chain-rpcs.ts # Chain RPC endpoint config (11 chains: EVM + Tron)
273-
├── constants.ts # Shared worker constants (DEPEG_THRESHOLD_BPS, DEX_FRESHNESS_SEC, D1_BATCH_SIZE, MIN_VALID_ASSET_COUNT, CACHE_PROFILES, ETHERSCAN_V2_BASE)
275+
├── circuit-breaker.ts # Per-source circuit breaker (3-strike open, 30-min probe, auto-alert on transitions)
276+
├── constants.ts # Shared worker constants (DEPEG_THRESHOLD_BPS, DEX_FRESHNESS_SEC, D1_BATCH_SIZE, MIN_VALID_ASSET_COUNT, CACHE_PROFILES, CIRCUIT_SOURCE)
274277
├── auth.ts # Timing-safe admin key comparison (SHA-256 + crypto.subtle.timingSafeEqual)
275278
├── alerts.ts # Alert sending (ntfy push notifications on cron failures)
276279
├── bigint.ts # bigIntToDecimal() helper for safe BigInt-to-number conversion (used by blacklist sync)

docs/data-pipeline.md

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,24 @@
22

33
## Supply Pipeline
44

5-
Supply data uses a simple two-source model:
5+
Supply data uses a two-source model with automatic fallback:
66

77
- **DefiLlama** — primary source for all stablecoins tracked by DefiLlama's stablecoin API
8-
- **CoinGecko market cap** — used only for gold/silver/fiat tokens that DefiLlama doesn't track (e.g. XAUT, PAXG, KAU)
8+
- **CoinGecko market cap** — used for gold/silver/fiat tokens that DefiLlama doesn't track (e.g. XAUT, PAXG, KAU), and as a **full supply fallback** when the DefiLlama stablecoins API is down (circuit breaker triggers `fallbackToCgSupply()`)
99

1010
No on-chain overrides, no CMC supply patches, no manual supply corrections.
1111

12+
### Circuit Breakers
13+
14+
All external data sources are protected by per-source circuit breakers (`worker/src/lib/circuit-breaker.ts`). State is persisted in the D1 `cache` table under keys like `circuit:defillama-stablecoins`.
15+
16+
- **Open threshold**: 3 consecutive failures
17+
- **Probe interval**: 30 minutes (one request allowed to test recovery)
18+
- **Alerts**: Webhook alert fires on open and close transitions
19+
- **Health impact**: Any open circuit triggers `degraded` status on `/api/health`
20+
21+
Sources tracked: `defillama-stablecoins`, `defillama-coins`, `defillama-yields`, `defillama-protocols`, `coingecko-prices`, `coingecko-mcap`.
22+
1223
### DefiLlama list vs detail API
1324

1425
The **list** endpoint (`stablecoins.llama.fi/stablecoins`) returns `circulating` values **already in USD** for all peg types — `peggedRUB`, `peggedEUR`, `peggedJPY`, etc. are all denominated in USD despite their key names.
@@ -19,7 +30,20 @@ Do **not** multiply list endpoint values by price — that would double-convert
1930

2031
## Price Enrichment Pipeline
2132

22-
`enrichMissingPrices()` in `worker/src/cron/enrich-prices.ts` uses a 5-pass system for assets with missing or zero prices:
33+
### Dual-Primary Price Validation
34+
35+
Before the enrichment pipeline runs, `fetchDualPrimaryPrices()` fetches prices from both the DefiLlama coins API and CoinGecko `/simple/price` **in parallel** for all assets with a valid `geckoId`. It cross-validates within 50 basis points:
36+
37+
- **Both agree (≤50 bps)**`priceConfidence: "high"`, use DL price
38+
- **Disagree (>50 bps)**`priceConfidence: "low"`, use closer-to-peg value, log divergence
39+
- **One source down**`priceConfidence: "single-source"`, use available
40+
- **Both down** → skip, falls through to enrichment pipeline
41+
42+
Each asset gets tagged with `priceConfidence` (high/single-source/low/fallback) and `supplySource` (defillama/coingecko-fallback).
43+
44+
### Enrichment Pipeline
45+
46+
`enrichMissingPrices()` in `worker/src/cron/enrich-prices.ts` uses a 5-pass system for assets still missing prices after dual-primary:
2347

2448
1. **Pass 1:** Contract address -> DefiLlama coins API (with multi-chain fallback)
2549
2. **Pass 2:** CoinGecko ID -> DefiLlama CoinGecko proxy
@@ -84,7 +108,7 @@ The live `/price/` endpoint requires no API key and has no documented rate limit
84108

85109
## Stability Index (PSI) Computation
86110

87-
`computeAndStoreStabilityIndex()` in `worker/src/cron/stability-index.ts` runs daily at 07:55 UTC and computes a composite ecosystem health score (0–100). Formula: `Score = 100 − severity − breadth − freezes + trend`. See `docs/stability-index.md` for full algorithm, calibration examples, and band definitions.
111+
`computeAndStoreStabilityIndex()` in `worker/src/cron/stability-index.ts` runs every 15 minutes and computes a composite ecosystem health score (0–100). Formula: `Score = 100 − severity − breadth + trend`. See `docs/stability-index.md` for full algorithm, calibration examples, and band definitions.
88112

89113
**Band classification:** `BEDROCK` (90–100), `STEADY` (75–89), `TREMOR` (60–74), `FRACTURE` (40–59), `CRISIS` (20–39), `MELTDOWN` (0–19)
90114

0 commit comments

Comments
 (0)