src/
├── app/
│ ├── page.tsx # Main page - state machine (idle/fetching/analyzing/complete/error)
│ ├── layout.tsx # Root layout with metadata, CSP, NetworkContext
│ ├── error.tsx # Error boundary
│ └── globals.css # Theme variables, scrollbar, focus styles
├── components/
│ ├── AddressInput.tsx # Search input with URL parsing, paste detection, type hints
│ ├── AddressSummary.tsx # Address stats (balance, received, sent, tx count)
│ ├── ApiSettings.tsx # Settings panel container/portal/focus trap
│ ├── ChainAnalysisPanel.tsx # Chain analysis findings grouped by category
│ ├── ConnectionBadge.tsx # Tor vs clearnet indicator
│ ├── DiagnosticLoader.tsx # Step-by-step heuristic progress with timer
│ ├── ExportButton.tsx # Copy formatted report to clipboard
│ ├── FindingCard.tsx # Collapsible finding with severity border colors
│ ├── GraphExplorerPanel.tsx # OXT-style graph wrapper (creates API client, wires hooks)
│ ├── Header.tsx # Sticky header with logo, badge, network selector
│ ├── InstallPrompt.tsx # PWA install banner
│ ├── PrivacyNotice.tsx # One-time dismissible privacy banner
│ ├── Remediation.tsx # "What to do next" prioritized action list
│ ├── ResultsPanel.tsx # Full results: score, findings, summaries, actions
│ ├── ScoreDisplay.tsx # Animated score count-up with grade badge
│ ├── ShareCardButton.tsx # Canvas-rendered share card for social media
│ ├── TxSummary.tsx # Visual I/O map with anonymity set coloring
│ ├── settings/
│ │ ├── AnalysisSettingsPanel.tsx # Depth/sats/timeout sliders, skip toggles
│ │ ├── LocaleSelector.tsx # Language dropdown
│ │ └── NetworkSettings.tsx # API URL, health check, diagnostics
│ └── viz/
│ ├── CoinJoinStructure.tsx # CoinJoin input/output mapping diagram
│ ├── GraphExplorer.tsx # OXT-style graph orchestrator (toolbar, sidebar, canvas)
│ ├── graph/ # OXT-style graph sub-components (see docs/adr-oxt-graph.md)
│ │ ├── types.ts # All graph-related TypeScript interfaces
│ │ ├── constants.ts # Node/port dimensions, zoom limits, colors
│ │ ├── layout.ts # Column-based DAG layout algorithm
│ │ ├── edge-utils.ts # Edge path calculation + port-to-port routing
│ │ ├── portLayout.ts # UTXO port position math for expanded nodes
│ │ ├── scriptStyles.ts # Script type colors, dashes, thickness, fingerprint encoding
│ │ ├── icons.tsx # Toolbar icon components
│ │ ├── GraphCanvas.tsx # Main SVG canvas (nodes, edges, pan/zoom, gestures)
│ │ ├── GraphMinimap.tsx # Minimap overlay for fullscreen navigation
│ │ ├── ExpandedNode.tsx # Expanded node with input/output UTXO ports
│ │ ├── GraphSidebar.tsx # 320px right sidebar (I/O, Analysis, Technical tabs)
│ │ └── index.ts # Barrel exports
│ ├── ScoreWaterfall.tsx # Finding impact waterfall chart
│ ├── TaintPathDiagram.tsx # Bithypha-style taint flow visualization
│ ├── TxFlowDiagram.tsx # Transaction I/O flow Sankey diagram
│ └── shared/svgConstants.ts # Shared SVG colors and constants
├── context/
│ └── NetworkContext.tsx # Network state provider (reads from URL ?network=)
├── hooks/
│ ├── useAnalysis.ts # Main orchestration hook with AbortController
│ ├── useAnalysisSettings.ts # Chain trace settings (depth, sats, timeout)
│ ├── useAnalysisState.ts # State types, initial state, helper factories
│ ├── useChainTrace.ts # Backward/forward recursive tracing + chain heuristics
│ ├── useGraphExpansion.ts # Expandable tx graph state (undo/reset/node cap)
│ ├── useKeyboardNav.ts # Keyboard shortcuts (Esc, /, Ctrl+K)
│ ├── useRecentScans.ts # localStorage with useSyncExternalStore
│ ├── useUrlState.ts # URL search params for network
│ └── useWalletAnalysis.ts # xpub/descriptor wallet-level audit
└── lib/
├── analysis/
│ ├── detect-input.ts # Input type detection + URL extraction
│ ├── orchestrator.ts # Heuristic registry, tx/address analysis entry points
│ ├── cross-heuristic.ts # Cross-heuristic suppression/boosting rules
│ ├── address-orchestrator.ts # Address analysis + destination pre-send checks
│ ├── heuristics/ # 32 tx-level + 6 address-level heuristic modules
│ │ └── tx-utils.ts # Shared utilities (getSpendableOutputs)
│ ├── chain/ # 13 chain analysis modules
│ │ ├── recursive-trace.ts # Multi-hop backward/forward tracing engine
│ │ ├── backward.ts # Input provenance analysis
│ │ ├── forward.ts # Output destination analysis
│ │ ├── clustering.ts # Address clustering
│ │ ├── entity-proximity.ts # Known entity proximity scan
│ │ ├── taint.ts # Proportional (haircut) taint flow
│ │ ├── linkability.ts # Linkability matrix analysis
│ │ ├── spending-patterns.ts # Spending pattern detection
│ │ ├── joinmarket.ts # JoinMarket-specific analysis
│ │ ├── coinjoin-quality.ts # CoinJoin quality assessment
│ │ ├── peel-chain-trace.ts # Peel chain following
│ │ ├── temporal.ts # Temporal pattern analysis
│ │ └── prospective.ts # Prospective privacy assessment
│ └── entity-filter/ # Entity matching (OFAC, exchanges, etc.)
│ ├── entity-match.ts # Address-to-entity lookup
│ └── filter-loader.ts # Binary index loader
├── api/
│ ├── client.ts # API client (mempool.space only, no fallback)
│ ├── mempool.ts # mempool.space REST API implementation
│ ├── cached-client.ts # Cached API client wrapper
│ ├── fetch-with-retry.ts # Retry logic, ApiError types
│ ├── rate-limiter.ts # Request rate limiter
│ ├── analysis-cache.ts # Analysis result caching (IndexedDB)
│ ├── idb-cache.ts # IndexedDB cache implementation
│ ├── url-diagnostics.ts # API URL validation and diagnostics
│ ├── enrich-prevouts.ts # Prevout data enrichment for self-hosted backends
│ └── types.ts # API response types
├── bitcoin/
│ ├── networks.ts # Network configs (mainnet, testnet4, signet)
│ ├── psbt.ts # PSBT parser using @scure/btc-signer
│ └── descriptor.ts # xpub/descriptor parser with BIP44/49/84/86
├── scoring/
│ └── score.ts # Base 70/93, sum impacts, clamp 0-100, grade
├── format.ts # formatSats, formatBtc, fmtN (locale-safe)
└── types.ts # Finding, ScoringResult, Grade types
| ID | Module | Impact | Description |
|---|---|---|---|
| coinbase | coinbase-detection.ts | 0 | Coinbase transaction detection |
| h1 | round-amount.ts | -5 to -15 | Round BTC/sat/USD/EUR amounts in outputs |
| h2 | change-detection.ts | -5 to -25 | Address type mismatch, round amount, self-send, fresh address |
| h3 | cioh.ts | -6 to -45 | Common Input Ownership Heuristic |
| h4 | coinjoin.ts | +15 to +30 | Whirlpool, WabiSabi, JoinMarket, Stonewall, simplified Stonewall |
| h5 | entropy.ts | -5 to +15 | Simplified Boltzmann entropy (capped at 8x8) |
| h6 | fee-analysis.ts | 0 to -2 | Round fee rate, RBF signaling, CPFP detection |
| h7 | op-return.ts | -5 to -8 | OP_RETURN metadata, protocol detection |
| h11 | wallet-fingerprint.ts | -2 to -6 | nLockTime, Low-R signatures, wallet identification |
| h17 | multisig-detection.ts | 0 to -3 | Multisig/escrow detection |
| anon | anonymity-set.ts | -1 to +5 | Equal-value output group analysis |
| timing | timing.ts | -1 to -3 | Mempool/off-hours timing analysis |
| script | script-type-mix.ts | -8 to +2 | Script uniformity, bare multisig detection |
| dust | dust-output.ts | -3 to -8 | Dust attack / tiny output detection |
| peel | peel-chain.ts | -3 to -8 | Peel chain detection |
| consolidation | consolidation.ts | -3 to -10 | Fan-in/fan-out consolidation patterns |
| unnecessary | unnecessary-input.ts | -2 to -5 | Unnecessary input detection |
| tx0 | coinjoin-premix.ts | 0 to +3 | CoinJoin premix (tx0) detection |
| bip69 | bip69.ts | -1 to -2 | BIP69 lexicographic ordering |
| bip47 | bip47-notification.ts | 0 to -3 | BIP47 notification transaction detection |
| exchange | exchange-pattern.ts | -2 to -5 | Exchange withdrawal pattern detection |
| coinsel | coin-selection.ts | -1 to -3 | Coin selection algorithm fingerprinting |
| witness | witness-analysis.ts | -1 to -3 | Witness data type/depth analysis |
| postmix | post-mix.ts | -5 to -15 | Post-mix consolidation detection |
| entity | entity-detection.ts | -5 to -10 | Known entity address detection |
| ID | Module | Impact | Description |
|---|---|---|---|
| h8 | address-reuse.ts | +3 to -93 | Address reuse count with severity scaling |
| h9 | utxo-analysis.ts | +2 to -11 | UTXO count, dust UTXOs |
| h10 | address-type.ts | -5 to 0 | P2TR/P2WPKH (0) > P2WSH (-2) > P2SH (-3) > P2PKH (-5) |
| spending | spending-analysis.ts | -5 to +2 | Counterparty diversity, cold storage |
| recurring | recurring-payment.ts | -2 to -5 | Recurring payment pattern detection |
| highactivity | high-activity-address.ts | -2 to -5 | High activity address detection |
| Step | Modules | Description |
|---|---|---|
| chain-backward | backward.ts | Input provenance - parent tx patterns, CoinJoin inputs |
| chain-forward | forward.ts | Output destinations - toxic merges, direct spends |
| chain-cluster | clustering.ts | Address clustering from traced tx graph |
| chain-spending | spending-patterns.ts | Spending pattern analysis across chain |
| chain-entity | entity-proximity.ts | Known entity proximity within N hops |
| chain-taint | taint.ts | Proportional (haircut) backward taint flow |
Additional chain modules (computed inline, no separate step):
linkability.ts- Linkability matrix for tx inputs/outputsrecursive-trace.ts- Multi-hop backward/forward tracing enginejoinmarket.ts- JoinMarket-specific chain analysiscoinjoin-quality.ts- CoinJoin quality assessmentpeel-chain-trace.ts- Peel chain following across hopstemporal.ts- Temporal pattern analysisprospective.ts- Prospective privacy assessment
- Base: 70/100 (transactions), 93/100 (addresses)
- Sum: All finding
scoreImpactvalues added - Clamp: Result clamped to 0-100
- Grades: A+ >= 90, B >= 75, C >= 50, D >= 25, F < 25
The cross-heuristic.ts module runs a post-processing pass after all heuristics:
CoinJoin suppression: When a CoinJoin is detected, suppresses CIOH, round-amount, change detection, script-mixed, low-entropy, wallet fingerprint, dust, timing, fee, anonymity-set, multisig/escrow, consolidation, BIP69, witness analysis, and coin selection findings. Stonewall gets partial CIOH reduction (-3 instead of 0) since all inputs are one wallet.
Multisig adjustment: Suppresses script-mixed penalty (structural, not a leak).
CIOH dedup: When CIOH fires on non-CoinJoin tx, suppresses redundant unnecessary-input and caps consolidation at -2.
Consolidation triple-penalty: When self-send consolidation detected, suppresses zero-entropy (inherent).
RBF x Change: When both RBF and change detection fire, boosts change confidence and adds -2 compound.
Compound stacking: Change detection boosted by corroborating wallet fingerprint, peel chain, and/or low entropy (max -6 boost).
Post-mix to entity: Escalates entity-known-output to critical when post-mix consolidation detected.
Post-mix + backward CoinJoin dedup: Zeros backward CoinJoin-input positive finding when negated by post-mix consolidation.
Wasabi + address reuse paradox: Flags contradiction between Wasabi fingerprint and address reuse.
Deterministic cap: Forces F grade when deterministic findings present (same-address-io, sweep).
- Static export:
output: "export"in next.config.ts. No server. GitHub Pages. - @scure/btc-signer: Used for PSBT parsing and xpub derivation. No bitcoinjs-lib.
- useSyncExternalStore for localStorage: Must cache parsed JSON for referential stability.
- 50ms tick between heuristics: Creates diagnostic effect. ~1.5s total for tx analysis.
- Hash-based routing:
#tx=.../#addr=.../#xpub=...for sharing. - Privacy-honest disclosure: Banner about mempool.space IP visibility.
- motion/react: Import from
motion/reactnotframer-motion. - Tailwind CSS 4:
@theme inlinein globals.css, not tailwind.config. - AbortController: Cancels in-flight requests on reset/new query.
- Running score during analysis: DiagnosticLoader shows live score tally and per-heuristic impact.
- Danger zone for F-grade: Pulsing red glow + warning banner for critical failures.
- Clickable addresses: TxSummary addresses are clickable to scan related addresses.
- Auto-expand remediation: D/F grades auto-open the "What to do next" section.
- Score breakdown waterfall: Visual bars showing each finding's relative impact on the score.
- Header as hash reset: Logo click clears hash (no full page reload), triggers hashchange listener.
- Entity filter: Binary index format (EIDX v2) with 1M+ named addresses, OFAC sanctions overlay.
- Chain tracing: Recursive backward/forward tracing with configurable depth, timeout, and minSats filter.
- Graph explorer: Interactive force-directed tx DAG with expand/collapse, undo, and node cap.
- i18n: 5 locales (en, es, de, fr, pt) via react-i18next with key-based lookups.
GET /api/tx/{txid}- Full transaction dataGET /api/tx/{txid}/hex- Raw hex (wallet fingerprinting)GET /api/tx/{txid}/outspends- Output spend statusGET /api/address/{addr}- Address statsGET /api/address/{addr}/utxo- UTXO setGET /api/address/{addr}/txs- Recent transactions (last 25)GET /api/v1/historical-price?currency=USD×tamp={ts}- Historical USD priceGET /api/v1/historical-price?currency=EUR×tamp={ts}- Historical EUR price
Base URLs:
- Mainnet:
https://mempool.space/api - Testnet4:
https://mempool.space/testnet4/api - Signet:
https://mempool.space/signet/api - Tor:
http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/api
All API queries go to a single provider (mempool.space by default, or user-configured endpoint). No secondary/fallback APIs are used. For wallet scans, hosted APIs are automatically throttled (3 addresses/batch, 500ms delay) to avoid rate limiting.
All colors defined as CSS custom properties in globals.css:
--severity-critical(#ef4444) /--severity-high(#f97316) /--severity-medium(#eab308) /--severity-low(#60a5fa) /--severity-good(#28d065)--bitcoin(#f7931a) /--danger(#ef4444) /--success(#28d065)- Dark theme only, no light mode
- OP_RETURN duplicate IDs: Finding IDs must be unique. Appends index when >1 OP_RETURN.
- Whirlpool vs WabiSabi: Whirlpool = 5+ equal outputs at known denominations, 5-10 total outputs. WabiSabi = 20+ inputs/outputs.
- ROADMAP.md was .gitignored: Use
git add -fto override. - CSP: Set via
<meta>tag since static export can't use server headers.
pnpm dev # Start dev server on :3000
pnpm build # Static export to out/
pnpm lint # ESLint
# Screenshots for UI verification
npx playwright screenshot --wait-for-timeout=7000 "http://localhost:3000/#tx=TXID" screenshot.png
npx playwright screenshot --full-page --viewport-size=375,812 "http://localhost:3000" mobile.png- Transaction graph visualization - GraphExplorer with multi-hop expansion
- Multi-transaction batch analysis - wallet analysis via xpub/descriptors
- Taint flow visualization - TaintPathDiagram (visx)
- CoinJoin structure diagram - CoinJoinStructure component
- Entity detection - 363+ entities, OFAC sanctions overlay
- Chain analysis - recursive backward/forward tracing up to 50 hops
- Share card generation - Canvas-rendered PNG share cards
- PSBT analysis - client-side parsing without API calls
- i18n - 5 languages (en, es, de, fr, pt)
- PDF report export
- Score comparison mode (before/after CoinJoin)
- Browser extension for mempool.space integration