From 3463af1d81c2712d657bbf30ecba98ffe5873729 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Wed, 24 Jun 2026 14:12:46 -0700 Subject: Fee-aware gift minimum; move gift entrypoint to Send page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Minimum: the flat 546-sat floor is replaced by a fee-aware minimum (dust + one claim fee of headroom, floored at 546) via giftMinimum(), so the recipient clears dust comfortably even if fees rise before they claim — e.g. ~1000 sats at 5 sat/vB, ~7300 at 50. The create form shows the live minimum and rejects below it; buildClaimTx keeps the hard 294-sat dust guard as a final backstop (claim fails rather than handing over dust). UI: the gift entrypoint moves from Settings to a "Gift some Bitcoin" link on the Send page, opening the gift view as a send sub-view with a back button. New strings translated across all 19 locales. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/wallet.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/wallet.js') diff --git a/src/wallet.js b/src/wallet.js index ad890d3..39848e8 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -838,10 +838,12 @@ export class Wallet { // direct to their own fresh address. Reserves the coin. Returns { code, ... }. createGift(amountSats, feeRate) { const gift = BigInt(Math.round(amountSats)); - if (gift < 546n) throw new Error('Gift amount is too small.'); const rate = Math.max(1, Math.round(feeRate)); const claimFee = BigInt(Math.ceil((11 + 68 + 31 * 2) * rate)); // claimer's 1-in 2-out tx const DUST = 294n; + // Require dust + one claim fee of headroom so the recipient stays well above + // dust even if fees rise before they claim. + if (gift < BigInt(giftMinimum(rate))) throw new Error('Gift amount is too small.'); const need = gift + claimFee + DUST; const coin = this.utxos .filter((u) => u.confirmed && !this.isReserved(utxoId(u))) @@ -1293,6 +1295,14 @@ export function utxoId(u) { // base64url PSBT: one SIGHASH_SINGLE-signed input + the sender's change output. // previewGift reports the room available to the claimer (before their fee); // buildClaimTx adds the claimer's output and finalizes a broadcastable tx. +// Smallest sensible gift at a given fee rate: dust + one claim fee of headroom +// (floored at 546 sats), so the recipient clears dust even if fees climb. +export function giftMinimum(feeRate) { + const rate = Math.max(1, Math.round(feeRate)); + const claimFee = Math.ceil((11 + 68 + 31 * 2) * rate); + return Math.max(546, 294 + claimFee); +} + export function previewGift(code) { try { const tx = btc.Transaction.fromPSBT(base64urlnopad.decode(code)); -- cgit v1.2.3