From dc42daaf139685aac3421a3aa8386893ff872d8b Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Wed, 2 Sep 2026 17:30:19 -0700 Subject: The 200ms of secp256k1 hiding in every repaint: the depth advisory priced a full uncooperative exit — every hop of every coin, an EC tweak per hop — on each wallet render; the estimate now memoizes on coin set + feerate, and decoded vtxos cache by id (bytes are immutable). Profiled on a real phone: point add/double/wNAF was 1.3s of a 4.3s six-tab-switch trace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr --- src/features/ark.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/features') diff --git a/src/features/ark.js b/src/features/ark.js index 72624b4..140eeb0 100644 --- a/src/features/ark.js +++ b/src/features/ark.js @@ -1929,16 +1929,26 @@ export function arkFeature(ctx) { // What a unilateral exit will need from Savings, BEFORE it starts: every // chain hop is a zero-fee tx bumped by a ~130 vB CPFP child, plus the // final claim. Priced at today's feerate — the driver reprices per hop. + // Memoized on the coin set + feerate: signedExitTxs rebuilds every hop of + // every coin's exit chain with an elliptic-curve tweak per hop — profiled + // at ~200ms of secp256k1 per call on a phone with depth-13 coins — and + // the depth advisory asked for it on EVERY wallet render. Same coins at + // the same feerate price the same, so pay once per change instead. + let _exitFeeMemo = { key: null, val: 0 }; function estimateExitFeeSat(mgr) { const feeRate = Math.max(1, (wallet.feeRates && wallet.feeRates.halfHourFee) || 2); + const coins = mgr.vtxos().filter((x) => x.state === 'spendable'); + const key = feeRate + '|' + coins.map((v) => v.id).join(','); + if (_exitFeeMemo.key === key) return _exitFeeMemo.val; let total = 0; - for (const v of mgr.vtxos().filter((x) => x.state === 'spendable')) { + for (const v of coins) { try { for (const txi of signedExitTxs(mgr._decoded(v), mgr.serverPub)) total += Math.ceil((txi.vsize + 130) * feeRate); total += Math.ceil(150 * feeRate); // the claim tx } catch {} } + _exitFeeMemo = { key, val: total }; return total; } -- cgit v1.2.3