From ba256ea51cb395d1d8820ad4ff5ca4e7948c807e Mon Sep 17 00:00:00 2001 From: Ross Savage <551697+dangeross@users.noreply.github.com> Date: Mon, 17 Mar 2025 14:11:00 +0100 Subject: [PATCH] Fix LNURL drain and payment info (#776) * Fix LNURL-pay drain not working * Add LNURL info and BIP353 address to Liquid payment details * Add bip353_address to LiquidAddress SendDestination --- .../include/breez_sdk_liquid.h | 3 + lib/bindings/src/breez_sdk_liquid.udl | 4 +- lib/core/src/frb_generated.rs | 38 +++++- lib/core/src/model.rs | 8 ++ lib/core/src/persist/mod.rs | 2 + lib/core/src/sdk.rs | 98 +++++++------- lib/wasm/src/model.rs | 1 + packages/dart/lib/src/frb_generated.dart | 21 ++- packages/dart/lib/src/frb_generated.io.dart | 12 ++ packages/dart/lib/src/model.dart | 14 +- packages/dart/lib/src/model.freezed.dart | 120 +++++++++++++++--- ...utter_breez_liquid_bindings_generated.dart | 6 + .../breezsdkliquid/BreezSDKLiquidMapper.kt | 19 ++- .../ios/BreezSDKLiquidMapper.swift | 20 ++- packages/react-native/src/index.ts | 3 + 15 files changed, 293 insertions(+), 76 deletions(-) diff --git a/lib/bindings/langs/flutter/breez_sdk_liquid/include/breez_sdk_liquid.h b/lib/bindings/langs/flutter/breez_sdk_liquid/include/breez_sdk_liquid.h index 24640d4..e31534c 100644 --- a/lib/bindings/langs/flutter/breez_sdk_liquid/include/breez_sdk_liquid.h +++ b/lib/bindings/langs/flutter/breez_sdk_liquid/include/breez_sdk_liquid.h @@ -150,6 +150,7 @@ typedef struct wire_cst_liquid_address_data { typedef struct wire_cst_SendDestination_LiquidAddress { struct wire_cst_liquid_address_data *address_data; + struct wire_cst_list_prim_u_8_strict *bip353_address; } wire_cst_SendDestination_LiquidAddress; typedef struct wire_cst_route_hint_hop { @@ -542,6 +543,8 @@ typedef struct wire_cst_PaymentDetails_Liquid { struct wire_cst_list_prim_u_8_strict *description; struct wire_cst_list_prim_u_8_strict *asset_id; struct wire_cst_asset_info *asset_info; + struct wire_cst_ln_url_info *lnurl_info; + struct wire_cst_list_prim_u_8_strict *bip353_address; } wire_cst_PaymentDetails_Liquid; typedef struct wire_cst_PaymentDetails_Bitcoin { diff --git a/lib/bindings/src/breez_sdk_liquid.udl b/lib/bindings/src/breez_sdk_liquid.udl index e9f3051..4941fa6 100644 --- a/lib/bindings/src/breez_sdk_liquid.udl +++ b/lib/bindings/src/breez_sdk_liquid.udl @@ -431,7 +431,7 @@ dictionary PrepareSendRequest { [Enum] interface SendDestination { - LiquidAddress(LiquidAddressData address_data); + LiquidAddress(LiquidAddressData address_data, string? bip353_address); Bolt11(LNInvoice invoice, string? bip353_address); Bolt12(LNOffer offer, u64 receiver_amount_sat, string? bip353_address); }; @@ -609,7 +609,7 @@ dictionary AssetInfo { [Enum] interface PaymentDetails { Lightning(string swap_id, string description, u32 liquid_expiration_blockheight, string? preimage, string? invoice, string? bolt12_offer, string? payment_hash, string? destination_pubkey, LnUrlInfo? lnurl_info, string? bip353_address, string? claim_tx_id, string? refund_tx_id, u64? refund_tx_amount_sat); - Liquid(string asset_id, string destination, string description, AssetInfo? asset_info); + Liquid(string asset_id, string destination, string description, AssetInfo? asset_info, LnUrlInfo? lnurl_info, string? bip353_address); Bitcoin(string swap_id, string description, boolean auto_accepted_fees, u32? bitcoin_expiration_blockheight, u32? liquid_expiration_blockheight, string? claim_tx_id, string? refund_tx_id, u64? refund_tx_amount_sat); }; diff --git a/lib/core/src/frb_generated.rs b/lib/core/src/frb_generated.rs index 758adc1..cb7ebfe 100644 --- a/lib/core/src/frb_generated.rs +++ b/lib/core/src/frb_generated.rs @@ -3959,11 +3959,15 @@ impl SseDecode for crate::model::PaymentDetails { let mut var_description = ::sse_decode(deserializer); let mut var_assetId = ::sse_decode(deserializer); let mut var_assetInfo = >::sse_decode(deserializer); + let mut var_lnurlInfo = >::sse_decode(deserializer); + let mut var_bip353Address = >::sse_decode(deserializer); return crate::model::PaymentDetails::Liquid { destination: var_destination, description: var_description, asset_id: var_assetId, asset_info: var_assetInfo, + lnurl_info: var_lnurlInfo, + bip353_address: var_bip353Address, }; } 2 => { @@ -4556,8 +4560,10 @@ impl SseDecode for crate::model::SendDestination { 0 => { let mut var_addressData = ::sse_decode(deserializer); + let mut var_bip353Address = >::sse_decode(deserializer); return crate::model::SendDestination::LiquidAddress { address_data: var_addressData, + bip353_address: var_bip353Address, }; } 1 => { @@ -6284,12 +6290,16 @@ impl flutter_rust_bridge::IntoDart for crate::model::PaymentDetails { description, asset_id, asset_info, + lnurl_info, + bip353_address, } => [ 1.into_dart(), destination.into_into_dart().into_dart(), description.into_into_dart().into_dart(), asset_id.into_into_dart().into_dart(), asset_info.into_into_dart().into_dart(), + lnurl_info.into_into_dart().into_dart(), + bip353_address.into_into_dart().into_dart(), ] .into_dart(), crate::model::PaymentDetails::Bitcoin { @@ -7005,9 +7015,15 @@ impl flutter_rust_bridge::IntoIntoDart for crate::model: impl flutter_rust_bridge::IntoDart for crate::model::SendDestination { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { match self { - crate::model::SendDestination::LiquidAddress { address_data } => { - [0.into_dart(), address_data.into_into_dart().into_dart()].into_dart() - } + crate::model::SendDestination::LiquidAddress { + address_data, + bip353_address, + } => [ + 0.into_dart(), + address_data.into_into_dart().into_dart(), + bip353_address.into_into_dart().into_dart(), + ] + .into_dart(), crate::model::SendDestination::Bolt11 { invoice, bip353_address, @@ -8578,12 +8594,16 @@ impl SseEncode for crate::model::PaymentDetails { description, asset_id, asset_info, + lnurl_info, + bip353_address, } => { ::sse_encode(1, serializer); ::sse_encode(destination, serializer); ::sse_encode(description, serializer); ::sse_encode(asset_id, serializer); >::sse_encode(asset_info, serializer); + >::sse_encode(lnurl_info, serializer); + >::sse_encode(bip353_address, serializer); } crate::model::PaymentDetails::Bitcoin { swap_id, @@ -9048,9 +9068,13 @@ impl SseEncode for crate::model::SendDestination { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { match self { - crate::model::SendDestination::LiquidAddress { address_data } => { + crate::model::SendDestination::LiquidAddress { + address_data, + bip353_address, + } => { ::sse_encode(0, serializer); ::sse_encode(address_data, serializer); + >::sse_encode(bip353_address, serializer); } crate::model::SendDestination::Bolt11 { invoice, @@ -10803,6 +10827,8 @@ mod io { description: ans.description.cst_decode(), asset_id: ans.asset_id.cst_decode(), asset_info: ans.asset_info.cst_decode(), + lnurl_info: ans.lnurl_info.cst_decode(), + bip353_address: ans.bip353_address.cst_decode(), } } 2 => { @@ -11234,6 +11260,7 @@ mod io { let ans = unsafe { self.kind.LiquidAddress }; crate::model::SendDestination::LiquidAddress { address_data: ans.address_data.cst_decode(), + bip353_address: ans.bip353_address.cst_decode(), } } 1 => { @@ -14598,6 +14625,8 @@ mod io { description: *mut wire_cst_list_prim_u_8_strict, asset_id: *mut wire_cst_list_prim_u_8_strict, asset_info: *mut wire_cst_asset_info, + lnurl_info: *mut wire_cst_ln_url_info, + bip353_address: *mut wire_cst_list_prim_u_8_strict, } #[repr(C)] #[derive(Clone, Copy)] @@ -14961,6 +14990,7 @@ mod io { #[derive(Clone, Copy)] pub struct wire_cst_SendDestination_LiquidAddress { address_data: *mut wire_cst_liquid_address_data, + bip353_address: *mut wire_cst_list_prim_u_8_strict, } #[repr(C)] #[derive(Clone, Copy)] diff --git a/lib/core/src/model.rs b/lib/core/src/model.rs index 752be36..ca71364 100644 --- a/lib/core/src/model.rs +++ b/lib/core/src/model.rs @@ -514,6 +514,8 @@ pub struct PrepareSendRequest { pub enum SendDestination { LiquidAddress { address_data: liquid::LiquidAddressData, + /// A BIP353 address, in case one was used to resolve this Liquid address + bip353_address: Option, }, Bolt11 { invoice: LNInvoice, @@ -1662,6 +1664,12 @@ pub enum PaymentDetails { /// The asset info derived from the [AssetMetadata] asset_info: Option, + + /// The payment LNURL info + lnurl_info: Option, + + /// The BIP353 address used to resolve this payment + bip353_address: Option, }, /// Swapping to or from the Bitcoin chain Bitcoin { diff --git a/lib/core/src/persist/mod.rs b/lib/core/src/persist/mod.rs index 60d2bd6..cd1fb98 100644 --- a/lib/core/src/persist/mod.rs +++ b/lib/core/src/persist/mod.rs @@ -825,6 +825,8 @@ impl Persister { .unwrap_or("Liquid transfer".to_string()), asset_id, asset_info, + lnurl_info: maybe_payment_details_lnurl_info, + bip353_address: maybe_payment_details_bip353_address, } } }; diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index a4d0d91..6841d79 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -1240,6 +1240,7 @@ impl LiquidSdk { liquid_address_data.asset_id = Some(asset_id.clone()); payment_destination = SendDestination::LiquidAddress { address_data: liquid_address_data, + bip353_address: None, }; } Ok(InputType::Bolt11 { invoice }) => { @@ -1289,6 +1290,7 @@ impl LiquidSdk { label: None, message: None, }, + bip353_address: None, }; (drain_amount_sat, drain_fees_sat, payment_destination) } @@ -1420,6 +1422,7 @@ impl LiquidSdk { match payment_destination { SendDestination::LiquidAddress { address_data: liquid_address_data, + bip353_address, } => { let Some(amount_sat) = liquid_address_data.amount_sat else { return Err(PaymentError::AmountMissing { @@ -1452,28 +1455,18 @@ impl LiquidSdk { *fees_sat, asset_id, )?; - self.pay_liquid(liquid_address_data.clone(), amount_sat, *fees_sat, true) - .await + let mut response = self + .pay_liquid(liquid_address_data.clone(), amount_sat, *fees_sat, true) + .await?; + self.insert_bip353_payment_details(bip353_address, &mut response)?; + Ok(response) } SendDestination::Bolt11 { invoice, bip353_address, } => { - let response = self.pay_bolt11_invoice(&invoice.bolt11, *fees_sat).await?; - if bip353_address.is_some() { - if let (Some(tx_id), Some(destination)) = - (&response.payment.tx_id, &response.payment.destination) - { - self.persister - .insert_or_update_payment_details(PaymentTxDetails { - tx_id: tx_id.clone(), - destination: destination.clone(), - description: None, - lnurl_info: None, - bip353_address: bip353_address.clone(), - })?; - } - } + let mut response = self.pay_bolt11_invoice(&invoice.bolt11, *fees_sat).await?; + self.insert_bip353_payment_details(bip353_address, &mut response)?; Ok(response) } SendDestination::Bolt12 { @@ -1485,28 +1478,41 @@ impl LiquidSdk { .swapper .get_bolt12_invoice(&offer.offer, *receiver_amount_sat) .await?; - let response = self + let mut response = self .pay_bolt12_invoice(offer, *receiver_amount_sat, &bolt12_invoice, *fees_sat) .await?; - if bip353_address.is_some() { - if let (Some(tx_id), Some(destination)) = - (&response.payment.tx_id, &response.payment.destination) - { - self.persister - .insert_or_update_payment_details(PaymentTxDetails { - tx_id: tx_id.clone(), - destination: destination.clone(), - description: None, - lnurl_info: None, - bip353_address: bip353_address.clone(), - })?; - } - } + self.insert_bip353_payment_details(bip353_address, &mut response)?; Ok(response) } } } + fn insert_bip353_payment_details( + &self, + bip353_address: &Option, + response: &mut SendPaymentResponse, + ) -> Result<()> { + if bip353_address.is_some() { + if let (Some(tx_id), Some(destination)) = + (&response.payment.tx_id, &response.payment.destination) + { + self.persister + .insert_or_update_payment_details(PaymentTxDetails { + tx_id: tx_id.clone(), + destination: destination.clone(), + description: None, + lnurl_info: None, + bip353_address: bip353_address.clone(), + })?; + // Get the payment with the bip353_address details + if let Some(payment) = self.persister.get_payment(tx_id)? { + response.payment = payment; + } + } + } + Ok(()) + } + async fn pay_bolt11_invoice( &self, invoice: &str, @@ -1674,6 +1680,8 @@ impl LiquidSdk { destination, description: description.unwrap_or("Liquid transfer".to_string()), asset_info, + lnurl_info: None, + bip353_address: None, }; Ok(SendPaymentResponse { @@ -3474,17 +3482,19 @@ impl LiquidSdk { .await .map_err(|e| LnUrlPayError::Generic { err: e.to_string() })?; - let destination = - if let SendDestination::Bolt11 { invoice, .. } = prepare_response.destination { - SendDestination::Bolt11 { - invoice, + let destination = match prepare_response.destination { + SendDestination::Bolt11 { invoice, .. } => SendDestination::Bolt11 { + invoice, + bip353_address: req.bip353_address, + }, + SendDestination::LiquidAddress { address_data, .. } => { + SendDestination::LiquidAddress { + address_data, bip353_address: req.bip353_address, } - } else { - return Err(LnUrlPayError::Generic { - err: "SendDestination for LNURL Pay is not BOLT11 invoice".to_string(), - }); - }; + } + destination => destination, + }; Ok(PrepareLnUrlPayResponse { destination, @@ -3514,7 +3524,7 @@ impl LiquidSdk { req: model::LnUrlPayRequest, ) -> Result { let prepare_response = req.prepare_response; - let payment = self + let mut payment = self .send_payment(&SendPaymentRequest { prepare_response: PrepareSendResponse { destination: prepare_response.destination.clone(), @@ -3592,7 +3602,7 @@ impl LiquidSdk { { self.persister .insert_or_update_payment_details(PaymentTxDetails { - tx_id, + tx_id: tx_id.clone(), destination, description, lnurl_info: Some(LnUrlInfo { @@ -3606,6 +3616,8 @@ impl LiquidSdk { }), bip353_address: None, })?; + // Get the payment with the lnurl_info details + payment = self.persister.get_payment(&tx_id)?.unwrap_or(payment); } Ok(LnUrlPayResult::EndpointSuccess { diff --git a/lib/wasm/src/model.rs b/lib/wasm/src/model.rs index 3d47e9f..577f339 100644 --- a/lib/wasm/src/model.rs +++ b/lib/wasm/src/model.rs @@ -418,6 +418,7 @@ pub struct PrepareSendRequest { pub enum SendDestination { LiquidAddress { address_data: LiquidAddressData, + bip353_address: Option, }, Bolt11 { invoice: LNInvoice, diff --git a/packages/dart/lib/src/frb_generated.dart b/packages/dart/lib/src/frb_generated.dart index b2f6a5b..222caca 100644 --- a/packages/dart/lib/src/frb_generated.dart +++ b/packages/dart/lib/src/frb_generated.dart @@ -2773,6 +2773,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { description: dco_decode_String(raw[2]), assetId: dco_decode_String(raw[3]), assetInfo: dco_decode_opt_box_autoadd_asset_info(raw[4]), + lnurlInfo: dco_decode_opt_box_autoadd_ln_url_info(raw[5]), + bip353Address: dco_decode_opt_String(raw[6]), ); case 2: return PaymentDetails_Bitcoin( @@ -3177,7 +3179,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { // Codec=Dco (DartCObject based), see doc to use other codecs switch (raw[0]) { case 0: - return SendDestination_LiquidAddress(addressData: dco_decode_box_autoadd_liquid_address_data(raw[1])); + return SendDestination_LiquidAddress( + addressData: dco_decode_box_autoadd_liquid_address_data(raw[1]), + bip353Address: dco_decode_opt_String(raw[2]), + ); case 1: return SendDestination_Bolt11( invoice: dco_decode_box_autoadd_ln_invoice(raw[1]), @@ -5089,11 +5094,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_description = sse_decode_String(deserializer); var var_assetId = sse_decode_String(deserializer); var var_assetInfo = sse_decode_opt_box_autoadd_asset_info(deserializer); + var var_lnurlInfo = sse_decode_opt_box_autoadd_ln_url_info(deserializer); + var var_bip353Address = sse_decode_opt_String(deserializer); return PaymentDetails_Liquid( destination: var_destination, description: var_description, assetId: var_assetId, assetInfo: var_assetInfo, + lnurlInfo: var_lnurlInfo, + bip353Address: var_bip353Address, ); case 2: var var_swapId = sse_decode_String(deserializer); @@ -5544,7 +5553,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { switch (tag_) { case 0: var var_addressData = sse_decode_box_autoadd_liquid_address_data(deserializer); - return SendDestination_LiquidAddress(addressData: var_addressData); + var var_bip353Address = sse_decode_opt_String(deserializer); + return SendDestination_LiquidAddress(addressData: var_addressData, bip353Address: var_bip353Address); case 1: var var_invoice = sse_decode_box_autoadd_ln_invoice(deserializer); var var_bip353Address = sse_decode_opt_String(deserializer); @@ -7319,12 +7329,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { description: final description, assetId: final assetId, assetInfo: final assetInfo, + lnurlInfo: final lnurlInfo, + bip353Address: final bip353Address, ): sse_encode_i_32(1, serializer); sse_encode_String(destination, serializer); sse_encode_String(description, serializer); sse_encode_String(assetId, serializer); sse_encode_opt_box_autoadd_asset_info(assetInfo, serializer); + sse_encode_opt_box_autoadd_ln_url_info(lnurlInfo, serializer); + sse_encode_opt_String(bip353Address, serializer); case PaymentDetails_Bitcoin( swapId: final swapId, description: final description, @@ -7669,9 +7683,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { void sse_encode_send_destination(SendDestination self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs switch (self) { - case SendDestination_LiquidAddress(addressData: final addressData): + case SendDestination_LiquidAddress(addressData: final addressData, bip353Address: final bip353Address): sse_encode_i_32(0, serializer); sse_encode_box_autoadd_liquid_address_data(addressData, serializer); + sse_encode_opt_String(bip353Address, serializer); case SendDestination_Bolt11(invoice: final invoice, bip353Address: final bip353Address): sse_encode_i_32(1, serializer); sse_encode_box_autoadd_ln_invoice(invoice, serializer); diff --git a/packages/dart/lib/src/frb_generated.io.dart b/packages/dart/lib/src/frb_generated.io.dart index d093664..87ccddb 100644 --- a/packages/dart/lib/src/frb_generated.io.dart +++ b/packages/dart/lib/src/frb_generated.io.dart @@ -3370,11 +3370,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { var pre_description = cst_encode_String(apiObj.description); var pre_asset_id = cst_encode_String(apiObj.assetId); var pre_asset_info = cst_encode_opt_box_autoadd_asset_info(apiObj.assetInfo); + var pre_lnurl_info = cst_encode_opt_box_autoadd_ln_url_info(apiObj.lnurlInfo); + var pre_bip353_address = cst_encode_opt_String(apiObj.bip353Address); wireObj.tag = 1; wireObj.kind.Liquid.destination = pre_destination; wireObj.kind.Liquid.description = pre_description; wireObj.kind.Liquid.asset_id = pre_asset_id; wireObj.kind.Liquid.asset_info = pre_asset_info; + wireObj.kind.Liquid.lnurl_info = pre_lnurl_info; + wireObj.kind.Liquid.bip353_address = pre_bip353_address; return; } if (apiObj is PaymentDetails_Bitcoin) { @@ -3811,8 +3815,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void cst_api_fill_to_wire_send_destination(SendDestination apiObj, wire_cst_send_destination wireObj) { if (apiObj is SendDestination_LiquidAddress) { var pre_address_data = cst_encode_box_autoadd_liquid_address_data(apiObj.addressData); + var pre_bip353_address = cst_encode_opt_String(apiObj.bip353Address); wireObj.tag = 0; wireObj.kind.LiquidAddress.address_data = pre_address_data; + wireObj.kind.LiquidAddress.bip353_address = pre_bip353_address; return; } if (apiObj is SendDestination_Bolt11) { @@ -6353,6 +6359,8 @@ final class wire_cst_liquid_address_data extends ffi.Struct { final class wire_cst_SendDestination_LiquidAddress extends ffi.Struct { external ffi.Pointer address_data; + + external ffi.Pointer bip353_address; } final class wire_cst_route_hint_hop extends ffi.Struct { @@ -6911,6 +6919,10 @@ final class wire_cst_PaymentDetails_Liquid extends ffi.Struct { external ffi.Pointer asset_id; external ffi.Pointer asset_info; + + external ffi.Pointer lnurl_info; + + external ffi.Pointer bip353_address; } final class wire_cst_PaymentDetails_Bitcoin extends ffi.Struct { diff --git a/packages/dart/lib/src/model.dart b/packages/dart/lib/src/model.dart index e3b3a30..bfdacda 100644 --- a/packages/dart/lib/src/model.dart +++ b/packages/dart/lib/src/model.dart @@ -896,6 +896,12 @@ sealed class PaymentDetails with _$PaymentDetails { /// The asset info derived from the [AssetMetadata] AssetInfo? assetInfo, + + /// The payment LNURL info + LnUrlInfo? lnurlInfo, + + /// The BIP353 address used to resolve this payment + String? bip353Address, }) = PaymentDetails_Liquid; /// Swapping to or from the Bitcoin chain @@ -1571,8 +1577,12 @@ sealed class SdkEvent with _$SdkEvent { sealed class SendDestination with _$SendDestination { const SendDestination._(); - const factory SendDestination.liquidAddress({required LiquidAddressData addressData}) = - SendDestination_LiquidAddress; + const factory SendDestination.liquidAddress({ + required LiquidAddressData addressData, + + /// A BIP353 address, in case one was used to resolve this Liquid address + String? bip353Address, + }) = SendDestination_LiquidAddress; const factory SendDestination.bolt11({ required LNInvoice invoice, diff --git a/packages/dart/lib/src/model.freezed.dart b/packages/dart/lib/src/model.freezed.dart index c34ed96..ba22669 100644 --- a/packages/dart/lib/src/model.freezed.dart +++ b/packages/dart/lib/src/model.freezed.dart @@ -1300,7 +1300,14 @@ abstract class _$$PaymentDetails_LiquidImplCopyWith<$Res> implements $PaymentDet ) = __$$PaymentDetails_LiquidImplCopyWithImpl<$Res>; @override @useResult - $Res call({String destination, String description, String assetId, AssetInfo? assetInfo}); + $Res call({ + String destination, + String description, + String assetId, + AssetInfo? assetInfo, + LnUrlInfo? lnurlInfo, + String? bip353Address, + }); } /// @nodoc @@ -1321,6 +1328,8 @@ class __$$PaymentDetails_LiquidImplCopyWithImpl<$Res> Object? description = null, Object? assetId = null, Object? assetInfo = freezed, + Object? lnurlInfo = freezed, + Object? bip353Address = freezed, }) { return _then( _$PaymentDetails_LiquidImpl( @@ -1344,6 +1353,16 @@ class __$$PaymentDetails_LiquidImplCopyWithImpl<$Res> ? _value.assetInfo : assetInfo // ignore: cast_nullable_to_non_nullable as AssetInfo?, + lnurlInfo: + freezed == lnurlInfo + ? _value.lnurlInfo + : lnurlInfo // ignore: cast_nullable_to_non_nullable + as LnUrlInfo?, + bip353Address: + freezed == bip353Address + ? _value.bip353Address + : bip353Address // ignore: cast_nullable_to_non_nullable + as String?, ), ); } @@ -1357,6 +1376,8 @@ class _$PaymentDetails_LiquidImpl extends PaymentDetails_Liquid { required this.description, required this.assetId, this.assetInfo, + this.lnurlInfo, + this.bip353Address, }) : super._(); /// Represents either a Liquid BIP21 URI or pure address @@ -1375,9 +1396,17 @@ class _$PaymentDetails_LiquidImpl extends PaymentDetails_Liquid { @override final AssetInfo? assetInfo; + /// The payment LNURL info + @override + final LnUrlInfo? lnurlInfo; + + /// The BIP353 address used to resolve this payment + @override + final String? bip353Address; + @override String toString() { - return 'PaymentDetails.liquid(destination: $destination, description: $description, assetId: $assetId, assetInfo: $assetInfo)'; + return 'PaymentDetails.liquid(destination: $destination, description: $description, assetId: $assetId, assetInfo: $assetInfo, lnurlInfo: $lnurlInfo, bip353Address: $bip353Address)'; } @override @@ -1388,11 +1417,14 @@ class _$PaymentDetails_LiquidImpl extends PaymentDetails_Liquid { (identical(other.destination, destination) || other.destination == destination) && (identical(other.description, description) || other.description == description) && (identical(other.assetId, assetId) || other.assetId == assetId) && - (identical(other.assetInfo, assetInfo) || other.assetInfo == assetInfo)); + (identical(other.assetInfo, assetInfo) || other.assetInfo == assetInfo) && + (identical(other.lnurlInfo, lnurlInfo) || other.lnurlInfo == lnurlInfo) && + (identical(other.bip353Address, bip353Address) || other.bip353Address == bip353Address)); } @override - int get hashCode => Object.hash(runtimeType, destination, description, assetId, assetInfo); + int get hashCode => + Object.hash(runtimeType, destination, description, assetId, assetInfo, lnurlInfo, bip353Address); /// Create a copy of PaymentDetails /// with the given fields replaced by the non-null parameter values. @@ -1409,6 +1441,8 @@ abstract class PaymentDetails_Liquid extends PaymentDetails { required final String description, required final String assetId, final AssetInfo? assetInfo, + final LnUrlInfo? lnurlInfo, + final String? bip353Address, }) = _$PaymentDetails_LiquidImpl; const PaymentDetails_Liquid._() : super._(); @@ -1425,6 +1459,12 @@ abstract class PaymentDetails_Liquid extends PaymentDetails { /// The asset info derived from the [AssetMetadata] AssetInfo? get assetInfo; + /// The payment LNURL info + LnUrlInfo? get lnurlInfo; + + /// The BIP353 address used to resolve this payment + String? get bip353Address; + /// Create a copy of PaymentDetails /// with the given fields replaced by the non-null parameter values. @override @@ -2601,12 +2641,22 @@ abstract class SdkEvent_Synced extends SdkEvent { } /// @nodoc -mixin _$SendDestination {} +mixin _$SendDestination { + /// A BIP353 address, in case one was used to resolve this Liquid address + String? get bip353Address => throw _privateConstructorUsedError; + + /// Create a copy of SendDestination + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $SendDestinationCopyWith get copyWith => throw _privateConstructorUsedError; +} /// @nodoc abstract class $SendDestinationCopyWith<$Res> { factory $SendDestinationCopyWith(SendDestination value, $Res Function(SendDestination) then) = _$SendDestinationCopyWithImpl<$Res, SendDestination>; + @useResult + $Res call({String? bip353Address}); } /// @nodoc @@ -2621,16 +2671,31 @@ class _$SendDestinationCopyWithImpl<$Res, $Val extends SendDestination> /// Create a copy of SendDestination /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({Object? bip353Address = freezed}) { + return _then( + _value.copyWith( + bip353Address: + freezed == bip353Address + ? _value.bip353Address + : bip353Address // ignore: cast_nullable_to_non_nullable + as String?, + ) + as $Val, + ); + } } /// @nodoc -abstract class _$$SendDestination_LiquidAddressImplCopyWith<$Res> { +abstract class _$$SendDestination_LiquidAddressImplCopyWith<$Res> implements $SendDestinationCopyWith<$Res> { factory _$$SendDestination_LiquidAddressImplCopyWith( _$SendDestination_LiquidAddressImpl value, $Res Function(_$SendDestination_LiquidAddressImpl) then, ) = __$$SendDestination_LiquidAddressImplCopyWithImpl<$Res>; + @override @useResult - $Res call({LiquidAddressData addressData}); + $Res call({LiquidAddressData addressData, String? bip353Address}); } /// @nodoc @@ -2646,7 +2711,7 @@ class __$$SendDestination_LiquidAddressImplCopyWithImpl<$Res> /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override - $Res call({Object? addressData = null}) { + $Res call({Object? addressData = null, Object? bip353Address = freezed}) { return _then( _$SendDestination_LiquidAddressImpl( addressData: @@ -2654,6 +2719,11 @@ class __$$SendDestination_LiquidAddressImplCopyWithImpl<$Res> ? _value.addressData : addressData // ignore: cast_nullable_to_non_nullable as LiquidAddressData, + bip353Address: + freezed == bip353Address + ? _value.bip353Address + : bip353Address // ignore: cast_nullable_to_non_nullable + as String?, ), ); } @@ -2662,14 +2732,18 @@ class __$$SendDestination_LiquidAddressImplCopyWithImpl<$Res> /// @nodoc class _$SendDestination_LiquidAddressImpl extends SendDestination_LiquidAddress { - const _$SendDestination_LiquidAddressImpl({required this.addressData}) : super._(); + const _$SendDestination_LiquidAddressImpl({required this.addressData, this.bip353Address}) : super._(); @override final LiquidAddressData addressData; + /// A BIP353 address, in case one was used to resolve this Liquid address + @override + final String? bip353Address; + @override String toString() { - return 'SendDestination.liquidAddress(addressData: $addressData)'; + return 'SendDestination.liquidAddress(addressData: $addressData, bip353Address: $bip353Address)'; } @override @@ -2677,11 +2751,12 @@ class _$SendDestination_LiquidAddressImpl extends SendDestination_LiquidAddress return identical(this, other) || (other.runtimeType == runtimeType && other is _$SendDestination_LiquidAddressImpl && - (identical(other.addressData, addressData) || other.addressData == addressData)); + (identical(other.addressData, addressData) || other.addressData == addressData) && + (identical(other.bip353Address, bip353Address) || other.bip353Address == bip353Address)); } @override - int get hashCode => Object.hash(runtimeType, addressData); + int get hashCode => Object.hash(runtimeType, addressData, bip353Address); /// Create a copy of SendDestination /// with the given fields replaced by the non-null parameter values. @@ -2696,25 +2771,33 @@ class _$SendDestination_LiquidAddressImpl extends SendDestination_LiquidAddress } abstract class SendDestination_LiquidAddress extends SendDestination { - const factory SendDestination_LiquidAddress({required final LiquidAddressData addressData}) = - _$SendDestination_LiquidAddressImpl; + const factory SendDestination_LiquidAddress({ + required final LiquidAddressData addressData, + final String? bip353Address, + }) = _$SendDestination_LiquidAddressImpl; const SendDestination_LiquidAddress._() : super._(); LiquidAddressData get addressData; + /// A BIP353 address, in case one was used to resolve this Liquid address + @override + String? get bip353Address; + /// Create a copy of SendDestination /// with the given fields replaced by the non-null parameter values. + @override @JsonKey(includeFromJson: false, includeToJson: false) _$$SendDestination_LiquidAddressImplCopyWith<_$SendDestination_LiquidAddressImpl> get copyWith => throw _privateConstructorUsedError; } /// @nodoc -abstract class _$$SendDestination_Bolt11ImplCopyWith<$Res> { +abstract class _$$SendDestination_Bolt11ImplCopyWith<$Res> implements $SendDestinationCopyWith<$Res> { factory _$$SendDestination_Bolt11ImplCopyWith( _$SendDestination_Bolt11Impl value, $Res Function(_$SendDestination_Bolt11Impl) then, ) = __$$SendDestination_Bolt11ImplCopyWithImpl<$Res>; + @override @useResult $Res call({LNInvoice invoice, String? bip353Address}); } @@ -2796,21 +2879,24 @@ abstract class SendDestination_Bolt11 extends SendDestination { LNInvoice get invoice; /// A BIP353 address, in case one was used to resolve this BOLT11 + @override String? get bip353Address; /// Create a copy of SendDestination /// with the given fields replaced by the non-null parameter values. + @override @JsonKey(includeFromJson: false, includeToJson: false) _$$SendDestination_Bolt11ImplCopyWith<_$SendDestination_Bolt11Impl> get copyWith => throw _privateConstructorUsedError; } /// @nodoc -abstract class _$$SendDestination_Bolt12ImplCopyWith<$Res> { +abstract class _$$SendDestination_Bolt12ImplCopyWith<$Res> implements $SendDestinationCopyWith<$Res> { factory _$$SendDestination_Bolt12ImplCopyWith( _$SendDestination_Bolt12Impl value, $Res Function(_$SendDestination_Bolt12Impl) then, ) = __$$SendDestination_Bolt12ImplCopyWithImpl<$Res>; + @override @useResult $Res call({LNOffer offer, BigInt receiverAmountSat, String? bip353Address}); } @@ -2909,10 +2995,12 @@ abstract class SendDestination_Bolt12 extends SendDestination { BigInt get receiverAmountSat; /// A BIP353 address, in case one was used to resolve this BOLT12 + @override String? get bip353Address; /// Create a copy of SendDestination /// with the given fields replaced by the non-null parameter values. + @override @JsonKey(includeFromJson: false, includeToJson: false) _$$SendDestination_Bolt12ImplCopyWith<_$SendDestination_Bolt12Impl> get copyWith => throw _privateConstructorUsedError; diff --git a/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart b/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart index 288c419..cabee2d 100644 --- a/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart +++ b/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart @@ -4291,6 +4291,8 @@ final class wire_cst_liquid_address_data extends ffi.Struct { final class wire_cst_SendDestination_LiquidAddress extends ffi.Struct { external ffi.Pointer address_data; + + external ffi.Pointer bip353_address; } final class wire_cst_route_hint_hop extends ffi.Struct { @@ -4849,6 +4851,10 @@ final class wire_cst_PaymentDetails_Liquid extends ffi.Struct { external ffi.Pointer asset_id; external ffi.Pointer asset_info; + + external ffi.Pointer lnurl_info; + + external ffi.Pointer bip353_address; } final class wire_cst_PaymentDetails_Bitcoin extends ffi.Struct { diff --git a/packages/react-native/android/src/main/java/com/breezsdkliquid/BreezSDKLiquidMapper.kt b/packages/react-native/android/src/main/java/com/breezsdkliquid/BreezSDKLiquidMapper.kt index 6b361ca..a5e399d 100644 --- a/packages/react-native/android/src/main/java/com/breezsdkliquid/BreezSDKLiquidMapper.kt +++ b/packages/react-native/android/src/main/java/com/breezsdkliquid/BreezSDKLiquidMapper.kt @@ -3479,7 +3479,18 @@ fun asPaymentDetails(paymentDetails: ReadableMap): PaymentDetails? { } else { null } - return PaymentDetails.Liquid(assetId, destination, description, assetInfo) + val lnurlInfo = + if (hasNonNullKey( + paymentDetails, + "lnurlInfo", + ) + ) { + paymentDetails.getMap("lnurlInfo")?.let { asLnUrlInfo(it) } + } else { + null + } + val bip353Address = if (hasNonNullKey(paymentDetails, "bip353Address")) paymentDetails.getString("bip353Address") else null + return PaymentDetails.Liquid(assetId, destination, description, assetInfo, lnurlInfo, bip353Address) } if (type == "bitcoin") { val swapId = paymentDetails.getString("swapId")!! @@ -3556,6 +3567,8 @@ fun readableMapOf(paymentDetails: PaymentDetails): ReadableMap? { pushToMap(map, "destination", paymentDetails.destination) pushToMap(map, "description", paymentDetails.description) pushToMap(map, "assetInfo", paymentDetails.assetInfo?.let { readableMapOf(it) }) + pushToMap(map, "lnurlInfo", paymentDetails.lnurlInfo?.let { readableMapOf(it) }) + pushToMap(map, "bip353Address", paymentDetails.bip353Address) } is PaymentDetails.Bitcoin -> { pushToMap(map, "type", "bitcoin") @@ -3763,7 +3776,8 @@ fun asSendDestination(sendDestination: ReadableMap): SendDestination? { if (type == "liquidAddress") { val addressData = sendDestination.getMap("addressData")?.let { asLiquidAddressData(it) }!! - return SendDestination.LiquidAddress(addressData) + val bip353Address = if (hasNonNullKey(sendDestination, "bip353Address")) sendDestination.getString("bip353Address") else null + return SendDestination.LiquidAddress(addressData, bip353Address) } if (type == "bolt11") { val invoice = sendDestination.getMap("invoice")?.let { asLnInvoice(it) }!! @@ -3785,6 +3799,7 @@ fun readableMapOf(sendDestination: SendDestination): ReadableMap? { is SendDestination.LiquidAddress -> { pushToMap(map, "type", "liquidAddress") pushToMap(map, "addressData", readableMapOf(sendDestination.addressData)) + pushToMap(map, "bip353Address", sendDestination.bip353Address) } is SendDestination.Bolt11 -> { pushToMap(map, "type", "bolt11") diff --git a/packages/react-native/ios/BreezSDKLiquidMapper.swift b/packages/react-native/ios/BreezSDKLiquidMapper.swift index 25f3c0d..40b53d6 100644 --- a/packages/react-native/ios/BreezSDKLiquidMapper.swift +++ b/packages/react-native/ios/BreezSDKLiquidMapper.swift @@ -4233,7 +4233,14 @@ enum BreezSDKLiquidMapper { _assetInfo = try asAssetInfo(assetInfo: assetInfoTmp) } - return PaymentDetails.liquid(assetId: _assetId, destination: _destination, description: _description, assetInfo: _assetInfo) + var _lnurlInfo: LnUrlInfo? + if let lnurlInfoTmp = paymentDetails["lnurlInfo"] as? [String: Any?] { + _lnurlInfo = try asLnUrlInfo(lnUrlInfo: lnurlInfoTmp) + } + + let _bip353Address = paymentDetails["bip353Address"] as? String + + return PaymentDetails.liquid(assetId: _assetId, destination: _destination, description: _description, assetInfo: _assetInfo, lnurlInfo: _lnurlInfo, bip353Address: _bip353Address) } if type == "bitcoin" { guard let _swapId = paymentDetails["swapId"] as? String else { @@ -4284,7 +4291,7 @@ enum BreezSDKLiquidMapper { ] case let .liquid( - assetId, destination, description, assetInfo + assetId, destination, description, assetInfo, lnurlInfo, bip353Address ): return [ "type": "liquid", @@ -4292,6 +4299,8 @@ enum BreezSDKLiquidMapper { "destination": destination, "description": description, "assetInfo": assetInfo == nil ? nil : dictionaryOf(assetInfo: assetInfo!), + "lnurlInfo": lnurlInfo == nil ? nil : dictionaryOf(lnUrlInfo: lnurlInfo!), + "bip353Address": bip353Address == nil ? nil : bip353Address, ] case let .bitcoin( @@ -4716,7 +4725,9 @@ enum BreezSDKLiquidMapper { } let _addressData = try asLiquidAddressData(liquidAddressData: addressDataTmp) - return SendDestination.liquidAddress(addressData: _addressData) + let _bip353Address = sendDestination["bip353Address"] as? String + + return SendDestination.liquidAddress(addressData: _addressData, bip353Address: _bip353Address) } if type == "bolt11" { guard let invoiceTmp = sendDestination["invoice"] as? [String: Any?] else { @@ -4748,11 +4759,12 @@ enum BreezSDKLiquidMapper { static func dictionaryOf(sendDestination: SendDestination) -> [String: Any?] { switch sendDestination { case let .liquidAddress( - addressData + addressData, bip353Address ): return [ "type": "liquidAddress", "addressData": dictionaryOf(liquidAddressData: addressData), + "bip353Address": bip353Address == nil ? nil : bip353Address, ] case let .bolt11( diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index 0e52010..506b00c 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -700,6 +700,8 @@ export type PaymentDetails = { destination: string description: string assetInfo?: AssetInfo + lnurlInfo?: LnUrlInfo + bip353Address?: string } | { type: PaymentDetailsVariant.BITCOIN, swapId: string @@ -797,6 +799,7 @@ export enum SendDestinationVariant { export type SendDestination = { type: SendDestinationVariant.LIQUID_ADDRESS, addressData: LiquidAddressData + bip353Address?: string } | { type: SendDestinationVariant.BOLT11, invoice: LnInvoice