| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Advanced
The main Settings tab now inlines the active wallet's own cards
(recovery phrase, name, public key) instead of linking to a sub-page,
followed by payment address, Ark status, network, and data source. All
nostr cards — address, account link, wallet connect, device sync — sit
under one Nostr heading via a new nostrSettingsCards seam. Offline
transfer, address rescan, and auto-logout move to an Advanced sub-page.
The Ark provider picker is gone: mainnet always rides coinos (a stale
stored choice no longer wins); dev networks keep their per-network
defaults. The per-account screen (Accounts → ⚙) reuses the same card
builders unchanged.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Dropped: silent payments (feature, wallet chains, scan worker, indexer
config), Boltz submarine/reverse swaps (feature + swap.js + tools), the
third-party ark swap bridge (config, client flows, settings card — the
ASP's own Lightning send is the one path now), and the Consolidate
coins settings action.
The NWC pouch is gone with them: background answering now spends the
WHOLE ark balance. The IndexedDB record mirrors the real manager state
plus the chain-3 keys for its live coins and a change-index window; the
worker pays through the ASP exactly like the open app, and the app
folds the worker's spends, change, and key allocations back in on
connect (additive merge + reconcile against the server). A leftover
chain-6 pouch from the short-lived pouch era is swept home
automatically using the keys stored in its own record. The stated
tradeoff: same-origin script access now reaches the ark balance, not a
carve-out — the ark balance is walking-around money, the on-chain
wallet's keys are never mirrored, and per-connection NWC budgets remain
the enforced ceiling.
Bundle: 726KB -> 602KB.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
| |
|
|
|
|
|
|
| |
Existing wallets with an explicitly chosen provider keep it; a wallet
that was riding the old default keeps its second.tech coins under that
server's namespace and can reselect it in Settings at any time.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
| |
Small bolt11 sends were unroutable by default: the ASP path's CLN fee
budget is a fraction of the ASP's own minimum fee, which rounds to zero
sats of routing budget on a zap-sized payment. The coinos bridge now
runs open (token gate removed server-side) so the default works for
every wallet; users can still pick Off or Custom in settings.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- ark presets gain coinos entries (ark.coinos.io on mainnet,
ark-staging.coinos.io on mutinynet) alongside Second's
- new per-network swap-bridge setting (preset/custom URL + access token)
with its own Settings card
When a bridge is configured, a bolt11 quote asks it first and shows its fee;
paying locks an HTLC only the bridge can claim, and only by revealing the
invoice preimage. Any failure — unreachable bridge, refused swap, unpayable
invoice — refunds and falls back to the ASP's own lightning-send, so the
payment never dead-ends. Hanging swaps are refunded on reconnect.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Tag each BOLTZ_PRESET with the network(s) it serves and add
boltzPresetsFor(net) so the provider dropdown only lists backends
valid for the active network — a mainnet wallet no longer shows the
local (regtest) or coinos-staging (mutinynet) backends. getBoltzProviderId()
now only honors a stored choice if it's valid for the current network,
so switching to mainnet can't silently keep using a regtest/mutinynet
backend, and the getBoltzProvider() fallback can no longer resolve to
staging. Also point the testnet default at custom (was the mainnet
Boltz API).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- Fee rates on the electrum backend came from Fulcrum's estimatefee
(Core's estimatesmartfee): historical, laggy, floored at 1 sat/vB, so
it showed 1 across the board while mempool.space showed 11/10. Pull the
configured explorer's live recommendation instead (/v1/fees/recommended,
then /fee-estimates), falling back to estimatefee only when the explorer
is unreachable — self-hosted/regtest behaviour unchanged.
- On mobile a backgrounded page leaves the socket half-open: TCP is dead
but readyState still reads OPEN, so wake() never reconnected and fired
scan RPCs into the void, waiting ~20s for the heartbeat to recover.
wake() now forces a reconnect when the socket has been silent past the
heartbeat window, so a payment shows the instant you refocus.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
| |
A replaced/evicted unconfirmed tx lingered forever as a phantom pending
receive (signetfaucet fee-bumps its batch payout every few minutes, so
History grew a new copy per bump). The poll now status-checks pending
txs — via /tx/:txid, since /tx/:txid/status answers 200 confirmed:false
for txids the node no longer knows — drops the vanished ones, and
re-fetches their addresses' coins and history, which _reconcileHeld
skips when the replacement pays identical amounts.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
| |
|
|
| |
Co-Authored-By: Claude Fable 5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
| |
Mainnet defaults to Second (ark.second.tech), regtest to the local
stack. Testnet/mutinynet get their own preset lists (off/custom only —
no public ASP exists there) instead of falling back to mainnet's, and
stay off by default.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- Settings: Ark server card (off by default, per-network presets incl.
Second mainnet + local regtest, custom URLs) with balance, coin count,
board form (on-chain -> Ark; funding output pinned to vout 0 via the
new buildTx noSort option), refresh/consolidate, and recent activity.
- Receive: Ark option in the address-type dropdown (QR + copy).
- Send: t?ark1 addresses are recognized in the normal send form and
route to an Ark confirmation (no mining fee) with a success view.
- Balance card: Ark balance line under the on-chain figure.
- ArkManager lifecycle: per-wallet instance created on account
activation, background sync every 5s (regtest) / 30s, state persisted
under the wallet cache key.
Verified end-to-end in a real browser (tools/ark/ui-test.js, puppeteer
against the regtest stack): receive, send, board, and refresh all green.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
|
| |
|
|
|
|
|
|
|
|
| |
Production Boltz service now live on cs at swap.coinos.io (dedicated mainnet
CLN, chain = coinos production bc on desk over Tailscale). Add it as a
selectable provider. Mainnet default stays on public Boltz Exchange until
our node has channel liquidity.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01RtuRuzDQ6ffngL5wFw4f9v
|
| |
|
|
|
|
|
|
|
| |
Point mutinynet wallets at the new self-hosted staging Boltz backend
(swap-staging.coinos.io) by default, instead of mainnet api.boltz.exchange.
Backend: dedicated boltz CLN + shared mut bitcoind on cs, behind a CORS shim
+ cloudflared.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The dev server already binds 0.0.0.0, but the app's regtest data source + SP
indexer pointed at localhost, which on a phone means the phone's own localhost.
Proxy them all through the dev server (same-origin), like /boltz already was:
HTTP by prefix (/boltz, /esplora, /sp) and WebSocket by exact path (/electrum →
Fulcrum, /sp/ws → SP indexer), with a Bun websocket relay. The regtest presets
become relative paths, and a small absWsUrl() resolves '/path' WS URLs to
ws(s)://<page-host>/path so they work from any device hitting the dev server.
No per-device config, no CORS. Verified: /electrum relays Fulcrum 2.1.1, /esplora
returns the tip height, and the desktop wallet still loads (balance 98,444) and
connects through the proxy with no errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
to fetch'
The regtest boltz API (localhost:9001) sends no CORS headers, so the browser
blocked cross-origin fetches — creating a Lightning invoice failed with 'failed
to fetch', and the limits GET silently fell back to defaults (min showed 25k
instead of regtest's real 50k). bun E2E tests didn't hit this (no CORS enforced).
dev.js now proxies /boltz/* to localhost:9001 (adding CORS + handling the OPTIONS
preflight), and the 'local' boltz preset points at the same-origin /boltz path.
Public providers (mainnet/testnet) already send CORS and are unaffected.
Verified: GET limits via proxy returns the real 50k/40.29M with CORS; POST
create-reverse succeeds; the browser swap form now shows Min 50,000.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |\
| |
| |
| |
| |
| | |
# Conflicts:
# src/api.js
# src/app.js
|
| | |
| |
| |
| |
| |
| |
| |
| | |
Renamed the default mainnet SP-indexer endpoint to sp.coinos.io. Also switched the
cloudflared bitcoin tunnel to http2 (QUIC was intermittently dropping edge
connections → transient 502s). tweaks.coinos.io still resolves to the same indexer.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
- Data-source and SP-indexer dropdowns now show the endpoint URL alongside the
name (e.g. 'coinos — wss://electrum.coinos.io'), so it's clear where data comes
from; 'Custom' stays bare.
- The tweak indexer is now served at https://tweaks.coinos.io (was halwallet.app/
sp): a cloudflared route on the bitcoin tunnel → the indexer at :8888, with a
DNS CNAME. Verified from a browser: /height, /mempool (1064 items), and /ws all
work (the coinos.io Bot Fight Mode challenges non-browser clients only).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
- Combined the separate Electrum and Explorer settings into a single data-source
list. Each preset knows its type (electrum vs esplora); a custom source's type
is detected from its URL scheme (ws/wss → Electrum, http/https → Esplora). The
settings UI is now one dropdown + a URL field for Custom, with a one-line note
showing the detected/selected type. getBackend/electrumCandidates/resolveHosts
derive from the selected source. Old split settings migrate automatically.
- coinos now points at wss://electrum.coinos.io (was wss://halwallet.app/electrum).
Set up as a cloudflared route on the bitcoin tunnel → the Fulcrum WS (:50003),
with a DNS CNAME. Verified the endpoint serves Electrum from a browser (Fulcrum
2.1.1 replies; cloudflare Bot Fight Mode only challenges non-browser clients).
Verified: combined list + auto-detection (unit + browser), old-settings migration,
regtest data flow unchanged, and the app loads over electrum.coinos.io with no
errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| | |
| |
| |
| |
| |
| |
| | |
Mainnet wallets now use https://halwallet.app/sp (our indexer on cs, behind
nginx /sp), enabling silent-payment receiving on mainnet.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
api.js: per-network SP indexer config (presets + custom, stored per network) with
getSpIndexerConfig/spIndexerUrl/spReceiveEnabled — regtest defaults to the local
indexer, others to custom; a hosted mainnet default slots in once we run one.
wallet.js: silentPaymentKeys() derives BIP-352 scan/spend keys from the master
seed (needs the mnemonic / depth-0 xprv), and silentPaymentAddress() returns the
sp1…/tsp1… address. Verified it matches the vector-validated e2e address.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |\|
| |
| |
| |
| | |
# Conflicts:
# src/app.js
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
The network card is now just the network picker. Each network's endpoints
(Electrum server + block explorer) live solely in the Data source card, whose
options change per network and are stored per network (so switching networks
remembers each one's choice). Removes the duplicate regtest Esplora/WS fields.
Adds mutinynet (signet — testnet address format, explorer at mutinynet.com).
Per-network defaults: mainnet → coinos Electrum, testnet/mutinynet → explorer,
regtest → local Fulcrum. Fixes tx-explorer links to use each network's web base
(no double /testnet).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| | |
| |
| |
| |
| |
| |
| |
| |
| | |
The data-source picker is now just Electrum vs Block explorer; coinos is the
first/default entry in the Electrum server list (our own Fulcrum), with public
servers as failover. Drops the standalone 'coinos' mode and the retired watcher
shim. Old 'coinos' selections migrate to electrum.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Flip COINOS_ELECTRUM on: the default 'coinos' mode is now a full Electrum backend
served same-origin at wss://halwallet.app/electrum (nginx -> Fulcrum on cs, synced
+ using bitcoind hashblock ZMQ), with the public Electrum servers as automatic
failover. Replaces the explorer-REST-data + coinos-watcher-push setup — one
connection to our own node now serves both data and instant notifications.
Verified the endpoint end-to-end through Cloudflare (server.version + get_balance).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |\| |
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Regtest now talks to a local Fulcrum over its native Electrum WebSocket for
both chain data and realtime push, instead of esplora REST + a separate watcher.
This matches prod (Fulcrum) and gives ~2-3x lower latency than the previous
electrs polling. getBackend()/electrumCandidates() route regtest to the
Electrum backend at the configured Fulcrum WS (default ws://localhost:50003);
the wallet's realtime path is ungated for regtest. ElectrumApi already covers
every call (utxos, history, fees, broadcast), incl. the swap feature.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
| |\|
| |
| |
| |
| | |
# Conflicts:
# src/app.js
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
On regtest there's no realtime push (coinos watcher is mainnet-only), so the UI
only updated on the 5–10s poll — a funded tx took a while to appear and a
just-mined block stayed "pending" until the next reconcile. electrs speaks the
Electrum protocol the watcher already understands, but only over TCP; route it
through a small WS<->TCP bridge (see coinos-server/electrum-ws) so the wallet
subscribes scripthashes and reconciles instantly on each notification. Data
still comes from esplora; this only adds the push channel. Configurable in
Settings (default ws://localhost:50003); falls back to polling if unreachable.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Replaces the single hardcoded Boltz endpoint with a provider selector.
Presets are independent Boltz-compatible instances from SwapMarket's list
(Boltz Exchange, Middle Way, ZEUS Swaps, Eldamar) plus Local (regtest) and a
Custom option; default is Local on regtest, Boltz Exchange otherwise. The
swap-status WebSocket is derived from the API URL (http->ws + /v2/ws) unless
overridden. Swaps remain non-custodial regardless of provider — a provider can
fail a swap but never take funds (hal claims/refunds on-chain).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
| |/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Non-custodial swaps via Boltz, fully client-side: hal holds the swap key +
preimage and claims (reverse) / refunds (submarine) on-chain via the taproot
script path, so Boltz can never strand funds.
- src/swap.js: tapleaf/tree construction (byte-exact vs boltz-core vectors),
swap P2TR address derivation (MuSig2 internal key + taptweak), script-path
claim/refund tx builder, bolt11 decode, Boltz api-v2 client, and a polling
SwapManager (reverse auto-claim; submarine fund + status + refund).
- wallet.js: deterministic swap-key derivation (dedicated chain) + persistence.
- api.js: configurable Boltz REST/WS endpoints.
- app.js: ⚡ Swap tab — Receive over LN (reverse) + Pay an invoice (submarine).
- tools/swap-*.test.js: vectors, live-address, and end-to-end regtest tests.
Verified end-to-end on regtest against a local Boltz wired to coinos's bc +
a dedicated CLN/hold node.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
| |
New `regtest` network (bech32 `bcrt`, testnet version bytes) selectable in
Settings, pointed at a configurable local Esplora (default http://localhost:3000).
Adds getNetwork/setNetwork + getRegtestEsplora persistence, a regtest branch in
resolveHosts, mainnet-only gating for the realtime watcher + Electrum backend,
and a Network selector card. Account loading reads the active network.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
| |
The Esplora broadcast cleared its abort timer right after fetch() resolved, so
the subsequent res.text() body read had no timeout — a stalled response body
hung the whole broadcast (and the send spinner) indefinitely, with the tx never
reaching the network. Keep the abort timer active through the body read, and add
a hard 30s outer timeout in the send handler so a wedged broadcast always surfaces
as 'timed out — try again' instead of spinning forever. (The silent-payment tx
itself is valid — verified structure + BIP143 signature over the real outputs.)
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Failover (live now): the Electrum backend now tries an ordered list of servers,
advancing to the next when one fails to connect or dies within ~5s. Named public
presets fall over to the other public servers; a custom your-node URL does NOT
fall over (won't silently leak your addresses to a server you didn't pick).
Verified: dead primary → auto-advances to a working server.
Cutover switch (dormant): COINOS_ELECTRUM (false) flips 'coinos' mode from
explorer-REST + watcher push to a full Electrum backend pointed at
wss://coinos.io/electrum, with the public servers as failover, and disables the
watcher for coinos mode. Flip it true once Fulcrum serves data behind that
endpoint — no new logic needed at cutover. Flag off = current behavior exactly
(default coinos → esplora + watcher), so this ships safely while Fulcrum syncs.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Replace the Esplora|Electrum backend toggle plus the separate 'Instant
notifications' on/off with one 'Data source' selector:
• coinos (instant) — default; explorer data + coinos push
• Block explorer — REST + polling, nothing sent to coinos (the opt-out)
• Electrum server — one WS server for data + push (your node / public)
Picking a non-coinos source IS the privacy opt-out, so the standalone toggle is
gone. A single 'mode' setting drives getBackend()/getRealtimeEnabled(), so the
wallet/wsUrl logic is unchanged; existing per-feature settings migrate in
(realtime=off → explorer, backend=electrum → electrum). coinos⇄explorer just
flips the watcher (no rescan); to/from electrum rebuilds the backend.
Sets up cleanly for a future coinos Fulcrum: 'coinos' mode upgrades from
REST+watcher to a full Electrum backend transparently.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
| |
Tested all six suggestions: 5 connect and return correct data (blockstream
electrs, btc.electroncash.dk / blackie / jochen Fulcrum, mempool.guide). Only
bitcoin.grey.pw:50004 was unreachable, so it's left out.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Settings → Data source now offers 'Electrum' alongside the Esplora REST explorer.
One Electrum WebSocket serves BOTH the wallet's data queries and scripthash push
notifications, so pointing Hal at your own Fulcrum/electrs gives a fully
self-hosted setup — no coinos, no public REST, nothing shared off your node.
- src/electrum.js: ElectrumApi maps the Esplora shapes the wallet consumes onto
Electrum methods (scripthash get_balance/listunspent/get_history, estimatefee,
transaction.broadcast). electrs has no verbose tx lookup, so it parses raw txs
and resolves input values from parent txs to compute fees (cached, in-flight
deduped, bounded-parallel). Validated field-for-field against mempool.space:
addressTxs (fee/vin/vout/status), getTx, outspend, balance, utxos.
- wallet.js: the realtime socket already spoke Electrum's notify protocol; added
request/response (_rpcCall) so the same connection answers data calls. Real
Electrum notifications carry no tx data, so they reconcile via the backend.
Replaced the fragile newline parser with a string-aware brace-depth scanner
that handles every framing (newline, one-per-frame, concatenated, split) —
fixes a buffer-poisoning hang under concurrent calls; also hardens the coinos
watcher path. Esplora stays the default; nothing changes for existing users.
Verified end-to-end against blockstream's electrs: concurrent transport, a full
fresh-wallet scan (~2s), and the parser unit-tested on all framings.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
| |
The WS watcher is the one place the app shares addresses off-device (it
subscribes your scripthashes to coinos to push instant payment updates). Add an
'Instant notifications' toggle in the explorer settings: off means the app never
connects to coinos and polls your chosen explorer instead — a bit slower, more
requests, but nothing is told to coinos. wsUrl returns null when disabled, so
startRealtime cleanly skips the socket and relies on the polling fallback.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Opening a gift link now checks whether its funding coin is already spent — via
the Esplora outspend endpoint, which reports a spend even by an unconfirmed
claim. The Claim button is gated until that check passes; if the coin is spent,
show an 'Already claimed' screen with a link to the claim tx instead of a
claimable amount. doClaim also re-checks on broadcast failure (the race window),
and refuses to run while unchecked/taken — so a second opener can't attempt a
doomed double-spend.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Replace the unreliable public WebSocket push (mempool/blockchain.info address
subscriptions never reliably fire) with a private notification service:
- tools/zmq-watcher.js: a standalone Bun service (reusing coinos lib/zmq.ts's
ZMTP subscriber) that reads Bitcoin Core's ZMQ rawtx/rawblock, computes the
scripthash of each output, and pushes a blockchain.scripthash.subscribe
notification to subscribed clients. No historical index (~24MB RAM) — just the
live tx stream. Runs on the coinos box reading its Core's ZMQ.
- wallet.js: realtime client now speaks the Electrum protocol (server.version,
scripthash.subscribe, server.ping) over the WS; any scripthash notification
triggers the existing refreshLive() (REST stays the source of truth).
- api.js: wsUrl -> wss://coinos.io/electrum.
Verified end-to-end: live mempool txs to subscribed addresses notify instantly.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- Signal opt-in RBF (BIP125, sequence 0xfffffffd) on all new sends so
they're replaceable.
- "Bump fee" on an unconfirmed sent tx (its detail view): rebuilds the
same inputs + recipient outputs at the Priority rate, taking the extra
fee from the change output (pulling in a confirmed coin if change can't
cover it), re-signs, and routes to a "Replace with a higher fee?"
confirmation showing old → new fee before broadcasting.
- New keys translated into all 19 locales.
Honest scope: P2A/TRUC/package-relay are L2/pre-signed-tx + node-relay
features that don't apply to a single-sig wallet broadcasting single txs
via public explorers, so they're intentionally out. CPFP (for receives /
unbumpable sends) is a possible follow-up.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- Settings → Block explorer: pick mempool.space (default, with
blockstream.info as a silent failover), blockstream.info only, or a
custom Esplora/electrs REST URL (e.g. your own node). The realtime
WebSocket only applies to mempool.space; other explorers fall back to
polling. Switching rebuilds the API and rescans. Replaces the old
undocumented explorer-proxy hack.
- How it works: merge the "For the technically curious" disclosure into
the main body so it's all visible under "The basics", and dedupe the
overlapping no-server/offline sentences. Mentions the new explorer
setting.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
| |
- Footer: "made with ♥ by Adam Soltys" (links adamsoltys.com) and "source
available on GitHub" (links the repo).
- Explorer endpoint now defaults to the public explorers; a rate-limited
machine can opt into a proxy via localStorage('explorer-proxy'). Correct for
a hosted multi-user deploy (each visitor uses their own IP).
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]>
|
| |
|
|
|
|
|
|
|
|
| |
With concurrency 1, a single hung request (no response) stalled the whole
serialized queue indefinitely — so balances/notifications never updated and
the console filled with ERR_TIMED_OUT. Each request now has a 10s abort
timeout; on timeout/network error the host is parked (20s) and we fail over
to the other explorer. Applies to broadcasts too.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
| |
Round-robin sent half of every scan to Blockstream, which hard-rate-limits
this IP (429 on each), and we kept routing back to it — stalling scans so new
balances never showed. Now hosts are tried in order (mempool first) and any
host that 429s is parked for 30s, so traffic stays on the healthy host and
Blockstream is fallback-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
| |
Post the signed tx to both mempool.space and Blockstream at once (outside the
throttle, since a send is urgent) so it's visible regardless of which explorer
the recipient watches. Succeeds if any accept; only fails if all do.
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]>
|