Pivot — DEFI@home (2026-04-23, factual update 2026-04-25). This spec was originally written around a three-phase enrichment pipeline (Phase 1 crawlers, Phase 2
@l2beat/discovery-based onchain workers, Phase 3 LLM classification) on a Next.js codebase forked from l2beat/l2beat. That plan is superseded. Risk-slice grading happens via DEFI@home: contributors run a pinned prompt through an LLM of their choice, submit JSON output as a pull request againstdata/submissions/<slug>/<slice>/, and a quorum bot merges todata/assessments/once ≥3 independent runs agree on grade and overlapping evidence. SeeREADME.md(top section),packages/prompts/(prompt source,PROMPT_VERSION = 12),packages/validator/(schema/quorum/reconcile CLIs), anddata/schema/slice-assessment.v2.json(output contract).Specifically superseded:
- Next.js + l2beat fork lineage — the shipped app is Astro 5 with one Svelte 5 island (
LandingTable.svelte). It is not a fork of l2beat/l2beat;@l2beat/discoveryis not in the workspace. Visual lineage from L2BEAT is preserved at the design level only.- 7-slice pizza — shipped pizza has 5 slices:
control,ability-to-exit,autonomy,open-access,verifiability. Defined once inapps/web/src/lib/pizza.tsandpackages/prompts/src/index.ts(SLICE_IDS).- Phase 1 crawlers / artifact extraction — replaced by DEFI@home submissions citing block explorers, pinned GitHub commits, and audit PDFs. No
data/artifacts/directory; no crawler workers; no robots.txt / UA / rate-limit policy needed.- Phase 2 onchain workers (
@l2beat/discoveryintegration, SQLite cache, contract / discovery tables) — DEFI@home contributors and the autorun GitHub Action read onchain state via block explorers and cite the URLs as evidence. The "Phase 2 may introduce a DB" provision lapses; the project stays git-native indefinitely.- Phase 3 LLM classification (machine-generated claims with hash + substring citation enforcement) — replaced by DEFI@home's per-submission JSON schema, which already enforces evidence URLs and the conditional "grade=unknown ⇒ unknowns[] non-empty, else evidence[] non-empty" rule.
- Full-SSG rendering — landing, methodology, and contribute pages are still prerendered, but
/protocol/[slug]is server-rendered with Vercel ISR (60s expiration). Seeapps/web/astro.config.mjs.- Defiscan stages at launch — what ships today is a tier system (
none/bronze/silver/gold) computed from quorum count + human signoff. Defiscan stages remain the planned grading model once human review formalizes; tiers are the data-readiness signal that gates publication. Seeapps/web/src/lib/tier.ts.What carries over unchanged: the DeFiLlama seed (
pnpm sync→data/defillama-snapshot.json), thepackages/registrymerge of snapshot + overlays, the read-only / git-native operating model, the radiographic visual design, and the[defillama]/[curated]provenance tagging. Phase 0 shipped with broader scope than originally planned and is the production codebase today.The provenance system gains one new tag class:
[assessment]for fields populated fromdata/assessments/<slug>/<slice>.json, replacing the planned[crawler]/[onchain]/[llm_inference]classes. Live GitHub Actions:validate-submission.yml(per-PR schema check),quorum.yml(merge-on-quorum),reconcile.yml(assessments → master),autorun.yml(Anthropic SDK runs as third voice),sync.yml(weekly DeFiLlama refresh). Defiscan stage adoption is the next milestone after master reconciliation stabilizes.
Build a live, evidence-based registry of DeFi protocols that can evolve into an L2BEAT-for-DeFi style review system.
The initial version is not a final risk rating system. It is a protocol registry and evidence intake layer with human-reviewed publication later.
defipunkd. Matches the repo; evokes L2BEAT lineage.
Use DeFiLlama as the protocol universe seed, then layer deterministic evidence collection and later human-reviewed analysis on top.
curl https://api.llama.fi/protocols
This avoids rebuilding the protocol master list from scratch and gets an MVP live quickly.
Built fresh as a pnpm workspace: apps/web (Astro 5 + one Svelte 5 island) plus packages/{registry, sync, prompts, validator}. Not a fork of any upstream codebase — the earlier plan to fork l2beat/l2beat was dropped during scaffolding. Visual and information-density inspiration from L2BEAT is preserved at the design level (one dense table per protocol, evidence-first density, monospace TVL).
The rubric is adapted from Defiscan's framework. Adoption of Defiscan stages verbatim is planned once human review formalizes; the live grading surface today is the tier system (see "Rubric" below).
License: MIT.
Version 1 is:
- a live registry of DeFi protocols (read-only; no user-writable surfaces at MVP)
- a transparency and control-surface evidence hub
- a review pipeline with explicit missingness and provenance (pipeline added in later phases)
It does not claim protocols are safe or unsafe based on incomplete data. Published reviewed profiles use a graded stage label (Defiscan stages) with prominent disclaimers.
MVP has no submission queue, no reviewer web UI, no contact form, no auth, no database. All writes go through the solo reviewer's terminal as git commits. Corrections and takedowns route to public GitHub PRs and issues on the project repo (linked from site footer).
Primary: DeFi power users and researchers. Assume familiarity with proxies, multisigs, timelocks, role-based admin. Lean into raw evidence density over marketing polish.
Solo builder, solo reviewer. Throughput is ~5–10 reviewed protocols per week, so the pipeline must be machine-first: deterministic workers do the grunt work, human review only kicks in at publication.
- DeFiLlama is the registry seed, not the final truth
- Stored artifacts and deterministic chain reads come before LLM analysis
- Missingness should be visible (even if undifferentiated at Phase 0)
- All claims must have provenance; LLM claims must carry citations enforced by hash + substring match (Phase 3)
- Onchain beats text in any conflict — but contradictions are shown publicly, not hidden
- Human review is required before stamp-of-approval publication
- Do not collapse everything into one score too early
- The moat is the admin/control-surface graph, not just scraped metadata
- Machine-first pipeline — the solo reviewer is the scarce resource
- Read-only MVP — no writes from the internet until Phase 3
- There is no database at Phase 0; the git repo is the source of truth. Protocol metadata lives as committed JSON/TS files. Every deploy is an immutable snapshot of the repo at a given SHA. When a DB finally enters (Phase 2+, for onchain data only), it remains derived state — any instance must be rebuildable from scratch using git + a live DeFiLlama fetch.
- Reviewer flow is already GitHub. Corrections are PRs; the review surface is
gh pr view. No second system to build. - Provenance is
git blame. Who curated a field, when, and why (commit message + PR discussion) comes free. Nosource_type/retrieved_at/artifact_hashcolumns to maintain at Phase 0. - Deploys are immutable. The site at commit X is deterministically reproducible. No "what did the DB look like on Tuesday" questions.
- Forks are trivial. Anyone can
git clonedefipunkd and run their own instance with zero infra beyond Vercel. - L2BEAT precedent. l2beat/l2beat keeps static project metadata in
packages/config/src/projects/**.ts; their DB only holds time-series. We go further and skip the DB entirely until Phase 2.
Pull https://api.llama.fi/protocols via pnpm sync — a node CLI run locally (or via a workflow_dispatch GitHub Action that commits the diff as a PR). It normalizes the payload and writes data/defillama-snapshot.json, which is committed to the repo.
Provides: protocol name, slug, category, chains, TVL, website, audit count, audit links, twitter, hallmarks, parent protocol relationships, other metadata.
Upstream etiquette: send a User-Agent: defipunkd (+<contact-url>). Manual triggering keeps frequency naturally low.
Ingest everything, enrich selectively. Every DeFiLlama entry becomes an entry in the snapshot. No junk filtering at MVP. Crawlers, onchain workers, and LLM steps (Phase 1+) only run on protocols that meet a quality bar defined at their owning phase.
All chains ingested, display-only. Phase 2 onchain analysis is Ethereum-mainnet only. Non-EVM onchain rows render as plain unknown at Phase 0.
Mirror DeFiLlama: if DeFiLlama delists, we delist. Soft-delete: the entry stays in the snapshot with a delisted_at field set by the sync script (14-day absence rule — see §"Interview decisions"). /protocol/{slug} continues to render — a <meta name="robots" content="noindex"> page preserving the last-known name, delisted_at, and a DeFiLlama link. (The originally-planned HTTP 410 route handler was dropped to keep static-output behavior consistent.)
For each protocol, display:
- name, family / parent, chains, category, TVL, website, audit count, audit links, twitter, hallmarks
last_synced_attimestamp (always visible)- gray "unknown" pizza chart in the detail-page header and inline on landing-table rows
All enrichment rows render as unknown with em-dash provenance at Phase 0.
/protocol/{slug} — one page per DeFiLlama slug. Chain deployments within the page use top-tabs across the protocol header, one per chain.
Breadcrumb: collapsed when family == instance (the common case at Phase 0).
List + detail pages live for all DeFiLlama protocols, fully statically rendered from data/defillama-snapshot.json + data/overlays/*.json, showing raw metadata + unknown missingness rows.
- final safety/risk scores
- automated multisig classification
- final admin/control-surface judgment
- audit quality scoring
- contract-level onchain privilege analysis
- public API (website only)
- authentication
- TVL sparklines / historical charts
- analytics / user tracking
- community submissions or reviewer UI
- contact form (GitHub issues/PRs only)
- crawl-ethics policy (no crawlers at MVP)
- R2/S3 artifact storage
- any database
Three planes, git-native at Phase 0:
- Seed discovery —
pnpm syncwritesdata/defillama-snapshot.json. - Evidence extraction (Phase 1) — crawler workers write artifacts into
data/artifacts/<sha256>+ JSON index entries, also committed. - Classification / review (Phase 3) — LLM-generated structured claims, human-reviewed via GitHub PRs.
data/
defillama-snapshot.json # one big normalized JSON blob, regenerated by pnpm sync
overlays/
<slug>.json # human-curated per-field overrides (Wikipedia-style)
schema/
slice-assessment.v2.json # JSON schema for DEFI@home submissions
submissions/
<slug>/<slice>/*.json # one file per LLM run (model + prompt_version + chat_url)
assessments/
<slug>/<slice>.json # quorum bot output, one merged record per (slug, slice)
master/ # reconciled master records from assessments
The originally-planned data/artifacts/ (Phase-1 SHA-256 blobs) is not created and not needed; DEFI@home submissions cite block-explorer URLs, pinned GitHub commits, and audit PDFs by URL.
data/defillama-snapshot.json— single file, rewritten whole on each sync. Not per-protocol files (avoids ~6k file changes per sync, keeps diffs legible, avoids filesystem pathology on Vercel build). Shape sketched below.data/overlays/<slug>.json— per-protocol partials validated by a Zod schema inpackages/registry. Schema is the source of truth; the TSOverlaytype is derived viaz.infer. Unknown keys and malformed values fail the build. Overlays override the snapshot on a per-field basis at merge time.
packages/registry exports:
listProtocols(): Protocol[]— merged snapshot + overlays, full index in memorygetProtocol(slug): Protocol | undefinedlistChildren(parentSlug): Protocol[]
Merge happens once at module load — at build time for prerendered pages, on cold-start of the Vercel serverless function for ISR-cached /protocol/[slug]. No DB, no async. Unknown slugs return undefined. Overlay handling: malformed overlays (Zod parse/validation failure, unknown keys) fail the build. Orphan overlays (valid schema, slug not in current snapshot) log a warning and are skipped — they do not resurrect the protocol.
- Frontend: TypeScript + Astro 5 with the Vercel adapter, plus exactly one Svelte 5 island (
apps/web/src/components/LandingTable.svelte, mountedclient:loadon/). Every other page and component is a zero-JS.astrocomponent. Typography via@fontsource-variable/ibm-plex-sansand@fontsource/ibm-plex-mono. - Rendering: hybrid (
output: "server"). Landing,/methodology, and/contributeare markedprerender = trueand emit static HTML at build./protocol/[slug]is server-rendered on demand with Vercel ISR (60s expiration) so master-file updates become visible without a redeploy. The static-output invariant for delisted slugs is a prerendered page with<meta name="robots" content="noindex">(no HTTP 410; the static page preserves last-known name +delisted_at+ DeFiLlama link). - Runtime data access: data files live outside
apps/weband are read at runtime by@defipunkd/registry. The Vercel adapter'sincludeFilesshipsdata/defillama-snapshot.json,data/overlays/,data/assessments/,data/master/, anddata/submissions/into the serverless function bundle. - Database: none. The git repo is the source of truth; the earlier provision for a Phase-2 SQLite cache lapses.
- Object storage: not used.
- Queue: none. Workers (sync, validator/quorum CLIs, autorun) run as node scripts invoked by GitHub Actions.
- Seed trigger:
pnpm synclocally, or.github/workflows/sync.yml(weekly Monday 06:00 UTC) which runs the same script and opens a PR with the diff. - Observability: GitHub Actions workflow logs + Vercel build logs. Nothing else.
Dark-only, radiographic palette at Phase 0. No light theme shipped. Aesthetic direction: clinical, unbothered, evidence-first — see .impeccable.md for full design context.
- Base: deep ink (
#08090c) with a tinted surface hierarchy (#10131a,#1e2330). - Primary text
#d8e4ec, muted text#6b7785(WCAG AA on base). - Single system accent: cool blue
#7bb4cc— links, active tabs, selection. Used sparingly; accents are evidentiary, not decorative. - Pizza slice palette (defined in
apps/web/src/lib/pizza.ts): greenoklch(0.60 0.130 150), orangeoklch(0.65 0.120 75), redoklch(0.52 0.170 22), unknown/grayoklch(0.55 0.015 235). Each slice color carries a single meaning. The 5-slice list is exported asPIZZA_SLICES: control, ability-to-exit, autonomy, open-access, verifiability. - CEX category short-circuits grading: all five slices render red regardless of per-dimension signals. Implemented via a
category === "CEX"guard inpizzaGradesFor(). Custodial exchanges fail every transparency axis by construction. - Typography: IBM Plex Sans + IBM Plex Mono, one-family carried hard. Mono +
tabular-numson every TVL display. - Dense table component inherited from L2BEAT near-verbatim; palette and type swapped.
- Logos:
logoURL from the DeFiLlama payload, rendered client-side with a letter-tile fallback for nulls and 404s.
Text tag in brackets, inline after the value. At Phase 0 there are two tags:
[defillama]— value came from the DeFiLlama snapshot[curated]— value came from adata/overlays/<slug>.jsonoverride
Example:
TVL $42M [defillama]
Website panoptic.xyz [curated]
Github unknown —
Admin unknown —
[human_review] and richer provenance classes (crawler, onchain, llm_inference) arrive with their owning phases. For Phase 0 curated content, git blame on the overlay file is the audit trail.
Superseded by DEFI@home (2026-04-23). Phase 0 shipped with broader scope than originally drafted (it now includes the assessment pipeline that was originally Phase 3). The Phase 1 / 2 / 3 prose below is kept as historical record. For the live architecture see "Current architecture (2026-04)" immediately after the legacy phase list.
- scaffold Next.js + pnpm workspace; prune l2beat aggressively
pnpm syncwritesdata/defillama-snapshot.jsondata/overlays/directory (empty; JSON files validated by Zod inpackages/registry)packages/registrymerges snapshot + overlays and exports the in-memory API- protocol detail pages statically rendered for all slugs
- delisted slugs → HTTP 410 (route handler)
- landing browse-all table + 11 category tabs
/methodologystatic MDX page- footer link to GitHub issues/PRs
noindexon/protocol/*pages- no DB, no secrets, no Vercel functions
Crawler workers (site, docs, github, audit) run as local node scripts. Output lands in data/artifacts/<sha256> + typed JSON index entries, committed via PR. Still no DB. R2/S3 decision re-evaluated here, but likely unnecessary if artifacts stay small.
Crawl-ethics policy (robots.txt, UA, rate-limit) decided at Phase 1 kickoff. Public contradictions UI activates here (crawler vs DeFiLlama).
- Integrate
@l2beat/discovery, ripping out rollup-specific code - Ethereum mainnet only
- canonical contracts, proxy detection, implementation/admin resolution, role extraction, Safe detection, timelock detection
- admin/control graph construction
- Canonical contract discovery uses DeFiLlama's own TVL adapter source code as primary signal. On adapter parse failure: leave
canonical_contractsnull — no LLM or scraping fallback. - This is where a DB may enter, but only for onchain tables (contracts, contract_relationships, discovery output). The seed registry stays git-native forever. Re-evaluate SQLite vs Postgres at Phase 2 kickoff.
- machine-generated structured claims with citations (enforced hash + substring)
- onchain-source contradictions join the public contradictions UI
- provisional machine summaries
- human review: already PR-native; a dedicated reviewer UI may or may not be needed
- published reviewed assessments using Defiscan stages with disclaimers
- LLM provider decision deferred to this phase
What actually ships, replacing the Phase 1/2/3 plan above:
Workspace layout
apps/
web/ Astro 5 site (output: server, ISR for /protocol/[slug])
one Svelte 5 island: LandingTable.svelte
data/
defillama-snapshot.json Full DeFiLlama seed
overlays/<slug>.json Curator overlays
schema/
slice-assessment.v2.json JSON schema for DEFI@home submissions
submissions/<slug>/<slice>/*.json
Raw per-contributor LLM runs (one per run)
assessments/<slug>/<slice>.json
Merged per-slice assessments (quorum bot output)
master/ Reconciled master records (assessments → master pipeline)
packages/
registry/ Snapshot + overlay merge + typed access + getAssessments
sync/ DeFiLlama fetcher / normalizer / 14-day delist logic
prompts/ DEFI@home prompt generator (PROMPT_VERSION = 12,
5 slice bodies in src/slices/*.ts)
validator/ CLIs: defipunkd-validate, defipunkd-quorum,
defipunkd-reconcile, defipunkd-priority-queue;
schema/quorum/cross-check/master logic
GitHub Actions (.github/workflows/):
validate-submission.yml— schema-validates every PR touchingdata/submissions/.quorum.yml— when ≥3 independent submissions agree on grade, opens a PR merging the consensus intodata/assessments/.reconcile.yml— promotes assessments intodata/master/records.autorun.yml— Anthropic SDK runs the pinned prompt as a "third voice" so quorum can be reached without waiting for human contributors on every slice.sync.yml— weekly Monday 06:00 UTC, runspnpm syncand opens a PR with the diff.
Slice set: control, ability-to-exit, autonomy, open-access, verifiability. Single source of truth: packages/prompts/src/index.ts SLICE_IDS and apps/web/src/lib/pizza.ts PIZZA_SLICES.
Submission contract: data/schema/slice-assessment.v2.json. Required fields: grade (green / orange / red / unknown), headline, short_headline, rationale.{findings, steelman, verdict}, evidence[], unknowns[], protocol_metadata. Conditional rule: grade=unknown ⇒ unknowns[] non-empty AND steelman is null; otherwise evidence[] non-empty AND steelman is {red, orange, green}.
Quorum: QUORUM_MIN = 3 independent models per (slug, slice). Submissions are weighted by public chat URL presence, count of block-explorer evidence, and fetched_at freshness. Consensus tags are strong (2+ grades match) / weak (split with majority) / disagreement. Logic in packages/validator/src/quorum.ts.
Manual / on-demand at MVP. Operator runs pnpm sync; the resulting diff to data/defillama-snapshot.json is committed (locally or via a workflow_dispatch action that opens a PR). Merging the PR redeploys Vercel. Freshness in days is acceptable.
Every sync regenerates the full snapshot. git diff is the mutation trail — there is no separate "material fields" diff logic at Phase 0, because there are no downstream workers to trigger. When workers arrive (Phase 1), the snapshot-diff-at-commit-time is a sufficient trigger signal.
A plain node CLI. Pulls DeFiLlama, normalizes, writes data/defillama-snapshot.json. Computes the 14-day delist rule by comparing current slugs against the previous snapshot and carrying forward last_seen_at / delisted_at timestamps. Applies parentProtocol as parent_slug. Derives is_dead from DeFiLlama's own deprecation signals (deadUrl, deadFrom, category hints).
Same shape as Phase 0 sync: local node scripts that write into data/ (artifacts + JSON index entries) and commit via PR. No queue, no DB.
Forked @l2beat/discovery. Output target TBD at Phase 2 (most likely a local SQLite cache plus committed discovery TS configs).
Designed when Phase 3 starts.
Deferred until Phase 3. Contact channel at MVP is a footer link to public GitHub issues and PRs. Corrections already flow naturally as PRs against data/overlays/.
At Phase 0, the data model is TypeScript types, not SQL tables.
type Snapshot = {
generated_at: string; // ISO UTC
protocols: Record<Slug, ProtocolSnapshot>;
};
type ProtocolSnapshot = {
slug: string;
name: string;
category: string; // raw DeFiLlama string
chains: string[];
tvl: number | null;
tvl_by_chain: Record<string, number>;
website: string | null;
twitter: string | null;
github: string[] | null; // DeFiLlama-supplied only
audit_count: number;
audit_links: string[];
hallmarks: Array<[number, string]>; // [unix_ts, description]
parent_slug: string | null; // from parentProtocol only
forked_from: number[] | null; // DeFiLlama forkedFrom list (numeric ids)
logo: string | null; // canonical URL from /protocols payload
is_dead: boolean; // derived from deadUrl/deadFrom/category
is_parent: boolean; // true for synthesized parent rows
first_seen_at: string; // ISO UTC, carried forward across syncs
last_seen_at: string; // ISO UTC, bumped when present in latest sync
delisted_at: string | null; // set after 14 consecutive days absent
};Overlays are JSON files validated by a Zod schema in packages/registry (the schema is the source of truth; the TS Overlay type is derived via z.infer). Each field is a strict subset of ProtocolSnapshot. Unknown keys fail Zod validation (and the build).
{
"website": "https://example.com",
"github": ["https://github.com/example/repo"]
}Three-state per-field semantics:
- key absent → defer to the snapshot
- key =
null→ curated "known to have no value" (overrides the snapshot with null) - key = value → override
Empty string / empty array is literal "" / [], not a sentinel. There is no hidden field (no takedown mechanism at Phase 0).
Merge rule: any defined overlay field wins over the snapshot. Undefined / omitted fields defer to the snapshot. The merged result carries an inline _provenance map on each field ("defillama" or "curated") used by the detail page to render the [defillama] / [curated] tag.
Conceptual tables for later phases — listed here so naming stays consistent when they land:
contracts,contract_relationships(Phase 2 onchain)claims,claim_evidence,reviews(Phase 3 classification/review)artifacts(content-addressed SHA-256; Phase 1+)
At Phase 2+, when a DB actually enters, these will be proper tables. Until then, they are placeholders for schema design, not code.
Public status values (unchanged from original plan):
listed— default; seed metadata onlyevidence_collecting— crawler workers have run (Phase 1)machine_summarized— classification worker has produced structured claims (Phase 3)needs_human_review— queued for reviewerreviewed— stamp-of-approval publicationmonitored— reviewed + active drift watch
At Phase 0 everything is listed.
Browse-all table by default. Fully static.
- Default view: top 200 by TVL within the active tab
- Server search across name, slug, category (in-memory substring match with prefix-boost ranking over the registry index)
- Default sort: TVL desc
- Filters: review status, per-slice pizza filter chips (wired; all
unknownat Phase 0) - Flat sortable table
L2BEAT-identical dense table layout, defipunkd palette. Every field row shows value + [defillama] / [curated] tag + missingness state.
- Chain sub-nav: tabs across the top, one per chain (top-N by per-chain TVL + "more" dropdown, threshold ~7)
- Breadcrumb collapsed when family == instance
last_synced_atfrom the snapshot: always visible- Delisted slugs: prerendered page with
<meta name="robots" content="noindex">, body preserves last-known name +delisted_at+ DeFiLlama link
No in-app charts at MVP. TVL history → link out to DeFiLlama.
Minimal: methodology link, GitHub issues/PRs link for corrections/takedowns, Defiscan credit.
Each protocol receives one of four tiers, computed in apps/web/src/lib/tier.ts from the per-slice consensus state:
none— no slice has reached quorum.bronze— ≥1 slice has quorum (≥3 independent model submissions).silver— all 5 slices have quorum.gold— at least one slice carries ahuman_signoffrecord.
QUORUM_MIN = 3. Tier rendering uses three-stop gradients (bronze: warm browns; silver: neutral grays; gold: warm yellows) defined in TIER_STOPS. Tier is a data-readiness signal, not a safety claim — a silver protocol has been independently graded by ≥3 models on every slice but has not been human-reviewed.
Defiscan stages remain the planned grading model and will be layered over the tier system once human review formalizes (gold-tier protocols are the natural starting set). The methodology page links to the Defiscan framework today and will document the eventual stage attachment when it ships.
- Astro 5 + Svelte 5 web app (landing, methodology, contribute, protocol detail).
pnpm syncwritesdata/defillama-snapshot.jsonwith 14-day delist rule +last_seen_at/first_seen_atcarry-forward.packages/registrymerges snapshot + overlays + assessments and exports the in-memory API used by the web app.packages/prompts(PROMPT_VERSION = 12) generates per-slice DEFI@home prompts with the protocol snapshot pre-pinned.packages/validatorprovidesdefipunkd-validate,defipunkd-quorum,defipunkd-reconcile, anddefipunkd-priority-queueCLIs.data/schema/slice-assessment.v2.jsonenforces submission shape (and is the contract referenced by every prompt).- 5 protocols have at least one assessed slice: aave, lido, morpho, pendle, uniswap. Lido has all 5 slices assessed.
- GitHub Actions:
validate-submission,quorum,reconcile,autorun,sync. - Tier system live in landing UI;
/contributedocuments the submission flow.
- Master reconciliation pipeline (
data/master/is wired but lightly populated). - More breadth: graduate more protocols past bronze toward silver.
- Indexable flip rule: per-protocol
noindex→ indexable once a protocol reaches silver or gold. - Defiscan stage attachment for gold-tier protocols.
Addendum resolving spec ambiguities. Where this section conflicts with earlier prose, this section wins.
- Axes (shipped): 5 slices —
control,ability-to-exit,autonomy,open-access,verifiability. (Earlier draft listed 7 axes split out finer; the 5-slice consolidation is what ships and what every prompt + schema + UI references.) - Placement: landing browse-all table row (tiny), protocol detail page header (large), and
/methodologylegend. - Phase 0 empty state: fully gray "unknown" pizza with em-dash tooltip.
- Color semantics: narrow green / orange / red risk palette for slices, with a muted gray for unknown. The cool-blue
#7bb4ccaccent is reserved for links and active state — never a slice fill. - CEX override: if
category === "CEX", all five slices render red. Decided because custodial exchanges fail every transparency dimension by construction; applying the normal per-dimension rubric would misleadingly show unknowns as gray. - Stage encoding (deferred): overall Defiscan stage = worst slice (rule retained for when stages are attached on top of tiers).
- Multi-chain handling: pizza reflects the primary (highest-TVL) chain only.
- Interaction: clicking a slice anchors/scrolls to the matching section of the detail dense table.
- Landing filter: per-slice filter chips on the browse-all table.
- Accessibility: deferred to Phase 3.
- Delist grace window: protocols absent from DeFiLlama for 14 consecutive days get
delisted_atset bypnpm sync. Requireslast_seen_atper protocol, carried forward across syncs. - Dead/alive signal: derived from DeFiLlama's own deprecation signals (
deadUrl,deadFrom, category hints). No independent inference at Phase 0. Stored asis_deadon each snapshot entry, recomputed every sync. Dead protocols are hidden from the default landing list. Direct URL still works. A "show inactive" toggle re-includes them. - Null TVL: render as
unknownwith em-dash.$0TVL renders literally as$0. Null ≠ zero. - TVL format:
$42.3Mwith one decimal, K/M/B suffixes. - Category: store raw from DeFiLlama, display raw, filter raw. Zero normalization.
- Sync cadence: daily manual
pnpm sync. No cron. - Sync concurrency: n/a — sync produces a file; git is the serialization point.
- Sync failure: the PR simply isn't opened; site keeps serving the last committed snapshot.
- "Last updated" timestamp: the snapshot's
generated_atdrives the detail-page "Updated" row. No separate material-change timestamp at Phase 0 (workers don't exist yet). - Time format: always UTC, ISO-like:
2026-04-21 14:02 UTC. - Rate limiting: none at Phase 0. Vercel platform defaults only. Sync runs off-Vercel.
- Family pages: no separate
/family/{slug}route. A parent's/protocol/{parent_slug}page gains a children table. If parent is not itself a slug, breadcrumb-only family signal. - Chain tabs with many deployments: top-N tabs by per-chain TVL + "more" dropdown. Threshold ~7 visible tabs.
- 410 page content: route handler returns 410; body preserves last-known protocol name,
delisted_at, and a DeFiLlama link. - noindex flip: per-protocol. Currently every
/protocol/*page carriesnoindex. Planned flip: a page becomes indexable once the protocol reaches silver or gold tier (replaces the originally-plannedmachine_summarizedtrigger).
- Search: substring match on name/slug/category with prefix-boost ranking, case-insensitive. In-memory over the registry index.
- Review-status filter default: all statuses shown.
- Audit links: row shows count + expandable list of URLs annotated with auditor domain. Default collapsed.
- Hallmarks: dedicated chronological timeline row, rendered as dated events with descriptions and
[defillama]badge. - Contradictions UI (from Phase 1): row displays the winning value with a small warning glyph; click expands to show alternative values + sources + resolution reasoning.
- Artifact storage: Phase 1, likely just in-repo under
data/artifacts/. - Database introduction: Phase 2, onchain-only, SQLite re-evaluated vs Postgres.
- Monitored-state drift rule: Phase 2.
- Reviewer identity/attribution: Phase 3 (though git commit authorship is already a partial answer).
- Pizza a11y: Phase 3.
Modeled on L2BEAT's landing, adapted to DeFi categories.
- Tab set (11 tabs):
All | Lending | DEX | Yield | Derivatives | Bridges | Liquid Staking | CDP | Stablecoins | RWA | Others - Default tab:
All. - Tab scope: each tab contains all protocols in that category across every chain.
- "Not Reviewed" tab: deferred. At Phase 0, 100% of protocols are
listed. - Multi-category protocols: bucketed by the primary DeFiLlama category only.
- Category mapping: a static TypeScript map (
category-map.ts) routes each DeFiLlama category string to one of the 11 buckets. Unmapped categories fall intoOthersand are logged at build time so the reviewer can add them.- Seed mappings (non-exhaustive):
Lending,Liquid Lending→ Lending;CDP→ CDP;Dexes,DEX Aggregator→ DEX;Yield,Yield Aggregator→ Yield;Derivatives,Options,Perps→ Derivatives;Cross Chain,Bridge→ Bridges;Liquid Staking,Liquid Restaking→ Liquid Staking; stablecoin-issuer categories → Stablecoins;RWA,RWA Lending→ RWA.
- Seed mappings (non-exhaustive):
- Tab counts: computed at build time from the merged registry. Counts exclude delisted and dead protocols.
| # | Name | Chain | Risks | Stage | Type | TVL |
|---|
#— TVL rank within current tab and sort.Name— links to/protocol/{slug}.Chain— primary chain (highest TVL) ++Nchip.Risks— pizza icon colored by per-slice grade (green / orange / red / unknown gray); hover/click expands. Unassessed slices render gray.Stage— Defiscan stage badge; currently unused (—for everyone) pending stage attachment over the tier system. The landing table additionally surfaces the protocol's tier (none / bronze / silver / gold) via a tier filter.Type— raw DeFiLlama category.TVL—$42.3Mformat.
- Default sort: TVL desc.
- Sortable columns:
#,Name,Chain,Stage,Type,TVL. - Risks column sort: by worst-slice grade, then count of red slices as tiebreaker.
- In-tab filters: tier chips (bronze / silver / gold) + per-slice pizza chips.
- Delisted + dead: excluded by default. "Show inactive" re-includes dead; delisted stays 410.
Top 200 by TVL per tab. "Show all" expands.
Pivot note (2026-04-22): the earlier DB-based plan (Neon Postgres, /api/sync Vercel route with shared secret, pgboss queue, per-row diff detection) is superseded by this git-native architecture. Skip any spec reading that contradicts this section.
Current Phase 0 inputs:
- GitHub repo:
guil-lambert/defipunkd - User-Agent contact URL:
https://github.com/guil-lambert/defipunkd— sent asUser-Agent: defipunkd (+https://github.com/guil-lambert/defipunkd)frompnpm sync. - Footer corrections/takedowns link:
https://github.com/guil-lambert/defipunkd/issues(and thedata/overlays/directory for PRs). - Vercel project slug:
defipunkd. - Node: 22 LTS, pinned via
.nvmrc+engines. - pnpm: 9 (latest), pinned via
packageManagerfor Corepack.
No database, no DATABASE_URL, no SYNC_SECRET, no Neon branch, no /api/sync route at Phase 0.
Addendum resolving further Phase 0 ambiguities. Where this section conflicts with earlier prose, this section wins.
- Format: JSON, not TypeScript. Files at
data/overlays/<slug>.json(flat layout, no sharding). Overlay authors can edit via GitHub web UI without fearing TS syntax; overlays are data, not code. - Validation: Zod schema in
packages/registry(schema is source of truth; TSOverlaytype derived viaz.infer). Any overlay that fails parse or validation fails the build. No silent skip on malformed overlays. - Empty vs unset semantics: explicit
nullsentinel. Three states per field:- key absent → defer to snapshot
- key =
null→ curated "known to have no value" (overrides snapshot with null) - key = value → override Empty string / empty array is not a magic sentinel; it means literally "" / [].
- Identity-with-snapshot: when an overlay value is byte-equal to the snapshot value, the field still renders
[curated]. The sync script emits a build-time warning listing overlay fields that exactly duplicate the snapshot, so the reviewer can trim noise. - Orphan overlays: if an overlay's slug is not present in the current snapshot, warn at build and skip. Do not fail the build; do not resurrect the protocol from the overlay.
- Takedown requests: no takedown mechanism at Phase 0. Policy is: delisting happens upstream at DeFiLlama. Revisit at Phase 3 when reviewer workflow formalizes. No
hiddenfield in the overlay schema.
- Slug is identity forever. If DeFiLlama renames a slug, old becomes delisted under the 14-day rule and new appears as first-seen. No DeFiLlama numeric
idtracking, no alias map at Phase 0.
- Strict slug-only linkage. DeFiLlama's
parentProtocolis only treated as a link when it matches an existing slug in the snapshot. String-label parents are ignored at Phase 0 (no slugification, no alias file). Breadcrumb collapses as usual.
- Null / empty category → bucket to
Otherstab (same bucket as unmapped categories). Logged at build.
- Trust DeFiLlama: no sanity check on protocol-count drops. If upstream returns 500 protocols where it used to return 6000, we write the snapshot. The 14-day grace window on
delisted_atabsorbs transient outages. - Determinism controls (Phase 0): pinned Node 22 LTS + pnpm 9 via
.nvmrc,engines, andpackageManager. Sorted-key JSON output and fixedgenerated_atsourcing are nice-to-have but not required for correctness at Phase 0. - Sync PR body: the sync script writes a markdown summary into the PR body (also emitted to stdout). Summary contains:
- counts: new protocols, newly
delisted_at,is_deadtoggles - TVL movers: every protocol whose TVL changed ≥±50% day-over-day (one list). No separate "top-N absolute" list.
- counts: new protocols, newly
- Raw JSON diff remains viewable on the PR for reviewers who want it; the summary is the primary review surface.
- Landing sort: null-TVL protocols sort after all ranked entries in every tab. In practice they never appear in the top-200 default view.
- Rendering:
unknownwith em-dash (unchanged from round 1).$0renders literally.
- Primary-chain flip policy: TBD — assessments are currently per-protocol (not per-chain), so the pizza is the same across chain tabs. Decide alongside Defiscan-stage attachment.
- Show every row, all unknown. No hiding of Phase-2 or Phase-3-dependent rows at Phase 0. The wall of em-dashes is intentional — it signals the roadmap and enforces transparency about missingness.
- Timestamp scope: single "Updated" row driven by snapshot
generated_at. Curated-field edit times are implicit ingit blameon the overlay file; no per-field UI timestamp.
- Full snapshot in dev, same file as prod. No subset dev snapshot, no env-flag filter. Prevents dev/prod divergence bugs; slower HMR accepted.
- Case-insensitive substring match on name / slug / category, with prefix-boost ranking. No punctuation normalization, no diacritic folding, no fuzzy/Levenshtein at Phase 0. Power-user audience assumed.
noindexmeta tag only on/protocol/*pages today. Norobots.txtDisallow, no Vercel bot mitigation. The originally-plannedmachine_summarizedtrigger for the indexable flip is replaced by a tier trigger: silver or gold (see "URL & navigation" above).
- Hybrid rendering replaces full SSG. The earlier intent to prerender all ~8000 protocol pages via
getStaticPaths()was dropped in favor ofoutput: "server"+ Vercel ISR (60s) on/protocol/[slug]. Build time stays bounded; master-file updates surface within the ISR window without a redeploy.
- Columns match the landing Summary table:
# | Name | Chain | Risks | Stage | Type | TVL, sorted TVL desc. Consistent UX with the browse-all table.
- Minimal content + pizza legend at Phase 0:
- "Registry only, no ratings" framing
- DeFiLlama seed + curated overlays explanation
- 5-slice pizza legend explaining each slice (control, ability-to-exit, autonomy, open-access, verifiability)
- Note that Defiscan stages arrive in Phase 3 with links out to Defiscan's framework
- Superseded. The project was scaffolded fresh rather than forked from l2beat/l2beat, so there are no rollup packages to nuke. Visual lineage from L2BEAT is preserved at the design level only.
One-page summary for cold readers, so you don't have to chase pivot notes.
What it is. A live, evidence-based DeFi protocol registry. DeFiLlama seeds the universe; contributors grade specific risk dimensions ("slices") by running a pinned LLM prompt and submitting JSON via PR; a quorum bot merges once independent runs agree.
Stack. Astro 5 (output: "server") + one Svelte 5 island (LandingTable.svelte) + Vercel adapter with ISR (60s) for /protocol/[slug]. Landing, methodology, and contribute pages prerender. No database. Node 22, pnpm 9.
Data layout.
data/defillama-snapshot.json— full DeFiLlama seed.data/overlays/<slug>.json— curator overrides.data/submissions/<slug>/<slice>/*.json— per-contributor LLM runs, one file per run.data/assessments/<slug>/<slice>.json— quorum-merged consensus.data/master/— reconciled master records.data/schema/slice-assessment.v2.json— submission contract.
Slices (5). control, ability-to-exit, autonomy, open-access, verifiability. Single source of truth in packages/prompts/src/index.ts and apps/web/src/lib/pizza.ts.
Tier system. none / bronze (≥1 slice quorum) / silver (all 5 slices quorum) / gold (any human signoff). Quorum threshold = 3 independent models (QUORUM_MIN). Logic in apps/web/src/lib/tier.ts.
Defiscan stages. Planned future layer over the tier system; not yet attached.
Workflows. validate-submission.yml (per-PR schema), quorum.yml (auto-merge on consensus), reconcile.yml (assessments → master), autorun.yml (Anthropic SDK third voice), sync.yml (weekly DeFiLlama refresh).
Coverage as of writing. 5 protocols with at least one assessed slice (aave, lido, morpho, pendle, uniswap). Lido has all 5 slices assessed.