From 07ac3d010a74f1aa84c463817c0a7ed7d510dbb6 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Fri, 14 Aug 2026 21:37:23 -0700 Subject: Zaps too gather what they need: several coins each lock an HTLC under one payment hash and the server sums them — the fragmented-balance error is gone from Lightning as well MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_014tE4U5nEjGTQy8PwYqamfv --- src/features/ark.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'src/features') diff --git a/src/features/ark.js b/src/features/ark.js index 9e0902a..aa9623b 100644 --- a/src/features/ark.js +++ b/src/features/ark.js @@ -629,13 +629,23 @@ export function arkFeature(ctx) { const routing = await mgr.lnRouteFee(invoice, p.amountSat); if (!ui.arkLnPay || ui.arkLnPay !== p || p.invoice !== invoice) return; p.routingFeeSat = routing; - const candidates = mgr.vtxos().filter((v) => v.state === 'spendable') - .sort((a, b) => a.amountSat - b.amountSat); + const spendables = mgr.vtxos().filter((v) => v.state === 'spendable'); let fee = null; - for (const v of candidates) { + for (const v of [...spendables].sort((a, b) => a.amountSat - b.amountSat)) { const f = lnSendFee(p.amountSat, mgr.info.lnSendFees, [v], tip) + routing; if (v.amountSat >= p.amountSat + f) { fee = f; break; } } + if (fee == null) { + // no single coin covers it — the pay path gathers several, so quote for + // the same largest-first set it will pick + const picked = []; + let sum = 0; + for (const v of [...spendables].sort((a, b) => b.amountSat - a.amountSat)) { + picked.push(v); sum += v.amountSat; + const f = lnSendFee(p.amountSat, mgr.info.lnSendFees, picked, tip) + routing; + if (sum >= p.amountSat + f) { fee = f; break; } + } + } if (fee == null) { const pendingCover = mgr.vtxos().some((v) => v.state === 'pending' && v.amountSat >= p.amountSat); if (pendingCover) { -- cgit v1.2.3