A Bitcoin transaction & block analyzer I built for the Summer of Bitcoin 2026 challenge.
Chain Lens has two parts — a CLI tool that parses raw Bitcoin transactions/blocks into detailed JSON reports, and a web visualizer that explains all of it in plain English with a clean dark UI.
📹 Watch the walkthrough on YouTube (< 2 min)
- Parses raw hex transactions byte-by-byte (both legacy and SegWit)
- Computes
txid,wtxid, fees, weight, vbytes — all from scratch - Classifies input/output scripts:
P2PKH,P2SH,P2WPKH,P2WSH,P2TR,OP_RETURN, nested variants likeP2SH-P2WPKH - Derives mainnet addresses (Base58Check, Bech32, Bech32m)
- Full script disassembly to ASM
- OP_RETURN payload decoding with protocol detection (Omni, OpenTimestamps)
- RBF signaling & BIP68 relative timelock detection
- SegWit discount / savings analysis
- Warnings: high fee, dust outputs, unknown scripts, RBF
- Reads Bitcoin Core raw
blk*.dat/rev*.datfiles directly - XOR key decryption support
- Parses block headers, all transactions, and undo data for prevouts
- Handles compressed script types in undo data (P2PKH, P2SH, P2PK, raw scripts)
- Computes & verifies merkle root
- Extracts BIP34 coinbase height
- Generates per-block stats (total fees, weight, fee rate, script type summary)
- Dark-themed, premium UI with particle animations
- Paste a fixture JSON or try built-in samples (P2PKH, SegWit, Multi-Input)
- Upload
blk*.dat+rev*.dat+xor.datfor block analysis - Transaction flow diagram (inputs → outputs with fee visualization)
- Expandable technical details per input/output
- SegWit savings breakdown with visual comparison
- Warning badges with explanations
- Block overview with expandable transaction list
- Health endpoint at
GET /api/health
chain-lens/
├── src/
│ ├── cli.ts # CLI entry point
│ ├── server.ts # Express server for web UI
│ ├── parser/
│ │ ├── transaction.ts # Raw tx parsing (legacy + SegWit)
│ │ ├── block.ts # Block + undo file parsing
│ │ ├── script.ts # Script classification, disassembly, address derivation
│ │ ├── merkle.ts # Merkle root computation
│ │ └── undo.ts # Undo data parsing (compressed scripts)
│ ├── utils/
│ │ ├── buffer-reader.ts # Low-level byte reader
│ │ ├── encoding.ts # Hex/byte conversions
│ │ └── hash.ts # Double SHA-256 hashing
│ └── types/ # TypeScript interfaces
├── public/
│ ├── index.html # Web UI
│ ├── style.css # Styling (dark theme, glassmorphism)
│ └── app.js # Frontend logic & rendering
├── fixtures/ # Sample transaction & block fixtures
├── cli.sh # CLI runner script
├── web.sh # Web server runner script
└── setup.sh # Dependency installer
- Node.js (v18+)
- npm
# Install dependencies and build
bash setup.sh# Analyze a single transaction
bash cli.sh fixtures/transactions/tx_legacy_p2pkh.json
# Analyze a block
bash cli.sh --block <blk.dat> <rev.dat> <xor.dat>Output goes to out/<txid>.json (transaction mode) or out/<block_hash>.json (block mode).
bash web.sh
# → http://127.0.0.1:3000-
Byte-level parsing — I read the raw hex byte by byte using a custom
BufferReader. SegWit transactions have marker + flag bytes and a witness section; legacy ones don't. The parser handles both. -
Hashing —
txidis the double-SHA256 of the serialized tx without the witness.wtxidincludes it. For blocks, the block hash is double-SHA256 of the 80-byte header. -
Script classification — I match output scriptPubKeys against known templates (e.g.,
OP_DUP OP_HASH160 <20 bytes> OP_EQUALVERIFY OP_CHECKSIG→ P2PKH). For inputs, I combine the scriptSig, witness, and prevout scriptPubKey to determine the spend type. -
Address derivation — P2PKH and P2SH use Base58Check encoding. P2WPKH/P2WSH use Bech32. P2TR uses Bech32m. All encoded from the hash inside the script.
-
Block parsing — The undo file stores prevout data in a compressed format. Bitcoin Core uses special nSize values (0 = P2PKH, 1 = P2SH, 2-5 = compressed P2PK, ≥6 = raw script). I decompress these to reconstruct the full scriptPubKeys.
-
Fee & weight — Fees = total input sats − total output sats. Weight uses the BIP141 formula:
(non-witness bytes × 4) + witness bytes.
{
"ok": true,
"txid": "...",
"fee_sats": 3456,
"fee_rate_sat_vb": 24.51,
"vin": [...],
"vout": [...],
"warnings": [{ "code": "RBF_SIGNALING" }],
"segwit_savings": { "savings_pct": 36.82 }
}{
"ok": true,
"mode": "block",
"block_header": { "block_hash": "...", "merkle_root_valid": true },
"tx_count": 150,
"coinbase": { "bip34_height": 800000 },
"transactions": [...],
"block_stats": { "total_fees_sats": 6250000 }
}{ "ok": false, "error": { "code": "INVALID_TX", "message": "..." } }| Layer | Tech |
|---|---|
| Language | TypeScript |
| Runtime | Node.js |
| Server | Express |
| Frontend | Vanilla HTML/CSS/JS |
| Encoding | bech32, bs58check |
| Styling | Custom dark theme with CSS animations |
No external Bitcoin libraries — all parsing logic is hand-written.
This project pushed me to actually understand Bitcoin at the byte level — how transactions are serialized, why SegWit introduced witness data, how script templates map to address types, and how Bitcoin Core stores block/undo data on disk. Writing the undo data decompressor for P2PK scripts was probably the trickiest part.
This project was built as part of the Summer of Bitcoin 2026 developer challenge.
