aboutsummaryrefslogtreecommitdiffstats
path: root/src/features
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-02 17:30:19 -0700
committerAdam Soltys <[email protected]>2026-09-02 17:30:19 -0700
commitdc42daaf139685aac3421a3aa8386893ff872d8b (patch)
tree8b5e9e0ab599efd384db0ecb74387f708cd7b576 /src/features
parent11a7919939f30318c6f2bf81058e57e2bb604b67 (diff)
downloadcoinosv3-dc42daaf139685aac3421a3aa8386893ff872d8b.tar.gz
coinosv3-dc42daaf139685aac3421a3aa8386893ff872d8b.zip
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
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
Diffstat (limited to 'src/features')
-rw-r--r--src/features/ark.js12
1 files changed, 11 insertions, 1 deletions
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;
}