| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
| |
mempool's track-addresses caps at 10 per connection; we were sending all coin
addresses + a 15-wide frontier, so the subscription was rejected and realtime
updates silently stopped. With the frontier model (coins handled by post-send
refresh + Nostr sync), only the next receive and change address need watching
— so track exactly those two.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Persist wallet state (addresses, utxos, history, indices, fees) as a NIP-44
encrypted, parameterized-replaceable event (kind 30078) addressed to self on
relay.coinos.io. The Nostr identity is derived from the same seed (NIP-06), so
no extra secret. On load we pull the latest state from Nostr — a device with
no local cache gets full state without re-scanning, and another device's
updates propagate — using savedAt to keep the newest copy. localStorage stays
as the instant/offline cache; state is published (debounced) on every change.
(Relay note: added kind 30078 to the strfry write-policy allowlist on prod.)
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
| |
Per request: an address is scanned once, then never auto-scanned again. The
routine poll now checks only the fresh frontier (next receive + change
address) for new deposits; coin-holding addresses are no longer polled and
the periodic full re-scan is removed. Spends on existing coins arrive via the
WebSocket while connected; a new Settings → Rescan button does a full scan to
catch anything missed (e.g. a payment to a reused old address).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
| |
This IP is rate-limited/banned by mempool.space, so REST + WS now go through
an nginx CORS proxy on the OVH VPS (mempool via /mp, blockstream via /bs).
Controlled by a single PROXY constant in api.js — set it to '' to revert to
the public explorers once the ban is lifted. Hosts are tagged by kind so fee
format detection still works.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
| |
Idle polls were re-querying every used address (~N requests each cycle). Now
the background poll checks only addresses that can change — those holding
coins and the next fresh receive/change address — and escalates to a full
scan only when one of them moves. Stable historical addresses stay cached.
A full scan still runs every 5 min as a safety net. Cuts steady-state request
volume dramatically for wallets with many used addresses.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
| |
The mempool WS sends no periodic traffic on a track-addresses-only
subscription, so a half-open connection (died without firing onclose) went
undetected and updates fell back to the slow poll. Add:
- ping every 15s ({action:'ping'} -> pong); if no message for 45s, force a
reconnect (handles half-open sockets).
- reconnect + refresh on tab refocus (visibilitychange) and network 'online'.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
The mempool.space WebSocket push already contains the transaction, so parse
it on arrival to add the incoming UTXO and bump pending immediately, instead
of waiting on a debounced re-scan. The scan still follows (400ms) to
reconcile spends/confirmations. Removes the ~3s our handler added on top of
mempool.space's own push latency.
(Measured: mempool.space itself takes ~8s to see a coinos-broadcast tx due to
p2p propagation — the real fix for incoming-payment latency is the sender
pushing directly to the explorer.)
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
429 handling: route every outgoing request (address, utxo, txs, fees,
broadcast) through one scheduler. A 429 from ANY request now pauses ALL
subsequent requests with exponential backoff (2s→30s), easing off after a
streak of successes — instead of only cooling down the same host/kind.
Fewer requests: derive balance from /address chain_stats (no /utxo needed
just to total), and on background polls fetch only /address. The heavier
/utxo + /txs are pulled only on first load or when the balance/addresses
actually change. Idle polls drop from ~3N requests to ~N.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Max button: fill the amount field with the computed maximum (n inputs, 1
output, current fee rate) so it gives visible feedback instead of just
blanking/disabling the field.
Rate limits: eliminate look-ahead (gap limit 1 — stop at the first unused
address, safe because we only ever expose one unused address at a time),
query one address at a time (API concurrency 1, 400ms spacing across two
backends), scan chains sequentially, and pause 500ms after each used address.
An empty scan is now ~2-3 requests instead of ~10+.
History cache: persist scanned state (addresses, utxos, txs, indices) in
localStorage keyed by sha256(seed) — the seed itself is never stored. Re-
importing the same seed in this browser shows the last-known balance/history
instantly while a fresh scan runs. Cleared by nothing automatically (public
chain data); different seeds get separate keys.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
- Remove the online/live status badge and the USD price line, which both
appeared after load and caused layout shift. Stop fetching price entirely.
- Persist the seed + passphrase in sessionStorage so a page refresh restores
the open wallet instead of returning to the unlock screen. Cleared on
logout and (by the browser) when the tab closes.
Note: sessionStorage holds the seed in plaintext for the tab session — an
acceptable tradeoff for this self-custody tool and scoped to the tab.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Relying solely on the mempool WebSocket was unreliable: it only pushes new
events (no replay), and pushes can be missed (drops, server quirks), so a
deposit could go unseen with no manual refresh to fall back on.
- Add a 12s background poll alongside the WebSocket.
- Make background refreshes "silent": scan({silent:true}) re-renders only when
the visible state (utxos/txs/fresh indices) actually changes, so polls never
flash the UI or steal input focus while typing.
- Skip fees/price fetches on routine polls to keep request volume low.
- WS events now trigger the same silent refresh (debounce 800ms).
Unit-tested: silent refresh emits once when a pending deposit appears and not
at all when nothing changed.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
- Remove the unlock-screen "Offline mode" checkbox and network selector.
Always mainnet. Attempt a scan on open; if navigator is offline or the
scan fails (network unreachable), fall back to offline automatically with
a banner + Retry instead of an upfront toggle.
- Drop the manual balance "Refresh" and receive "Check for payments"
buttons; the WebSocket keeps things current.
- Update balances silently: add a wallet.loaded flag so the balance only
dims on the very first load and history/coins only show a spinner before
the first load. Background WS-driven rescans no longer flash spinners.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
| |
Since the wallet only ever exposes one unused receive address at a time,
used addresses stay contiguous (gap never exceeds 1), so a gap limit of 5 is
a safe margin and cuts a scan from ~40 to ~10 requests.
Also reword the receive note: the address advances automatically (via the
realtime WebSocket) when paid, so drop the confusing "scan again".
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
|
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]>
|