From 190c13a338462a7a76ee65e9486e42c0165a65f4 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Fri, 3 Jul 2026 15:08:26 -0700 Subject: Ark: wire into the wallet UI - 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 Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy --- src/api.js | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'src/api.js') diff --git a/src/api.js b/src/api.js index a170584..17deded 100644 --- a/src/api.js +++ b/src/api.js @@ -181,6 +181,55 @@ const deriveBoltzWs = (api) => api ? api.replace(/^http/, 'ws').replace(/\/+$/, export function getBoltzApi() { return getBoltzProvider().api; } export function getBoltzWs() { const p = getBoltzProvider(); return p.ws || deriveBoltzWs(p.api); } +// Ark server (ASP) — Second's bark/captaind protocol, spoken natively over +// gRPC-web (src/ark). Off by default: enabling Ark is an explicit opt-in per +// network. The esplora URL is the chain source the Ark client checks anchors +// and confirmations against (independent from the wallet's own data source so +// each preset ships a known-good pairing). +const ARK_PRESETS_BY_NET = { + mainnet: [ + { id: 'off', label: 'Off', ark: '', esplora: '' }, + { id: 'second', label: 'Second (ark.second.tech)', ark: 'https://ark.second.tech', esplora: 'https://mempool.second.tech/api' }, + { id: 'custom', label: 'Custom…', ark: '', esplora: '' }, + ], + regtest: [ + { id: 'off', label: 'Off', ark: '', esplora: '' }, + { id: 'local', label: 'Local (regtest)', ark: 'http://localhost:3535', esplora: 'http://localhost:30002' }, + { id: 'custom', label: 'Custom…', ark: '', esplora: '' }, + ], +}; +const ARK_PROVIDER_KEY = 'btc-wallet-ark-provider'; // selected preset id, per network +const ARK_CUSTOM_KEY = 'btc-wallet-ark-custom'; // { ark, esplora } for custom, per network + +export function arkPresets(net = getNetwork()) { + return ARK_PRESETS_BY_NET[net] || ARK_PRESETS_BY_NET.mainnet; +} +export function getArkProviderId(net = getNetwork()) { + try { + const id = localStorage.getItem(nk(ARK_PROVIDER_KEY, net)); + if (id && arkPresets(net).some((p) => p.id === id)) return id; + } catch {} + return 'off'; +} +export function setArkProviderId(id, net = getNetwork()) { + try { localStorage.setItem(nk(ARK_PROVIDER_KEY, net), id); } catch {} +} +export function getArkCustom(net = getNetwork()) { + try { const c = JSON.parse(localStorage.getItem(nk(ARK_CUSTOM_KEY, net)) || 'null'); if (c) return { ark: c.ark || '', esplora: c.esplora || '' }; } catch {} + return { ark: '', esplora: '' }; +} +export function setArkCustom({ ark, esplora }, net = getNetwork()) { + try { localStorage.setItem(nk(ARK_CUSTOM_KEY, net), JSON.stringify({ ark: (ark || '').trim(), esplora: (esplora || '').trim() })); } catch {} +} +// The active Ark endpoints, or null when Ark is off / incompletely configured. +export function getArkConfig(net = getNetwork()) { + const id = getArkProviderId(net); + if (id === 'off') return null; + const p = arkPresets(net).find((x) => x.id === id); + const cfg = p.id === 'custom' ? getArkCustom(net) : { ark: p.ark, esplora: p.esplora }; + return cfg.ark && cfg.esplora ? cfg : null; +} + // Silent-payment (BIP-352) tweak indexer — a separate, optional endpoint used // only for SP receiving. It serves public tweak/UTXO data (the scan key never // leaves the device), but a public one can observe which blocks hold your -- cgit v1.2.3