From 3f225d2481c1a2d1ed33607294f6299c9a39b01b Mon Sep 17 00:00:00 2001 From: amackillop Date: Wed, 29 Jul 2026 08:56:09 -0700 Subject: [PATCH 1/2] Expose feeClaim on MdkNodeOptions The ldk-node bump to f43d869 (0.1.87) added the fee_claim parameter to set_liquidity_source_lsps4 but pinned it to None. This threads it from a new optional MdkNodeOptions field instead, so a consumer can present a signed LSPS4 fee claim at registration. agent-wallet is the first consumer (MDK-1271, 0.5% grant); merchant checkout callers that omit the field are byte-for-byte unchanged on the wire. index.d.ts is regenerated by napi build; besides feeClaim it picks up two doc comments that had drifted since the last regen (startReceiving, getMaxSendable). --- index.d.ts | 9 +++------ src/lib.rs | 5 ++--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/index.d.ts b/index.d.ts index 1b3dbcf..1466554 100644 --- a/index.d.ts +++ b/index.d.ts @@ -39,6 +39,7 @@ export interface MdkNodeOptions { scoringParamOverrides?: ScoringParamOverrides splice?: SpliceConfig maxSendable?: MaxSendableConfig + feeClaim?: string } /** * Configuration for the auto-splice manager. The manager wakes up every @@ -155,6 +156,7 @@ export declare class MdkNode { * * If `splice.enabled` is set on construction (the default), also spawns * the auto-splice background task on the dedicated splice runtime. + * Start the node. Call once before polling for events. */ startReceiving(): void /** @@ -188,10 +190,7 @@ export declare class MdkNode { * Lightning right now, with routing-fee headroom subtracted. * * When `destination` is supplied, the fee budget comes from a real - * `find_route` against that destination (currently zero-amount BOLT11 - * only). BOLT12, LNURL-pay, and HRN destinations parse but fall back - * to the destination-agnostic buffer until invoice/HRN resolution - * moves into the estimator. + * `find_route` against that destination. * * Returns `null` when no usable LSP channel exists. `Some(amountMsat: 0)` * is distinct from `null` — it means a channel exists but the balance @@ -203,8 +202,6 @@ export declare class MdkNode { * Throws `InvalidArg` when the destination cannot be parsed, dictates * its own amount (fixed-amount BOLT11/BOLT12), or carries no Lightning * method. Throws `GenericFailure` when routing fails outright. - * - * Read-only; safe to call whether or not the node has been started. */ getMaxSendable(destination?: string | undefined | null): MaxSendableEstimate | null /** diff --git a/src/lib.rs b/src/lib.rs index 2d9fca1..28a4b35 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -303,6 +303,7 @@ pub struct MdkNodeOptions { pub scoring_param_overrides: Option, pub splice: Option, pub max_sendable: Option, + pub fee_claim: Option, } /// Configuration for the auto-splice manager. The manager wakes up every @@ -513,9 +514,7 @@ impl MdkNode { let logger_arc = Arc::clone(logger_instance()); let logger: Arc = logger_arc; builder.set_custom_logger(logger); - // Third arg is the optional LSPS4 fee_claim (added in ldk-node #37). None - // preserves prior behavior (no fee claim configured). - builder.set_liquidity_source_lsps4(lsp_node_id, lsp_address, None); + builder.set_liquidity_source_lsps4(lsp_node_id, lsp_address, options.fee_claim.clone()); if let Some(scoring) = options.scoring_param_overrides { let mut fee_params = ProbabilisticScoringFeeParameters::default(); From df48c5459869b103a06f02f6e7007fb53380b2b6 Mon Sep 17 00:00:00 2001 From: amackillop Date: Wed, 29 Jul 2026 10:59:12 -0700 Subject: [PATCH 2/2] Appease clippy 1.97 question_mark in next_event CI's clippy moved to 1.97, which now flags the outer match-Some-else-return-None in next_event (pre-existing code, first CI run since the toolchain moved). Mechanical: the suggested `?` transform plus the rustfmt arm collapse it enables; `git diff -w` is three hunks. No behavior change. --- src/lib.rs | 164 +++++++++++++++++++++++++---------------------------- 1 file changed, 78 insertions(+), 86 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 28a4b35..c850f1d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -718,95 +718,87 @@ impl MdkNode { #[napi] pub fn next_event(&self) -> Option { loop { - match self.node().next_event() { - Some(event) => { - let payment_event = match &event { - Event::PaymentClaimable { - payment_hash, - claimable_amount_msat, - .. - } => Some(PaymentEvent { - event_type: PaymentEventType::Claimable, - payment_hash: bytes_to_hex(&payment_hash.0), - amount_msat: Some(*claimable_amount_msat as i64), - reason: None, - payer_note: None, - payment_id: None, - preimage: None, - }), - Event::PaymentReceived { - payment_id, - payment_hash, - amount_msat, - .. - } => { - let payer_note = payment_id.and_then(|pid| { - self - .node() - .payment(&pid) - .and_then(|details| match details.kind { - PaymentKind::Bolt12Offer { payer_note, .. } => { - payer_note.map(|n| n.to_string()) - } - PaymentKind::Bolt12Refund { payer_note, .. } => { - payer_note.map(|n| n.to_string()) - } - _ => None, - }) - }); - Some(PaymentEvent { - event_type: PaymentEventType::Received, - payment_hash: bytes_to_hex(&payment_hash.0), - amount_msat: Some(*amount_msat as i64), - reason: None, - payer_note, - payment_id: None, - preimage: None, + let event = self.node().next_event()?; + let payment_event = match &event { + Event::PaymentClaimable { + payment_hash, + claimable_amount_msat, + .. + } => Some(PaymentEvent { + event_type: PaymentEventType::Claimable, + payment_hash: bytes_to_hex(&payment_hash.0), + amount_msat: Some(*claimable_amount_msat as i64), + reason: None, + payer_note: None, + payment_id: None, + preimage: None, + }), + Event::PaymentReceived { + payment_id, + payment_hash, + amount_msat, + .. + } => { + let payer_note = payment_id.and_then(|pid| { + self + .node() + .payment(&pid) + .and_then(|details| match details.kind { + PaymentKind::Bolt12Offer { payer_note, .. } => payer_note.map(|n| n.to_string()), + PaymentKind::Bolt12Refund { payer_note, .. } => payer_note.map(|n| n.to_string()), + _ => None, }) - } - Event::PaymentFailed { - payment_id: event_pid, - payment_hash, - reason, - .. - } => payment_hash.map(|h| PaymentEvent { - event_type: PaymentEventType::Failed, - payment_hash: bytes_to_hex(&h.0), - amount_msat: None, - reason: reason.map(|r| format!("{r:?}")), - payer_note: None, - payment_id: event_pid.map(|id| bytes_to_hex(&id.0)), - preimage: None, - }), - Event::PaymentSuccessful { - payment_id: event_pid, - payment_hash, - payment_preimage, - .. - } => Some(PaymentEvent { - event_type: PaymentEventType::Sent, - payment_hash: bytes_to_hex(&payment_hash.0), - amount_msat: None, - reason: None, - payer_note: None, - payment_id: event_pid.map(|id| bytes_to_hex(&id.0)), - preimage: payment_preimage.map(|p| bytes_to_hex(&p.0)), - }), - _ => None, - }; - - // If this is a payment event we care about, return it (without ACKing) - if payment_event.is_some() { - return payment_event; - } - - // For non-payment events, ACK and continue to next event - // Potentially problematic if a payout is happening at the same time - // but this is the existing behavior. - let _ = self.node().event_handled(); + }); + Some(PaymentEvent { + event_type: PaymentEventType::Received, + payment_hash: bytes_to_hex(&payment_hash.0), + amount_msat: Some(*amount_msat as i64), + reason: None, + payer_note, + payment_id: None, + preimage: None, + }) } - None => return None, + Event::PaymentFailed { + payment_id: event_pid, + payment_hash, + reason, + .. + } => payment_hash.map(|h| PaymentEvent { + event_type: PaymentEventType::Failed, + payment_hash: bytes_to_hex(&h.0), + amount_msat: None, + reason: reason.map(|r| format!("{r:?}")), + payer_note: None, + payment_id: event_pid.map(|id| bytes_to_hex(&id.0)), + preimage: None, + }), + Event::PaymentSuccessful { + payment_id: event_pid, + payment_hash, + payment_preimage, + .. + } => Some(PaymentEvent { + event_type: PaymentEventType::Sent, + payment_hash: bytes_to_hex(&payment_hash.0), + amount_msat: None, + reason: None, + payer_note: None, + payment_id: event_pid.map(|id| bytes_to_hex(&id.0)), + preimage: payment_preimage.map(|p| bytes_to_hex(&p.0)), + }), + _ => None, + }; + + // If this is a payment event we care about, return it (without ACKing) + if payment_event.is_some() { + return payment_event; } + + // For non-payment events, ACK and continue to next event + // Potentially problematic if a payout is happening at the same time + // but this is the existing behavior. + let _ = self.node().event_handled(); } }