aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-08-28 19:37:52 -0700
committerAdam Soltys <[email protected]>2026-08-28 19:37:52 -0700
commitefd504ece47e684f77d85de990e97d90af0c221b (patch)
tree199da00fa8532bb2d3fe802815e6e534a636f407
parent69d7b3d9411741d6b77b59bc35059cc56cc4fb61 (diff)
downloadcoinosv3-efd504ece47e684f77d85de990e97d90af0c221b.tar.gz
coinosv3-efd504ece47e684f77d85de990e97d90af0c221b.zip
The tradeoff gets a quiet advanced card, not a quiz: when coins run four or more hops deep, Settings grows an "exit cost" card naming today's trustless-exit price and offering a one-tap renewal that flattens the history — invisible to everyone whose coins are shallow
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
-rw-r--r--src/features/ark.js29
-rw-r--r--src/i18n.js5
2 files changed, 33 insertions, 1 deletions
diff --git a/src/features/ark.js b/src/features/ark.js
index 269cfb9..66eef18 100644
--- a/src/features/ark.js
+++ b/src/features/ark.js
@@ -1466,6 +1466,33 @@ export function arkFeature(ctx) {
}
}
+ // The exit-cost tradeoff, surfaced only when it's real: every off-chain
+ // hop a coin accumulates makes its trustless exit one on-chain transaction
+ // more expensive; a renewal flattens the history back to a single hop.
+ // Most users never need to think about this — the card only appears once
+ // coins run deep enough for the difference to matter.
+ const EXIT_DEPTH_ADVISORY = 4;
+ function exitDepthCard() {
+ if (!arkAvailable() || wallet.watchOnly || !ark || !ark.state) return null;
+ const spend = (ark.state.vtxos || []).filter((v) => v.state === 'spendable');
+ if (!spend.length) return null;
+ let depth = 0;
+ for (const v of spend) { try { depth = Math.max(depth, ark._decoded(v).genesis.length); } catch {} }
+ if (depth < EXIT_DEPTH_ADVISORY) return null;
+ let exitFee = 0; try { exitFee = estimateExitFeeSat(ark); } catch {}
+ let renewFee = 0; try { renewFee = ark.refreshFee(spend, ark._tipH || 0); } catch {}
+ return h('div', { class: 'card col', style: 'gap:8px' },
+ h('h3', {}, t('arkDepthTitle')),
+ h('p', { class: 'small muted', style: 'margin:0' },
+ t('arkDepthBody', { d: String(depth), fee: fmtAmount(exitFee), renew: renewFee > 0 ? fmtAmount(renewFee) + ' sats' : t('arkDepthFree') })),
+ h('button', { class: 'btn-block', disabled: !!ui.arkBusy, onClick: () => {
+ // fire and let the round machinery carry it — rounds can be an hour out
+ ark.refresh(spend.map((v) => v.id)).catch((e) => toast(e.message));
+ toast(t('arkDepthRenewed'));
+ render();
+ } }, t('arkDepthBtn')));
+ }
+
function autoWithdrawCard() {
if (!arkAvailable() || wallet.watchOnly) return null;
const st = awState();
@@ -2325,7 +2352,7 @@ export function arkFeature(ctx) {
// swaps feature): the ASP pays invoices natively over ark.
canLnPay() { return arkAvailable() && !wallet.watchOnly && !!(ark && ark.info); },
lnSpendableSat() { const b = arkBalance(); return b ? b.spendableSat : 0; },
- settingsCards() { return [autoWithdrawCard()]; },
+ settingsCards() { return [autoWithdrawCard(), exitDepthCard()]; },
// The onboarding wizard's top-up step borrows the board form wholesale.
arkBoardForm() {
if (!arkAvailable() || wallet.watchOnly) return null;
diff --git a/src/i18n.js b/src/i18n.js
index a0a251d..624ca15 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -199,6 +199,11 @@ const STR = {
onbBoardTitle: 'Top up Spending',
onbBoardBody: 'Move some sats from Savings into Spending — payments there are instant, with no mining fees.',
topUpFromSavings: 'Top up from Savings',
+ arkDepthTitle: 'Advanced · exit cost',
+ arkDepthBody: 'Your coins’ off-chain history is {d} hops deep. A trustless exit would currently cost about {fee} sats in mining fees — each hop is one more on-chain transaction. Renewing now flattens the history back to a single hop for {renew}.',
+ arkDepthFree: 'free',
+ arkDepthBtn: 'Renew now',
+ arkDepthRenewed: 'Renewal requested — your coins flatten when the next round completes.',
arkUniUneconomical: 'A trustless on-chain exit would cost about {fee} sats in mining fees — more than the {n} sats it would move, so it isn’t offered. For a balance this size, send it out over Lightning instead: no mining fees at all.',
arkUniFallbackBody: 'The server didn’t cooperate, but your money can still move without it: your whole Spending balance exits to Savings on-chain. It takes about a day, and roughly {fee} sats of mining fees will be paid from Savings. Funds land in Savings — send them on from there once it completes.',
arkUniFallbackShort: 'Savings holds {have} sats — about {need} sats are needed for the mining fees. Top up Savings first.',