Skip to content

Priyanshu-u07/coin-smith

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸͺ™ Coin Smith

A Bitcoin PSBT transaction builder I built for the Summer of Bitcoin 2026 challenge (Week 2).

Coin Smith takes a set of UTXOs, payment targets, and a fee rate β€” then selects coins, computes fees/change, and produces a fully valid unsigned PSBT (BIP-174). It ships with a CLI for machine-checkable output and a web visualizer that explains everything in plain English.

TypeScript Node.js Express Bitcoin


🎬 Demo

πŸ“Ή Watch the walkthrough on YouTube (< 2 min)


πŸ“Έ Screenshot

Coin Smith UI


✨ What it does

Coin Selection & Transaction Building

  • Parses fixture inputs defensively β€” rejects malformed UTXOs, addresses, amounts
  • Implements coin selection strategy (greedy) with support for policy.max_inputs enforcement
  • Computes accurate vbytes estimation across all script types (P2PKH, P2SH-P2WPKH, P2WPKH, P2WSH, P2TR)
  • Calculates fees from target fee_rate_sat_vb Γ— estimated vbytes
  • Smart change handling β€” only creates change when it's above the dust threshold (546 sats)
  • Handles edge cases: send-all (no change), dust change absorption, fee/change boundary conditions

PSBT Generation

  • Builds valid BIP-174 PSBTs with bitcoinjs-lib
  • Includes witness_utxo metadata for each input
  • Correct nSequence and nLockTime per BIP-125 (RBF) and locktime rules
  • Anti-fee-sniping support (nLockTime = current_height when applicable)
  • Base64 encoded output ready for hardware wallet signing

RBF & Locktime

  • Full RBF signaling via nSequence (BIP-125)
  • Absolute locktime support (block height and unix timestamp)
  • Anti-fee-sniping: sets nLockTime = current_height when rbf: true and no explicit locktime
  • Correct interaction matrix between rbf, locktime, and current_height fields

Warnings & Safety

  • HIGH_FEE β€” fee > 1M sats or rate > 200 sat/vB
  • DUST_CHANGE β€” change output below 546 sats
  • SEND_ALL β€” no change created, leftover consumed as fee
  • RBF_SIGNALING β€” transaction opts into Replace-By-Fee

Web Visualizer

  • Dark-themed UI with clean layout
  • Paste fixture JSON to build a PSBT instantly
  • Visual breakdown of selected inputs β†’ payment outputs + change
  • Fee, fee rate, vbytes display
  • RBF signaling indicator and locktime info
  • Warning badges with explanations
  • Health endpoint at GET /api/health

πŸ—οΈ Project Structure

coin-smith/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ cli.ts              # CLI entry point β€” fixture β†’ JSON report
β”‚   β”œβ”€β”€ coin-selection.ts   # UTXO selection algorithm
β”‚   β”œβ”€β”€ psbt-builder.ts     # PSBT construction with bitcoinjs-lib
β”‚   β”œβ”€β”€ rbf-locktime.ts     # RBF signaling & locktime logic
β”‚   β”œβ”€β”€ weight.ts           # vbytes estimation per script type
β”‚   β”œβ”€β”€ validate.ts         # Input validation & error handling
β”‚   β”œβ”€β”€ warnings.ts         # Warning detection (high fee, dust, etc.)
β”‚   β”œβ”€β”€ types.ts            # TypeScript interfaces
β”‚   β”œβ”€β”€ server.ts           # Express server for web UI
β”‚   └── __tests__/
β”‚       β”œβ”€β”€ coin-selection.test.ts
β”‚       β”œβ”€β”€ rbf-locktime.test.ts
β”‚       β”œβ”€β”€ validate.test.ts
β”‚       └── weight.test.ts
β”œβ”€β”€ public/
β”‚   β”œβ”€β”€ index.html          # Web UI
β”‚   β”œβ”€β”€ style.css           # Styling (dark theme)
β”‚   └── app.js              # Frontend logic & rendering
β”œβ”€β”€ fixtures/               # 24 test fixtures (various scenarios)
β”œβ”€β”€ cli.sh                  # CLI runner script
β”œβ”€β”€ web.sh                  # Web server runner script
└── setup.sh                # Dependency installer

πŸš€ Getting Started

Prerequisites

  • Node.js (v18+)
  • npm

Setup

bash setup.sh

Run the CLI

# Basic P2WPKH transaction with change
bash cli.sh fixtures/basic_change_p2wpkh.json

# Send-all (no change)
bash cli.sh fixtures/send_all_dust_change.json

# RBF with locktime
bash cli.sh fixtures/rbf_with_locktime.json

Output goes to out/<fixture_name>.json.

Run Tests

npm test

Run the Web UI

bash web.sh
# β†’ http://127.0.0.1:3000

πŸ”§ How it works

  1. Validation β€” Parse the fixture JSON and validate every field: UTXOs must have valid txids (64 hex chars), amounts must be positive, scriptPubKeys must match claimed script types, addresses must be valid for the network.

  2. Coin Selection β€” I use a greedy algorithm that sorts UTXOs by value (largest first) and selects until we have enough to cover payments + estimated fee. It respects policy.max_inputs if set.

  3. Fee Calculation β€” Estimate vbytes based on input/output script types (each type has a known weight contribution). Fee = ceil(fee_rate Γ— vbytes). The tricky part is that adding/removing a change output changes the vbytes, which changes the fee β€” so it requires iteration.

  4. Change Logic β€” If inputs - payments - fee β‰₯ 546 (dust threshold), create a change output. Otherwise, the leftover gets absorbed into the fee (send-all). Never create dust outputs.

  5. PSBT Construction β€” Using bitcoinjs-lib, build the unsigned transaction with correct nVersion, nLockTime, and per-input nSequence values. Attach witness_utxo data for each input. Serialize as base64.

  6. RBF/Locktime β€” Follow the interaction matrix between rbf, locktime, and current_height to set the correct nSequence and nLockTime values.


πŸ“‹ Output Format

{
  "ok": true,
  "network": "mainnet",
  "strategy": "greedy",
  "selected_inputs": [...],
  "outputs": [
    { "n": 0, "value_sats": 70000, "script_type": "p2wpkh", "is_change": false },
    { "n": 1, "value_sats": 29300, "script_type": "p2wpkh", "is_change": true }
  ],
  "change_index": 1,
  "fee_sats": 700,
  "fee_rate_sat_vb": 5.0,
  "vbytes": 140,
  "rbf_signaling": true,
  "locktime": 850000,
  "locktime_type": "block_height",
  "psbt_base64": "cHNidP8BAFICAAAA...",
  "warnings": [{ "code": "RBF_SIGNALING" }]
}

πŸ§ͺ Tests

4 test suites covering:

Suite What it tests
coin-selection.test.ts Selection algorithm, insufficient funds, max_inputs policy
weight.test.ts vbytes estimation for all script types
rbf-locktime.test.ts All combinations of rbf/locktime/current_height
validate.test.ts Input validation, error handling, edge cases

πŸ› οΈ Tech Stack

Layer Tech
Language TypeScript
Runtime Node.js
Server Express
Bitcoin bitcoinjs-lib, ecpair, tiny-secp256k1
Testing Jest + ts-jest
Frontend Vanilla HTML/CSS/JS

πŸ“ What I learned

This project taught me how Bitcoin wallets actually work under the hood β€” how coin selection isn't just "pick UTXOs until you have enough" but involves carefully balancing fees, change outputs, and dust rules. The fee/change circular dependency (adding change changes the size, which changes the fee, which might eliminate the change) was surprisingly tricky to get right.

Building the PSBT from scratch also gave me a deeper understanding of BIP-174 and why the signing workflow is split into creation β†’ signing β†’ finalization steps.


πŸ“„ License

MIT β€” see LICENSE.

Built as part of the Summer of Bitcoin 2026 developer challenge.

About

PSBT Transaction Builder

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors