diff options
| author | Adam Soltys <[email protected]> | 2026-06-24 20:28:12 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-06-24 20:28:12 -0700 |
| commit | 08d1753e8433fcae6e9068e87cff93813f0ed76d (patch) | |
| tree | 82e36ab3181c80d48217bb79a03940813e84b47d /src/app.js | |
| parent | aeb6a68e15b04ff35c8154a8f219dcca0a07de0e (diff) | |
| download | coinosv3-08d1753e8433fcae6e9068e87cff93813f0ed76d.tar.gz coinosv3-08d1753e8433fcae6e9068e87cff93813f0ed76d.zip | |
Gift links: stop pre-reserving fee, show it at claim time
Previously createGift reserved a claim-fee headroom on top of the entered
amount based on the fee rate at creation. Because the fee was only actually
charged at claim time, fee-rate drift between the two moments silently ate
into (or padded) the recipient's amount.
Now the entered amount is exactly the recipient's full received total (sum of
inputs less our change); no fee is reserved. The claim screen shows that full
total as the headline and surfaces the live network-fee estimate on the Claim
button, where it's deducted from the recipient's balance at claim time.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Diffstat (limited to 'src/app.js')
| -rw-r--r-- | src/app.js | 13 |
1 files changed, 9 insertions, 4 deletions
@@ -1288,8 +1288,12 @@ function claimScreen() { ); } const pv = previewGift(ui.claimCode); - const estFee = Math.ceil((11 + 68 + 31 * 2) * ((wallet.feeRates && wallet.feeRates.halfHourFee) || 5)); - const approx = pv ? Math.max(0, pv.room - estFee) : 0; + // The headline is the full amount received (inputs minus the sender's change). + // The network fee is determined now, at claim time, and comes out of that + // amount โ we surface the estimate on the Claim button so it isn't a surprise. + const rate = Math.max(1, Math.round((wallet.feeRates && wallet.feeRates.halfHourFee) || 5)); + const estFee = pv ? Math.ceil((11 + 68 * pv.inputs + 31 * 2) * rate) : 0; + const total = pv ? pv.room : 0; return h( 'div', { class: 'col', style: 'gap:16px' }, @@ -1298,14 +1302,15 @@ function claimScreen() { h('div', { class: 'check-badge', style: 'background:var(--accent)' }, '๐'), h('h2', { style: 'margin:0' }, t('giftWelcome')), h('div', { class: 'amt', style: 'font-size:30px' }, - h('span', { class: 'amount-pos' }, '~' + fmtAmount(approx)), ' ', unitTag('unit') + h('span', { class: 'amount-pos' }, fmtAmount(total)), ' ', unitTag('unit') ), h('p', { class: 'muted', style: 'margin:0' }, t('claimBody')) ), ui.claimError && h('div', { class: 'notice err' }, ui.claimError), ui.busy ? h('button', { class: 'btn-primary btn-block', disabled: true }, h('span', { class: 'spinner' })) - : h('button', { class: 'btn-primary btn-block', onClick: doClaim }, t('claimBtn')) + : h('button', { class: 'btn-primary btn-block', onClick: doClaim }, + t('claimBtn') + ' ยท ' + t('claimFeeNote', { n: fmtAmount(estFee) + ' ' + unitLabel() })) ); } |
