{"id":30334045,"url":"https://github.com/ac12644/bitcoin-wallet-api-node","last_synced_at":"2026-05-08T14:09:04.818Z","repository":{"id":309583687,"uuid":"1036852159","full_name":"ac12644/bitcoin-wallet-api-node","owner":"ac12644","description":"Password-protected Bitcoin wallet backend (Node/Express + bitcore). Create single/HD wallets, send (RBF), time-lock, QR (BIP21), fee estimates, mempool.space integration. Testnet-ready.","archived":false,"fork":false,"pushed_at":"2025-08-12T17:20:06.000Z","size":1024,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-12T18:38:00.093Z","etag":null,"topics":["backend","bitcoin","bitcoin-core","bitcoin-wallet","cryptocurrency","nodejs"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ac12644.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-08-12T17:16:44.000Z","updated_at":"2025-08-12T17:21:12.000Z","dependencies_parsed_at":"2025-08-12T18:38:14.357Z","dependency_job_id":null,"html_url":"https://github.com/ac12644/bitcoin-wallet-api-node","commit_stats":null,"previous_names":["ac12644/bitcoin-wallet-api-node"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/ac12644/bitcoin-wallet-api-node","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ac12644%2Fbitcoin-wallet-api-node","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ac12644%2Fbitcoin-wallet-api-node/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ac12644%2Fbitcoin-wallet-api-node/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ac12644%2Fbitcoin-wallet-api-node/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ac12644","download_url":"https://codeload.github.com/ac12644/bitcoin-wallet-api-node/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ac12644%2Fbitcoin-wallet-api-node/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32783508,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T08:22:46.396Z","status":"ssl_error","status_checked_at":"2026-05-08T08:22:45.650Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backend","bitcoin","bitcoin-core","bitcoin-wallet","cryptocurrency","nodejs"],"created_at":"2025-08-18T05:00:53.165Z","updated_at":"2026-05-08T14:09:04.813Z","avatar_url":"https://github.com/ac12644.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- PROJECT SHIELDS --\u003e\n\n[![Forks][forks-shield]][forks-url]\n[![Stargazers][stars-shield]][stars-url]\n[![Issues][issues-shield]][issues-url]\n\n# Bitcoin Wallet API (TypeScript) — Password-Protected Keystore\n\n\u003e A learning-friendly Bitcoin wallet backend in **TypeScript** with a simple web UI.\n\u003e Create single/HD wallets, store keys encrypted with a **password-protected keystore**, send BTC (RBF), build **time-locked** transactions, generate BIP21 + QR codes, estimate fees, verify txs, and query balances/tx history from mempool.space. **Testnet-ready** by default.\n\n\u003cp align=\"center\"\u003e   \u003cimg src=\"asset/screenshot.png\" alt=\"Playground screenshot\" width=\"520\"/\u003e \u003c/p\u003e\n\n## Highlights\n\n- **TypeScript** Node/Express backend\n- **Keystore**: AES-256-GCM with scrypt key derivation + bcrypt password hash\n- **Single wallets** (encrypted WIF) \u0026 **HD wallets** (encrypted BIP39 mnemonic)\n- **Send BTC** (RBF), **time-lock** (nLockTime), **reimburse**\n- **BIP21 + QR** generator\n- **Fee estimate** (sat/vB) and basic **address validation**\n- **Balance \u0026 transactions** via mempool.space\n- Minimal **index.html** UI (no build step)\n- Clean migration away from legacy _bitcore_ to **bitcoinjs-lib** + **@scure/bip32/bip39**\n\n---\n\n## Stack\n\n- Runtime: Node.js 18+\n- Language: TypeScript\n- Web: Express\n- Bitcoin: `bitcoinjs-lib`, `@scure/bip32`, `@scure/bip39`, `tiny-secp256k1`\n- Crypto: Node `crypto` (AES-256-GCM, scrypt), `bcrypt`\n- Data: `node-localstorage` (filesystem) — **for demos only**\n- HTTP: `axios`\n- QR: `qrcode`\n\n---\n\n## Quick Start\n\n```bash\n# 1) Install\nnpm install\n\n# 2) Dev run (watch)\nnpm run dev\n\n```\n\nOpen http://localhost:3000 to use the Playground UI.\n\n### Environment\n\nCreate `.env` (optional):\n\n```bash\nPORT=3000\nNETWORK=testnet   # \"testnet\" (default) or \"mainnet\"\n```\n\n\u003e The UI badge reads the network; API uses mempool.space testnet4/mainnet accordingly.\n\n---\n\n## Project Structure\n\n```\n.\n├─ index.html                 # Playground UI (served at \"/\")\n├─ src/\n│  ├─ server.ts               # Express app wiring (routes + index.html)\n│  ├─ controllers/            # All controllers (TS)\n│  ├─ routes/                 # Express routers (TS)\n│  └─ lib/\n│     ├─ net.ts               # NETWORK \u0026 mempool API host\n│     ├─ fees.ts              # Fee helpers (sat/vB, vsize)\n│     └─ keystore.ts          # AES-GCM + bcrypt, localstorage JSON\n├─ keystore/                  # Encrypted key records (gitignored)\n├─ scratch/                   # Legacy/demo storage (gitignored)\n├─ qrCodes/                   # Generated QR cache (gitignored)\n├─ .gitignore\n├─ package.json\n└─ tsconfig.json\n```\n\n---\n\n## Security Model (Important)\n\n- **Never** commits secrets: `keystore/`, `qrCodes/`, `scratch/` are **gitignored**.\n- Keystore records use:\n  - **AES-256-GCM** (random salt + 12-byte IV/nonce + auth tag)\n  - **scrypt** (password + salt) to derive encryption key\n  - **bcrypt** for server-side password verification\n- You **must supply the password** to unlock a wallet (for send / timelock / reimburse).\n- For production, consider **HSM/KMS** (Cloud KMS, HashiCorp Vault), proper DB, HTTPS, rate limiting, authN/authZ, audit logging, and _no plaintext mnemonic export_ endpoints.\n\n\u003e This repo is for learning. **Don’t use as-is in production** without hardening.\n\n---\n\n## Scripts\n\n```json\n\"scripts\": {\n  \"dev\": \"tsx watch src/server.ts\",\n  \"build\": \"tsc\",\n  \"start\": \"node dist/server.js\"\n}\n```\n\n`src/server.ts` simply imports `app` and calls `listen`. You can also run `src/app.ts` directly (it includes a `require.main` guard).\n\n---\n\n## API Overview\n\nAll responses are JSON. Errors return `{ error: string }` with appropriate status codes.\n\n### Wallets\n\n#### Create single wallet (encrypted WIF)\n\n```\nPOST /wallet\n```\n\n```json\n{ \"password\": \"string\" }\n```\n\n**200**\n\n```json\n{ \"id\": \"keystoreId\", \"address\": \"tb1...\" }\n```\n\n#### Create HD wallet (encrypted mnemonic)\n\n```\nPOST /wallet/hd\n```\n\n```json\n{ \"password\": \"string\" }\n```\n\n**200**\n\n```json\n{\n  \"id\": \"keystoreId\",\n  \"xpub\": \"vpub/xpub...\",\n  \"address\": \"first derived / watch-only\"\n}\n```\n\n\u003e HD derivation: BIP39 mnemonic → seed → BIP32 root (via `@scure/bip39` \u0026 `@scure/bip32`).\n\u003e We store the **mnemonic encrypted**; return **xpub** for watch-only derivations.\n\n#### Import HD wallet (from mnemonic)\n\n```\nPOST /wallet/retrieve\n```\n\n```json\n{ \"mnemonic\": \"word1 ... word12/24\", \"password\": \"string\" }\n```\n\n**200**\n\n```json\n{ \"id\": \"keystoreId\", \"xpub\": \"vpub/xpub...\", \"address\": \"watch-only root\" }\n```\n\n#### Create Multisig address\n\n```\nPOST /wallet/multisig\n```\n\n```json\n{\n  \"publicKeys\": [\"02...\",\"03...\", \"...\"],\n  \"requiredSignatures\": 2,\n  \"script\": \"p2sh\" | \"p2wsh\" | \"p2sh-p2wsh\"\n}\n```\n\n**200**\n\n```json\n{\n  \"address\": \"2N... / tb1q... / etc\",\n  \"m\": 2,\n  \"n\": 3\n}\n```\n\n#### Retrieve mnemonic (HD only)\n\n```\nPOST /wallet/mnemonic\n```\n\n```json\n{ \"walletId\": \"keystoreId\", \"password\": \"string\" }\n```\n\n**200**\n\n```json\n{ \"mnemonic\": \"word1 word2 ...\" }\n```\n\n\u003e Exposing the mnemonic is **dangerous**; keep this endpoint **disabled** in production.\n\n---\n\n### Spending\n\n\u003e For all _send-like_ actions you must provide the **wallet password** and either:\n\u003e\n\u003e - `walletId` (preferred), or\n\u003e - `fromAddress` (the server will look up the keystore record by address).\n\n#### Send BTC (RBF)\n\n```\nPOST /sendbtc\n```\n\n```json\n{\n  \"to\": \"tb1...\",\n  \"amount\": \"0.001\", // BTC string\n  \"password\": \"string\",\n  \"walletId\": \"keystoreId\", // preferred\n  \"fromAddress\": \"tb1...\" // optional fallback\n}\n```\n\n**200**\n\n```json\n{\n  \"txId\": \"hex\",\n  \"feeSatoshis\": 1234,\n  \"feerateSatPerVb\": 5.4\n}\n```\n\n- Picks all UTXOs for `fromAddress`\n- Signs with decrypted WIF (single) or derived key (HD, first path)\n- RBF enabled (sequence `\u003c 0xFFFFFFFE`)\n\n#### Time-locked transaction (build only)\n\n```\nPOST /timeLock\n```\n\n```json\n{\n  \"recipientAddress\": \"tb1...\",\n  \"amountInBTC\": \"0.001\",\n  \"timestamp\": 1767225600, // Unix seconds in the future\n  \"password\": \"string\",\n  \"walletId\": \"keystoreId\",\n  \"fromAddress\": \"tb1...\"\n}\n```\n\n**200**\n\n```json\n{\n  \"txHex\": \"02000000...\",\n  \"lockTime\": 1767225600,\n  \"feeSatoshis\": 1234,\n  \"feerateSatPerVb\": 5.4\n}\n```\n\n\u003e You can broadcast the hex later via mempool.space or your own broadcaster.\n\n#### Reimburse BTC (same as send, semantic wrapper)\n\n```\nPOST /reimburseBtc/reimburseBitcoin\n```\n\n```json\n{\n  \"to\": \"tb1...\",\n  \"amount\": \"0.001\",\n  \"password\": \"string\",\n  \"walletId\": \"keystoreId\",\n  \"fromAddress\": \"tb1...\"\n}\n```\n\n**200**\n\n```json\n{\n  \"txId\": \"hex\",\n  \"feeSatoshis\": 1234,\n  \"feerateSatPerVb\": 5.4\n}\n```\n\n---\n\n### Read-only \u0026 Utilities\n\n#### Address balance\n\n```\nGET /transactions/balance/:address\n```\n\n**200**\n\n```json\n{\n  \"confirmedBTC\": \"0.00500000\",\n  \"pendingBTC\": \"0\",\n  \"confirmedSats\": 500000,\n  \"pendingSats\": 0\n}\n```\n\n#### Address transactions (latest page)\n\n```\nGET /transactions/:address\n```\n\n**200**\n\n```json\n{\n  \"transactions\": [\n    /* mempool.space tx objects */\n  ]\n}\n```\n\n#### Verify txids (confirmed? confirmations?)\n\n```\nPOST /verifyTx\n```\n\n```json\n{ \"txids\": [\"hex1\", \"hex2\"] } // or a single string\n```\n\n**200**\n\n```json\n[\n  {\n    \"txid\": \"hex1\",\n    \"confirmed\": true,\n    \"confirmations\": 3,\n    \"block_height\": 123456\n  },\n  { \"txid\": \"hex2\", \"confirmed\": false, \"confirmations\": 0 }\n]\n```\n\n#### Fee estimate (mean of 1..6 targets)\n\n```\nGET /estimateFee\n```\n\n**200**\n\n```json\n{ \"feerateSatPerVb\": 7.2 }\n```\n\n#### Validate address (basic)\n\n```\nGET /validateAddress?address=tb1...\n```\n\n**200**\n\n```json\n{\n  \"address\": \"tb1...\",\n  \"isValid\": true,\n  \"network\": \"testnet\",\n  \"matchesConfiguredNetwork\": true\n}\n```\n\n#### BIP21 + QR\n\n```\nGET /payment/payment-request-qr?address=tb1...\u0026amount=0.001\u0026message=Invoice%20123\n```\n\n**200**\n\n```json\n{\n  \"success\": true,\n  \"id\": \"170...\",\n  \"bip21\": \"bitcoin:tb1...?amount=0.001\u0026message=Invoice%20123\",\n  \"dataUrl\": \"data:image/png;base64,...\"\n}\n```\n\n#### Historical price data (USD)\n\n```\nGET /historicalData?startDate=2024-01-01\u0026endDate=2024-02-01\n```\n\n**200**\n\n```json\n{ \"prices\": [...], \"market_caps\": [...], \"total_volumes\": [...] }\n```\n\n---\n\n## The Playground UI (index.html)\n\n- **Wallet Manager** (right panel): create **Single** or **HD** wallets (requires password), import HD by mnemonic, rename, set active.\n- **Active address** (top left): copy/set manually; shows **confirmed/pending** balance.\n- **Receive**: BIP21 + **QR** generator.\n- **Send**: enter **password** to unlock keystore and sign.\n- **Time-lock**: enter **password** to build a future-spendable transaction (returns `txHex`).\n- **Console**: shows raw JSON for debugging.\n\n\u003e The UI sends `walletId` (when available) and your **password** to unlock \u0026 sign.\n\n---\n\n## Libraries \u0026 Rationale\n\n**Use**\n\n- `bitcoinjs-lib` — actively maintained, widely used\n- `@scure/bip39` \u0026 `@scure/bip32` — modern, audited primitives\n- `tiny-secp256k1` — native secp256k1 bindings for bitcoinjs\n- `qrcode`, `axios`, `zod` (optionally for input validation)\n\n**Avoid/Removed**\n\n- `bitcore-lib`, `bitcore-mnemonic` — older, not TypeScript-first\n- Any lib that’s unmaintained or with unclear security posture\n\n---\n\n## .gitignore (keystore safety)\n\nAlready included:\n\n```\nbash\n\n\nCopyEdit\nnode_modules/\n.env\n/scratch/\n/qrCodes/\n/keystore/\n**/.DS_Store\n```\n\n---\n\n## Deployment\n\n- **Do not** use GitHub Pages/GitHub Actions runners as a backend host.\n- Use a server/host that runs Node (Render, Fly.io, Railway, Heroku-like, VPS, Docker/K8s).\n- Enable HTTPS, add CORS rules if serving the UI from another origin, add auth/rate limiting.\n\n---\n\n## Troubleshooting\n\n- **“Invalid password”**: the keystore record was found, but bcrypt mismatch.\n- **“Address not for configured network”**: you’re on `NETWORK=testnet` but using mainnet addr (or vice versa).\n- **Insufficient balance / dust**: ensure amount \u003e dust (~546 sats for legacy p2pkh; similar thresholds for segwit), and covers fee.\n- **UTXO empty**: fund your address using a testnet faucet.\n\n---\n\n## Example cURL\n\n```bash\n# Create single wallet\ncurl -sX POST http://localhost:3000/wallet \\\n  -H \"content-type: application/json\" \\\n  -d '{\"password\":\"hunter2\"}'\n\n# Send BTC\ncurl -sX POST http://localhost:3000/sendbtc \\\n  -H \"content-type: application/json\" \\\n  -d '{\"to\":\"tb1q...\",\"amount\":\"0.0002\",\"password\":\"hunter2\",\"walletId\":\"\u003cid\u003e\"}'\n```\n\n---\n\n## Roadmap / Ideas\n\n- Derivation paths \u0026 account management for HD (BIP44/84/86)\n- PSBT import/export\n- Watch-only xpub accounts with server-side UTXO set\n- Replace localstorage with SQLite/Postgres\n- Optional Cloud KMS/HSM integration\n\n---\n\n### Disclaimer\n\nThis project is for **educational purposes** only. Keys are stored on the server (encrypted), but a compromised server compromises funds. For production, use dedicated key management (HSM/KMS), remove mnemonic export, add authentication, logging, and monitoring.\n\n\u003c!-- MARKDOWN LINKS \u0026 IMAGES --\u003e\n\n[forks-shield]: https://img.shields.io/github/forks/ac12644/bitcoin-wallet-api-node?style=for-the-badge\n[forks-url]: https://github.com/ac12644/bitcoin-wallet-api-node/network/members\n[stars-shield]: https://img.shields.io/github/stars/ac12644/bitcoin-wallet-api-node?style=for-the-badge\n[stars-url]: https://github.com/ac12644/bitcoin-wallet-api-node/stargazers\n[issues-shield]: https://img.shields.io/github/issues/ac12644/bitcoin-wallet-api-node?style=for-the-badge\n[issues-url]: https://github.com/ac12644/bitcoin-wallet-api-node/issues\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fac12644%2Fbitcoin-wallet-api-node","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fac12644%2Fbitcoin-wallet-api-node","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fac12644%2Fbitcoin-wallet-api-node/lists"}