Simple max-sendable estimator#39
Conversation
Ported from mdkd PR #20 (MDK-863). Consumers like the moneydevkit.com payouts page need a "max sendable over Lightning right now" number to drive one-click drains, and raw getBalance() overstates that by the routing-fee buffer because mdk consolidates outbound liquidity onto its LSP channel. The precise version of this would call Router::find_route and invert per-hop fees, but ldk-node keeps the router private. v0 is destination-agnostic: subtract a percentage buffer (default 1%, 10-sat floor) from the sum of next_outbound_htlc_limit_msat across usable LSP channels. v1 will swap the body of compute_estimate for real fee inversion once the ldk-node fork lands; the accessor above this stays put across that change. NoUsableChannel only fires when the filter matches zero channels. A balance fully consumed by the buffer returns Ok(amount_msat: 0) so the UI can render "0 sats" instead of "Unavailable" once a channel exists.
Adds an optional `maxSendable` field on MdkNodeOptions so JS callers can override the routing-fee buffer used by the estimator. Defaults are 100 bps and 10 sats, matching mdkd PR #20.
Exposes the v0 max-sendable estimator to JS. Builds a ChannelSnapshot projection from list_channels, resolves the napi config to native types, and calls max_sendable::compute_estimate. NoUsableChannel maps to `null`; `Some(amountMsat: 0)` is the dust case. That distinction lets the UI tell "no channel yet" apart from "channel exists but the fee buffer ate the balance". No `_while_running` sibling because the accessor neither starts nor stops the node, same as listChannels. Regenerates index.d.ts for the new symbols.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6087bbf664
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| Some( | ||
| acc | ||
| .unwrap_or(0) | ||
| .saturating_add(c.next_outbound_htlc_limit_msat), |
There was a problem hiding this comment.
Enforce per-HTLC minimum in max-sendable estimate
compute_estimate sums only next_outbound_htlc_limit_msat, so it can return a positive amount_msat that is still unsendable when the channel’s next_outbound_htlc_minimum_msat is higher than that amount. In that case callers will surface a “max sendable” value that immediately fails at payment time, which defeats the estimator’s purpose for low-liquidity/high-min-HTLC channels. Include the per-channel minimum constraint (or clamp to zero when estimated amount falls below it) when computing the result.
Useful? React with 👍 / 👎.
Why
Consumers of lightning-js want a "max sendable over Lightning right now" number for one-click drain flows (e.g. payouts in moneydevkit.com). Raw
getBalance()overstates that because mdk consolidates outbound liquidity on its LSP channel and the payer needsbalance - routing_fee(amount).What
Adds
MdkNode#getMaxSendable(), returning eithernull(no usable LSP channel) or{ amountMsat, feeBudgetMsat }. The dust case (channel exists but the fee buffer eats the balance) is{ amountMsat: 0, feeBudgetMsat: <buffer> }, distinct fromnull, so the UI can tell "no channel yet" apart from "fully spent on fees".v0 is destination-agnostic: subtract a configurable percentage buffer (default 1%, 10-sat floor) from the sum of usable LSP channels'
next_outbound_htlc_limit_msat.Config
Optional
maxSendableonMdkNodeOptions:Both fields are optional. Bad input from JS (negative floor, bps above
u16::MAX) clamps silently rather than throwing in the constructor, mirroring howSpliceConfighandles the same thing.