From 93631253030839509551d7e1e6a995ed60e31b96 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sun, 28 Jun 2026 19:14:30 -0700 Subject: Swaps: bound reverse amount by the provider's published limits, not a hardcoded 50k MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit doReverse rejected anything under a hardcoded 50,000 sats — which happens to be the regtest provider's minimum, but mainnet boltz's real minimum is 25,000 (and providers/networks differ), so valid 25k-50k mainnet swaps were refused and the max was unchecked client-side. Add SwapManager.reverseLimits() (GET /v2/swap/reverse, cached per API) and use it in the swap UI: show the real min/max range under the amount field and validate against it. Reset the cached limits when the network or provider changes. Verified: regtest min/max 50,000/40,294,967; mainnet 25,000/25,000,000. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/swap.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'src/swap.js') diff --git a/src/swap.js b/src/swap.js index 3d4cf54..de89214 100644 --- a/src/swap.js +++ b/src/swap.js @@ -250,6 +250,22 @@ export class SwapManager { _stop(id) { const t = this.timers.get(id); if (t) clearInterval(t); this.timers.delete(id); } _spendFee() { return Math.max(250, Math.ceil(170 * this.feeRate)); } // ~1-input taproot spend + // The selected provider's reverse-swap limits (sat), fetched + cached per API. + // Lets the UI bound the amount to what the provider actually accepts instead of + // a hardcoded guess (providers + networks differ). Falls back conservatively. + async reverseLimits() { + const api = this.getApi(); + if (this._revLim && this._revLimApi === api) return this._revLim; + let lim = { min: 25000, max: 25000000 }; + try { + const res = await boltzReq(api, '/v2/swap/reverse'); + const b = res && res.BTC && res.BTC.BTC && res.BTC.BTC.limits; + if (b && b.minimal) lim = { min: b.minimal, max: b.maximal }; + } catch {} + this._revLim = lim; this._revLimApi = api; + return lim; + } + // REVERSE: hal receives over Lightning. Returns a record whose `.invoice` // (bolt11) the user has someone pay; hal then claims the lockup on-chain. async startReverse(amountSat) { -- cgit v1.2.3