aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/features/ark.js30
-rw-r--r--src/i18n.js15
2 files changed, 41 insertions, 4 deletions
diff --git a/src/features/ark.js b/src/features/ark.js
index 8df45fe..96abcb1 100644
--- a/src/features/ark.js
+++ b/src/features/ark.js
@@ -2128,6 +2128,33 @@ export function arkFeature(ctx) {
const tick = (checked, onChange, title) => h('input', {
type: 'checkbox', checked, title, style: 'flex:0 0 auto;margin:0', onChange,
});
+ // "Exit now" asks once, with the numbers on the table, before anything
+ // is published: an exit cannot be called back.
+ if (ui.arkExitConfirm) {
+ const savings = wallet.spendable || 0;
+ const short = savings < exitFee;
+ const row = (k, v, cls = '') => h('div', { class: 'row between' }, h('span', { class: 'small muted' }, k), h('span', { class: 'small' + (cls ? ' ' + cls : '') }, v));
+ return h('div', { class: 'col', style: 'gap:16px' },
+ h('div', { class: 'card col', style: 'gap:10px' },
+ h('h3', { style: 'margin:0' }, t('arkExitConfirmTitle')),
+ h('p', { class: 'small muted', style: 'margin:0' }, t('arkExitConfirmBody')),
+ h('div', { class: 'col', style: 'gap:4px' },
+ row(t('arkExitConfirmCoins'), String(spend.length)),
+ row(t('arkExitConfirmAmount'), fmtSats(totalSat) + ' sats'),
+ row(t('arkExitConfirmFee'), fmtSats(exitFee) + ' sats'),
+ row(t('arkExitConfirmSavings'), fmtSats(savings) + ' sats', short ? 'err' : '')),
+ short ? h('div', { class: 'notice err small' }, t('arkExitFeeShort', { need: fmtSats(exitFee), have: fmtSats(savings) })) : null,
+ h('p', { class: 'small muted', style: 'margin:0' }, t('arkExitConfirmWait')),
+ h('button', { class: 'btn-primary btn-block', disabled: !!ui.arkBusy || short || !spend.length, onClick: () => {
+ ui.arkExitConfirm = null;
+ doArkExit();
+ ui.arkCoinsSel = null;
+ back();
+ ui.tab = 'history'; // the exit lives as a pending record there
+ render();
+ } }, ui.arkBusy === 'exit' ? h('span', { class: 'spinner sm' }) : t('arkExitConfirmGo'))),
+ h('button', { class: 'btn-ghost btn-block', onClick: () => { ui.arkExitConfirm = null; render(); } }, t('cancel')));
+ }
return h('div', { class: 'col', style: 'gap:16px' },
h('div', { class: 'card col', style: 'gap:8px' },
h('h3', { style: 'margin:0' }, t('arkCoinsTitle')),
@@ -2175,7 +2202,8 @@ export function arkFeature(ctx) {
h('div', { class: 'card col', style: 'gap:8px' },
h('h4', { style: 'margin:0' }, t('arkCoinsExitTitle')),
h('p', { class: 'small muted', style: 'margin:0' },
- t('arkCoinsExitDesc', { fee: fmtSats(exitFee), after: fmtSats(afterFee) }))),
+ t('arkCoinsExitDesc', { fee: fmtSats(exitFee), after: fmtSats(afterFee) })),
+ h('button', { class: 'btn-ghost btn-block', disabled: !!ui.arkBusy || !spend.length, onClick: () => { ui.arkExitConfirm = true; render(); } }, t('arkCoinsExitBtn'))),
h('div', { class: 'card col', style: 'gap:8px' },
h('h4', { style: 'margin:0' }, t('arkCoinsRenewTitle')),
// the round cadence comes from the server (it has been an hour and
diff --git a/src/i18n.js b/src/i18n.js
index 66ed1a8..62f29fe 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -367,7 +367,7 @@ const STR = {
onbBoardBody: 'Move some sats from Savings into Spending — payments there are instant, with no mining fees.',
topUpFromSavings: 'Top up from Savings',
arkRenewedHistory: 'Renewed',
- arkDepthNotice: 'Uncooperative exit costs about {fee} sats.',
+ arkDepthNotice: 'Unilateral exit costs about {fee} sats.',
arkDepthFree: 'free',
arkDepthBtn: 'Manage',
arkExpiredNotice: '{amount} has passed its expiry. The server can still renew it — until then it can\u2019t be spent or exited.',
@@ -387,10 +387,19 @@ const STR = {
arkFeeChain: 'On-chain fee',
arkFeeService: 'Service fee',
arkCoinsExpired: 'expired',
- arkCoinsExitTitle: 'Uncooperative exit',
+ arkCoinsExitTitle: 'Unilateral exit',
+ arkCoinsExitBtn: 'Exit now',
+ arkExitConfirmTitle: 'Start a unilateral exit?',
+ arkExitConfirmBody: 'This brings your Spending coins to Savings on-chain without the server, by publishing each coin’s transaction history. Nothing needs the server’s cooperation — that is the point — but it is slow and it costs mining fees.',
+ arkExitConfirmCoins: 'Coins',
+ arkExitConfirmAmount: 'Amount',
+ arkExitConfirmFee: 'Mining fees (about, paid from Savings)',
+ arkExitConfirmSavings: 'Savings available for fees',
+ arkExitConfirmWait: 'The funds arrive in Savings only once each coin’s timelock has run out — hours to days. An exit can’t be stopped once it has started.',
+ arkExitConfirmGo: 'Start the exit',
arkCoinsExitDesc: 'If the server vanished today, publishing every transaction in each coin’s history would cost about {fee} sats in mining fees. After a renewal it would be one fresh coin, one hop: about {after} sats.',
arkCoinsRenewTitle: 'Renewal',
- arkCoinsRenewDesc: 'Your wallet renews coins automatically once they are close to expiring and renewal is free. Renewing early is an option if you would rather pay a small fee now to keep your exit cost low in the uncooperative case — the coinos server not responding when you want to bring your funds on-chain. A renewal replaces your coins with a single fresh one in the next round ({every}), which flattens their history. The fee depends on how close the coin is to expiry — the closer, the cheaper:',
+ arkCoinsRenewDesc: 'Your wallet renews coins automatically once they are close to expiring and renewal is free. Renewing early is an option if you would rather pay a small fee now to keep your exit cost low should the coinos server ever stop responding when you want to bring your funds on-chain. A renewal replaces your coins with a single fresh one in the next round ({every}), which flattens their history. The fee depends on how close the coin is to expiry — the closer, the cheaper:',
arkCoinsRoundEvery: 'rounds run every {n} minutes',
arkCoinsRoundEveryOne: 'rounds run every minute',
arkCoinsRenewNowFee: 'Renew now for {fee}',