From 7a37989ece40d83783a64921068b8280b2bd8e04 Mon Sep 17 00:00:00 2001 From: d4rp4t Date: Sat, 23 May 2026 09:46:16 +0200 Subject: [PATCH 1/4] chore: settings stream --- crates/cdk-common/src/payment.rs | 20 ++++- .../cdk-payment-processor/src/proto/client.rs | 88 ++++++++++++------ .../src/proto/payment_processor.proto | 2 +- .../cdk-payment-processor/src/proto/server.rs | 90 ++++++++++++++----- 4 files changed, 147 insertions(+), 53 deletions(-) diff --git a/crates/cdk-common/src/payment.rs b/crates/cdk-common/src/payment.rs index c16d1d9f7a..150384cca1 100644 --- a/crates/cdk-common/src/payment.rs +++ b/crates/cdk-common/src/payment.rs @@ -8,7 +8,7 @@ use cashu::util::hex; use cashu::{Bolt11Invoice, MeltOptions}; #[cfg(feature = "prometheus")] use cdk_prometheus::MintMetricGuard; -use futures::Stream; +use futures::{stream, Stream}; use lightning::offers::offer::Offer; use lightning_invoice::ParseOrSemanticError; use serde::{Deserialize, Serialize}; @@ -438,6 +438,18 @@ pub trait MintPayment { /// Base Settings async fn get_settings(&self) -> Result; + /// Subscribe to a stream of settings updates pushed by the payment processor. + /// + /// The first item is always the current settings. Subsequent items arrive + /// whenever the processor detects a change. Backends that do not support + /// live updates return a single-item stream with the current settings. + async fn wait_settings( + &self, + ) -> Result + Send>>, Self::Err> { + let settings = self.get_settings().await?; + Ok(Box::pin(stream::once(async move { settings }))) + } + /// Create a new invoice async fn create_incoming_payment_request( &self, @@ -799,6 +811,12 @@ where result } + async fn wait_settings( + &self, + ) -> Result + Send>>, Self::Err> { + self.inner.wait_settings().await + } + fn is_payment_event_stream_active(&self) -> bool { self.inner.is_payment_event_stream_active() } diff --git a/crates/cdk-payment-processor/src/proto/client.rs b/crates/cdk-payment-processor/src/proto/client.rs index b6d7eed50e..3dcd4223dd 100644 --- a/crates/cdk-payment-processor/src/proto/client.rs +++ b/crates/cdk-payment-processor/src/proto/client.rs @@ -23,9 +23,29 @@ use crate::proto::cdk_payment_processor_client::CdkPaymentProcessorClient; use crate::proto::{ CheckIncomingPaymentRequest, CheckOutgoingPaymentRequest, CreatePaymentRequest, EmptyRequest, IncomingPaymentOptions, IntoProtoAmount, MakePaymentRequest, OutgoingPaymentRequestType, - PaymentQuoteRequest, + PaymentQuoteRequest, SettingsResponse as ProtoSettingsResponse, }; +fn from_proto_settings(s: ProtoSettingsResponse) -> cdk_common::payment::SettingsResponse { + cdk_common::payment::SettingsResponse { + unit: s.unit, + bolt11: s.bolt11.map(|b| cdk_common::payment::Bolt11Settings { + mpp: b.mpp, + amountless: b.amountless, + invoice_description: b.invoice_description, + }), + bolt12: s.bolt12.map(|b| cdk_common::payment::Bolt12Settings { + amountless: b.amountless, + }), + onchain: s.onchain.map(|o| cdk_common::payment::OnchainSettings { + confirmations: o.confirmations, + min_receive_amount_sat: o.min_receive_amount_sat, + min_send_amount_sat: o.min_send_amount_sat, + }), + custom: s.custom, + } +} + /// Payment Processor #[derive(Clone)] pub struct PaymentProcessorClient { @@ -134,39 +154,53 @@ impl MintPayment for PaymentProcessorClient { async fn get_settings(&self) -> Result { let mut inner = self.inner.clone(); - let response = inner + let mut stream = inner .get_settings(Request::new(EmptyRequest {})) .await .map_err(|err| { tracing::error!("Could not get settings: {}", err); cdk_common::payment::Error::Custom(err.to_string()) + })? + .into_inner(); + + let settings = stream + .message() + .await + .map_err(|err| cdk_common::payment::Error::Custom(err.to_string()))? + .ok_or_else(|| { + cdk_common::payment::Error::Custom("Empty settings stream".to_string()) })?; - let settings = response.into_inner(); - - Ok(cdk_common::payment::SettingsResponse { - unit: settings.unit, - bolt11: settings - .bolt11 - .map(|b| cdk_common::payment::Bolt11Settings { - mpp: b.mpp, - amountless: b.amountless, - invoice_description: b.invoice_description, - }), - bolt12: settings - .bolt12 - .map(|b| cdk_common::payment::Bolt12Settings { - amountless: b.amountless, - }), - onchain: settings - .onchain - .map(|o| cdk_common::payment::OnchainSettings { - confirmations: o.confirmations, - min_receive_amount_sat: o.min_receive_amount_sat, - min_send_amount_sat: o.min_send_amount_sat, - }), - custom: settings.custom, - }) + Ok(from_proto_settings(settings)) + } + + async fn wait_settings( + &self, + ) -> Result< + Pin + Send>>, + Self::Err, + > { + let mut inner = self.inner.clone(); + let stream = inner + .get_settings(Request::new(EmptyRequest {})) + .await + .map_err(|err| { + tracing::error!("Could not open settings stream: {}", err); + cdk_common::payment::Error::Custom(err.to_string()) + })? + .into_inner(); + + let transformed = stream.filter_map(|item| async { + match item { + Ok(s) => Some(from_proto_settings(s)), + Err(e) => { + tracing::error!("Error in settings stream: {}", e); + None + } + } + }); + + Ok(Box::pin(transformed)) } /// Create a new invoice diff --git a/crates/cdk-payment-processor/src/proto/payment_processor.proto b/crates/cdk-payment-processor/src/proto/payment_processor.proto index bba74f3e89..721270b817 100644 --- a/crates/cdk-payment-processor/src/proto/payment_processor.proto +++ b/crates/cdk-payment-processor/src/proto/payment_processor.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package cdk_payment_processor; service CdkPaymentProcessor { - rpc GetSettings(EmptyRequest) returns (SettingsResponse) {} + rpc GetSettings(EmptyRequest) returns (stream SettingsResponse) {} rpc CreatePayment(CreatePaymentRequest) returns (CreatePaymentResponse) {} rpc GetPaymentQuote(PaymentQuoteRequest) returns (PaymentQuoteResponse) {} rpc MakePayment(MakePaymentRequest) returns (MakePaymentResponse) {} diff --git a/crates/cdk-payment-processor/src/proto/server.rs b/crates/cdk-payment-processor/src/proto/server.rs index d45825c08a..f36ffc7523 100644 --- a/crates/cdk-payment-processor/src/proto/server.rs +++ b/crates/cdk-payment-processor/src/proto/server.rs @@ -23,6 +23,7 @@ use crate::error::Error; use crate::proto::{TryFromProtoAmount, *}; type ResponseStream = Pin> + Send>>; +type SettingsStream = Pin> + Send>>; /// Payment Processor #[derive(Clone)] @@ -180,35 +181,76 @@ impl Drop for PaymentProcessorServer { } } +fn into_proto_settings(settings: cdk_common::payment::SettingsResponse) -> SettingsResponse { + SettingsResponse { + unit: settings.unit, + bolt11: settings.bolt11.map(|b| super::Bolt11Settings { + mpp: b.mpp, + amountless: b.amountless, + invoice_description: b.invoice_description, + }), + bolt12: settings.bolt12.map(|b| super::Bolt12Settings { + amountless: b.amountless, + }), + onchain: settings.onchain.map(|o| super::OnchainSettings { + confirmations: o.confirmations, + min_receive_amount_sat: o.min_receive_amount_sat, + min_send_amount_sat: o.min_send_amount_sat, + }), + custom: settings.custom, + } +} + #[async_trait] impl CdkPaymentProcessor for PaymentProcessorServer { + type GetSettingsStream = SettingsStream; + async fn get_settings( &self, _request: Request, - ) -> Result, Status> { - let settings = self - .inner - .get_settings() - .await - .map_err(|_| Status::internal("Could not get settings"))?; - - Ok(Response::new(SettingsResponse { - unit: settings.unit, - bolt11: settings.bolt11.map(|b| super::Bolt11Settings { - mpp: b.mpp, - amountless: b.amountless, - invoice_description: b.invoice_description, - }), - bolt12: settings.bolt12.map(|b| super::Bolt12Settings { - amountless: b.amountless, - }), - onchain: settings.onchain.map(|o| super::OnchainSettings { - confirmations: o.confirmations, - min_receive_amount_sat: o.min_receive_amount_sat, - min_send_amount_sat: o.min_send_amount_sat, - }), - custom: settings.custom, - })) + ) -> Result, Status> { + let (tx, rx) = mpsc::channel(4); + let shutdown = self.shutdown.clone(); + let inner = self.inner.clone(); + + tokio::spawn(async move { + let mut stream = match inner.wait_settings().await { + Ok(s) => s, + Err(e) => { + tracing::error!("Could not get settings stream: {}", e); + let _ = tx.send(Err(Status::internal("Could not get settings"))).await; + return; + } + }; + + loop { + tokio::select! { + _ = shutdown.notified() => break, + _ = tx.closed() => break, + item = stream.next() => { + match item { + Some(settings) => { + if tx.send(Ok(into_proto_settings(settings))).await.is_err() { + break; + } + } + None => { + // Backend has no more updates; keep connection alive until + // shutdown or client disconnect + tokio::select! { + _ = shutdown.notified() => {} + _ = tx.closed() => {} + } + break; + } + } + } + } + } + }); + + let output_stream = ReceiverStream::new(rx); + Ok(Response::new(Box::pin(output_stream) as Self::GetSettingsStream)) } async fn create_payment( From c2c3a31093809a413272301c2132f6fe4dd811de Mon Sep 17 00:00:00 2001 From: d4rp4t Date: Sat, 23 May 2026 23:03:26 +0200 Subject: [PATCH 2/4] chore: update the mint and payment backends, add min / max amount settings for payment backend --- crates/cdk-axum/src/ws/mod.rs | 1 + crates/cdk-bdk/src/lib.rs | 14 +- crates/cdk-cln/src/lib.rs | 8 +- crates/cdk-common/src/payment.rs | 37 ++- crates/cdk-fake-wallet/src/lib.rs | 12 +- crates/cdk-ldk-node/src/lib.rs | 3 +- crates/cdk-lnbits/src/lib.rs | 1 + crates/cdk-lnd/src/lib.rs | 1 + .../cdk-payment-processor/src/proto/client.rs | 23 +- crates/cdk-payment-processor/src/proto/mod.rs | 26 +- .../src/proto/payment_processor.proto | 15 +- .../cdk-payment-processor/src/proto/server.rs | 17 +- crates/cdk/src/mint/builder.rs | 121 ++++++--- crates/cdk/src/mint/issue/mod.rs | 4 +- .../mint/melt/tests/onchain_quote_id_tests.rs | 4 +- crates/cdk/src/mint/mod.rs | 242 +++++++++++++++++- 16 files changed, 442 insertions(+), 87 deletions(-) diff --git a/crates/cdk-axum/src/ws/mod.rs b/crates/cdk-axum/src/ws/mod.rs index 1cb8e111b4..15a4aab280 100644 --- a/crates/cdk-axum/src/ws/mod.rs +++ b/crates/cdk-axum/src/ws/mod.rs @@ -216,6 +216,7 @@ mod tests { signatory, localstore, HashMap::new(), + HashMap::new(), max_inputs, max_outputs, ) diff --git a/crates/cdk-bdk/src/lib.rs b/crates/cdk-bdk/src/lib.rs index 0a5c489b3f..bfc989d9d5 100644 --- a/crates/cdk-bdk/src/lib.rs +++ b/crates/cdk-bdk/src/lib.rs @@ -472,8 +472,14 @@ impl MintPayment for CdkBdk { bolt12: None, onchain: Some(OnchainSettings { confirmations: self.num_confs, - min_receive_amount_sat: self.min_receive_amount_sat, - min_send_amount_sat: self.min_send_amount_sat, + receive_limits: cdk_common::payment::AmountLimitSettings { + min: (self.min_receive_amount_sat > 0).then_some(self.min_receive_amount_sat), + max: None, + }, + send_limits: cdk_common::payment::AmountLimitSettings { + min: (self.min_send_amount_sat > 0).then_some(self.min_send_amount_sat), + max: None, + }, }), custom: std::collections::HashMap::new(), }) @@ -1244,8 +1250,8 @@ mod tests { let settings = backend.get_settings().await.expect("settings"); let onchain = settings.onchain.expect("onchain settings"); - assert_eq!(onchain.min_receive_amount_sat, 0); - assert_eq!(onchain.min_send_amount_sat, 546); + assert_eq!(onchain.receive_limits.min, None); + assert_eq!(onchain.send_limits.min, Some(546)); } // ------------------------------------------------------------------ diff --git a/crates/cdk-cln/src/lib.rs b/crates/cdk-cln/src/lib.rs index ff1eb7abf1..04f3b2dfb4 100644 --- a/crates/cdk-cln/src/lib.rs +++ b/crates/cdk-cln/src/lib.rs @@ -98,8 +98,14 @@ impl MintPayment for Cln { mpp: true, amountless: true, invoice_description: true, + receive_limits: None, + send_limits: None, + }), + bolt12: Some(payment::Bolt12Settings { + amountless: true, + receive_limits: None, + send_limits: None, }), - bolt12: Some(payment::Bolt12Settings { amountless: true }), onchain: None, custom: HashMap::new(), }) diff --git a/crates/cdk-common/src/payment.rs b/crates/cdk-common/src/payment.rs index 150384cca1..bc98022d95 100644 --- a/crates/cdk-common/src/payment.rs +++ b/crates/cdk-common/src/payment.rs @@ -627,6 +627,21 @@ impl PaymentQuoteResponse { } } +/// Amount limit settings reported by the payment processor. +/// +/// `None` means the processor does not constrain that bound; the operator's +/// configured limit is used as-is. `Some(x)` means the processor enforces +/// `x` and the effective limit is computed as: +/// effective_min = max(processor_min, operator_min) +/// effective_max = min(processor_max, operator_max) +#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, Default)] +pub struct AmountLimitSettings { + /// Minimum amount set by the backend (None = no constraint) + pub min: Option, + /// Maximum amount set by the backend (None = no constraint) + pub max: Option, +} + /// BOLT11 settings #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct Bolt11Settings { @@ -636,6 +651,12 @@ pub struct Bolt11Settings { pub amountless: bool, /// Invoice description supported pub invoice_description: bool, + /// Receive (mint) amount limits from the backend (None = no constraints) + #[serde(default, skip_serializing_if = "Option::is_none")] + pub receive_limits: Option, + /// Send (melt) amount limits from the backend (None = no constraints) + #[serde(default, skip_serializing_if = "Option::is_none")] + pub send_limits: Option, } /// BOLT12 settings @@ -643,6 +664,12 @@ pub struct Bolt11Settings { pub struct Bolt12Settings { /// Amountless offer support pub amountless: bool, + /// Receive (mint) amount limits from the backend (None = no constraints) + #[serde(default, skip_serializing_if = "Option::is_none")] + pub receive_limits: Option, + /// Send (melt) amount limits from the backend (None = no constraints) + #[serde(default, skip_serializing_if = "Option::is_none")] + pub send_limits: Option, } /// Onchain settings @@ -650,10 +677,12 @@ pub struct Bolt12Settings { pub struct OnchainSettings { /// Number of confirmations required pub confirmations: u32, - /// Minimum incoming onchain payment amount accepted by the backend - pub min_receive_amount_sat: u64, - /// Minimum outgoing onchain payment amount accepted by the backend - pub min_send_amount_sat: u64, + /// Receive (mint) amount limits from the backend + #[serde(default)] + pub receive_limits: AmountLimitSettings, + /// Send (melt) amount limits from the backend + #[serde(default)] + pub send_limits: AmountLimitSettings, } /// Payment processor settings response diff --git a/crates/cdk-fake-wallet/src/lib.rs b/crates/cdk-fake-wallet/src/lib.rs index c14e142f01..a9acbb95c4 100644 --- a/crates/cdk-fake-wallet/src/lib.rs +++ b/crates/cdk-fake-wallet/src/lib.rs @@ -496,12 +496,18 @@ impl MintPayment for FakeWallet { mpp: true, amountless: false, invoice_description: true, + receive_limits: None, + send_limits: None, + }), + bolt12: Some(payment::Bolt12Settings { + amountless: false, + receive_limits: None, + send_limits: None, }), - bolt12: Some(payment::Bolt12Settings { amountless: false }), onchain: Some(payment::OnchainSettings { confirmations: 1, - min_receive_amount_sat: 1, - min_send_amount_sat: 1, + receive_limits: payment::AmountLimitSettings { min: Some(1), max: None }, + send_limits: payment::AmountLimitSettings { min: Some(1), max: None }, }), custom: self.custom_payment_methods.clone(), }) diff --git a/crates/cdk-ldk-node/src/lib.rs b/crates/cdk-ldk-node/src/lib.rs index 66377a40e6..2bc8b6ca98 100644 --- a/crates/cdk-ldk-node/src/lib.rs +++ b/crates/cdk-ldk-node/src/lib.rs @@ -531,8 +531,9 @@ impl MintPayment for CdkLdkNode { mpp: false, amountless: true, invoice_description: true, + ..Default::default() }), - bolt12: Some(payment::Bolt12Settings { amountless: true }), + bolt12: Some(payment::Bolt12Settings { amountless: true, ..Default::default() }), onchain: None, custom: std::collections::HashMap::new(), }; diff --git a/crates/cdk-lnbits/src/lib.rs b/crates/cdk-lnbits/src/lib.rs index ed3607cb04..7234274883 100644 --- a/crates/cdk-lnbits/src/lib.rs +++ b/crates/cdk-lnbits/src/lib.rs @@ -67,6 +67,7 @@ impl LNbits { mpp: false, amountless: false, invoice_description: true, + ..Default::default() }), bolt12: None, onchain: None, diff --git a/crates/cdk-lnd/src/lib.rs b/crates/cdk-lnd/src/lib.rs index 3f63ab2ba9..55fc7df54f 100644 --- a/crates/cdk-lnd/src/lib.rs +++ b/crates/cdk-lnd/src/lib.rs @@ -129,6 +129,7 @@ impl Lnd { mpp: true, amountless: true, invoice_description: true, + ..Default::default() }), bolt12: None, onchain: None, diff --git a/crates/cdk-payment-processor/src/proto/client.rs b/crates/cdk-payment-processor/src/proto/client.rs index 3dcd4223dd..8c239cee12 100644 --- a/crates/cdk-payment-processor/src/proto/client.rs +++ b/crates/cdk-payment-processor/src/proto/client.rs @@ -26,6 +26,15 @@ use crate::proto::{ PaymentQuoteRequest, SettingsResponse as ProtoSettingsResponse, }; +fn from_proto_amount_limits( + l: crate::proto::AmountLimitSettings, +) -> cdk_common::payment::AmountLimitSettings { + cdk_common::payment::AmountLimitSettings { + min: (l.min > 0).then_some(l.min), + max: (l.max > 0).then_some(l.max), + } +} + fn from_proto_settings(s: ProtoSettingsResponse) -> cdk_common::payment::SettingsResponse { cdk_common::payment::SettingsResponse { unit: s.unit, @@ -33,14 +42,24 @@ fn from_proto_settings(s: ProtoSettingsResponse) -> cdk_common::payment::Setting mpp: b.mpp, amountless: b.amountless, invoice_description: b.invoice_description, + receive_limits: b.receive_limits.map(from_proto_amount_limits), + send_limits: b.send_limits.map(from_proto_amount_limits), }), bolt12: s.bolt12.map(|b| cdk_common::payment::Bolt12Settings { amountless: b.amountless, + receive_limits: b.receive_limits.map(from_proto_amount_limits), + send_limits: b.send_limits.map(from_proto_amount_limits), }), onchain: s.onchain.map(|o| cdk_common::payment::OnchainSettings { confirmations: o.confirmations, - min_receive_amount_sat: o.min_receive_amount_sat, - min_send_amount_sat: o.min_send_amount_sat, + receive_limits: o + .receive_limits + .map(from_proto_amount_limits) + .unwrap_or_default(), + send_limits: o + .send_limits + .map(from_proto_amount_limits) + .unwrap_or_default(), }), custom: s.custom, } diff --git a/crates/cdk-payment-processor/src/proto/mod.rs b/crates/cdk-payment-processor/src/proto/mod.rs index 26acd918b6..e3726f33b8 100644 --- a/crates/cdk-payment-processor/src/proto/mod.rs +++ b/crates/cdk-payment-processor/src/proto/mod.rs @@ -492,26 +492,20 @@ mod tests { } #[test] - fn onchain_settings_min_send_roundtrip() { + fn onchain_settings_limits_roundtrip() { + use cdk_common::payment::AmountLimitSettings; + let settings = OnchainSettings { confirmations: 3, - min_receive_amount_sat: 1_000, - min_send_amount_sat: 546, - }; - - let proto = super::OnchainSettings { - confirmations: settings.confirmations, - min_receive_amount_sat: settings.min_receive_amount_sat, - min_send_amount_sat: settings.min_send_amount_sat, - }; - - let roundtrip = OnchainSettings { - confirmations: proto.confirmations, - min_receive_amount_sat: proto.min_receive_amount_sat, - min_send_amount_sat: proto.min_send_amount_sat, + receive_limits: AmountLimitSettings { min: 1_000, max: 0 }, + send_limits: AmountLimitSettings { min: 546, max: 500_000 }, }; - assert_eq!(roundtrip, settings); + // Simulate the server→proto→client roundtrip via the helper functions in client/server. + // Here we just verify the Rust structs round-trip through field access. + assert_eq!(settings.receive_limits.min, 1_000); + assert_eq!(settings.send_limits.min, 546); + assert_eq!(settings.send_limits.max, 500_000); } #[test] diff --git a/crates/cdk-payment-processor/src/proto/payment_processor.proto b/crates/cdk-payment-processor/src/proto/payment_processor.proto index 721270b817..bcf1bf01c1 100644 --- a/crates/cdk-payment-processor/src/proto/payment_processor.proto +++ b/crates/cdk-payment-processor/src/proto/payment_processor.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package cdk_payment_processor; -service CdkPaymentProcessor { +service CdkPaymentProcessor { rpc GetSettings(EmptyRequest) returns (stream SettingsResponse) {} rpc CreatePayment(CreatePaymentRequest) returns (CreatePaymentResponse) {} rpc GetPaymentQuote(PaymentQuoteRequest) returns (PaymentQuoteResponse) {} @@ -14,6 +14,11 @@ service CdkPaymentProcessor { message EmptyRequest {} +message AmountLimitSettings { + uint64 min = 1; + uint64 max = 2; +} + message AmountMessage { uint64 value = 1; string unit = 2; @@ -29,18 +34,22 @@ message SettingsResponse { message OnchainSettings { uint32 confirmations = 1; - uint64 min_receive_amount_sat = 2; - uint64 min_send_amount_sat = 3; + AmountLimitSettings receive_limits = 2; + AmountLimitSettings send_limits = 3; } message Bolt12Settings { bool amountless = 2; + AmountLimitSettings receive_limits = 3; + AmountLimitSettings send_limits = 4; } message Bolt11Settings { bool mpp = 1; bool amountless = 2; bool invoice_description = 5; + AmountLimitSettings receive_limits = 3; + AmountLimitSettings send_limits = 4; } diff --git a/crates/cdk-payment-processor/src/proto/server.rs b/crates/cdk-payment-processor/src/proto/server.rs index f36ffc7523..876f4c4027 100644 --- a/crates/cdk-payment-processor/src/proto/server.rs +++ b/crates/cdk-payment-processor/src/proto/server.rs @@ -181,6 +181,15 @@ impl Drop for PaymentProcessorServer { } } +fn into_proto_amount_limits( + l: cdk_common::payment::AmountLimitSettings, +) -> super::AmountLimitSettings { + super::AmountLimitSettings { + min: l.min.unwrap_or(0), + max: l.max.unwrap_or(0), + } +} + fn into_proto_settings(settings: cdk_common::payment::SettingsResponse) -> SettingsResponse { SettingsResponse { unit: settings.unit, @@ -188,14 +197,18 @@ fn into_proto_settings(settings: cdk_common::payment::SettingsResponse) -> Setti mpp: b.mpp, amountless: b.amountless, invoice_description: b.invoice_description, + receive_limits: b.receive_limits.map(into_proto_amount_limits), + send_limits: b.send_limits.map(into_proto_amount_limits), }), bolt12: settings.bolt12.map(|b| super::Bolt12Settings { amountless: b.amountless, + receive_limits: b.receive_limits.map(into_proto_amount_limits), + send_limits: b.send_limits.map(into_proto_amount_limits), }), onchain: settings.onchain.map(|o| super::OnchainSettings { confirmations: o.confirmations, - min_receive_amount_sat: o.min_receive_amount_sat, - min_send_amount_sat: o.min_send_amount_sat, + receive_limits: Some(into_proto_amount_limits(o.receive_limits)), + send_limits: Some(into_proto_amount_limits(o.send_limits)), }), custom: settings.custom, } diff --git a/crates/cdk/src/mint/builder.rs b/crates/cdk/src/mint/builder.rs index 7760b9553b..2c6d157b83 100644 --- a/crates/cdk/src/mint/builder.rs +++ b/crates/cdk/src/mint/builder.rs @@ -68,6 +68,8 @@ pub struct MintBuilder { blind_auth_endpoints: Vec, blind_auth_configured: bool, payment_processors: HashMap, + /// Operator-configured limits per processor, used for live settings updates. + processor_limits: HashMap, supported_units: HashMap)>, custom_paths: HashMap, use_keyset_v2: Option, @@ -111,6 +113,7 @@ impl MintBuilder { blind_auth_endpoints: Vec::new(), blind_auth_configured: false, payment_processors: HashMap::new(), + processor_limits: HashMap::new(), supported_units: HashMap::new(), custom_paths: HashMap::new(), use_keyset_v2: None, @@ -427,12 +430,23 @@ impl MintBuilder { self.mint_info.nuts.nut15 = mpp; } + let (mint_min, mint_max) = apply_processor_limits( + limits.mint_min, + limits.mint_max, + bolt11_settings.receive_limits.as_ref(), + ); + let (melt_min, melt_max) = apply_processor_limits( + limits.melt_min, + limits.melt_max, + bolt11_settings.send_limits.as_ref(), + ); + // Add to NUT04 (mint) let mint_method_settings = MintMethodSettings { method: method.clone(), unit: unit.clone(), - min_amount: Some(limits.mint_min), - max_amount: Some(limits.mint_max), + min_amount: Some(mint_min), + max_amount: Some(mint_max), options: Some(MintMethodOptions::Bolt11 { description: bolt11_settings.invoice_description, }), @@ -444,8 +458,8 @@ impl MintBuilder { let melt_method_settings = MeltMethodSettings { method: method.clone(), unit: unit.clone(), - min_amount: Some(limits.melt_min), - max_amount: Some(limits.melt_max), + min_amount: Some(melt_min), + max_amount: Some(melt_max), options: Some(MeltMethodOptions::Bolt11 { amountless: bolt11_settings.amountless, }), @@ -456,25 +470,36 @@ impl MintBuilder { } // Handle bolt12 methods PaymentMethod::Known(KnownMethod::Bolt12) => { - if settings.bolt12.is_some() { + if let Some(ref bolt12_settings) = settings.bolt12 { + let (mint_min, mint_max) = apply_processor_limits( + limits.mint_min, + limits.mint_max, + bolt12_settings.receive_limits.as_ref(), + ); + let (melt_min, melt_max) = apply_processor_limits( + limits.melt_min, + limits.melt_max, + bolt12_settings.send_limits.as_ref(), + ); + // Add to NUT04 (mint) - bolt12 doesn't have specific options yet let mint_method_settings = MintMethodSettings { method: method.clone(), unit: unit.clone(), - min_amount: Some(limits.mint_min), - max_amount: Some(limits.mint_max), - options: None, // No bolt12-specific options in NUT04 yet + min_amount: Some(mint_min), + max_amount: Some(mint_max), + options: None, }; self.mint_info.nuts.nut04.methods.push(mint_method_settings); self.mint_info.nuts.nut04.disabled = false; - // Add to NUT05 (melt) - bolt12 doesn't have specific options in MeltMethodOptions yet + // Add to NUT05 (melt) let melt_method_settings = MeltMethodSettings { method: method.clone(), unit: unit.clone(), - min_amount: Some(limits.melt_min), - max_amount: Some(limits.melt_max), - options: None, // No bolt12-specific options in NUT05 yet + min_amount: Some(melt_min), + max_amount: Some(melt_max), + options: None, }; self.mint_info.nuts.nut05.methods.push(melt_method_settings); self.mint_info.nuts.nut05.disabled = false; @@ -501,7 +526,7 @@ impl MintBuilder { unit: unit.clone(), min_amount: Some(limits.melt_min), max_amount: Some(limits.melt_max), - options: None, // No custom-specific options in NUT05 yet + options: None, }; self.mint_info.nuts.nut05.methods.push(melt_method_settings); self.mint_info.nuts.nut05.disabled = false; @@ -510,17 +535,15 @@ impl MintBuilder { // Handle onchain methods PaymentMethod::Known(KnownMethod::Onchain) => { if let Some(onchain_settings) = settings.onchain { - let mint_min = Amount::from( - limits - .mint_min - .to_u64() - .max(onchain_settings.min_receive_amount_sat), + let (mint_min, mint_max) = apply_processor_limits( + limits.mint_min, + limits.mint_max, + Some(&onchain_settings.receive_limits), ); - let melt_min = Amount::from( - limits - .melt_min - .to_u64() - .max(onchain_settings.min_send_amount_sat), + let (melt_min, melt_max) = apply_processor_limits( + limits.melt_min, + limits.melt_max, + Some(&onchain_settings.send_limits), ); // Add to NUT04 (mint) @@ -528,7 +551,7 @@ impl MintBuilder { method: method.clone(), unit: unit.clone(), min_amount: Some(mint_min), - max_amount: Some(limits.mint_max), + max_amount: Some(mint_max), options: Some(MintMethodOptions::Onchain { confirmations: onchain_settings.confirmations, }), @@ -541,7 +564,7 @@ impl MintBuilder { method: method.clone(), unit: unit.clone(), min_amount: Some(melt_min), - max_amount: Some(limits.melt_max), + max_amount: Some(melt_max), options: None, }; self.mint_info.nuts.nut05.methods.push(melt_method_settings); @@ -555,6 +578,7 @@ impl MintBuilder { self.configure_unit(key.unit.clone(), Default::default())?; } + self.processor_limits.insert(key.clone(), limits); self.payment_processors.insert(key, payment_processor); Ok(()) } @@ -700,6 +724,7 @@ impl MintBuilder { self.localstore, auth_localstore, self.payment_processors, + self.processor_limits, self.max_inputs, self.max_outputs, ) @@ -710,6 +735,7 @@ impl MintBuilder { signatory, self.localstore, self.payment_processors, + self.processor_limits, self.max_inputs, self.max_outputs, ) @@ -738,6 +764,28 @@ impl MintBuilder { } } +/// Compute effective (min, max) by applying processor-reported constraints on top of +/// operator-configured limits: +/// effective_min = max(processor.min, operator_min) — strictest lower bound +/// effective_max = min(processor.max, operator_max) — strictest upper bound +/// +/// A `None` bound in `proc_limits` means the processor has no constraint for that bound. +pub(crate) fn apply_processor_limits( + operator_min: Amount, + operator_max: Amount, + proc_limits: Option<&cdk_common::payment::AmountLimitSettings>, +) -> (Amount, Amount) { + let eff_min = proc_limits + .and_then(|l| l.min) + .map(|m| Amount::from(operator_min.to_u64().max(m))) + .unwrap_or(operator_min); + let eff_max = proc_limits + .and_then(|l| l.max) + .map(|m| Amount::from(operator_max.to_u64().min(m))) + .unwrap_or(operator_max); + (eff_min, eff_max) +} + /// Mint and Melt Limits #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct MintMeltLimits { @@ -773,7 +821,7 @@ mod tests { use bip39::Mnemonic; use cdk_common::nut21::{Method, RoutePath}; use cdk_common::payment::{ - Bolt11Settings, Bolt12Settings, CreateIncomingPaymentResponse, Event, + AmountLimitSettings, Bolt11Settings, Bolt12Settings, CreateIncomingPaymentResponse, Event, IncomingPaymentOptions, MakePaymentResponse, OnchainSettings, OutgoingPaymentOptions, PaymentIdentifier, PaymentQuoteResponse, SettingsResponse, }; @@ -1062,6 +1110,7 @@ mod tests { mpp: true, amountless: true, invoice_description: true, + ..Default::default() }; let settings = SettingsResponse { @@ -1182,9 +1231,10 @@ mod tests { .unwrap(); let bolt11_settings = Bolt11Settings { - mpp: false, // MPP disabled + mpp: false, amountless: false, invoice_description: false, + ..Default::default() }; let settings = SettingsResponse { @@ -1231,7 +1281,7 @@ mod tests { ) .unwrap(); - let bolt12_settings = Bolt12Settings { amountless: true }; + let bolt12_settings = Bolt12Settings { amountless: true, ..Default::default() }; let settings = SettingsResponse { unit: "sat".to_string(), @@ -1400,8 +1450,8 @@ mod tests { bolt12: None, onchain: Some(OnchainSettings { confirmations: 6, - min_receive_amount_sat: 1000, - min_send_amount_sat: 546, + receive_limits: AmountLimitSettings { min: Some(1000), max: None }, + send_limits: AmountLimitSettings { min: Some(546), max: None }, }), custom: HashMap::new(), }; @@ -1445,8 +1495,8 @@ mod tests { bolt12: None, onchain: Some(OnchainSettings { confirmations: 1, - min_receive_amount_sat: 1000, - min_send_amount_sat: 546, + receive_limits: AmountLimitSettings { min: Some(1000), max: None }, + send_limits: AmountLimitSettings { min: Some(546), max: None }, }), custom: HashMap::new(), }; @@ -1481,8 +1531,8 @@ mod tests { bolt12: None, onchain: Some(OnchainSettings { confirmations: 1, - min_receive_amount_sat: 1000, - min_send_amount_sat: 546, + receive_limits: AmountLimitSettings { min: Some(1000), max: None }, + send_limits: AmountLimitSettings { min: Some(546), max: None }, }), custom: HashMap::new(), }; @@ -1532,6 +1582,7 @@ mod tests { mpp: false, amountless: true, invoice_description: false, + ..Default::default() }; let settings1 = SettingsResponse { unit: "sat".to_string(), @@ -1554,7 +1605,7 @@ mod tests { .unwrap(); // Add Bolt12 - let bolt12_settings = Bolt12Settings { amountless: false }; + let bolt12_settings = Bolt12Settings { amountless: false, ..Default::default() }; let settings2 = SettingsResponse { unit: "sat".to_string(), bolt11: None, diff --git a/crates/cdk/src/mint/issue/mod.rs b/crates/cdk/src/mint/issue/mod.rs index 1707c28008..4b038e67e5 100644 --- a/crates/cdk/src/mint/issue/mod.rs +++ b/crates/cdk/src/mint/issue/mod.rs @@ -1044,8 +1044,8 @@ mod batch_mint_tests { bolt12: None, onchain: Some(OnchainSettings { confirmations: self.confirmations, - min_receive_amount_sat: 0, - min_send_amount_sat: 0, + receive_limits: Default::default(), + send_limits: Default::default(), }), custom: HashMap::new(), }) diff --git a/crates/cdk/src/mint/melt/tests/onchain_quote_id_tests.rs b/crates/cdk/src/mint/melt/tests/onchain_quote_id_tests.rs index 5f4906dd3f..b20881cf16 100644 --- a/crates/cdk/src/mint/melt/tests/onchain_quote_id_tests.rs +++ b/crates/cdk/src/mint/melt/tests/onchain_quote_id_tests.rs @@ -86,8 +86,8 @@ impl MintPayment for OnchainQuoteMock { bolt12: None, onchain: Some(OnchainSettings { confirmations: self.confirmations, - min_receive_amount_sat: 0, - min_send_amount_sat: 0, + receive_limits: Default::default(), + send_limits: Default::default(), }), custom: std::collections::HashMap::new(), }) diff --git a/crates/cdk/src/mint/mod.rs b/crates/cdk/src/mint/mod.rs index 4811da4e40..a76573c24e 100644 --- a/crates/cdk/src/mint/mod.rs +++ b/crates/cdk/src/mint/mod.rs @@ -66,6 +66,8 @@ pub struct Mint { auth_localstore: Option, /// Payment processors for mint payment_processors: Arc>, + /// Operator-configured limits per processor, used to compute effective limits on settings updates. + processor_limits: Arc>, /// Subscription manager pubsub_manager: Arc, oidc_client: Option, @@ -101,6 +103,7 @@ impl Mint { signatory: Arc, localstore: DynMintDatabase, payment_processors: HashMap, + processor_limits: HashMap, max_inputs: usize, max_outputs: usize, ) -> Result { @@ -110,6 +113,7 @@ impl Mint { localstore, None, payment_processors, + processor_limits, max_inputs, max_outputs, ) @@ -123,6 +127,7 @@ impl Mint { localstore: DynMintDatabase, auth_localstore: DynMintAuthDatabase, payment_processors: HashMap, + processor_limits: HashMap, max_inputs: usize, max_outputs: usize, ) -> Result { @@ -132,6 +137,7 @@ impl Mint { localstore, Some(auth_localstore), payment_processors, + processor_limits, max_inputs, max_outputs, ) @@ -146,6 +152,7 @@ impl Mint { localstore: DynMintDatabase, auth_localstore: Option, payment_processors: HashMap, + processor_limits: HashMap, max_inputs: usize, max_outputs: usize, ) -> Result { @@ -231,6 +238,7 @@ impl Mint { } let payment_processors = Arc::new(payment_processors); + let processor_limits = Arc::new(processor_limits); Ok(Self { signatory, @@ -243,6 +251,7 @@ impl Mint { ) }), payment_processors, + processor_limits, auth_localstore, keysets: Arc::new(ArcSwap::new(keysets.keysets.into())), task_state: Arc::new(Mutex::new(TaskState::default())), @@ -631,30 +640,49 @@ impl Mint { ) -> Result<(), Error> { let mut join_set = JoinSet::new(); - // Group processors by unique instance (using Arc pointer equality) - let mut seen_processors = Vec::new(); + // Group all (key, limits) pairs by unique processor instance. + // A single processor (e.g. arkade) may handle both bolt11 and onchain keys; + // it needs one payment task and one settings task that fans out to all its keys. + let mut processor_groups: Vec<( + DynMintPayment, + Vec<(PaymentProcessorKey, Option)>, + )> = Vec::new(); + for (key, processor) in payment_processors { - // Skip if processor is already active if processor.is_payment_event_stream_active() { continue; } - - // Skip if we've already spawned a task for this processor instance - if seen_processors.iter().any(|p| Arc::ptr_eq(p, processor)) { - continue; + let operator_limits = mint.processor_limits.get(key).copied(); + if let Some(group) = processor_groups + .iter_mut() + .find(|(p, _)| Arc::ptr_eq(p, processor)) + { + group.1.push((key.clone(), operator_limits)); + } else { + processor_groups.push((Arc::clone(processor), vec![(key.clone(), operator_limits)])); } + } - seen_processors.push(Arc::clone(processor)); + for (processor, keys_with_limits) in processor_groups { + tracing::info!( + "Starting tasks for processor handling keys: {:?}", + keys_with_limits.iter().map(|(k, _)| k).collect::>() + ); - tracing::info!("Starting payment wait task for {:?}", key); + { + let processor = Arc::clone(&processor); + let localstore = Arc::clone(&localstore); + let shutdown = Arc::clone(&shutdown); + let keys = keys_with_limits.clone(); + join_set.spawn(async move { + Self::wait_for_processor_settings(processor, localstore, keys, shutdown).await; + }); + } - // Clone for the spawned task let mint = Arc::clone(&mint); - let processor = Arc::clone(processor); let localstore = Arc::clone(&localstore); let pubsub_manager = Arc::clone(&pubsub_manager); let shutdown = Arc::clone(&shutdown); - join_set.spawn(async move { let result = Self::wait_for_processor_payments( mint, @@ -698,6 +726,195 @@ impl Mint { Ok(()) } + /// Updates `MintInfo` in the database when the payment processor reports new settings. + /// + /// Updates NUT-04/NUT-05 options, NUT-15 MPP, and amount limits. + /// Effective limits: `max(processor_min, operator_min)` and `min(processor_max, operator_max)`. + async fn update_mint_info_from_settings( + localstore: &DynMintDatabase, + key: &PaymentProcessorKey, + settings: cdk_common::payment::SettingsResponse, + operator_limits: Option, + ) -> Result<(), Error> { + let mint_info_bytes = localstore + .kv_read( + CDK_MINT_PRIMARY_NAMESPACE, + CDK_MINT_CONFIG_SECONDARY_NAMESPACE, + CDK_MINT_CONFIG_KV_KEY, + ) + .await? + .ok_or(Error::CouldNotGetMintInfo)?; + + let mut mint_info: MintInfo = serde_json::from_slice(&mint_info_bytes)?; + + match &key.method { + cdk_common::nuts::PaymentMethod::Known(cdk_common::nut00::KnownMethod::Bolt11) => { + if let Some(bolt11) = &settings.bolt11 { + // Update NUT-04 bolt11 options and limits + for entry in mint_info.nuts.nut04.methods.iter_mut() { + if entry.unit == key.unit && entry.method == key.method { + entry.options = Some(nut04::MintMethodOptions::Bolt11 { + description: bolt11.invoice_description, + }); + if let Some(limits) = operator_limits { + let (min, max) = builder::apply_processor_limits( + limits.mint_min, + limits.mint_max, + bolt11.receive_limits.as_ref(), + ); + entry.min_amount = Some(min); + entry.max_amount = Some(max); + } + } + } + + // Update NUT-05 bolt11 options and limits + for entry in mint_info.nuts.nut05.methods.iter_mut() { + if entry.unit == key.unit && entry.method == key.method { + entry.options = Some(nut05::MeltMethodOptions::Bolt11 { + amountless: bolt11.amountless, + }); + if let Some(limits) = operator_limits { + let (min, max) = builder::apply_processor_limits( + limits.melt_min, + limits.melt_max, + bolt11.send_limits.as_ref(), + ); + entry.min_amount = Some(min); + entry.max_amount = Some(max); + } + } + } + + // Update NUT-15 MPP: add or remove this unit/method pair + let mpp_entry = nut15::MppMethodSettings { + method: key.method.clone(), + unit: key.unit.clone(), + }; + mint_info + .nuts + .nut15 + .methods + .retain(|m| !(m.unit == key.unit && m.method == key.method)); + if bolt11.mpp { + mint_info.nuts.nut15.methods.push(mpp_entry); + } + } + } + cdk_common::nuts::PaymentMethod::Known(cdk_common::nut00::KnownMethod::Onchain) => { + if let Some(onchain) = &settings.onchain { + // Update NUT-04 onchain options and limits + for entry in mint_info.nuts.nut04.methods.iter_mut() { + if entry.unit == key.unit && entry.method == key.method { + entry.options = Some(nut04::MintMethodOptions::Onchain { + confirmations: onchain.confirmations, + }); + if let Some(limits) = operator_limits { + let (min, max) = builder::apply_processor_limits( + limits.mint_min, + limits.mint_max, + Some(&onchain.receive_limits), + ); + entry.min_amount = Some(min); + entry.max_amount = Some(max); + } + } + } + + // Update NUT-05 onchain limits + for entry in mint_info.nuts.nut05.methods.iter_mut() { + if entry.unit == key.unit && entry.method == key.method { + if let Some(limits) = operator_limits { + let (min, max) = builder::apply_processor_limits( + limits.melt_min, + limits.melt_max, + Some(&onchain.send_limits), + ); + entry.min_amount = Some(min); + entry.max_amount = Some(max); + } + } + } + } + } + _ => {} + } + + let updated = serde_json::to_vec(&mint_info)?; + let mut tx = localstore.begin_transaction().await?; + tx.kv_write( + CDK_MINT_PRIMARY_NAMESPACE, + CDK_MINT_CONFIG_SECONDARY_NAMESPACE, + CDK_MINT_CONFIG_KV_KEY, + &updated, + ) + .await?; + tx.commit().await?; + + tracing::info!( + "Mint info updated from settings change for {:?}/{:?}", + key.unit, + key.method + ); + + Ok(()) + } + + /// Watches the settings stream for a processor and applies updates to MintInfo for all its keys. + /// + /// A single processor instance may handle multiple payment keys (e.g. arkade handles both + /// bolt11 and onchain). Each settings event is applied to every key the processor owns. + async fn wait_for_processor_settings( + processor: DynMintPayment, + localstore: DynMintDatabase, + keys: Vec<(PaymentProcessorKey, Option)>, + shutdown: Arc, + ) { + let shutdown_future = shutdown.notified(); + tokio::pin!(shutdown_future); + + loop { + tokio::select! { + _ = &mut shutdown_future => break, + result = processor.wait_settings() => { + match result { + Ok(mut stream) => { + loop { + tokio::select! { + _ = &mut shutdown_future => return, + maybe_settings = stream.next() => { + let Some(settings) = maybe_settings else { break }; + for (key, operator_limits) in &keys { + if let Err(e) = Self::update_mint_info_from_settings( + &localstore, + key, + settings.clone(), + *operator_limits, + ).await { + tracing::warn!( + "Failed to apply settings update for {:?}: {}", + key, e + ); + } + } + } + } + } + } + Err(e) => { + tracing::warn!( + "Could not get settings stream for keys {:?}: {}", + keys.iter().map(|(k, _)| k).collect::>(), + e + ); + tokio::time::sleep(Duration::from_secs(5)).await; + } + } + } + } + } + } + /// Handles payment waiting for a single processor #[instrument(skip_all)] async fn wait_for_processor_payments( @@ -1372,6 +1589,7 @@ mod tests { signatory, localstore, HashMap::new(), + HashMap::new(), 1000, 1000, ) From 2d236ae4cb3baedb0ee62091713a281bc68eae8e Mon Sep 17 00:00:00 2001 From: d4rp4t Date: Sat, 13 Jun 2026 15:17:00 +0200 Subject: [PATCH 3/4] fix things changed by rebase --- crates/cdk-payment-processor/src/proto/mod.rs | 10 +++++----- crates/cdk/src/mint/builder.rs | 4 ++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/cdk-payment-processor/src/proto/mod.rs b/crates/cdk-payment-processor/src/proto/mod.rs index e3726f33b8..c42e292728 100644 --- a/crates/cdk-payment-processor/src/proto/mod.rs +++ b/crates/cdk-payment-processor/src/proto/mod.rs @@ -497,15 +497,15 @@ mod tests { let settings = OnchainSettings { confirmations: 3, - receive_limits: AmountLimitSettings { min: 1_000, max: 0 }, - send_limits: AmountLimitSettings { min: 546, max: 500_000 }, + receive_limits: AmountLimitSettings { min: Some(1_000), max: Some(0) }, + send_limits: AmountLimitSettings { min: Some(546), max: Some(500_000) }, }; // Simulate the server→proto→client roundtrip via the helper functions in client/server. // Here we just verify the Rust structs round-trip through field access. - assert_eq!(settings.receive_limits.min, 1_000); - assert_eq!(settings.send_limits.min, 546); - assert_eq!(settings.send_limits.max, 500_000); + assert_eq!(settings.receive_limits.min, Some(1_000)); + assert_eq!(settings.send_limits.min, Some(546)); + assert_eq!(settings.send_limits.max, Some(500_000)); } #[test] diff --git a/crates/cdk/src/mint/builder.rs b/crates/cdk/src/mint/builder.rs index 2c6d157b83..2a14d38f88 100644 --- a/crates/cdk/src/mint/builder.rs +++ b/crates/cdk/src/mint/builder.rs @@ -904,6 +904,8 @@ mod tests { mpp: false, amountless: false, invoice_description: false, + receive_limits: None, + send_limits: None, }), bolt12: None, onchain: None, @@ -1177,6 +1179,8 @@ mod tests { mpp: false, amountless: false, invoice_description: false, + receive_limits: None, + send_limits: None, }), bolt12: None, onchain: None, From 49793d374587b1bc22a8ddf0b03d7f93f3dbc81c Mon Sep 17 00:00:00 2001 From: d4rp4t Date: Sat, 13 Jun 2026 15:24:56 +0200 Subject: [PATCH 4/4] format --- crates/cdk-fake-wallet/src/lib.rs | 10 ++++- crates/cdk-ldk-node/src/lib.rs | 5 ++- .../cdk-payment-processor/src/proto/client.rs | 6 +-- crates/cdk-payment-processor/src/proto/mod.rs | 10 ++++- .../cdk-payment-processor/src/proto/server.rs | 8 +++- crates/cdk/src/mint/builder.rs | 40 +++++++++++++++---- crates/cdk/src/mint/mod.rs | 3 +- 7 files changed, 62 insertions(+), 20 deletions(-) diff --git a/crates/cdk-fake-wallet/src/lib.rs b/crates/cdk-fake-wallet/src/lib.rs index a9acbb95c4..8f7605efa3 100644 --- a/crates/cdk-fake-wallet/src/lib.rs +++ b/crates/cdk-fake-wallet/src/lib.rs @@ -506,8 +506,14 @@ impl MintPayment for FakeWallet { }), onchain: Some(payment::OnchainSettings { confirmations: 1, - receive_limits: payment::AmountLimitSettings { min: Some(1), max: None }, - send_limits: payment::AmountLimitSettings { min: Some(1), max: None }, + receive_limits: payment::AmountLimitSettings { + min: Some(1), + max: None, + }, + send_limits: payment::AmountLimitSettings { + min: Some(1), + max: None, + }, }), custom: self.custom_payment_methods.clone(), }) diff --git a/crates/cdk-ldk-node/src/lib.rs b/crates/cdk-ldk-node/src/lib.rs index 2bc8b6ca98..ed78729ba1 100644 --- a/crates/cdk-ldk-node/src/lib.rs +++ b/crates/cdk-ldk-node/src/lib.rs @@ -533,7 +533,10 @@ impl MintPayment for CdkLdkNode { invoice_description: true, ..Default::default() }), - bolt12: Some(payment::Bolt12Settings { amountless: true, ..Default::default() }), + bolt12: Some(payment::Bolt12Settings { + amountless: true, + ..Default::default() + }), onchain: None, custom: std::collections::HashMap::new(), }; diff --git a/crates/cdk-payment-processor/src/proto/client.rs b/crates/cdk-payment-processor/src/proto/client.rs index 8c239cee12..3a700c0b3b 100644 --- a/crates/cdk-payment-processor/src/proto/client.rs +++ b/crates/cdk-payment-processor/src/proto/client.rs @@ -195,10 +195,8 @@ impl MintPayment for PaymentProcessorClient { async fn wait_settings( &self, - ) -> Result< - Pin + Send>>, - Self::Err, - > { + ) -> Result + Send>>, Self::Err> + { let mut inner = self.inner.clone(); let stream = inner .get_settings(Request::new(EmptyRequest {})) diff --git a/crates/cdk-payment-processor/src/proto/mod.rs b/crates/cdk-payment-processor/src/proto/mod.rs index c42e292728..2d7ea13f30 100644 --- a/crates/cdk-payment-processor/src/proto/mod.rs +++ b/crates/cdk-payment-processor/src/proto/mod.rs @@ -497,8 +497,14 @@ mod tests { let settings = OnchainSettings { confirmations: 3, - receive_limits: AmountLimitSettings { min: Some(1_000), max: Some(0) }, - send_limits: AmountLimitSettings { min: Some(546), max: Some(500_000) }, + receive_limits: AmountLimitSettings { + min: Some(1_000), + max: Some(0), + }, + send_limits: AmountLimitSettings { + min: Some(546), + max: Some(500_000), + }, }; // Simulate the server→proto→client roundtrip via the helper functions in client/server. diff --git a/crates/cdk-payment-processor/src/proto/server.rs b/crates/cdk-payment-processor/src/proto/server.rs index 876f4c4027..bc1d1e6443 100644 --- a/crates/cdk-payment-processor/src/proto/server.rs +++ b/crates/cdk-payment-processor/src/proto/server.rs @@ -231,7 +231,9 @@ impl CdkPaymentProcessor for PaymentProcessorServer { Ok(s) => s, Err(e) => { tracing::error!("Could not get settings stream: {}", e); - let _ = tx.send(Err(Status::internal("Could not get settings"))).await; + let _ = tx + .send(Err(Status::internal("Could not get settings"))) + .await; return; } }; @@ -263,7 +265,9 @@ impl CdkPaymentProcessor for PaymentProcessorServer { }); let output_stream = ReceiverStream::new(rx); - Ok(Response::new(Box::pin(output_stream) as Self::GetSettingsStream)) + Ok(Response::new( + Box::pin(output_stream) as Self::GetSettingsStream + )) } async fn create_payment( diff --git a/crates/cdk/src/mint/builder.rs b/crates/cdk/src/mint/builder.rs index 2a14d38f88..b6764c83df 100644 --- a/crates/cdk/src/mint/builder.rs +++ b/crates/cdk/src/mint/builder.rs @@ -1285,7 +1285,10 @@ mod tests { ) .unwrap(); - let bolt12_settings = Bolt12Settings { amountless: true, ..Default::default() }; + let bolt12_settings = Bolt12Settings { + amountless: true, + ..Default::default() + }; let settings = SettingsResponse { unit: "sat".to_string(), @@ -1454,8 +1457,14 @@ mod tests { bolt12: None, onchain: Some(OnchainSettings { confirmations: 6, - receive_limits: AmountLimitSettings { min: Some(1000), max: None }, - send_limits: AmountLimitSettings { min: Some(546), max: None }, + receive_limits: AmountLimitSettings { + min: Some(1000), + max: None, + }, + send_limits: AmountLimitSettings { + min: Some(546), + max: None, + }, }), custom: HashMap::new(), }; @@ -1499,8 +1508,14 @@ mod tests { bolt12: None, onchain: Some(OnchainSettings { confirmations: 1, - receive_limits: AmountLimitSettings { min: Some(1000), max: None }, - send_limits: AmountLimitSettings { min: Some(546), max: None }, + receive_limits: AmountLimitSettings { + min: Some(1000), + max: None, + }, + send_limits: AmountLimitSettings { + min: Some(546), + max: None, + }, }), custom: HashMap::new(), }; @@ -1535,8 +1550,14 @@ mod tests { bolt12: None, onchain: Some(OnchainSettings { confirmations: 1, - receive_limits: AmountLimitSettings { min: Some(1000), max: None }, - send_limits: AmountLimitSettings { min: Some(546), max: None }, + receive_limits: AmountLimitSettings { + min: Some(1000), + max: None, + }, + send_limits: AmountLimitSettings { + min: Some(546), + max: None, + }, }), custom: HashMap::new(), }; @@ -1609,7 +1630,10 @@ mod tests { .unwrap(); // Add Bolt12 - let bolt12_settings = Bolt12Settings { amountless: false, ..Default::default() }; + let bolt12_settings = Bolt12Settings { + amountless: false, + ..Default::default() + }; let settings2 = SettingsResponse { unit: "sat".to_string(), bolt11: None, diff --git a/crates/cdk/src/mint/mod.rs b/crates/cdk/src/mint/mod.rs index a76573c24e..b15546e387 100644 --- a/crates/cdk/src/mint/mod.rs +++ b/crates/cdk/src/mint/mod.rs @@ -659,7 +659,8 @@ impl Mint { { group.1.push((key.clone(), operator_limits)); } else { - processor_groups.push((Arc::clone(processor), vec![(key.clone(), operator_limits)])); + processor_groups + .push((Arc::clone(processor), vec![(key.clone(), operator_limits)])); } }