aboutsummaryrefslogtreecommitdiffstats
path: root/build.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-06-14 23:01:03 -0700
committerAdam Soltys <[email protected]>2026-06-14 23:01:03 -0700
commitc5443946031ec32a7b3008fa751eb7916654a9f6 (patch)
tree372e8d0ccaa2dec58a6bcaaa19d3902b80eb4396 /build.js
parent683927e4132651b9198e5421fae16e9c60978fbc (diff)
downloadcoinosv3-c5443946031ec32a7b3008fa751eb7916654a9f6.tar.gz
coinosv3-c5443946031ec32a7b3008fa751eb7916654a9f6.zip
Rebuild as single-file offline BIP84 wallet
Replace the SvelteKit/daisyUI prototype with a vanilla JS + hand-rolled CSS app that Bun bundles (code + scure/noble crypto + QR + CSS) into one self-contained dist/index.html runnable offline from file://. Wallet core (src/wallet.js): - BIP84 native-segwit HD wallet from BIP39 seed (+optional passphrase) - gap-limit scan of receive/change chains, aggregate balance, tx history - fresh receive/change addresses; signs with witnessUtxo only (enables offline signing with no prev-tx fetch) - coin selection + fee estimation via btc-signer selectUTXO; send-max; manual coin control - offline JSON snapshot export/import (keyless) + signed-tx hex/QR export - realtime mempool.space WebSocket: track-addresses -> debounced auto refresh of balances/history (no polling); live status badge UI (src/app.js): clean light paper-wallet; create (with optional, skippable backup verification) / import flow; receive (QR) / send / history / coins / tools tabs; clickable logo returns home. API (src/api.js): mempool.space wrapper with retry/backoff for scan-burst rate limiting; single network choke-point for offline enforcement. Verified: BIP84 spec vectors, build/sign/parse for auto/max/manual flows, snapshot roundtrip, live scan + WebSocket, headless-Chrome UI smoke tests. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Diffstat (limited to 'build.js')
-rw-r--r--build.js53
1 files changed, 53 insertions, 0 deletions
diff --git a/build.js b/build.js
new file mode 100644
index 0000000..486908e
--- /dev/null
+++ b/build.js
@@ -0,0 +1,53 @@
+// Build a single, self-contained dist/index.html.
+//
+// Everything — app code, the @scure/@noble crypto libs, the QR generator, and
+// all CSS — is bundled and inlined so the result is one file you can save and
+// open offline straight from the filesystem (file://). No server, no network.
+
+import { mkdir } from 'node:fs/promises';
+
+const FAVICON =
+ 'data:image/svg+xml,' +
+ encodeURIComponent(
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="7" fill="#f7931a"/><text x="16" y="23" font-size="20" font-family="Arial" font-weight="bold" text-anchor="middle" fill="#fff">₿</text></svg>`
+ );
+
+export async function buildHtml({ minify = true } = {}) {
+ const result = await Bun.build({
+ entrypoints: ['./src/app.js'],
+ target: 'browser',
+ minify,
+ });
+ if (!result.success) {
+ for (const log of result.logs) console.error(log);
+ throw new Error('bundle failed');
+ }
+ let js = await result.outputs[0].text();
+ // Guard against a literal </script> inside the bundle closing our tag early.
+ js = js.replaceAll('</script', '<\\/script');
+ const css = await Bun.file('./src/style.css').text();
+
+ return `<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
+<meta name="color-scheme" content="light">
+<title>Bitcoin Wallet</title>
+<link rel="icon" href="${FAVICON}">
+<style>${css}</style>
+</head>
+<body>
+<div id="app"></div>
+<script>${js}</script>
+</body>
+</html>`;
+}
+
+if (import.meta.main) {
+ await mkdir('dist', { recursive: true });
+ const html = await buildHtml({ minify: true });
+ await Bun.write('dist/index.html', html);
+ const kb = (Buffer.byteLength(html) / 1024).toFixed(0);
+ console.log(`✓ dist/index.html written (${kb} KB) — open it offline, no server needed`);
+}