From 1e1a00d3f86ce957dee87240ff6bb601f7951c5e Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Wed, 6 Nov 2024 20:16:03 +0100 Subject: [PATCH 01/22] Add initial version of paying to a BOLT12 offer --- cli/Cargo.lock | 149 +++++++++++++++++- cli/src/commands.rs | 48 +++--- lib/Cargo.lock | 147 ++++++++++++++++- .../include/breez_sdk_liquid.h | 9 +- lib/bindings/src/breez_sdk_liquid.udl | 1 + lib/core/Cargo.toml | 5 +- lib/core/src/model.rs | 5 +- lib/core/src/persist/mod.rs | 13 +- lib/core/src/sdk.rs | 135 ++++++++++++---- lib/core/src/swapper/boltz/mod.rs | 6 + lib/core/src/swapper/mod.rs | 2 + lib/core/src/utils.rs | 18 ++- 12 files changed, 463 insertions(+), 75 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 4d0b8f500..ca5fbf1bb 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -194,6 +194,12 @@ dependencies = [ "backtrace", ] +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "asn1-rs" version = "0.6.2" @@ -355,6 +361,16 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base58ck" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" +dependencies = [ + "bitcoin-internals 0.3.0", + "bitcoin_hashes 0.14.0", +] + [[package]] name = "base64" version = "0.13.1" @@ -385,6 +401,12 @@ version = "0.10.0-beta" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea" +[[package]] +name = "bech32" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" + [[package]] name = "bip21" version = "0.2.0" @@ -440,14 +462,31 @@ checksum = "6c85783c2fe40083ea54a33aa2f0ba58831d90fcd190f5bdc47e74e84d2a96ae" dependencies = [ "base64 0.21.7", "bech32 0.10.0-beta", - "bitcoin-internals", + "bitcoin-internals 0.2.0", "bitcoin_hashes 0.13.0", - "hex-conservative", + "hex-conservative 0.1.2", "hex_lit", "secp256k1 0.28.2", "serde", ] +[[package]] +name = "bitcoin" +version = "0.32.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "788902099d47c8682efe6a7afb01c8d58b9794ba66c06affd81c3d6b560743eb" +dependencies = [ + "base58ck", + "bech32 0.11.0", + "bitcoin-internals 0.3.0", + "bitcoin-io", + "bitcoin-units", + "bitcoin_hashes 0.14.0", + "hex-conservative 0.2.1", + "hex_lit", + "secp256k1 0.29.1", +] + [[package]] name = "bitcoin-internals" version = "0.2.0" @@ -457,12 +496,33 @@ dependencies = [ "serde", ] +[[package]] +name = "bitcoin-internals" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" + +[[package]] +name = "bitcoin-io" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" + [[package]] name = "bitcoin-private" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" +[[package]] +name = "bitcoin-units" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" +dependencies = [ + "bitcoin-internals 0.3.0", +] + [[package]] name = "bitcoin_hashes" version = "0.11.0" @@ -484,11 +544,21 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" dependencies = [ - "bitcoin-internals", - "hex-conservative", + "bitcoin-internals 0.2.0", + "hex-conservative 0.1.2", "serde", ] +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative 0.2.1", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -539,7 +609,7 @@ dependencies = [ [[package]] name = "boltz-client" version = "0.1.3" -source = "git+https://github.com/SatoshiPortal/boltz-rust?branch=trunk#8b8bddeb362b6d62ae92ed9a6cb759e1d0506a12" +source = "git+https://github.com/ok300/boltz-rust?branch=ok300-support-bolt12#5e5181552e822ce2b6c2b6642f903acba3118b16" dependencies = [ "bip39", "bitcoin 0.31.2", @@ -588,6 +658,7 @@ dependencies = [ "futures-util", "glob", "hex", + "lightning 0.0.125", "log", "lwk_common", "lwk_signer", @@ -1422,6 +1493,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex_lit" version = "0.1.1" @@ -1799,7 +1879,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d9b36ae12b379905bfc429ce5d4e8ca4a55c8dd3de73074309bd0bcc053bcac" dependencies = [ "bitcoin 0.30.2", - "hex-conservative", + "hex-conservative 0.1.2", +] + +[[package]] +name = "lightning" +version = "0.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "767f388e50251da71f95a3737d6db32c9729f9de6427a54fa92bb994d04d793f" +dependencies = [ + "bech32 0.9.1", + "bitcoin 0.32.4", + "lightning-invoice 0.32.0", + "lightning-types", ] [[package]] @@ -1829,6 +1921,28 @@ dependencies = [ "secp256k1 0.27.0", ] +[[package]] +name = "lightning-invoice" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ab9f6ea77e20e3129235e62a2e6bd64ed932363df104e864ee65ccffb54a8f" +dependencies = [ + "bech32 0.9.1", + "bitcoin 0.32.4", + "lightning-types", +] + +[[package]] +name = "lightning-types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1083b8d9137000edf3bfcb1ff011c0d25e0cdd2feb98cc21d6765e64a494148f" +dependencies = [ + "bech32 0.9.1", + "bitcoin 0.32.4", + "hex-conservative 0.2.1", +] + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -1981,7 +2095,7 @@ checksum = "3127e10529a57a8f7fa9b1332c4c2f72baadaca6777798f910dff3c922620b14" dependencies = [ "bech32 0.10.0-beta", "bitcoin 0.31.2", - "bitcoin-internals", + "bitcoin-internals 0.2.0", ] [[package]] @@ -2992,6 +3106,16 @@ dependencies = [ "serde", ] +[[package]] +name = "secp256k1" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +dependencies = [ + "bitcoin_hashes 0.13.0", + "secp256k1-sys 0.10.1", +] + [[package]] name = "secp256k1-sys" version = "0.6.1" @@ -3019,6 +3143,15 @@ dependencies = [ "cc", ] +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + [[package]] name = "secp256k1-zkp" version = "0.10.0" @@ -3996,7 +4129,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/cli/src/commands.rs b/cli/src/commands.rs index 39ff2d6e9..30cad8563 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use std::thread; use std::time::Duration; -use anyhow::Result; +use anyhow::{anyhow, Result}; use breez_sdk_liquid::prelude::*; use clap::{arg, Parser}; use qrcode_rs::render::unicode; @@ -21,10 +21,14 @@ use serde_json::to_string_pretty; pub(crate) enum Command { /// Send a payment directly or via a swap SendPayment { - /// Invoice which has to be paid + /// Invoice which has to be paid (BOLT11 or BOLT12) #[arg(long)] invoice: Option, + /// BOLT12 offer. If specified, amount_sat must also be set. + #[arg(long)] + offer: Option, + /// Either BIP21 URI or Liquid address we intend to pay to #[arg(long)] address: Option, @@ -286,12 +290,12 @@ pub(crate) async fn handle_command( InputType::Bolt11 { invoice } => result.push_str(&build_qr_text(&invoice.bolt11)), InputType::LiquidAddress { address } => { result.push_str(&build_qr_text(&address.to_uri().map_err(|e| { - anyhow::anyhow!("Could not build BIP21 from address data: {e:?}") + anyhow!("Could not build BIP21 from address data: {e:?}") })?)) } InputType::BitcoinAddress { address } => { result.push_str(&build_qr_text(&address.to_uri().map_err(|e| { - anyhow::anyhow!("Could not build BIP21 from address data: {e:?}") + anyhow!("Could not build BIP21 from address data: {e:?}") })?)) } _ => {} @@ -308,24 +312,24 @@ pub(crate) async fn handle_command( } Command::SendPayment { invoice, + offer, address, amount_sat, delay, } => { - let destination = match (invoice, address) { - (None, None) => { - return Err(anyhow::anyhow!( - "Must specify either an invoice or a direct/BIP21 address." - )) - } - (Some(invoice), None) => invoice, - (None, Some(address)) => address, - (Some(_), Some(_)) => { - return Err(anyhow::anyhow!( - "Cannot specify both invoice and address at the same time." + let destination = match (invoice, offer, address) { + (Some(invoice), None, None) => Ok(invoice), + (None, Some(offer), None) => match amount_sat { + Some(_) => Ok(offer), + None => Err(anyhow!( + "Must specify either a BOLT11 invoice, a BOLT12 offer or a direct/BIP21 address." )) - } - }; + }, + (None, None, Some(address)) => Ok(address), + _ => Err(anyhow!( + "Must specify either a BOLT11 invoice, a BOLT12 offer or a direct/BIP21 address." + )) + }?; let prepare_response = sdk .prepare_send_payment(&PrepareSendRequest { @@ -368,7 +372,7 @@ pub(crate) async fn handle_command( let amount = match drain.unwrap_or(false) { true => PayOnchainAmount::Drain, false => PayOnchainAmount::Receiver { - amount_sat: receiver_amount_sat.ok_or(anyhow::anyhow!( + amount_sat: receiver_amount_sat.ok_or(anyhow!( "Must specify `receiver_amount_sat` if not draining" ))?, }, @@ -482,7 +486,7 @@ pub(crate) async fn handle_command( match maybe_payment { Some(payment) => command_result!(payment), None => { - return Err(anyhow::anyhow!("Payment not found.")); + return Err(anyhow!("Payment not found.")); } } } @@ -585,7 +589,7 @@ pub(crate) async fn handle_command( .await?; Ok(pay_res) } - _ => Err(anyhow::anyhow!("Invalid input")), + _ => Err(anyhow!("Invalid input")), }?; command_result!(res) @@ -609,7 +613,7 @@ pub(crate) async fn handle_command( .await?; Ok(withdraw_res) } - _ => Err(anyhow::anyhow!("Invalid input")), + _ => Err(anyhow!("Invalid input")), }?; command_result!(res) @@ -622,7 +626,7 @@ pub(crate) async fn handle_command( let auth_res = sdk.lnurl_auth(ad).await?; serde_json::to_string_pretty(&auth_res).map_err(|e| e.into()) } - _ => Err(anyhow::anyhow!("Unexpected result type")), + _ => Err(anyhow!("Unexpected result type")), }?; command_result!(res) diff --git a/lib/Cargo.lock b/lib/Cargo.lock index 28dfd6c8a..bed971841 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -188,6 +188,12 @@ dependencies = [ "backtrace", ] +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "askama" version = "0.11.1" @@ -429,6 +435,16 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base58ck" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" +dependencies = [ + "bitcoin-internals 0.3.0", + "bitcoin_hashes 0.14.0", +] + [[package]] name = "base64" version = "0.13.1" @@ -468,6 +484,12 @@ version = "0.10.0-beta" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea" +[[package]] +name = "bech32" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" + [[package]] name = "bincode" version = "1.3.3" @@ -554,14 +576,31 @@ checksum = "6c85783c2fe40083ea54a33aa2f0ba58831d90fcd190f5bdc47e74e84d2a96ae" dependencies = [ "base64 0.21.7", "bech32 0.10.0-beta", - "bitcoin-internals", + "bitcoin-internals 0.2.0", "bitcoin_hashes 0.13.0", - "hex-conservative", + "hex-conservative 0.1.2", "hex_lit", "secp256k1 0.28.2", "serde", ] +[[package]] +name = "bitcoin" +version = "0.32.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "788902099d47c8682efe6a7afb01c8d58b9794ba66c06affd81c3d6b560743eb" +dependencies = [ + "base58ck", + "bech32 0.11.0", + "bitcoin-internals 0.3.0", + "bitcoin-io", + "bitcoin-units", + "bitcoin_hashes 0.14.0", + "hex-conservative 0.2.1", + "hex_lit", + "secp256k1 0.29.1", +] + [[package]] name = "bitcoin-internals" version = "0.2.0" @@ -571,12 +610,33 @@ dependencies = [ "serde", ] +[[package]] +name = "bitcoin-internals" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" + +[[package]] +name = "bitcoin-io" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" + [[package]] name = "bitcoin-private" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" +[[package]] +name = "bitcoin-units" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" +dependencies = [ + "bitcoin-internals 0.3.0", +] + [[package]] name = "bitcoin_hashes" version = "0.11.0" @@ -598,11 +658,21 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" dependencies = [ - "bitcoin-internals", - "hex-conservative", + "bitcoin-internals 0.2.0", + "hex-conservative 0.1.2", "serde", ] +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative 0.2.1", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -653,7 +723,7 @@ dependencies = [ [[package]] name = "boltz-client" version = "0.1.3" -source = "git+https://github.com/SatoshiPortal/boltz-rust?branch=trunk#8b8bddeb362b6d62ae92ed9a6cb759e1d0506a12" +source = "git+https://github.com/ok300/boltz-rust?branch=ok300-support-bolt12#5e5181552e822ce2b6c2b6642f903acba3118b16" dependencies = [ "bip39", "bitcoin 0.31.2", @@ -687,6 +757,7 @@ dependencies = [ "glob", "hex", "lazy_static", + "lightning 0.0.125", "log", "lwk_common", "lwk_signer", @@ -1606,6 +1677,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex_lit" version = "0.1.1" @@ -2002,7 +2082,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d9b36ae12b379905bfc429ce5d4e8ca4a55c8dd3de73074309bd0bcc053bcac" dependencies = [ "bitcoin 0.30.2", - "hex-conservative", + "hex-conservative 0.1.2", +] + +[[package]] +name = "lightning" +version = "0.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "767f388e50251da71f95a3737d6db32c9729f9de6427a54fa92bb994d04d793f" +dependencies = [ + "bech32 0.9.1", + "bitcoin 0.32.4", + "lightning-invoice 0.32.0", + "lightning-types", ] [[package]] @@ -2032,6 +2124,28 @@ dependencies = [ "secp256k1 0.27.0", ] +[[package]] +name = "lightning-invoice" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ab9f6ea77e20e3129235e62a2e6bd64ed932363df104e864ee65ccffb54a8f" +dependencies = [ + "bech32 0.9.1", + "bitcoin 0.32.4", + "lightning-types", +] + +[[package]] +name = "lightning-types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1083b8d9137000edf3bfcb1ff011c0d25e0cdd2feb98cc21d6765e64a494148f" +dependencies = [ + "bech32 0.9.1", + "bitcoin 0.32.4", + "hex-conservative 0.2.1", +] + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -2194,7 +2308,7 @@ checksum = "3127e10529a57a8f7fa9b1332c4c2f72baadaca6777798f910dff3c922620b14" dependencies = [ "bech32 0.10.0-beta", "bitcoin 0.31.2", - "bitcoin-internals", + "bitcoin-internals 0.2.0", ] [[package]] @@ -3240,6 +3354,16 @@ dependencies = [ "serde", ] +[[package]] +name = "secp256k1" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +dependencies = [ + "bitcoin_hashes 0.13.0", + "secp256k1-sys 0.10.1", +] + [[package]] name = "secp256k1-sys" version = "0.6.1" @@ -3267,6 +3391,15 @@ dependencies = [ "cc", ] +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + [[package]] name = "secp256k1-zkp" version = "0.10.0" 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 d02b1bd78..8de814972 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 @@ -171,9 +171,14 @@ typedef struct wire_cst_SendDestination_Bolt11 { struct wire_cst_ln_invoice *invoice; } wire_cst_SendDestination_Bolt11; +typedef struct wire_cst_SendDestination_Bolt12 { + struct wire_cst_list_prim_u_8_strict *invoice; +} wire_cst_SendDestination_Bolt12; + typedef union SendDestinationKind { struct wire_cst_SendDestination_LiquidAddress LiquidAddress; struct wire_cst_SendDestination_Bolt11 Bolt11; + struct wire_cst_SendDestination_Bolt12 Bolt12; } SendDestinationKind; typedef struct wire_cst_send_destination { @@ -1108,7 +1113,7 @@ WireSyncRust2DartDco frbgen_breez_liquid_wire__crate__bindings__default_config(i void frbgen_breez_liquid_wire__crate__bindings__parse(int64_t port_, struct wire_cst_list_prim_u_8_strict *input); -WireSyncRust2DartDco frbgen_breez_liquid_wire__crate__bindings__parse_invoice(struct wire_cst_list_prim_u_8_strict *input); +WireSyncRust2DartDco frbgen_breez_liquid_wire__crate__bindings__parse_bolt11_invoice(struct wire_cst_list_prim_u_8_strict *input); void frbgen_breez_liquid_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk(const void *ptr); @@ -1324,7 +1329,7 @@ static int64_t dummy_method_to_enforce_bundling(void) { dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__connect); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__default_config); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__parse); - dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__parse_invoice); + dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__parse_bolt11_invoice); dummy_var ^= ((int64_t) (void*) store_dart_post_cobject); return dummy_var; } diff --git a/lib/bindings/src/breez_sdk_liquid.udl b/lib/bindings/src/breez_sdk_liquid.udl index 290267558..553506246 100644 --- a/lib/bindings/src/breez_sdk_liquid.udl +++ b/lib/bindings/src/breez_sdk_liquid.udl @@ -380,6 +380,7 @@ dictionary PrepareSendRequest { interface SendDestination { LiquidAddress(LiquidAddressData address_data); Bolt11(LNInvoice invoice); + Bolt12(string invoice); }; dictionary PrepareSendResponse { diff --git a/lib/core/Cargo.toml b/lib/core/Cargo.toml index 9a9fb5add..72554549f 100644 --- a/lib/core/Cargo.toml +++ b/lib/core/Cargo.toml @@ -14,12 +14,15 @@ frb = ["dep:flutter_rust_bridge"] [dependencies] anyhow = { workspace = true } bip39 = "2.0.0" -boltz-client = { git = "https://github.com/SatoshiPortal/boltz-rust", branch = "trunk" } +#boltz-client = { git = "https://github.com/SatoshiPortal/boltz-rust", branch = "trunk" } +boltz-client = { git = "https://github.com/ok300/boltz-rust", branch = "ok300-support-bolt12" } chrono = "0.4" env_logger = "0.11" flutter_rust_bridge = { version = "=2.4.0", features = [ "chrono", ], optional = true } +# We need at least lightning v0.0.125 for the Bolt12 structs. The lightning version from sdk-common is too old (v0.0.118, matching vls-core). +lightning = "0.0.125" log = { workspace = true } lwk_common = "0.7.0" lwk_signer = "0.7.0" diff --git a/lib/core/src/model.rs b/lib/core/src/model.rs index 467a315b3..e00c71012 100644 --- a/lib/core/src/model.rs +++ b/lib/core/src/model.rs @@ -345,7 +345,7 @@ pub struct OnchainPaymentLimitsResponse { #[derive(Debug, Serialize, Clone)] pub struct PrepareSendRequest { /// The destination we intend to pay to. - /// Supports BIP21 URIs, BOLT11 invoices and Liquid addresses + /// Supports BIP21 URIs, BOLT11 invoices, BOLT12 offers and Liquid addresses pub destination: String, /// Should only be set when paying directly onchain or to a BIP21 URI @@ -362,6 +362,9 @@ pub enum SendDestination { Bolt11 { invoice: LNInvoice, }, + Bolt12 { + offer: String, + }, } /// Returned when calling [crate::sdk::LiquidSdk::prepare_send_payment]. diff --git a/lib/core/src/persist/mod.rs b/lib/core/src/persist/mod.rs index 5f3967b37..a14b610ce 100644 --- a/lib/core/src/persist/mod.rs +++ b/lib/core/src/persist/mod.rs @@ -312,11 +312,14 @@ impl Persister { preimage: maybe_send_swap_preimage, bolt11: maybe_send_swap_invoice.clone(), payment_hash: maybe_send_swap_payment_hash, - description: maybe_send_swap_description.unwrap_or_else(|| { - maybe_send_swap_invoice - .and_then(|bolt11| get_invoice_description!(bolt11)) - .unwrap_or("Lightning payment".to_string()) - }), + + description: "Lightning payment".to_string(), + // TODO Should not assume this is bolt11 + // description: maybe_send_swap_description.unwrap_or_else(|| { + // maybe_send_swap_invoice + // .and_then(|bolt11| get_invoice_description!(bolt11)) + // .unwrap_or("Lightning payment".to_string()) + // }), payer_amount_sat: maybe_send_swap_payer_amount_sat.unwrap_or(0), receiver_amount_sat: maybe_send_swap_receiver_amount_sat.unwrap_or(0), refund_tx_id: maybe_send_swap_refund_tx_id, diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index 5f43fe168..ba26ec29e 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -706,7 +706,7 @@ impl LiquidSdk { /// # Arguments /// /// * `req` - the [PrepareSendRequest] containing: - /// * `destination` - Either a Liquid BIP21 URI/address or a BOLT11 invoice + /// * `destination` - Either a Liquid BIP21 URI/address. a BOLT11 invoice or a BOLT12 offer /// * `amount_sat` - Should only be specified when paying directly onchain or via amount-less BIP21 /// /// # Returns @@ -723,10 +723,10 @@ impl LiquidSdk { let receiver_amount_sat; let payment_destination; - match sdk_common::input_parser::parse(&req.destination).await? { - InputType::LiquidAddress { + match sdk_common::input_parser::parse(&req.destination).await { + Ok(InputType::LiquidAddress { address: mut liquid_address_data, - } => { + }) => { let amount_sat = match (liquid_address_data.amount_sat, req.amount_sat) { (None, None) => { return Err(PaymentError::AmountMissing { err: "`amount_sat` must be present when paying to a `SendDestination::LiquidAddress`".to_string() }); @@ -756,7 +756,7 @@ impl LiquidSdk { address_data: liquid_address_data, }; } - InputType::Bolt11 { invoice } => { + Ok(InputType::Bolt11 { invoice }) => { self.ensure_send_is_not_self_transfer(&invoice.bolt11)?; self.validate_invoice(&invoice.bolt11)?; @@ -788,11 +788,54 @@ impl LiquidSdk { }; payment_destination = SendDestination::Bolt11 { invoice }; } - _ => { + Ok(_) => { return Err(PaymentError::Generic { err: "Destination is not valid".to_string(), }); } + Err(_) => { + // TODO Workaround to avoid adding an InputType::Bolt12 variant in sdk-common + + // TODO TBD: Should InputType::Bolt12_Offer be added to sdk-common? Or Bolt12_Invoice? + // If yes, with full parsing and mapping to LNInvoice? + + let amount_sat = req + .amount_sat + .ok_or_else(|| anyhow!("Expected amount when processing BOLT12 offer"))?; + + let offer = &req.destination; + info!("Got BOLT12 offer, fetching BOLT12 invoice"); + + let invoice_str = self.swapper.get_bolt12_invoice(offer, amount_sat)?; + let invoice_parsed = utils::parse_bolt12_invoice(&invoice_str)?; + + // TODO If supporting receive: validate it's not a self-transfer + // TODO Validate invoice + + receiver_amount_sat = invoice_parsed.amount_msats() / 1_000; + + if let Some(amount_sat) = req.amount_sat { + ensure_sdk!( + receiver_amount_sat == amount_sat, + PaymentError::Generic { err: "Amount in the payment request is not the same as the one in the invoice".to_string() } + ); + } + + let lbtc_pair = self.validate_submarine_pairs(receiver_amount_sat)?; + + // TODO Check for MRH for Bolt12 + // TODO If MRH, fallback to onchain + + let boltz_fees_total = lbtc_pair.fees.total(receiver_amount_sat); + let lockup_fees_sat = self + .estimate_lockup_tx_fee_send(receiver_amount_sat + boltz_fees_total) + .await?; + fees_sat = boltz_fees_total + lockup_fees_sat; + + payment_destination = SendDestination::Bolt12 { + offer: req.destination.to_string(), + }; + } }; let payer_amount_sat = receiver_amount_sat + fees_sat; @@ -832,6 +875,7 @@ impl LiquidSdk { pub async fn send_payment( &self, req: &SendPaymentRequest, + // TODO Req must include BOLT12 invoice (which is not available during prepare) and amount ) -> Result { self.ensure_is_started().await?; @@ -869,18 +913,24 @@ impl LiquidSdk { .await } SendDestination::Bolt11 { invoice } => { - self.pay_invoice(&invoice.bolt11, *fees_sat).await + self.pay_bolt11_invoice(&invoice.bolt11, *fees_sat).await + } + SendDestination::Bolt12 { offer } => { + // TODO Amount hardcoded to 1_000 because we don't yet have it in the request + let bolt12_invoice = self.swapper.get_bolt12_invoice(offer, 1_000)?; + + self.pay_bolt12_invoice(&bolt12_invoice, *fees_sat).await } } } - async fn pay_invoice( + async fn pay_bolt11_invoice( &self, invoice: &str, fees_sat: u64, ) -> Result { self.ensure_send_is_not_self_transfer(invoice)?; - self.validate_invoice(invoice)?; + let bolt11_invoice = self.validate_invoice(invoice)?; let amount_sat = get_invoice_amount!(invoice); let payer_amount_sat = amount_sat + fees_sat; @@ -889,6 +939,11 @@ impl LiquidSdk { PaymentError::InsufficientFunds ); + let description = match bolt11_invoice.description() { + Bolt11InvoiceDescription::Direct(msg) => Some(msg.to_string()), + Bolt11InvoiceDescription::Hash(_) => None, + }; + match self.swapper.check_for_mrh(invoice)? { // If we find a valid MRH, extract the BIP21 address and pay to it via onchain tx Some((address, _)) => { @@ -909,10 +964,48 @@ impl LiquidSdk { } // If no MRH found, perform usual swap - None => self.send_payment_via_swap(invoice, fees_sat).await, + None => { + self.send_payment_via_swap( + invoice, + &bolt11_invoice.payment_hash().to_string(), + description, + amount_sat, + fees_sat, + ) + .await + } } } + async fn pay_bolt12_invoice( + &self, + invoice: &str, + fees_sat: u64, + ) -> Result { + // TODO Ensure it's not self-transfer + // TODO Validate invoice + + let invoice_parsed = utils::parse_bolt12_invoice(invoice)?; + + let amount_sat = invoice_parsed.amount_msats() / 1_000; + let payer_amount_sat = amount_sat + fees_sat; + ensure_sdk!( + payer_amount_sat <= self.get_info().await?.balance_sat, + PaymentError::InsufficientFunds + ); + + // TODO CHeck for MRH (if present, pay via Liquid) + + self.send_payment_via_swap( + invoice, + &invoice_parsed.payment_hash().to_string(), + invoice_parsed.description().map(|desc| desc.to_string()), + amount_sat, + fees_sat, + ) + .await + } + /// Performs a Send Payment by doing an onchain tx to a L-BTC address async fn pay_liquid( &self, @@ -977,20 +1070,11 @@ impl LiquidSdk { async fn send_payment_via_swap( &self, invoice: &str, + payment_hash: &str, + description: Option, + receiver_amount_sat: u64, fees_sat: u64, ) -> Result { - let bolt11_invoice = invoice - .trim() - .parse::() - .map_err(|err| PaymentError::invalid_invoice(&err.to_string()))?; - let receiver_amount_sat = - bolt11_invoice - .amount_milli_satoshis() - .ok_or(PaymentError::invalid_invoice( - "Invoice does not contain an amount", - ))? - / 1000; - let lbtc_pair = self.validate_submarine_pairs(receiver_amount_sat)?; let boltz_fees_total = lbtc_pair.fees.total(receiver_amount_sat); let lockup_tx_fees_sat = self @@ -1047,17 +1131,12 @@ impl LiquidSdk { let swap_id = &create_response.id; let create_response_json = SendSwap::from_boltz_struct_to_json(&create_response, swap_id)?; - let payment_hash = bolt11_invoice.payment_hash().to_string(); - let description = match bolt11_invoice.description() { - Bolt11InvoiceDescription::Direct(msg) => Some(msg.to_string()), - Bolt11InvoiceDescription::Hash(_) => None, - }; let payer_amount_sat = fees_sat + receiver_amount_sat; let swap = SendSwap { id: swap_id.clone(), invoice: invoice.to_string(), - payment_hash: Some(payment_hash), + payment_hash: Some(payment_hash.to_string()), description, preimage: None, payer_amount_sat, diff --git a/lib/core/src/swapper/boltz/mod.rs b/lib/core/src/swapper/boltz/mod.rs index 6ee15ac92..4698d8cf5 100644 --- a/lib/core/src/swapper/boltz/mod.rs +++ b/lib/core/src/swapper/boltz/mod.rs @@ -413,4 +413,10 @@ impl Swapper for BoltzSwapper { ) .map_err(Into::into) } + + fn get_bolt12_invoice(&self, offer: &str, amount_sat: u64) -> Result { + let invoice_res = self.client.get_bolt12_invoice(offer, amount_sat)?; + info!("Received BOLT12 invoice response: {invoice_res:?}"); + Ok(invoice_res.invoice) + } } diff --git a/lib/core/src/swapper/mod.rs b/lib/core/src/swapper/mod.rs index 499069410..bc8ab01fd 100644 --- a/lib/core/src/swapper/mod.rs +++ b/lib/core/src/swapper/mod.rs @@ -104,6 +104,8 @@ pub trait Swapper: Send + Sync { &self, invoice: &str, ) -> Result, PaymentError>; + + fn get_bolt12_invoice(&self, offer: &str, amount_sat: u64) -> Result; } #[async_trait] diff --git a/lib/core/src/utils.rs b/lib/core/src/utils.rs index 14e8e1205..c10af5e74 100644 --- a/lib/core/src/utils.rs +++ b/lib/core/src/utils.rs @@ -2,13 +2,16 @@ use std::str::FromStr; use std::time::{SystemTime, UNIX_EPOCH}; use crate::error::{PaymentError, SdkResult}; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, ensure, Result}; +use lightning::offers::invoice::Bolt12Invoice; use lwk_wollet::elements::encode::deserialize; use lwk_wollet::elements::hex::FromHex; use lwk_wollet::elements::{ LockTime::{self, *}, Transaction, }; +use sdk_common::bitcoin::bech32; +use sdk_common::bitcoin::bech32::FromBase32; pub(crate) fn now() -> u32 { SystemTime::now() @@ -49,3 +52,16 @@ pub(crate) fn deserialize_tx_hex(tx_hex: &str) -> Result { |err| anyhow!("Could not deserialize transaction: {err:?}"), )?)?) } + +/// Parsing logic that decodes a string into a [Bolt12Invoice]. +/// +/// It matches the encoding logic on Boltz side. +pub(crate) fn parse_bolt12_invoice(invoice: &str) -> Result { + let (hrp, data) = bech32::decode_without_checksum(invoice)?; + ensure!(hrp.as_str() == "lni", "Invalid HRP"); + + let data = Vec::::from_base32(&data)?; + + lightning::offers::invoice::Bolt12Invoice::try_from(data) + .map_err(|e| anyhow!("Failed to parse BOLT12: {e:?}")) +} From 06e6529afc4641637c789ad0278aa2a7004ba9b2 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Sun, 10 Nov 2024 14:44:13 +0100 Subject: [PATCH 02/22] Bump sdk-common --- lib/Cargo.lock | 4 +- .../include/breez_sdk_liquid.h | 11 ++- lib/bindings/src/breez_sdk_liquid.udl | 3 +- lib/core/Cargo.toml | 3 +- lib/core/src/bindings.rs | 1 + lib/core/src/frb_generated.rs | 93 ++++++++++++++----- packages/dart/lib/src/bindings.dart | 3 + packages/dart/lib/src/bindings.freezed.dart | 77 +++++++++++++++ packages/dart/lib/src/frb_generated.dart | 52 +++++++---- packages/dart/lib/src/frb_generated.io.dart | 36 +++++-- packages/dart/lib/src/model.dart | 5 +- packages/dart/lib/src/model.freezed.dart | 79 ++++++++++++++++ ...utter_breez_liquid_bindings_generated.dart | 12 +++ 13 files changed, 327 insertions(+), 52 deletions(-) diff --git a/lib/Cargo.lock b/lib/Cargo.lock index bed971841..fcb9d69d4 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -3293,8 +3293,8 @@ dependencies = [ [[package]] name = "sdk-common" -version = "0.5.2" -source = "git+https://github.com/breez/breez-sdk?rev=441a9fd50c32098b2887e960c8a4bcc5956da1af#441a9fd50c32098b2887e960c8a4bcc5956da1af" +version = "0.6.2" +source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#fa01549242a786f3fc9ccffa134e47ba462838db" dependencies = [ "aes 0.8.4", "anyhow", 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 8de814972..0b717ba58 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 @@ -172,7 +172,7 @@ typedef struct wire_cst_SendDestination_Bolt11 { } wire_cst_SendDestination_Bolt11; typedef struct wire_cst_SendDestination_Bolt12 { - struct wire_cst_list_prim_u_8_strict *invoice; + struct wire_cst_list_prim_u_8_strict *offer; } wire_cst_SendDestination_Bolt12; typedef union SendDestinationKind { @@ -626,6 +626,10 @@ typedef struct wire_cst_InputType_Bolt11 { struct wire_cst_ln_invoice *invoice; } wire_cst_InputType_Bolt11; +typedef struct wire_cst_InputType_Bolt12 { + struct wire_cst_list_prim_u_8_strict *offer; +} wire_cst_InputType_Bolt12; + typedef struct wire_cst_InputType_NodeId { struct wire_cst_list_prim_u_8_strict *node_id; } wire_cst_InputType_NodeId; @@ -654,6 +658,7 @@ typedef union InputTypeKind { struct wire_cst_InputType_BitcoinAddress BitcoinAddress; struct wire_cst_InputType_LiquidAddress LiquidAddress; struct wire_cst_InputType_Bolt11 Bolt11; + struct wire_cst_InputType_Bolt12 Bolt12; struct wire_cst_InputType_NodeId NodeId; struct wire_cst_InputType_Url Url; struct wire_cst_InputType_LnUrlPay LnUrlPay; @@ -1113,7 +1118,7 @@ WireSyncRust2DartDco frbgen_breez_liquid_wire__crate__bindings__default_config(i void frbgen_breez_liquid_wire__crate__bindings__parse(int64_t port_, struct wire_cst_list_prim_u_8_strict *input); -WireSyncRust2DartDco frbgen_breez_liquid_wire__crate__bindings__parse_bolt11_invoice(struct wire_cst_list_prim_u_8_strict *input); +WireSyncRust2DartDco frbgen_breez_liquid_wire__crate__bindings__parse_invoice(struct wire_cst_list_prim_u_8_strict *input); void frbgen_breez_liquid_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk(const void *ptr); @@ -1329,7 +1334,7 @@ static int64_t dummy_method_to_enforce_bundling(void) { dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__connect); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__default_config); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__parse); - dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__parse_bolt11_invoice); + dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__parse_invoice); dummy_var ^= ((int64_t) (void*) store_dart_post_cobject); return dummy_var; } diff --git a/lib/bindings/src/breez_sdk_liquid.udl b/lib/bindings/src/breez_sdk_liquid.udl index 553506246..22c39efdc 100644 --- a/lib/bindings/src/breez_sdk_liquid.udl +++ b/lib/bindings/src/breez_sdk_liquid.udl @@ -43,6 +43,7 @@ interface InputType { BitcoinAddress(BitcoinAddressData address); LiquidAddress(LiquidAddressData address); Bolt11(LNInvoice invoice); + Bolt12(string offer); NodeId(string node_id); Url(string url); LnUrlPay(LnUrlPayRequestData data); @@ -380,7 +381,7 @@ dictionary PrepareSendRequest { interface SendDestination { LiquidAddress(LiquidAddressData address_data); Bolt11(LNInvoice invoice); - Bolt12(string invoice); + Bolt12(string offer); }; dictionary PrepareSendResponse { diff --git a/lib/core/Cargo.toml b/lib/core/Cargo.toml index 72554549f..8ac29d36e 100644 --- a/lib/core/Cargo.toml +++ b/lib/core/Cargo.toml @@ -30,7 +30,8 @@ lwk_wollet = { git = "https://github.com/dangeross/lwk", branch = "savage-full-s #lwk_wollet = "0.7.0" rusqlite = { version = "0.31", features = ["backup", "bundled"] } rusqlite_migration = "1.0" -sdk-common = { git = "https://github.com/breez/breez-sdk", rev = "441a9fd50c32098b2887e960c8a4bcc5956da1af", features = ["liquid"]} +#sdk-common = { git = "https://github.com/breez/breez-sdk", rev = "441a9fd50c32098b2887e960c8a4bcc5956da1af", features = ["liquid"]} +sdk-common = { git = "https://github.com/breez/breez-sdk", branch = "ok300-sdk-common-parse-bolt12-offer", features = ["liquid"]} serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.116" strum = "0.25" diff --git a/lib/core/src/bindings.rs b/lib/core/src/bindings.rs index 7b82badff..d36e16faa 100644 --- a/lib/core/src/bindings.rs +++ b/lib/core/src/bindings.rs @@ -334,6 +334,7 @@ pub enum _InputType { BitcoinAddress { address: BitcoinAddressData }, LiquidAddress { address: LiquidAddressData }, Bolt11 { invoice: LNInvoice }, + Bolt12 { offer: String }, NodeId { node_id: String }, Url { url: String }, LnUrlPay { data: LnUrlPayRequestData }, diff --git a/lib/core/src/frb_generated.rs b/lib/core/src/frb_generated.rs index 54c3f65aa..78309adbc 100644 --- a/lib/core/src/frb_generated.rs +++ b/lib/core/src/frb_generated.rs @@ -1798,6 +1798,9 @@ const _: fn() = || { crate::bindings::InputType::Bolt11 { invoice } => { let _: crate::bindings::LNInvoice = invoice; } + crate::bindings::InputType::Bolt12 { offer } => { + let _: String = offer; + } crate::bindings::InputType::NodeId { node_id } => { let _: String = node_id; } @@ -2432,30 +2435,34 @@ impl SseDecode for crate::bindings::InputType { }; } 3 => { + let mut var_offer = ::sse_decode(deserializer); + return crate::bindings::InputType::Bolt12 { offer: var_offer }; + } + 4 => { let mut var_nodeId = ::sse_decode(deserializer); return crate::bindings::InputType::NodeId { node_id: var_nodeId, }; } - 4 => { + 5 => { let mut var_url = ::sse_decode(deserializer); return crate::bindings::InputType::Url { url: var_url }; } - 5 => { + 6 => { let mut var_data = ::sse_decode(deserializer); return crate::bindings::InputType::LnUrlPay { data: var_data }; } - 6 => { + 7 => { let mut var_data = ::sse_decode(deserializer); return crate::bindings::InputType::LnUrlWithdraw { data: var_data }; } - 7 => { + 8 => { let mut var_data = ::sse_decode(deserializer); return crate::bindings::InputType::LnUrlAuth { data: var_data }; } - 8 => { + 9 => { let mut var_data = ::sse_decode(deserializer); return crate::bindings::InputType::LnUrlError { data: var_data }; } @@ -3887,6 +3894,10 @@ impl SseDecode for crate::model::SendDestination { invoice: var_invoice, }; } + 2 => { + let mut var_offer = ::sse_decode(deserializer); + return crate::model::SendDestination::Bolt12 { offer: var_offer }; + } _ => { unimplemented!(""); } @@ -4453,23 +4464,26 @@ impl flutter_rust_bridge::IntoDart for FrbWrapper { crate::bindings::InputType::Bolt11 { invoice } => { [2.into_dart(), invoice.into_into_dart().into_dart()].into_dart() } + crate::bindings::InputType::Bolt12 { offer } => { + [3.into_dart(), offer.into_into_dart().into_dart()].into_dart() + } crate::bindings::InputType::NodeId { node_id } => { - [3.into_dart(), node_id.into_into_dart().into_dart()].into_dart() + [4.into_dart(), node_id.into_into_dart().into_dart()].into_dart() } crate::bindings::InputType::Url { url } => { - [4.into_dart(), url.into_into_dart().into_dart()].into_dart() + [5.into_dart(), url.into_into_dart().into_dart()].into_dart() } crate::bindings::InputType::LnUrlPay { data } => { - [5.into_dart(), data.into_into_dart().into_dart()].into_dart() + [6.into_dart(), data.into_into_dart().into_dart()].into_dart() } crate::bindings::InputType::LnUrlWithdraw { data } => { - [6.into_dart(), data.into_into_dart().into_dart()].into_dart() + [7.into_dart(), data.into_into_dart().into_dart()].into_dart() } crate::bindings::InputType::LnUrlAuth { data } => { - [7.into_dart(), data.into_into_dart().into_dart()].into_dart() + [8.into_dart(), data.into_into_dart().into_dart()].into_dart() } crate::bindings::InputType::LnUrlError { data } => { - [8.into_dart(), data.into_into_dart().into_dart()].into_dart() + [9.into_dart(), data.into_into_dart().into_dart()].into_dart() } _ => { unimplemented!(""); @@ -5935,6 +5949,9 @@ impl flutter_rust_bridge::IntoDart for crate::model::SendDestination { crate::model::SendDestination::Bolt11 { invoice } => { [1.into_dart(), invoice.into_into_dart().into_dart()].into_dart() } + crate::model::SendDestination::Bolt12 { offer } => { + [2.into_dart(), offer.into_into_dart().into_dart()].into_dart() + } _ => { unimplemented!(""); } @@ -6387,28 +6404,32 @@ impl SseEncode for crate::bindings::InputType { ::sse_encode(2, serializer); ::sse_encode(invoice, serializer); } - crate::bindings::InputType::NodeId { node_id } => { + crate::bindings::InputType::Bolt12 { offer } => { ::sse_encode(3, serializer); + ::sse_encode(offer, serializer); + } + crate::bindings::InputType::NodeId { node_id } => { + ::sse_encode(4, serializer); ::sse_encode(node_id, serializer); } crate::bindings::InputType::Url { url } => { - ::sse_encode(4, serializer); + ::sse_encode(5, serializer); ::sse_encode(url, serializer); } crate::bindings::InputType::LnUrlPay { data } => { - ::sse_encode(5, serializer); + ::sse_encode(6, serializer); ::sse_encode(data, serializer); } crate::bindings::InputType::LnUrlWithdraw { data } => { - ::sse_encode(6, serializer); + ::sse_encode(7, serializer); ::sse_encode(data, serializer); } crate::bindings::InputType::LnUrlAuth { data } => { - ::sse_encode(7, serializer); + ::sse_encode(8, serializer); ::sse_encode(data, serializer); } crate::bindings::InputType::LnUrlError { data } => { - ::sse_encode(8, serializer); + ::sse_encode(9, serializer); ::sse_encode(data, serializer); } _ => { @@ -7543,6 +7564,10 @@ impl SseEncode for crate::model::SendDestination { ::sse_encode(1, serializer); ::sse_encode(invoice, serializer); } + crate::model::SendDestination::Bolt12 { offer } => { + ::sse_encode(2, serializer); + ::sse_encode(offer, serializer); + } _ => { unimplemented!(""); } @@ -8302,36 +8327,42 @@ mod io { } } 3 => { + let ans = unsafe { self.kind.Bolt12 }; + crate::bindings::InputType::Bolt12 { + offer: ans.offer.cst_decode(), + } + } + 4 => { let ans = unsafe { self.kind.NodeId }; crate::bindings::InputType::NodeId { node_id: ans.node_id.cst_decode(), } } - 4 => { + 5 => { let ans = unsafe { self.kind.Url }; crate::bindings::InputType::Url { url: ans.url.cst_decode(), } } - 5 => { + 6 => { let ans = unsafe { self.kind.LnUrlPay }; crate::bindings::InputType::LnUrlPay { data: ans.data.cst_decode(), } } - 6 => { + 7 => { let ans = unsafe { self.kind.LnUrlWithdraw }; crate::bindings::InputType::LnUrlWithdraw { data: ans.data.cst_decode(), } } - 7 => { + 8 => { let ans = unsafe { self.kind.LnUrlAuth }; crate::bindings::InputType::LnUrlAuth { data: ans.data.cst_decode(), } } - 8 => { + 9 => { let ans = unsafe { self.kind.LnUrlError }; crate::bindings::InputType::LnUrlError { data: ans.data.cst_decode(), @@ -9335,6 +9366,12 @@ mod io { invoice: ans.invoice.cst_decode(), } } + 2 => { + let ans = unsafe { self.kind.Bolt12 }; + crate::model::SendDestination::Bolt12 { + offer: ans.offer.cst_decode(), + } + } _ => unreachable!(), } } @@ -11533,6 +11570,7 @@ mod io { BitcoinAddress: wire_cst_InputType_BitcoinAddress, LiquidAddress: wire_cst_InputType_LiquidAddress, Bolt11: wire_cst_InputType_Bolt11, + Bolt12: wire_cst_InputType_Bolt12, NodeId: wire_cst_InputType_NodeId, Url: wire_cst_InputType_Url, LnUrlPay: wire_cst_InputType_LnUrlPay, @@ -11558,6 +11596,11 @@ mod io { } #[repr(C)] #[derive(Clone, Copy)] + pub struct wire_cst_InputType_Bolt12 { + offer: *mut wire_cst_list_prim_u_8_strict, + } + #[repr(C)] + #[derive(Clone, Copy)] pub struct wire_cst_InputType_NodeId { node_id: *mut wire_cst_list_prim_u_8_strict, } @@ -12414,6 +12457,7 @@ mod io { pub union SendDestinationKind { LiquidAddress: wire_cst_SendDestination_LiquidAddress, Bolt11: wire_cst_SendDestination_Bolt11, + Bolt12: wire_cst_SendDestination_Bolt12, nil__: (), } #[repr(C)] @@ -12428,6 +12472,11 @@ mod io { } #[repr(C)] #[derive(Clone, Copy)] + pub struct wire_cst_SendDestination_Bolt12 { + offer: *mut wire_cst_list_prim_u_8_strict, + } + #[repr(C)] + #[derive(Clone, Copy)] pub struct wire_cst_send_payment_request { prepare_response: wire_cst_prepare_send_response, } diff --git a/packages/dart/lib/src/bindings.dart b/packages/dart/lib/src/bindings.dart index c97737fb2..0710371d9 100644 --- a/packages/dart/lib/src/bindings.dart +++ b/packages/dart/lib/src/bindings.dart @@ -281,6 +281,9 @@ sealed class InputType with _$InputType { const factory InputType.bolt11({ required LNInvoice invoice, }) = InputType_Bolt11; + const factory InputType.bolt12({ + required String offer, + }) = InputType_Bolt12; const factory InputType.nodeId({ required String nodeId, }) = InputType_NodeId; diff --git a/packages/dart/lib/src/bindings.freezed.dart b/packages/dart/lib/src/bindings.freezed.dart index bb26c07c2..71907d6de 100644 --- a/packages/dart/lib/src/bindings.freezed.dart +++ b/packages/dart/lib/src/bindings.freezed.dart @@ -462,6 +462,83 @@ abstract class InputType_Bolt11 extends InputType { _$$InputType_Bolt11ImplCopyWith<_$InputType_Bolt11Impl> get copyWith => throw _privateConstructorUsedError; } +/// @nodoc +abstract class _$$InputType_Bolt12ImplCopyWith<$Res> { + factory _$$InputType_Bolt12ImplCopyWith( + _$InputType_Bolt12Impl value, $Res Function(_$InputType_Bolt12Impl) then) = + __$$InputType_Bolt12ImplCopyWithImpl<$Res>; + @useResult + $Res call({String offer}); +} + +/// @nodoc +class __$$InputType_Bolt12ImplCopyWithImpl<$Res> extends _$InputTypeCopyWithImpl<$Res, _$InputType_Bolt12Impl> + implements _$$InputType_Bolt12ImplCopyWith<$Res> { + __$$InputType_Bolt12ImplCopyWithImpl( + _$InputType_Bolt12Impl _value, $Res Function(_$InputType_Bolt12Impl) _then) + : super(_value, _then); + + /// Create a copy of InputType + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? offer = null, + }) { + return _then(_$InputType_Bolt12Impl( + offer: null == offer + ? _value.offer + : offer // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$InputType_Bolt12Impl extends InputType_Bolt12 { + const _$InputType_Bolt12Impl({required this.offer}) : super._(); + + @override + final String offer; + + @override + String toString() { + return 'InputType.bolt12(offer: $offer)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$InputType_Bolt12Impl && + (identical(other.offer, offer) || other.offer == offer)); + } + + @override + int get hashCode => Object.hash(runtimeType, offer); + + /// Create a copy of InputType + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$InputType_Bolt12ImplCopyWith<_$InputType_Bolt12Impl> get copyWith => + __$$InputType_Bolt12ImplCopyWithImpl<_$InputType_Bolt12Impl>(this, _$identity); +} + +abstract class InputType_Bolt12 extends InputType { + const factory InputType_Bolt12({required final String offer}) = _$InputType_Bolt12Impl; + const InputType_Bolt12._() : super._(); + + String get offer; + + /// Create a copy of InputType + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$InputType_Bolt12ImplCopyWith<_$InputType_Bolt12Impl> get copyWith => throw _privateConstructorUsedError; +} + /// @nodoc abstract class _$$InputType_NodeIdImplCopyWith<$Res> { factory _$$InputType_NodeIdImplCopyWith( diff --git a/packages/dart/lib/src/frb_generated.dart b/packages/dart/lib/src/frb_generated.dart index a382c9dbb..6b9e24709 100644 --- a/packages/dart/lib/src/frb_generated.dart +++ b/packages/dart/lib/src/frb_generated.dart @@ -1760,26 +1760,30 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { invoice: dco_decode_box_autoadd_ln_invoice(raw[1]), ); case 3: + return InputType_Bolt12( + offer: dco_decode_String(raw[1]), + ); + case 4: return InputType_NodeId( nodeId: dco_decode_String(raw[1]), ); - case 4: + case 5: return InputType_Url( url: dco_decode_String(raw[1]), ); - case 5: + case 6: return InputType_LnUrlPay( data: dco_decode_box_autoadd_ln_url_pay_request_data(raw[1]), ); - case 6: + case 7: return InputType_LnUrlWithdraw( data: dco_decode_box_autoadd_ln_url_withdraw_request_data(raw[1]), ); - case 7: + case 8: return InputType_LnUrlAuth( data: dco_decode_box_autoadd_ln_url_auth_request_data(raw[1]), ); - case 8: + case 9: return InputType_LnUrlError( data: dco_decode_box_autoadd_ln_url_error_data(raw[1]), ); @@ -2836,6 +2840,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return SendDestination_Bolt11( invoice: dco_decode_box_autoadd_ln_invoice(raw[1]), ); + case 2: + return SendDestination_Bolt12( + offer: dco_decode_String(raw[1]), + ); default: throw Exception("unreachable"); } @@ -3539,21 +3547,24 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_invoice = sse_decode_box_autoadd_ln_invoice(deserializer); return InputType_Bolt11(invoice: var_invoice); case 3: + var var_offer = sse_decode_String(deserializer); + return InputType_Bolt12(offer: var_offer); + case 4: var var_nodeId = sse_decode_String(deserializer); return InputType_NodeId(nodeId: var_nodeId); - case 4: + case 5: var var_url = sse_decode_String(deserializer); return InputType_Url(url: var_url); - case 5: + case 6: var var_data = sse_decode_box_autoadd_ln_url_pay_request_data(deserializer); return InputType_LnUrlPay(data: var_data); - case 6: + case 7: var var_data = sse_decode_box_autoadd_ln_url_withdraw_request_data(deserializer); return InputType_LnUrlWithdraw(data: var_data); - case 7: + case 8: var var_data = sse_decode_box_autoadd_ln_url_auth_request_data(deserializer); return InputType_LnUrlAuth(data: var_data); - case 8: + case 9: var var_data = sse_decode_box_autoadd_ln_url_error_data(deserializer); return InputType_LnUrlError(data: var_data); default: @@ -4655,6 +4666,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { case 1: var var_invoice = sse_decode_box_autoadd_ln_invoice(deserializer); return SendDestination_Bolt11(invoice: var_invoice); + case 2: + var var_offer = sse_decode_String(deserializer); + return SendDestination_Bolt12(offer: var_offer); default: throw UnimplementedError(''); } @@ -5406,23 +5420,26 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { case InputType_Bolt11(invoice: final invoice): sse_encode_i_32(2, serializer); sse_encode_box_autoadd_ln_invoice(invoice, serializer); - case InputType_NodeId(nodeId: final nodeId): + case InputType_Bolt12(offer: final offer): sse_encode_i_32(3, serializer); + sse_encode_String(offer, serializer); + case InputType_NodeId(nodeId: final nodeId): + sse_encode_i_32(4, serializer); sse_encode_String(nodeId, serializer); case InputType_Url(url: final url): - sse_encode_i_32(4, serializer); + sse_encode_i_32(5, serializer); sse_encode_String(url, serializer); case InputType_LnUrlPay(data: final data): - sse_encode_i_32(5, serializer); + sse_encode_i_32(6, serializer); sse_encode_box_autoadd_ln_url_pay_request_data(data, serializer); case InputType_LnUrlWithdraw(data: final data): - sse_encode_i_32(6, serializer); + sse_encode_i_32(7, serializer); sse_encode_box_autoadd_ln_url_withdraw_request_data(data, serializer); case InputType_LnUrlAuth(data: final data): - sse_encode_i_32(7, serializer); + sse_encode_i_32(8, serializer); sse_encode_box_autoadd_ln_url_auth_request_data(data, serializer); case InputType_LnUrlError(data: final data): - sse_encode_i_32(8, serializer); + sse_encode_i_32(9, serializer); sse_encode_box_autoadd_ln_url_error_data(data, serializer); default: throw UnimplementedError(''); @@ -6340,6 +6357,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { case SendDestination_Bolt11(invoice: final invoice): sse_encode_i_32(1, serializer); sse_encode_box_autoadd_ln_invoice(invoice, serializer); + case SendDestination_Bolt12(offer: final offer): + sse_encode_i_32(2, serializer); + sse_encode_String(offer, serializer); default: throw UnimplementedError(''); } diff --git a/packages/dart/lib/src/frb_generated.io.dart b/packages/dart/lib/src/frb_generated.io.dart index d5f2bf4b6..83ad1af60 100644 --- a/packages/dart/lib/src/frb_generated.io.dart +++ b/packages/dart/lib/src/frb_generated.io.dart @@ -2064,39 +2064,45 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { wireObj.kind.Bolt11.invoice = pre_invoice; return; } + if (apiObj is InputType_Bolt12) { + var pre_offer = cst_encode_String(apiObj.offer); + wireObj.tag = 3; + wireObj.kind.Bolt12.offer = pre_offer; + return; + } if (apiObj is InputType_NodeId) { var pre_node_id = cst_encode_String(apiObj.nodeId); - wireObj.tag = 3; + wireObj.tag = 4; wireObj.kind.NodeId.node_id = pre_node_id; return; } if (apiObj is InputType_Url) { var pre_url = cst_encode_String(apiObj.url); - wireObj.tag = 4; + wireObj.tag = 5; wireObj.kind.Url.url = pre_url; return; } if (apiObj is InputType_LnUrlPay) { var pre_data = cst_encode_box_autoadd_ln_url_pay_request_data(apiObj.data); - wireObj.tag = 5; + wireObj.tag = 6; wireObj.kind.LnUrlPay.data = pre_data; return; } if (apiObj is InputType_LnUrlWithdraw) { var pre_data = cst_encode_box_autoadd_ln_url_withdraw_request_data(apiObj.data); - wireObj.tag = 6; + wireObj.tag = 7; wireObj.kind.LnUrlWithdraw.data = pre_data; return; } if (apiObj is InputType_LnUrlAuth) { var pre_data = cst_encode_box_autoadd_ln_url_auth_request_data(apiObj.data); - wireObj.tag = 7; + wireObj.tag = 8; wireObj.kind.LnUrlAuth.data = pre_data; return; } if (apiObj is InputType_LnUrlError) { var pre_data = cst_encode_box_autoadd_ln_url_error_data(apiObj.data); - wireObj.tag = 8; + wireObj.tag = 9; wireObj.kind.LnUrlError.data = pre_data; return; } @@ -2899,6 +2905,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { wireObj.kind.Bolt11.invoice = pre_invoice; return; } + if (apiObj is SendDestination_Bolt12) { + var pre_offer = cst_encode_String(apiObj.offer); + wireObj.tag = 2; + wireObj.kind.Bolt12.offer = pre_offer; + return; + } } @protected @@ -5190,10 +5202,16 @@ final class wire_cst_SendDestination_Bolt11 extends ffi.Struct { external ffi.Pointer invoice; } +final class wire_cst_SendDestination_Bolt12 extends ffi.Struct { + external ffi.Pointer offer; +} + final class SendDestinationKind extends ffi.Union { external wire_cst_SendDestination_LiquidAddress LiquidAddress; external wire_cst_SendDestination_Bolt11 Bolt11; + + external wire_cst_SendDestination_Bolt12 Bolt12; } final class wire_cst_send_destination extends ffi.Struct { @@ -5810,6 +5828,10 @@ final class wire_cst_InputType_Bolt11 extends ffi.Struct { external ffi.Pointer invoice; } +final class wire_cst_InputType_Bolt12 extends ffi.Struct { + external ffi.Pointer offer; +} + final class wire_cst_InputType_NodeId extends ffi.Struct { external ffi.Pointer node_id; } @@ -5841,6 +5863,8 @@ final class InputTypeKind extends ffi.Union { external wire_cst_InputType_Bolt11 Bolt11; + external wire_cst_InputType_Bolt12 Bolt12; + external wire_cst_InputType_NodeId NodeId; external wire_cst_InputType_Url Url; diff --git a/packages/dart/lib/src/model.dart b/packages/dart/lib/src/model.dart index bc2ea5cb8..f5bba405f 100644 --- a/packages/dart/lib/src/model.dart +++ b/packages/dart/lib/src/model.dart @@ -1001,7 +1001,7 @@ class PrepareRefundResponse { /// An argument when calling [crate::sdk::LiquidSdk::prepare_send_payment]. class PrepareSendRequest { /// The destination we intend to pay to. - /// Supports BIP21 URIs, BOLT11 invoices and Liquid addresses + /// Supports BIP21 URIs, BOLT11 invoices, BOLT12 offers and Liquid addresses final String destination; /// Should only be set when paying directly onchain or to a BIP21 URI @@ -1257,6 +1257,9 @@ sealed class SendDestination with _$SendDestination { const factory SendDestination.bolt11({ required LNInvoice invoice, }) = SendDestination_Bolt11; + const factory SendDestination.bolt12({ + required String offer, + }) = SendDestination_Bolt12; } /// An argument when calling [crate::sdk::LiquidSdk::send_payment]. diff --git a/packages/dart/lib/src/model.freezed.dart b/packages/dart/lib/src/model.freezed.dart index 067875218..cb4daa227 100644 --- a/packages/dart/lib/src/model.freezed.dart +++ b/packages/dart/lib/src/model.freezed.dart @@ -1924,3 +1924,82 @@ abstract class SendDestination_Bolt11 extends SendDestination { _$$SendDestination_Bolt11ImplCopyWith<_$SendDestination_Bolt11Impl> get copyWith => throw _privateConstructorUsedError; } + +/// @nodoc +abstract class _$$SendDestination_Bolt12ImplCopyWith<$Res> { + factory _$$SendDestination_Bolt12ImplCopyWith( + _$SendDestination_Bolt12Impl value, $Res Function(_$SendDestination_Bolt12Impl) then) = + __$$SendDestination_Bolt12ImplCopyWithImpl<$Res>; + @useResult + $Res call({String offer}); +} + +/// @nodoc +class __$$SendDestination_Bolt12ImplCopyWithImpl<$Res> + extends _$SendDestinationCopyWithImpl<$Res, _$SendDestination_Bolt12Impl> + implements _$$SendDestination_Bolt12ImplCopyWith<$Res> { + __$$SendDestination_Bolt12ImplCopyWithImpl( + _$SendDestination_Bolt12Impl _value, $Res Function(_$SendDestination_Bolt12Impl) _then) + : super(_value, _then); + + /// Create a copy of SendDestination + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? offer = null, + }) { + return _then(_$SendDestination_Bolt12Impl( + offer: null == offer + ? _value.offer + : offer // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$SendDestination_Bolt12Impl extends SendDestination_Bolt12 { + const _$SendDestination_Bolt12Impl({required this.offer}) : super._(); + + @override + final String offer; + + @override + String toString() { + return 'SendDestination.bolt12(offer: $offer)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SendDestination_Bolt12Impl && + (identical(other.offer, offer) || other.offer == offer)); + } + + @override + int get hashCode => Object.hash(runtimeType, offer); + + /// Create a copy of SendDestination + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$SendDestination_Bolt12ImplCopyWith<_$SendDestination_Bolt12Impl> get copyWith => + __$$SendDestination_Bolt12ImplCopyWithImpl<_$SendDestination_Bolt12Impl>(this, _$identity); +} + +abstract class SendDestination_Bolt12 extends SendDestination { + const factory SendDestination_Bolt12({required final String offer}) = _$SendDestination_Bolt12Impl; + const SendDestination_Bolt12._() : super._(); + + String get offer; + + /// Create a copy of SendDestination + /// with the given fields replaced by the non-null parameter values. + @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 46b986ea1..c5de23f8e 100644 --- a/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart +++ b/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart @@ -3986,10 +3986,16 @@ final class wire_cst_SendDestination_Bolt11 extends ffi.Struct { external ffi.Pointer invoice; } +final class wire_cst_SendDestination_Bolt12 extends ffi.Struct { + external ffi.Pointer offer; +} + final class SendDestinationKind extends ffi.Union { external wire_cst_SendDestination_LiquidAddress LiquidAddress; external wire_cst_SendDestination_Bolt11 Bolt11; + + external wire_cst_SendDestination_Bolt12 Bolt12; } final class wire_cst_send_destination extends ffi.Struct { @@ -4606,6 +4612,10 @@ final class wire_cst_InputType_Bolt11 extends ffi.Struct { external ffi.Pointer invoice; } +final class wire_cst_InputType_Bolt12 extends ffi.Struct { + external ffi.Pointer offer; +} + final class wire_cst_InputType_NodeId extends ffi.Struct { external ffi.Pointer node_id; } @@ -4637,6 +4647,8 @@ final class InputTypeKind extends ffi.Union { external wire_cst_InputType_Bolt11 Bolt11; + external wire_cst_InputType_Bolt12 Bolt12; + external wire_cst_InputType_NodeId NodeId; external wire_cst_InputType_Url Url; From f4d6a3bddb1333d4fa2c16739d7c302711b435e9 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Sun, 10 Nov 2024 15:00:15 +0100 Subject: [PATCH 03/22] SendDestination: Bolt12 type stores amount in addition to offer --- cli/Cargo.lock | 4 +-- .../include/breez_sdk_liquid.h | 1 + lib/bindings/src/breez_sdk_liquid.udl | 2 +- lib/core/src/frb_generated.rs | 26 ++++++++++++--- lib/core/src/model.rs | 1 + lib/core/src/sdk.rs | 33 +++++++++---------- packages/dart/lib/src/frb_generated.dart | 7 ++-- packages/dart/lib/src/frb_generated.io.dart | 5 +++ packages/dart/lib/src/model.dart | 1 + packages/dart/lib/src/model.freezed.dart | 23 +++++++++---- ...utter_breez_liquid_bindings_generated.dart | 3 ++ 11 files changed, 72 insertions(+), 34 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index ca5fbf1bb..6afc8b8eb 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -3045,8 +3045,8 @@ dependencies = [ [[package]] name = "sdk-common" -version = "0.5.2" -source = "git+https://github.com/breez/breez-sdk?rev=441a9fd50c32098b2887e960c8a4bcc5956da1af#441a9fd50c32098b2887e960c8a4bcc5956da1af" +version = "0.6.2" +source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#fa01549242a786f3fc9ccffa134e47ba462838db" dependencies = [ "aes 0.8.4", "anyhow", 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 0b717ba58..c149413a6 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 @@ -173,6 +173,7 @@ typedef struct wire_cst_SendDestination_Bolt11 { typedef struct wire_cst_SendDestination_Bolt12 { struct wire_cst_list_prim_u_8_strict *offer; + uint64_t receiver_amount_sat; } wire_cst_SendDestination_Bolt12; typedef union SendDestinationKind { diff --git a/lib/bindings/src/breez_sdk_liquid.udl b/lib/bindings/src/breez_sdk_liquid.udl index 22c39efdc..c75a2c327 100644 --- a/lib/bindings/src/breez_sdk_liquid.udl +++ b/lib/bindings/src/breez_sdk_liquid.udl @@ -381,7 +381,7 @@ dictionary PrepareSendRequest { interface SendDestination { LiquidAddress(LiquidAddressData address_data); Bolt11(LNInvoice invoice); - Bolt12(string offer); + Bolt12(string offer, u64 receiver_amount_sat); }; dictionary PrepareSendResponse { diff --git a/lib/core/src/frb_generated.rs b/lib/core/src/frb_generated.rs index 78309adbc..d89847f2d 100644 --- a/lib/core/src/frb_generated.rs +++ b/lib/core/src/frb_generated.rs @@ -3896,7 +3896,11 @@ impl SseDecode for crate::model::SendDestination { } 2 => { let mut var_offer = ::sse_decode(deserializer); - return crate::model::SendDestination::Bolt12 { offer: var_offer }; + let mut var_receiverAmountSat = ::sse_decode(deserializer); + return crate::model::SendDestination::Bolt12 { + offer: var_offer, + receiver_amount_sat: var_receiverAmountSat, + }; } _ => { unimplemented!(""); @@ -5949,9 +5953,15 @@ impl flutter_rust_bridge::IntoDart for crate::model::SendDestination { crate::model::SendDestination::Bolt11 { invoice } => { [1.into_dart(), invoice.into_into_dart().into_dart()].into_dart() } - crate::model::SendDestination::Bolt12 { offer } => { - [2.into_dart(), offer.into_into_dart().into_dart()].into_dart() - } + crate::model::SendDestination::Bolt12 { + offer, + receiver_amount_sat, + } => [ + 2.into_dart(), + offer.into_into_dart().into_dart(), + receiver_amount_sat.into_into_dart().into_dart(), + ] + .into_dart(), _ => { unimplemented!(""); } @@ -7564,9 +7574,13 @@ impl SseEncode for crate::model::SendDestination { ::sse_encode(1, serializer); ::sse_encode(invoice, serializer); } - crate::model::SendDestination::Bolt12 { offer } => { + crate::model::SendDestination::Bolt12 { + offer, + receiver_amount_sat, + } => { ::sse_encode(2, serializer); ::sse_encode(offer, serializer); + ::sse_encode(receiver_amount_sat, serializer); } _ => { unimplemented!(""); @@ -9370,6 +9384,7 @@ mod io { let ans = unsafe { self.kind.Bolt12 }; crate::model::SendDestination::Bolt12 { offer: ans.offer.cst_decode(), + receiver_amount_sat: ans.receiver_amount_sat.cst_decode(), } } _ => unreachable!(), @@ -12474,6 +12489,7 @@ mod io { #[derive(Clone, Copy)] pub struct wire_cst_SendDestination_Bolt12 { offer: *mut wire_cst_list_prim_u_8_strict, + receiver_amount_sat: u64, } #[repr(C)] #[derive(Clone, Copy)] diff --git a/lib/core/src/model.rs b/lib/core/src/model.rs index e00c71012..48996de06 100644 --- a/lib/core/src/model.rs +++ b/lib/core/src/model.rs @@ -364,6 +364,7 @@ pub enum SendDestination { }, Bolt12 { offer: String, + receiver_amount_sat: u64, }, } diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index ba26ec29e..3dbc67008 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -788,25 +788,14 @@ impl LiquidSdk { }; payment_destination = SendDestination::Bolt11 { invoice }; } - Ok(_) => { - return Err(PaymentError::Generic { - err: "Destination is not valid".to_string(), - }); - } - Err(_) => { - // TODO Workaround to avoid adding an InputType::Bolt12 variant in sdk-common - - // TODO TBD: Should InputType::Bolt12_Offer be added to sdk-common? Or Bolt12_Invoice? - // If yes, with full parsing and mapping to LNInvoice? - + Ok(InputType::Bolt12 { offer }) => { let amount_sat = req .amount_sat .ok_or_else(|| anyhow!("Expected amount when processing BOLT12 offer"))?; - let offer = &req.destination; info!("Got BOLT12 offer, fetching BOLT12 invoice"); - let invoice_str = self.swapper.get_bolt12_invoice(offer, amount_sat)?; + let invoice_str = self.swapper.get_bolt12_invoice(&offer, amount_sat)?; let invoice_parsed = utils::parse_bolt12_invoice(&invoice_str)?; // TODO If supporting receive: validate it's not a self-transfer @@ -834,8 +823,14 @@ impl LiquidSdk { payment_destination = SendDestination::Bolt12 { offer: req.destination.to_string(), + receiver_amount_sat, }; } + _ => { + return Err(PaymentError::Generic { + err: "Destination is not valid".to_string(), + }); + } }; let payer_amount_sat = receiver_amount_sat + fees_sat; @@ -875,7 +870,6 @@ impl LiquidSdk { pub async fn send_payment( &self, req: &SendPaymentRequest, - // TODO Req must include BOLT12 invoice (which is not available during prepare) and amount ) -> Result { self.ensure_is_started().await?; @@ -915,10 +909,13 @@ impl LiquidSdk { SendDestination::Bolt11 { invoice } => { self.pay_bolt11_invoice(&invoice.bolt11, *fees_sat).await } - SendDestination::Bolt12 { offer } => { - // TODO Amount hardcoded to 1_000 because we don't yet have it in the request - let bolt12_invoice = self.swapper.get_bolt12_invoice(offer, 1_000)?; - + SendDestination::Bolt12 { + offer, + receiver_amount_sat, + } => { + let bolt12_invoice = self + .swapper + .get_bolt12_invoice(offer, *receiver_amount_sat)?; self.pay_bolt12_invoice(&bolt12_invoice, *fees_sat).await } } diff --git a/packages/dart/lib/src/frb_generated.dart b/packages/dart/lib/src/frb_generated.dart index 6b9e24709..740460055 100644 --- a/packages/dart/lib/src/frb_generated.dart +++ b/packages/dart/lib/src/frb_generated.dart @@ -2843,6 +2843,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { case 2: return SendDestination_Bolt12( offer: dco_decode_String(raw[1]), + receiverAmountSat: dco_decode_u_64(raw[2]), ); default: throw Exception("unreachable"); @@ -4668,7 +4669,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return SendDestination_Bolt11(invoice: var_invoice); case 2: var var_offer = sse_decode_String(deserializer); - return SendDestination_Bolt12(offer: var_offer); + var var_receiverAmountSat = sse_decode_u_64(deserializer); + return SendDestination_Bolt12(offer: var_offer, receiverAmountSat: var_receiverAmountSat); default: throw UnimplementedError(''); } @@ -6357,9 +6359,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { case SendDestination_Bolt11(invoice: final invoice): sse_encode_i_32(1, serializer); sse_encode_box_autoadd_ln_invoice(invoice, serializer); - case SendDestination_Bolt12(offer: final offer): + case SendDestination_Bolt12(offer: final offer, receiverAmountSat: final receiverAmountSat): sse_encode_i_32(2, serializer); sse_encode_String(offer, serializer); + sse_encode_u_64(receiverAmountSat, serializer); default: throw UnimplementedError(''); } diff --git a/packages/dart/lib/src/frb_generated.io.dart b/packages/dart/lib/src/frb_generated.io.dart index 83ad1af60..924fa66db 100644 --- a/packages/dart/lib/src/frb_generated.io.dart +++ b/packages/dart/lib/src/frb_generated.io.dart @@ -2907,8 +2907,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { } if (apiObj is SendDestination_Bolt12) { var pre_offer = cst_encode_String(apiObj.offer); + var pre_receiver_amount_sat = cst_encode_u_64(apiObj.receiverAmountSat); wireObj.tag = 2; wireObj.kind.Bolt12.offer = pre_offer; + wireObj.kind.Bolt12.receiver_amount_sat = pre_receiver_amount_sat; return; } } @@ -5204,6 +5206,9 @@ final class wire_cst_SendDestination_Bolt11 extends ffi.Struct { final class wire_cst_SendDestination_Bolt12 extends ffi.Struct { external ffi.Pointer offer; + + @ffi.Uint64() + external int receiver_amount_sat; } final class SendDestinationKind extends ffi.Union { diff --git a/packages/dart/lib/src/model.dart b/packages/dart/lib/src/model.dart index f5bba405f..921900936 100644 --- a/packages/dart/lib/src/model.dart +++ b/packages/dart/lib/src/model.dart @@ -1259,6 +1259,7 @@ sealed class SendDestination with _$SendDestination { }) = SendDestination_Bolt11; const factory SendDestination.bolt12({ required String offer, + required BigInt receiverAmountSat, }) = SendDestination_Bolt12; } diff --git a/packages/dart/lib/src/model.freezed.dart b/packages/dart/lib/src/model.freezed.dart index cb4daa227..cab3702f1 100644 --- a/packages/dart/lib/src/model.freezed.dart +++ b/packages/dart/lib/src/model.freezed.dart @@ -1931,7 +1931,7 @@ abstract class _$$SendDestination_Bolt12ImplCopyWith<$Res> { _$SendDestination_Bolt12Impl value, $Res Function(_$SendDestination_Bolt12Impl) then) = __$$SendDestination_Bolt12ImplCopyWithImpl<$Res>; @useResult - $Res call({String offer}); + $Res call({String offer, BigInt receiverAmountSat}); } /// @nodoc @@ -1948,12 +1948,17 @@ class __$$SendDestination_Bolt12ImplCopyWithImpl<$Res> @override $Res call({ Object? offer = null, + Object? receiverAmountSat = null, }) { return _then(_$SendDestination_Bolt12Impl( offer: null == offer ? _value.offer : offer // ignore: cast_nullable_to_non_nullable as String, + receiverAmountSat: null == receiverAmountSat + ? _value.receiverAmountSat + : receiverAmountSat // ignore: cast_nullable_to_non_nullable + as BigInt, )); } } @@ -1961,14 +1966,16 @@ class __$$SendDestination_Bolt12ImplCopyWithImpl<$Res> /// @nodoc class _$SendDestination_Bolt12Impl extends SendDestination_Bolt12 { - const _$SendDestination_Bolt12Impl({required this.offer}) : super._(); + const _$SendDestination_Bolt12Impl({required this.offer, required this.receiverAmountSat}) : super._(); @override final String offer; + @override + final BigInt receiverAmountSat; @override String toString() { - return 'SendDestination.bolt12(offer: $offer)'; + return 'SendDestination.bolt12(offer: $offer, receiverAmountSat: $receiverAmountSat)'; } @override @@ -1976,11 +1983,13 @@ class _$SendDestination_Bolt12Impl extends SendDestination_Bolt12 { return identical(this, other) || (other.runtimeType == runtimeType && other is _$SendDestination_Bolt12Impl && - (identical(other.offer, offer) || other.offer == offer)); + (identical(other.offer, offer) || other.offer == offer) && + (identical(other.receiverAmountSat, receiverAmountSat) || + other.receiverAmountSat == receiverAmountSat)); } @override - int get hashCode => Object.hash(runtimeType, offer); + int get hashCode => Object.hash(runtimeType, offer, receiverAmountSat); /// Create a copy of SendDestination /// with the given fields replaced by the non-null parameter values. @@ -1992,10 +2001,12 @@ class _$SendDestination_Bolt12Impl extends SendDestination_Bolt12 { } abstract class SendDestination_Bolt12 extends SendDestination { - const factory SendDestination_Bolt12({required final String offer}) = _$SendDestination_Bolt12Impl; + const factory SendDestination_Bolt12( + {required final String offer, required final BigInt receiverAmountSat}) = _$SendDestination_Bolt12Impl; const SendDestination_Bolt12._() : super._(); String get offer; + BigInt get receiverAmountSat; /// Create a copy of SendDestination /// with the given fields replaced by the non-null parameter values. diff --git a/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart b/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart index c5de23f8e..1c316e193 100644 --- a/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart +++ b/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart @@ -3988,6 +3988,9 @@ final class wire_cst_SendDestination_Bolt11 extends ffi.Struct { final class wire_cst_SendDestination_Bolt12 extends ffi.Struct { external ffi.Pointer offer; + + @ffi.Uint64() + external int receiver_amount_sat; } final class SendDestinationKind extends ffi.Union { From 5521b1e2166c0d70b733b671e1091590188c986d Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Sun, 10 Nov 2024 15:08:43 +0100 Subject: [PATCH 04/22] prepare_send_payment: remove fetching Bolt12 invoice --- lib/core/src/sdk.rs | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index 3dbc67008..8b804ceac 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -793,28 +793,10 @@ impl LiquidSdk { .amount_sat .ok_or_else(|| anyhow!("Expected amount when processing BOLT12 offer"))?; - info!("Got BOLT12 offer, fetching BOLT12 invoice"); - - let invoice_str = self.swapper.get_bolt12_invoice(&offer, amount_sat)?; - let invoice_parsed = utils::parse_bolt12_invoice(&invoice_str)?; - - // TODO If supporting receive: validate it's not a self-transfer - // TODO Validate invoice - - receiver_amount_sat = invoice_parsed.amount_msats() / 1_000; - - if let Some(amount_sat) = req.amount_sat { - ensure_sdk!( - receiver_amount_sat == amount_sat, - PaymentError::Generic { err: "Amount in the payment request is not the same as the one in the invoice".to_string() } - ); - } + receiver_amount_sat = amount_sat; let lbtc_pair = self.validate_submarine_pairs(receiver_amount_sat)?; - // TODO Check for MRH for Bolt12 - // TODO If MRH, fallback to onchain - let boltz_fees_total = lbtc_pair.fees.total(receiver_amount_sat); let lockup_fees_sat = self .estimate_lockup_tx_fee_send(receiver_amount_sat + boltz_fees_total) @@ -822,7 +804,7 @@ impl LiquidSdk { fees_sat = boltz_fees_total + lockup_fees_sat; payment_destination = SendDestination::Bolt12 { - offer: req.destination.to_string(), + offer, receiver_amount_sat, }; } From 8244e1cde2d0c9e09fbdb836aaa00d7b37d4244a Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Sun, 10 Nov 2024 15:18:17 +0100 Subject: [PATCH 05/22] Re-generate RN bindings --- .../breezsdkliquid/BreezSDKLiquidMapper.kt | 18 +++++++++++ .../ios/BreezSDKLiquidMapper.swift | 32 +++++++++++++++++++ packages/react-native/src/index.ts | 11 ++++++- 3 files changed, 60 insertions(+), 1 deletion(-) 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 b9c340314..a7318bf38 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 @@ -2427,6 +2427,10 @@ fun asInputType(inputType: ReadableMap): InputType? { val invoice = inputType.getMap("invoice")?.let { asLnInvoice(it) }!! return InputType.Bolt11(invoice) } + if (type == "bolt12") { + val offer = inputType.getString("offer")!! + return InputType.Bolt12(offer) + } if (type == "nodeId") { val nodeId = inputType.getString("nodeId")!! return InputType.NodeId(nodeId) @@ -2469,6 +2473,10 @@ fun readableMapOf(inputType: InputType): ReadableMap? { pushToMap(map, "type", "bolt11") pushToMap(map, "invoice", readableMapOf(inputType.invoice)) } + is InputType.Bolt12 -> { + pushToMap(map, "type", "bolt12") + pushToMap(map, "offer", inputType.offer) + } is InputType.NodeId -> { pushToMap(map, "type", "nodeId") pushToMap(map, "nodeId", inputType.nodeId) @@ -2960,6 +2968,11 @@ fun asSendDestination(sendDestination: ReadableMap): SendDestination? { val invoice = sendDestination.getMap("invoice")?.let { asLnInvoice(it) }!! return SendDestination.Bolt11(invoice) } + if (type == "bolt12") { + val offer = sendDestination.getString("offer")!! + val receiverAmountSat = sendDestination.getDouble("receiverAmountSat").toULong() + return SendDestination.Bolt12(offer, receiverAmountSat) + } return null } @@ -2974,6 +2987,11 @@ fun readableMapOf(sendDestination: SendDestination): ReadableMap? { pushToMap(map, "type", "bolt11") pushToMap(map, "invoice", readableMapOf(sendDestination.invoice)) } + is SendDestination.Bolt12 -> { + pushToMap(map, "type", "bolt12") + pushToMap(map, "offer", sendDestination.offer) + pushToMap(map, "receiverAmountSat", sendDestination.receiverAmountSat) + } } return map } diff --git a/packages/react-native/ios/BreezSDKLiquidMapper.swift b/packages/react-native/ios/BreezSDKLiquidMapper.swift index 2efdbb4e7..e11cb5919 100644 --- a/packages/react-native/ios/BreezSDKLiquidMapper.swift +++ b/packages/react-native/ios/BreezSDKLiquidMapper.swift @@ -2830,6 +2830,12 @@ enum BreezSDKLiquidMapper { return InputType.bolt11(invoice: _invoice) } + if type == "bolt12" { + guard let _offer = inputType["offer"] as? String else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "offer", typeName: "InputType")) + } + return InputType.bolt12(offer: _offer) + } if type == "nodeId" { guard let _nodeId = inputType["nodeId"] as? String else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "nodeId", typeName: "InputType")) @@ -2904,6 +2910,14 @@ enum BreezSDKLiquidMapper { "invoice": dictionaryOf(lnInvoice: invoice), ] + case let .bolt12( + offer + ): + return [ + "type": "bolt12", + "offer": offer, + ] + case let .nodeId( nodeId ): @@ -3770,6 +3784,15 @@ enum BreezSDKLiquidMapper { return SendDestination.bolt11(invoice: _invoice) } + if type == "bolt12" { + guard let _offer = sendDestination["offer"] as? String else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "offer", typeName: "SendDestination")) + } + guard let _receiverAmountSat = sendDestination["receiverAmountSat"] as? UInt64 else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "receiverAmountSat", typeName: "SendDestination")) + } + return SendDestination.bolt12(offer: _offer, receiverAmountSat: _receiverAmountSat) + } throw SdkError.Generic(message: "Unexpected type \(type) for enum SendDestination") } @@ -3791,6 +3814,15 @@ enum BreezSDKLiquidMapper { "type": "bolt11", "invoice": dictionaryOf(lnInvoice: invoice), ] + + case let .bolt12( + offer, receiverAmountSat + ): + return [ + "type": "bolt12", + "offer": offer, + "receiverAmountSat": receiverAmountSat, + ] } } diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index dac70d1d1..6ddc7e9e2 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -425,6 +425,7 @@ export enum InputTypeVariant { BITCOIN_ADDRESS = "bitcoinAddress", LIQUID_ADDRESS = "liquidAddress", BOLT11 = "bolt11", + BOLT12 = "bolt12", NODE_ID = "nodeId", URL = "url", LN_URL_PAY = "lnUrlPay", @@ -442,6 +443,9 @@ export type InputType = { } | { type: InputTypeVariant.BOLT11, invoice: LnInvoice +} | { + type: InputTypeVariant.BOLT12, + offer: string } | { type: InputTypeVariant.NODE_ID, nodeId: string @@ -627,7 +631,8 @@ export type SdkEvent = { export enum SendDestinationVariant { LIQUID_ADDRESS = "liquidAddress", - BOLT11 = "bolt11" + BOLT11 = "bolt11", + BOLT12 = "bolt12" } export type SendDestination = { @@ -636,6 +641,10 @@ export type SendDestination = { } | { type: SendDestinationVariant.BOLT11, invoice: LnInvoice +} | { + type: SendDestinationVariant.BOLT12, + offer: string + receiverAmountSat: number } export enum SuccessActionVariant { From e76c8f92386f8c088fa982ada0ca2af45bc4bc8c Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Wed, 13 Nov 2024 10:13:17 +0100 Subject: [PATCH 06/22] Bump sdk-common with new Bolt12Offer(String) --- cli/Cargo.lock | 2 +- lib/Cargo.lock | 2 +- .../include/breez_sdk_liquid.h | 6 +-- lib/bindings/src/breez_sdk_liquid.udl | 2 +- lib/core/src/bindings.rs | 2 +- lib/core/src/frb_generated.rs | 16 ++++---- lib/core/src/sdk.rs | 2 +- packages/dart/lib/src/bindings.dart | 4 +- packages/dart/lib/src/bindings.freezed.dart | 40 ++++++++++--------- packages/dart/lib/src/frb_generated.dart | 6 +-- packages/dart/lib/src/frb_generated.io.dart | 8 ++-- ...utter_breez_liquid_bindings_generated.dart | 4 +- 12 files changed, 48 insertions(+), 46 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 6afc8b8eb..40a7c943e 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -3046,7 +3046,7 @@ dependencies = [ [[package]] name = "sdk-common" version = "0.6.2" -source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#fa01549242a786f3fc9ccffa134e47ba462838db" +source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#a8a7057320aca4f5b03708a11de0ea66dc931d80" dependencies = [ "aes 0.8.4", "anyhow", diff --git a/lib/Cargo.lock b/lib/Cargo.lock index fcb9d69d4..9568faf09 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -3294,7 +3294,7 @@ dependencies = [ [[package]] name = "sdk-common" version = "0.6.2" -source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#fa01549242a786f3fc9ccffa134e47ba462838db" +source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#a8a7057320aca4f5b03708a11de0ea66dc931d80" dependencies = [ "aes 0.8.4", "anyhow", 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 c149413a6..95642aecf 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 @@ -627,9 +627,9 @@ typedef struct wire_cst_InputType_Bolt11 { struct wire_cst_ln_invoice *invoice; } wire_cst_InputType_Bolt11; -typedef struct wire_cst_InputType_Bolt12 { +typedef struct wire_cst_InputType_Bolt12Offer { struct wire_cst_list_prim_u_8_strict *offer; -} wire_cst_InputType_Bolt12; +} wire_cst_InputType_Bolt12Offer; typedef struct wire_cst_InputType_NodeId { struct wire_cst_list_prim_u_8_strict *node_id; @@ -659,7 +659,7 @@ typedef union InputTypeKind { struct wire_cst_InputType_BitcoinAddress BitcoinAddress; struct wire_cst_InputType_LiquidAddress LiquidAddress; struct wire_cst_InputType_Bolt11 Bolt11; - struct wire_cst_InputType_Bolt12 Bolt12; + struct wire_cst_InputType_Bolt12Offer Bolt12Offer; struct wire_cst_InputType_NodeId NodeId; struct wire_cst_InputType_Url Url; struct wire_cst_InputType_LnUrlPay LnUrlPay; diff --git a/lib/bindings/src/breez_sdk_liquid.udl b/lib/bindings/src/breez_sdk_liquid.udl index c75a2c327..d53ccf0cb 100644 --- a/lib/bindings/src/breez_sdk_liquid.udl +++ b/lib/bindings/src/breez_sdk_liquid.udl @@ -43,7 +43,7 @@ interface InputType { BitcoinAddress(BitcoinAddressData address); LiquidAddress(LiquidAddressData address); Bolt11(LNInvoice invoice); - Bolt12(string offer); + Bolt12Offer(string offer); NodeId(string node_id); Url(string url); LnUrlPay(LnUrlPayRequestData data); diff --git a/lib/core/src/bindings.rs b/lib/core/src/bindings.rs index d36e16faa..27aaa9ac4 100644 --- a/lib/core/src/bindings.rs +++ b/lib/core/src/bindings.rs @@ -334,7 +334,7 @@ pub enum _InputType { BitcoinAddress { address: BitcoinAddressData }, LiquidAddress { address: LiquidAddressData }, Bolt11 { invoice: LNInvoice }, - Bolt12 { offer: String }, + Bolt12Offer { offer: String }, NodeId { node_id: String }, Url { url: String }, LnUrlPay { data: LnUrlPayRequestData }, diff --git a/lib/core/src/frb_generated.rs b/lib/core/src/frb_generated.rs index d89847f2d..f29e47ac1 100644 --- a/lib/core/src/frb_generated.rs +++ b/lib/core/src/frb_generated.rs @@ -1798,7 +1798,7 @@ const _: fn() = || { crate::bindings::InputType::Bolt11 { invoice } => { let _: crate::bindings::LNInvoice = invoice; } - crate::bindings::InputType::Bolt12 { offer } => { + crate::bindings::InputType::Bolt12Offer { offer } => { let _: String = offer; } crate::bindings::InputType::NodeId { node_id } => { @@ -2436,7 +2436,7 @@ impl SseDecode for crate::bindings::InputType { } 3 => { let mut var_offer = ::sse_decode(deserializer); - return crate::bindings::InputType::Bolt12 { offer: var_offer }; + return crate::bindings::InputType::Bolt12Offer { offer: var_offer }; } 4 => { let mut var_nodeId = ::sse_decode(deserializer); @@ -4468,7 +4468,7 @@ impl flutter_rust_bridge::IntoDart for FrbWrapper { crate::bindings::InputType::Bolt11 { invoice } => { [2.into_dart(), invoice.into_into_dart().into_dart()].into_dart() } - crate::bindings::InputType::Bolt12 { offer } => { + crate::bindings::InputType::Bolt12Offer { offer } => { [3.into_dart(), offer.into_into_dart().into_dart()].into_dart() } crate::bindings::InputType::NodeId { node_id } => { @@ -6414,7 +6414,7 @@ impl SseEncode for crate::bindings::InputType { ::sse_encode(2, serializer); ::sse_encode(invoice, serializer); } - crate::bindings::InputType::Bolt12 { offer } => { + crate::bindings::InputType::Bolt12Offer { offer } => { ::sse_encode(3, serializer); ::sse_encode(offer, serializer); } @@ -8341,8 +8341,8 @@ mod io { } } 3 => { - let ans = unsafe { self.kind.Bolt12 }; - crate::bindings::InputType::Bolt12 { + let ans = unsafe { self.kind.Bolt12Offer }; + crate::bindings::InputType::Bolt12Offer { offer: ans.offer.cst_decode(), } } @@ -11585,7 +11585,7 @@ mod io { BitcoinAddress: wire_cst_InputType_BitcoinAddress, LiquidAddress: wire_cst_InputType_LiquidAddress, Bolt11: wire_cst_InputType_Bolt11, - Bolt12: wire_cst_InputType_Bolt12, + Bolt12Offer: wire_cst_InputType_Bolt12Offer, NodeId: wire_cst_InputType_NodeId, Url: wire_cst_InputType_Url, LnUrlPay: wire_cst_InputType_LnUrlPay, @@ -11611,7 +11611,7 @@ mod io { } #[repr(C)] #[derive(Clone, Copy)] - pub struct wire_cst_InputType_Bolt12 { + pub struct wire_cst_InputType_Bolt12Offer { offer: *mut wire_cst_list_prim_u_8_strict, } #[repr(C)] diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index 8b804ceac..6b0d91927 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -788,7 +788,7 @@ impl LiquidSdk { }; payment_destination = SendDestination::Bolt11 { invoice }; } - Ok(InputType::Bolt12 { offer }) => { + Ok(InputType::Bolt12Offer { offer }) => { let amount_sat = req .amount_sat .ok_or_else(|| anyhow!("Expected amount when processing BOLT12 offer"))?; diff --git a/packages/dart/lib/src/bindings.dart b/packages/dart/lib/src/bindings.dart index 0710371d9..edd4b4899 100644 --- a/packages/dart/lib/src/bindings.dart +++ b/packages/dart/lib/src/bindings.dart @@ -281,9 +281,9 @@ sealed class InputType with _$InputType { const factory InputType.bolt11({ required LNInvoice invoice, }) = InputType_Bolt11; - const factory InputType.bolt12({ + const factory InputType.bolt12Offer({ required String offer, - }) = InputType_Bolt12; + }) = InputType_Bolt12Offer; const factory InputType.nodeId({ required String nodeId, }) = InputType_NodeId; diff --git a/packages/dart/lib/src/bindings.freezed.dart b/packages/dart/lib/src/bindings.freezed.dart index 71907d6de..3db72819b 100644 --- a/packages/dart/lib/src/bindings.freezed.dart +++ b/packages/dart/lib/src/bindings.freezed.dart @@ -463,19 +463,20 @@ abstract class InputType_Bolt11 extends InputType { } /// @nodoc -abstract class _$$InputType_Bolt12ImplCopyWith<$Res> { - factory _$$InputType_Bolt12ImplCopyWith( - _$InputType_Bolt12Impl value, $Res Function(_$InputType_Bolt12Impl) then) = - __$$InputType_Bolt12ImplCopyWithImpl<$Res>; +abstract class _$$InputType_Bolt12OfferImplCopyWith<$Res> { + factory _$$InputType_Bolt12OfferImplCopyWith( + _$InputType_Bolt12OfferImpl value, $Res Function(_$InputType_Bolt12OfferImpl) then) = + __$$InputType_Bolt12OfferImplCopyWithImpl<$Res>; @useResult $Res call({String offer}); } /// @nodoc -class __$$InputType_Bolt12ImplCopyWithImpl<$Res> extends _$InputTypeCopyWithImpl<$Res, _$InputType_Bolt12Impl> - implements _$$InputType_Bolt12ImplCopyWith<$Res> { - __$$InputType_Bolt12ImplCopyWithImpl( - _$InputType_Bolt12Impl _value, $Res Function(_$InputType_Bolt12Impl) _then) +class __$$InputType_Bolt12OfferImplCopyWithImpl<$Res> + extends _$InputTypeCopyWithImpl<$Res, _$InputType_Bolt12OfferImpl> + implements _$$InputType_Bolt12OfferImplCopyWith<$Res> { + __$$InputType_Bolt12OfferImplCopyWithImpl( + _$InputType_Bolt12OfferImpl _value, $Res Function(_$InputType_Bolt12OfferImpl) _then) : super(_value, _then); /// Create a copy of InputType @@ -485,7 +486,7 @@ class __$$InputType_Bolt12ImplCopyWithImpl<$Res> extends _$InputTypeCopyWithImpl $Res call({ Object? offer = null, }) { - return _then(_$InputType_Bolt12Impl( + return _then(_$InputType_Bolt12OfferImpl( offer: null == offer ? _value.offer : offer // ignore: cast_nullable_to_non_nullable @@ -496,22 +497,22 @@ class __$$InputType_Bolt12ImplCopyWithImpl<$Res> extends _$InputTypeCopyWithImpl /// @nodoc -class _$InputType_Bolt12Impl extends InputType_Bolt12 { - const _$InputType_Bolt12Impl({required this.offer}) : super._(); +class _$InputType_Bolt12OfferImpl extends InputType_Bolt12Offer { + const _$InputType_Bolt12OfferImpl({required this.offer}) : super._(); @override final String offer; @override String toString() { - return 'InputType.bolt12(offer: $offer)'; + return 'InputType.bolt12Offer(offer: $offer)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _$InputType_Bolt12Impl && + other is _$InputType_Bolt12OfferImpl && (identical(other.offer, offer) || other.offer == offer)); } @@ -523,20 +524,21 @@ class _$InputType_Bolt12Impl extends InputType_Bolt12 { @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') - _$$InputType_Bolt12ImplCopyWith<_$InputType_Bolt12Impl> get copyWith => - __$$InputType_Bolt12ImplCopyWithImpl<_$InputType_Bolt12Impl>(this, _$identity); + _$$InputType_Bolt12OfferImplCopyWith<_$InputType_Bolt12OfferImpl> get copyWith => + __$$InputType_Bolt12OfferImplCopyWithImpl<_$InputType_Bolt12OfferImpl>(this, _$identity); } -abstract class InputType_Bolt12 extends InputType { - const factory InputType_Bolt12({required final String offer}) = _$InputType_Bolt12Impl; - const InputType_Bolt12._() : super._(); +abstract class InputType_Bolt12Offer extends InputType { + const factory InputType_Bolt12Offer({required final String offer}) = _$InputType_Bolt12OfferImpl; + const InputType_Bolt12Offer._() : super._(); String get offer; /// Create a copy of InputType /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) - _$$InputType_Bolt12ImplCopyWith<_$InputType_Bolt12Impl> get copyWith => throw _privateConstructorUsedError; + _$$InputType_Bolt12OfferImplCopyWith<_$InputType_Bolt12OfferImpl> get copyWith => + throw _privateConstructorUsedError; } /// @nodoc diff --git a/packages/dart/lib/src/frb_generated.dart b/packages/dart/lib/src/frb_generated.dart index 740460055..d486df0e3 100644 --- a/packages/dart/lib/src/frb_generated.dart +++ b/packages/dart/lib/src/frb_generated.dart @@ -1760,7 +1760,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { invoice: dco_decode_box_autoadd_ln_invoice(raw[1]), ); case 3: - return InputType_Bolt12( + return InputType_Bolt12Offer( offer: dco_decode_String(raw[1]), ); case 4: @@ -3549,7 +3549,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return InputType_Bolt11(invoice: var_invoice); case 3: var var_offer = sse_decode_String(deserializer); - return InputType_Bolt12(offer: var_offer); + return InputType_Bolt12Offer(offer: var_offer); case 4: var var_nodeId = sse_decode_String(deserializer); return InputType_NodeId(nodeId: var_nodeId); @@ -5422,7 +5422,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { case InputType_Bolt11(invoice: final invoice): sse_encode_i_32(2, serializer); sse_encode_box_autoadd_ln_invoice(invoice, serializer); - case InputType_Bolt12(offer: final offer): + case InputType_Bolt12Offer(offer: final offer): sse_encode_i_32(3, serializer); sse_encode_String(offer, serializer); case InputType_NodeId(nodeId: final nodeId): diff --git a/packages/dart/lib/src/frb_generated.io.dart b/packages/dart/lib/src/frb_generated.io.dart index 924fa66db..1cb75932d 100644 --- a/packages/dart/lib/src/frb_generated.io.dart +++ b/packages/dart/lib/src/frb_generated.io.dart @@ -2064,10 +2064,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { wireObj.kind.Bolt11.invoice = pre_invoice; return; } - if (apiObj is InputType_Bolt12) { + if (apiObj is InputType_Bolt12Offer) { var pre_offer = cst_encode_String(apiObj.offer); wireObj.tag = 3; - wireObj.kind.Bolt12.offer = pre_offer; + wireObj.kind.Bolt12Offer.offer = pre_offer; return; } if (apiObj is InputType_NodeId) { @@ -5833,7 +5833,7 @@ final class wire_cst_InputType_Bolt11 extends ffi.Struct { external ffi.Pointer invoice; } -final class wire_cst_InputType_Bolt12 extends ffi.Struct { +final class wire_cst_InputType_Bolt12Offer extends ffi.Struct { external ffi.Pointer offer; } @@ -5868,7 +5868,7 @@ final class InputTypeKind extends ffi.Union { external wire_cst_InputType_Bolt11 Bolt11; - external wire_cst_InputType_Bolt12 Bolt12; + external wire_cst_InputType_Bolt12Offer Bolt12Offer; external wire_cst_InputType_NodeId NodeId; diff --git a/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart b/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart index 1c316e193..7b47f3f20 100644 --- a/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart +++ b/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart @@ -4615,7 +4615,7 @@ final class wire_cst_InputType_Bolt11 extends ffi.Struct { external ffi.Pointer invoice; } -final class wire_cst_InputType_Bolt12 extends ffi.Struct { +final class wire_cst_InputType_Bolt12Offer extends ffi.Struct { external ffi.Pointer offer; } @@ -4650,7 +4650,7 @@ final class InputTypeKind extends ffi.Union { external wire_cst_InputType_Bolt11 Bolt11; - external wire_cst_InputType_Bolt12 Bolt12; + external wire_cst_InputType_Bolt12Offer Bolt12Offer; external wire_cst_InputType_NodeId NodeId; From 47841e4d33e601db6c0c71dec480c503218f6b37 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Wed, 13 Nov 2024 09:14:23 +0000 Subject: [PATCH 07/22] Update lib/core/src/sdk.rs Co-authored-by: Ross Savage <551697+dangeross@users.noreply.github.com> --- lib/core/src/sdk.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index 6b0d91927..f8b2599d7 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -706,7 +706,7 @@ impl LiquidSdk { /// # Arguments /// /// * `req` - the [PrepareSendRequest] containing: - /// * `destination` - Either a Liquid BIP21 URI/address. a BOLT11 invoice or a BOLT12 offer + /// * `destination` - Either a Liquid BIP21 URI/address, a BOLT11 invoice or a BOLT12 offer /// * `amount_sat` - Should only be specified when paying directly onchain or via amount-less BIP21 /// /// # Returns From f5839e61f7d21c7c0ab4197eeddd5e9a888e2b77 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Wed, 13 Nov 2024 10:46:47 +0100 Subject: [PATCH 08/22] Re-generate RN bindings --- .../main/java/com/breezsdkliquid/BreezSDKLiquidMapper.kt | 8 ++++---- packages/react-native/ios/BreezSDKLiquidMapper.swift | 8 ++++---- packages/react-native/src/index.ts | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) 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 29915c40c..fa6549446 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 @@ -2427,9 +2427,9 @@ fun asInputType(inputType: ReadableMap): InputType? { val invoice = inputType.getMap("invoice")?.let { asLnInvoice(it) }!! return InputType.Bolt11(invoice) } - if (type == "bolt12") { + if (type == "bolt12Offer") { val offer = inputType.getString("offer")!! - return InputType.Bolt12(offer) + return InputType.Bolt12Offer(offer) } if (type == "nodeId") { val nodeId = inputType.getString("nodeId")!! @@ -2473,8 +2473,8 @@ fun readableMapOf(inputType: InputType): ReadableMap? { pushToMap(map, "type", "bolt11") pushToMap(map, "invoice", readableMapOf(inputType.invoice)) } - is InputType.Bolt12 -> { - pushToMap(map, "type", "bolt12") + is InputType.Bolt12Offer -> { + pushToMap(map, "type", "bolt12Offer") pushToMap(map, "offer", inputType.offer) } is InputType.NodeId -> { diff --git a/packages/react-native/ios/BreezSDKLiquidMapper.swift b/packages/react-native/ios/BreezSDKLiquidMapper.swift index 0159ceb47..dc076c0fb 100644 --- a/packages/react-native/ios/BreezSDKLiquidMapper.swift +++ b/packages/react-native/ios/BreezSDKLiquidMapper.swift @@ -2827,11 +2827,11 @@ enum BreezSDKLiquidMapper { return InputType.bolt11(invoice: _invoice) } - if type == "bolt12" { + if type == "bolt12Offer" { guard let _offer = inputType["offer"] as? String else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "offer", typeName: "InputType")) } - return InputType.bolt12(offer: _offer) + return InputType.bolt12Offer(offer: _offer) } if type == "nodeId" { guard let _nodeId = inputType["nodeId"] as? String else { @@ -2907,11 +2907,11 @@ enum BreezSDKLiquidMapper { "invoice": dictionaryOf(lnInvoice: invoice), ] - case let .bolt12( + case let .bolt12Offer( offer ): return [ - "type": "bolt12", + "type": "bolt12Offer", "offer": offer, ] diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index 15c41a2b4..cd425df65 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -425,7 +425,7 @@ export enum InputTypeVariant { BITCOIN_ADDRESS = "bitcoinAddress", LIQUID_ADDRESS = "liquidAddress", BOLT11 = "bolt11", - BOLT12 = "bolt12", + BOLT12_OFFER = "bolt12Offer", NODE_ID = "nodeId", URL = "url", LN_URL_PAY = "lnUrlPay", @@ -444,7 +444,7 @@ export type InputType = { type: InputTypeVariant.BOLT11, invoice: LnInvoice } | { - type: InputTypeVariant.BOLT12, + type: InputTypeVariant.BOLT12_OFFER, offer: string } | { type: InputTypeVariant.NODE_ID, From 68449c282afea5b3f8a2e5a46f54449f5c8fd816 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Wed, 13 Nov 2024 10:48:48 +0100 Subject: [PATCH 09/22] Fix tests --- lib/core/src/persist/address.rs | 6 +++--- lib/core/src/test_utils/swapper.rs | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/core/src/persist/address.rs b/lib/core/src/persist/address.rs index 8c0c46722..4e66b51d1 100644 --- a/lib/core/src/persist/address.rs +++ b/lib/core/src/persist/address.rs @@ -108,7 +108,7 @@ mod tests { let (_temp_dir, storage) = new_persister()?; let address = "tlq1pq2amlulhea6ltq7x3eu9atsc2nnrer7yt7xve363zxedqwu2mk6ctcyv9awl8xf28cythreqklt5q0qqwsxzlm6wu4z6d574adl9zh2zmr0h85gt534n"; - storage.insert_or_update_reserved_address(&address, 100)?; + storage.insert_or_update_reserved_address(address, 100)?; let maybe_reserved_address = storage.next_expired_reserved_address(99)?; // Under the expiry, not popped @@ -134,13 +134,13 @@ mod tests { let (_temp_dir, storage) = new_persister()?; let address = "tlq1pq2amlulhea6ltq7x3eu9atsc2nnrer7yt7xve363zxedqwu2mk6ctcyv9awl8xf28cythreqklt5q0qqwsxzlm6wu4z6d574adl9zh2zmr0h85gt534n"; - storage.insert_or_update_reserved_address(&address, 100)?; + storage.insert_or_update_reserved_address(address, 100)?; let maybe_reserved_address = storage.next_expired_reserved_address(99)?; // Under the expiry, not popped assert!(maybe_reserved_address.is_none()); - storage.delete_reserved_address(&address)?; + storage.delete_reserved_address(address)?; let maybe_reserved_address = storage.next_expired_reserved_address(101)?; // Over the expired, but already deleted diff --git a/lib/core/src/test_utils/swapper.rs b/lib/core/src/test_utils/swapper.rs index 599039bed..e4ba678c5 100644 --- a/lib/core/src/test_utils/swapper.rs +++ b/lib/core/src/test_utils/swapper.rs @@ -309,4 +309,8 @@ impl Swapper for MockSwapper { // Ok(Some(("".to_string(), 0.0))) unimplemented!() } + + fn get_bolt12_invoice(&self, _offer: &str, _amount_sat: u64) -> Result { + unimplemented!() + } } From 790d8f205658468be994a57c67220f75ab0fbb49 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Wed, 13 Nov 2024 10:57:54 +0100 Subject: [PATCH 10/22] Address invoice description persistence Don't try to extract invoice description, because it is added later during persist. --- lib/core/src/persist/mod.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/core/src/persist/mod.rs b/lib/core/src/persist/mod.rs index fce7967bc..2971471b2 100644 --- a/lib/core/src/persist/mod.rs +++ b/lib/core/src/persist/mod.rs @@ -312,14 +312,8 @@ impl Persister { preimage: maybe_send_swap_preimage, bolt11: maybe_send_swap_invoice.clone(), payment_hash: maybe_send_swap_payment_hash, - - description: "Lightning payment".to_string(), - // TODO Should not assume this is bolt11 - // description: maybe_send_swap_description.unwrap_or_else(|| { - // maybe_send_swap_invoice - // .and_then(|bolt11| get_invoice_description!(bolt11)) - // .unwrap_or("Lightning payment".to_string()) - // }), + description: maybe_send_swap_description + .unwrap_or("Lightning payment".to_string()), payer_amount_sat: maybe_send_swap_payer_amount_sat.unwrap_or(0), receiver_amount_sat: maybe_send_swap_receiver_amount_sat.unwrap_or(0), refund_tx_id: maybe_send_swap_refund_tx_id, From 0219b9988123cc1e7f854888326233993ead8eaa Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:26:14 +0100 Subject: [PATCH 11/22] Bump sdk-common to use new InputType variant --- .../include/breez_sdk_liquid.h | 45 ++- lib/bindings/src/breez_sdk_liquid.udl | 18 +- lib/core/src/bindings.rs | 25 +- lib/core/src/frb_generated.rs | 371 +++++++++++++++++- packages/dart/lib/src/bindings.dart | 58 ++- packages/dart/lib/src/bindings.freezed.dart | 195 ++++++++- packages/dart/lib/src/frb_generated.dart | 196 ++++++++- packages/dart/lib/src/frb_generated.io.dart | 211 +++++++++- ...utter_breez_liquid_bindings_generated.dart | 86 +++- .../breezsdkliquid/BreezSDKLiquidMapper.kt | 90 ++++- .../ios/BreezSDKLiquidMapper.swift | 138 ++++++- packages/react-native/src/index.ts | 26 +- 12 files changed, 1435 insertions(+), 24 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 2c03e6281..1994f6740 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 @@ -480,6 +480,25 @@ typedef struct wire_cst_aes_success_action_data_result { union AesSuccessActionDataResultKind kind; } wire_cst_aes_success_action_data_result; +typedef struct wire_cst_Amount_Bitcoin { + uint64_t amount_msat; +} wire_cst_Amount_Bitcoin; + +typedef struct wire_cst_Amount_Currency { + struct wire_cst_list_prim_u_8_strict *iso4217_code; + uint64_t fractional_amount; +} wire_cst_Amount_Currency; + +typedef union AmountKind { + struct wire_cst_Amount_Bitcoin Bitcoin; + struct wire_cst_Amount_Currency Currency; +} AmountKind; + +typedef struct wire_cst_amount { + int32_t tag; + union AmountKind kind; +} wire_cst_amount; + typedef struct wire_cst_bitcoin_address_data { struct wire_cst_list_prim_u_8_strict *address; int32_t network; @@ -488,6 +507,21 @@ typedef struct wire_cst_bitcoin_address_data { struct wire_cst_list_prim_u_8_strict *message; } wire_cst_bitcoin_address_data; +typedef struct wire_cst_list_String { + struct wire_cst_list_prim_u_8_strict **ptr; + int32_t len; +} wire_cst_list_String; + +typedef struct wire_cst_ln_offer { + struct wire_cst_list_prim_u_8_strict *bolt12; + struct wire_cst_list_String *chains; + struct wire_cst_amount *amount; + struct wire_cst_list_prim_u_8_strict *description; + uint64_t *absolute_expiry; + struct wire_cst_list_prim_u_8_strict *issuer; + struct wire_cst_list_prim_u_8_strict *signing_pubkey; +} wire_cst_ln_offer; + typedef struct wire_cst_ln_url_error_data { struct wire_cst_list_prim_u_8_strict *reason; } wire_cst_ln_url_error_data; @@ -628,7 +662,7 @@ typedef struct wire_cst_InputType_Bolt11 { } wire_cst_InputType_Bolt11; typedef struct wire_cst_InputType_Bolt12Offer { - struct wire_cst_list_prim_u_8_strict *offer; + struct wire_cst_ln_offer *offer; } wire_cst_InputType_Bolt12Offer; typedef struct wire_cst_InputType_NodeId { @@ -1131,6 +1165,8 @@ struct wire_cst_aes_success_action_data_decrypted *frbgen_breez_liquid_cst_new_b struct wire_cst_aes_success_action_data_result *frbgen_breez_liquid_cst_new_box_autoadd_aes_success_action_data_result(void); +struct wire_cst_amount *frbgen_breez_liquid_cst_new_box_autoadd_amount(void); + struct wire_cst_backup_request *frbgen_breez_liquid_cst_new_box_autoadd_backup_request(void); struct wire_cst_binding_event_listener *frbgen_breez_liquid_cst_new_box_autoadd_binding_event_listener(void); @@ -1157,6 +1193,8 @@ struct wire_cst_list_payments_request *frbgen_breez_liquid_cst_new_box_autoadd_l struct wire_cst_ln_invoice *frbgen_breez_liquid_cst_new_box_autoadd_ln_invoice(void); +struct wire_cst_ln_offer *frbgen_breez_liquid_cst_new_box_autoadd_ln_offer(void); + struct wire_cst_ln_url_auth_request_data *frbgen_breez_liquid_cst_new_box_autoadd_ln_url_auth_request_data(void); struct wire_cst_ln_url_error_data *frbgen_breez_liquid_cst_new_box_autoadd_ln_url_error_data(void); @@ -1219,6 +1257,8 @@ uint64_t *frbgen_breez_liquid_cst_new_box_autoadd_u_64(uint64_t value); struct wire_cst_url_success_action_data *frbgen_breez_liquid_cst_new_box_autoadd_url_success_action_data(void); +struct wire_cst_list_String *frbgen_breez_liquid_cst_new_list_String(int32_t len); + struct wire_cst_list_fiat_currency *frbgen_breez_liquid_cst_new_list_fiat_currency(int32_t len); struct wire_cst_list_locale_overrides *frbgen_breez_liquid_cst_new_list_locale_overrides(int32_t len); @@ -1243,6 +1283,7 @@ static int64_t dummy_method_to_enforce_bundling(void) { dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_aes_success_action_data); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_aes_success_action_data_decrypted); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_aes_success_action_data_result); + dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_amount); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_backup_request); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_binding_event_listener); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_bitcoin_address_data); @@ -1256,6 +1297,7 @@ static int64_t dummy_method_to_enforce_bundling(void) { dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_list_payment_details); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_list_payments_request); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_ln_invoice); + dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_ln_offer); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_ln_url_auth_request_data); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_ln_url_error_data); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_ln_url_pay_error_data); @@ -1287,6 +1329,7 @@ static int64_t dummy_method_to_enforce_bundling(void) { dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_u_32); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_u_64); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_url_success_action_data); + dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_String); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_fiat_currency); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_locale_overrides); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_localized_name); diff --git a/lib/bindings/src/breez_sdk_liquid.udl b/lib/bindings/src/breez_sdk_liquid.udl index d3cce213d..ecae1d4ae 100644 --- a/lib/bindings/src/breez_sdk_liquid.udl +++ b/lib/bindings/src/breez_sdk_liquid.udl @@ -38,12 +38,28 @@ dictionary RouteHintHop { u64? htlc_maximum_msat; }; +[Enum] +interface Amount { + Bitcoin(u64 amount_msat); + Currency(string iso4217_code, u64 fractional_amount); +}; + +dictionary LNOffer { + string bolt12; + sequence chains; + string? description; + string? signing_pubkey; + Amount? amount; + u64? absolute_expiry; + string? issuer; +}; + [Enum] interface InputType { BitcoinAddress(BitcoinAddressData address); LiquidAddress(LiquidAddressData address); Bolt11(LNInvoice invoice); - Bolt12Offer(string offer); + Bolt12Offer(LNOffer offer); NodeId(string node_id); Url(string url); LnUrlPay(LnUrlPayRequestData data); diff --git a/lib/core/src/bindings.rs b/lib/core/src/bindings.rs index 27aaa9ac4..483cac58e 100644 --- a/lib/core/src/bindings.rs +++ b/lib/core/src/bindings.rs @@ -329,12 +329,35 @@ pub struct _RouteHintHop { pub htlc_maximum_msat: Option, } + +#[frb(mirror(Amount))] +pub enum _Amount { + Bitcoin { + amount_msat: u64, + }, + Currency { + iso4217_code: String, + fractional_amount: u64, + }, +} + +#[frb(mirror(LNOffer))] +pub struct _LNOffer { + pub bolt12: String, + pub chains: Vec, + pub amount: Option, + pub description: Option, + pub absolute_expiry: Option, + pub issuer: Option, + pub signing_pubkey: Option, +} + #[frb(mirror(InputType))] pub enum _InputType { BitcoinAddress { address: BitcoinAddressData }, LiquidAddress { address: LiquidAddressData }, Bolt11 { invoice: LNInvoice }, - Bolt12Offer { offer: String }, + Bolt12Offer { offer: LNOffer }, NodeId { node_id: String }, Url { url: String }, LnUrlPay { data: LnUrlPayRequestData }, diff --git a/lib/core/src/frb_generated.rs b/lib/core/src/frb_generated.rs index 58e76e252..d361c26ad 100644 --- a/lib/core/src/frb_generated.rs +++ b/lib/core/src/frb_generated.rs @@ -1765,6 +1765,18 @@ const _: fn() = || { let _: String = reason; } } + match None::.unwrap() { + crate::bindings::Amount::Bitcoin { amount_msat } => { + let _: u64 = amount_msat; + } + crate::bindings::Amount::Currency { + iso4217_code, + fractional_amount, + } => { + let _: String = iso4217_code; + let _: u64 = fractional_amount; + } + } { let BitcoinAddressData = None::.unwrap(); let _: String = BitcoinAddressData.address; @@ -1799,7 +1811,7 @@ const _: fn() = || { let _: crate::bindings::LNInvoice = invoice; } crate::bindings::InputType::Bolt12Offer { offer } => { - let _: String = offer; + let _: crate::bindings::LNOffer = offer; } crate::bindings::InputType::NodeId { node_id } => { let _: String = node_id; @@ -1844,6 +1856,16 @@ const _: fn() = || { let _: Vec = LNInvoice.payment_secret; let _: u64 = LNInvoice.min_final_cltv_expiry_delta; } + { + let LNOffer = None::.unwrap(); + let _: String = LNOffer.bolt12; + let _: Vec = LNOffer.chains; + let _: Option = LNOffer.amount; + let _: Option = LNOffer.description; + let _: Option = LNOffer.absolute_expiry; + let _: Option = LNOffer.issuer; + let _: Option = LNOffer.signing_pubkey; + } { let LnUrlAuthRequestData = None::.unwrap(); let _: String = LnUrlAuthRequestData.k1; @@ -2185,6 +2207,32 @@ impl SseDecode for crate::bindings::AesSuccessActionDataResult { } } +impl SseDecode for crate::bindings::Amount { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut tag_ = ::sse_decode(deserializer); + match tag_ { + 0 => { + let mut var_amountMsat = ::sse_decode(deserializer); + return crate::bindings::Amount::Bitcoin { + amount_msat: var_amountMsat, + }; + } + 1 => { + let mut var_iso4217Code = ::sse_decode(deserializer); + let mut var_fractionalAmount = ::sse_decode(deserializer); + return crate::bindings::Amount::Currency { + iso4217_code: var_iso4217Code, + fractional_amount: var_fractionalAmount, + }; + } + _ => { + unimplemented!(""); + } + } + } +} + impl SseDecode for crate::model::BackupRequest { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -2435,7 +2483,7 @@ impl SseDecode for crate::bindings::InputType { }; } 3 => { - let mut var_offer = ::sse_decode(deserializer); + let mut var_offer = ::sse_decode(deserializer); return crate::bindings::InputType::Bolt12Offer { offer: var_offer }; } 4 => { @@ -2531,6 +2579,18 @@ impl SseDecode for crate::model::LiquidNetwork { } } +impl SseDecode for Vec { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut len_ = ::sse_decode(deserializer); + let mut ans_ = vec![]; + for idx_ in 0..len_ { + ans_.push(::sse_decode(deserializer)); + } + return ans_; + } +} + impl SseDecode for Vec { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -2727,6 +2787,28 @@ impl SseDecode for crate::bindings::LNInvoice { } } +impl SseDecode for crate::bindings::LNOffer { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_bolt12 = ::sse_decode(deserializer); + let mut var_chains = >::sse_decode(deserializer); + let mut var_amount = >::sse_decode(deserializer); + let mut var_description = >::sse_decode(deserializer); + let mut var_absoluteExpiry = >::sse_decode(deserializer); + let mut var_issuer = >::sse_decode(deserializer); + let mut var_signingPubkey = >::sse_decode(deserializer); + return crate::bindings::LNOffer { + bolt12: var_bolt12, + chains: var_chains, + amount: var_amount, + description: var_description, + absolute_expiry: var_absoluteExpiry, + issuer: var_issuer, + signing_pubkey: var_signingPubkey, + }; + } +} + impl SseDecode for crate::bindings::duplicates::LnUrlAuthError { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -3154,6 +3236,17 @@ impl SseDecode for Option { } } +impl SseDecode for Option { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + if (::sse_decode(deserializer)) { + return Some(::sse_decode(deserializer)); + } else { + return None; + } + } +} + impl SseDecode for Option { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -4197,6 +4290,39 @@ impl flutter_rust_bridge::IntoIntoDart { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + match self.0 { + crate::bindings::Amount::Bitcoin { amount_msat } => { + [0.into_dart(), amount_msat.into_into_dart().into_dart()].into_dart() + } + crate::bindings::Amount::Currency { + iso4217_code, + fractional_amount, + } => [ + 1.into_dart(), + iso4217_code.into_into_dart().into_dart(), + fractional_amount.into_into_dart().into_dart(), + ] + .into_dart(), + _ => { + unimplemented!(""); + } + } + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive + for FrbWrapper +{ +} +impl flutter_rust_bridge::IntoIntoDart> + for crate::bindings::Amount +{ + fn into_into_dart(self) -> FrbWrapper { + self.into() + } +} +// Codec=Dco (DartCObject based), see doc to use other codecs impl flutter_rust_bridge::IntoDart for crate::model::BackupRequest { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { [self.backup_path.into_into_dart().into_dart()].into_dart() @@ -4685,6 +4811,32 @@ impl flutter_rust_bridge::IntoIntoDart> } } // Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for FrbWrapper { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + [ + self.0.bolt12.into_into_dart().into_dart(), + self.0.chains.into_into_dart().into_dart(), + self.0.amount.into_into_dart().into_dart(), + self.0.description.into_into_dart().into_dart(), + self.0.absolute_expiry.into_into_dart().into_dart(), + self.0.issuer.into_into_dart().into_dart(), + self.0.signing_pubkey.into_into_dart().into_dart(), + ] + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive + for FrbWrapper +{ +} +impl flutter_rust_bridge::IntoIntoDart> + for crate::bindings::LNOffer +{ + fn into_into_dart(self) -> FrbWrapper { + self.into() + } +} +// Codec=Dco (DartCObject based), see doc to use other codecs impl flutter_rust_bridge::IntoDart for crate::bindings::duplicates::LnUrlAuthError { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { match self { @@ -6242,6 +6394,29 @@ impl SseEncode for crate::bindings::AesSuccessActionDataResult { } } +impl SseEncode for crate::bindings::Amount { + // 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::bindings::Amount::Bitcoin { amount_msat } => { + ::sse_encode(0, serializer); + ::sse_encode(amount_msat, serializer); + } + crate::bindings::Amount::Currency { + iso4217_code, + fractional_amount, + } => { + ::sse_encode(1, serializer); + ::sse_encode(iso4217_code, serializer); + ::sse_encode(fractional_amount, serializer); + } + _ => { + unimplemented!(""); + } + } + } +} + impl SseEncode for crate::model::BackupRequest { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -6422,7 +6597,7 @@ impl SseEncode for crate::bindings::InputType { } crate::bindings::InputType::Bolt12Offer { offer } => { ::sse_encode(3, serializer); - ::sse_encode(offer, serializer); + ::sse_encode(offer, serializer); } crate::bindings::InputType::NodeId { node_id } => { ::sse_encode(4, serializer); @@ -6500,6 +6675,16 @@ impl SseEncode for crate::model::LiquidNetwork { } } +impl SseEncode for Vec { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.len() as _, serializer); + for item in self { + ::sse_encode(item, serializer); + } + } +} + impl SseEncode for Vec { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -6649,6 +6834,19 @@ impl SseEncode for crate::bindings::LNInvoice { } } +impl SseEncode for crate::bindings::LNOffer { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.bolt12, serializer); + >::sse_encode(self.chains, serializer); + >::sse_encode(self.amount, serializer); + >::sse_encode(self.description, serializer); + >::sse_encode(self.absolute_expiry, serializer); + >::sse_encode(self.issuer, serializer); + >::sse_encode(self.signing_pubkey, serializer); + } +} + impl SseEncode for crate::bindings::duplicates::LnUrlAuthError { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -6986,6 +7184,16 @@ impl SseEncode for Option { } } +impl SseEncode for Option { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.is_some(), serializer); + if let Some(value) = self { + ::sse_encode(value, serializer); + } + } +} + impl SseEncode for Option { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -7872,6 +8080,27 @@ mod io { } } } + impl CstDecode for wire_cst_amount { + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> crate::bindings::Amount { + match self.tag { + 0 => { + let ans = unsafe { self.kind.Bitcoin }; + crate::bindings::Amount::Bitcoin { + amount_msat: ans.amount_msat.cst_decode(), + } + } + 1 => { + let ans = unsafe { self.kind.Currency }; + crate::bindings::Amount::Currency { + iso4217_code: ans.iso4217_code.cst_decode(), + fractional_amount: ans.fractional_amount.cst_decode(), + } + } + _ => unreachable!(), + } + } + } impl CstDecode for wire_cst_backup_request { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> crate::model::BackupRequest { @@ -7925,6 +8154,13 @@ mod io { CstDecode::::cst_decode(*wrap).into() } } + impl CstDecode for *mut wire_cst_amount { + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> crate::bindings::Amount { + let wrap = unsafe { flutter_rust_bridge::for_generated::box_from_leak_ptr(self) }; + CstDecode::::cst_decode(*wrap).into() + } + } impl CstDecode for *mut wire_cst_backup_request { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> crate::model::BackupRequest { @@ -8014,6 +8250,13 @@ mod io { CstDecode::::cst_decode(*wrap).into() } } + impl CstDecode for *mut wire_cst_ln_offer { + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> crate::bindings::LNOffer { + let wrap = unsafe { flutter_rust_bridge::for_generated::box_from_leak_ptr(self) }; + CstDecode::::cst_decode(*wrap).into() + } + } impl CstDecode for *mut wire_cst_ln_url_auth_request_data { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> crate::bindings::LnUrlAuthRequestData { @@ -8443,6 +8686,16 @@ mod io { } } } + impl CstDecode> for *mut wire_cst_list_String { + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> Vec { + let vec = unsafe { + let wrap = flutter_rust_bridge::for_generated::box_from_leak_ptr(self); + flutter_rust_bridge::for_generated::vec_from_leak_ptr(wrap.ptr, wrap.len) + }; + vec.into_iter().map(CstDecode::cst_decode).collect() + } + } impl CstDecode> for *mut wire_cst_list_fiat_currency { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> Vec { @@ -8594,6 +8847,20 @@ mod io { } } } + impl CstDecode for wire_cst_ln_offer { + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> crate::bindings::LNOffer { + crate::bindings::LNOffer { + bolt12: self.bolt12.cst_decode(), + chains: self.chains.cst_decode(), + amount: self.amount.cst_decode(), + description: self.description.cst_decode(), + absolute_expiry: self.absolute_expiry.cst_decode(), + issuer: self.issuer.cst_decode(), + signing_pubkey: self.signing_pubkey.cst_decode(), + } + } + } impl CstDecode for wire_cst_ln_url_auth_error { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> crate::bindings::duplicates::LnUrlAuthError { @@ -9559,6 +9826,19 @@ mod io { Self::new_with_null_ptr() } } + impl NewWithNullPtr for wire_cst_amount { + fn new_with_null_ptr() -> Self { + Self { + tag: -1, + kind: AmountKind { nil__: () }, + } + } + } + impl Default for wire_cst_amount { + fn default() -> Self { + Self::new_with_null_ptr() + } + } impl NewWithNullPtr for wire_cst_backup_request { fn new_with_null_ptr() -> Self { Self { @@ -9841,6 +10121,24 @@ mod io { Self::new_with_null_ptr() } } + impl NewWithNullPtr for wire_cst_ln_offer { + fn new_with_null_ptr() -> Self { + Self { + bolt12: core::ptr::null_mut(), + chains: core::ptr::null_mut(), + amount: core::ptr::null_mut(), + description: core::ptr::null_mut(), + absolute_expiry: core::ptr::null_mut(), + issuer: core::ptr::null_mut(), + signing_pubkey: core::ptr::null_mut(), + } + } + } + impl Default for wire_cst_ln_offer { + fn default() -> Self { + Self::new_with_null_ptr() + } + } impl NewWithNullPtr for wire_cst_ln_url_auth_error { fn new_with_null_ptr() -> Self { Self { @@ -11005,6 +11303,11 @@ mod io { ) } + #[no_mangle] + pub extern "C" fn frbgen_breez_liquid_cst_new_box_autoadd_amount() -> *mut wire_cst_amount { + flutter_rust_bridge::for_generated::new_leak_box_ptr(wire_cst_amount::new_with_null_ptr()) + } + #[no_mangle] pub extern "C" fn frbgen_breez_liquid_cst_new_box_autoadd_backup_request( ) -> *mut wire_cst_backup_request { @@ -11103,6 +11406,11 @@ mod io { ) } + #[no_mangle] + pub extern "C" fn frbgen_breez_liquid_cst_new_box_autoadd_ln_offer() -> *mut wire_cst_ln_offer { + flutter_rust_bridge::for_generated::new_leak_box_ptr(wire_cst_ln_offer::new_with_null_ptr()) + } + #[no_mangle] pub extern "C" fn frbgen_breez_liquid_cst_new_box_autoadd_ln_url_auth_request_data( ) -> *mut wire_cst_ln_url_auth_request_data { @@ -11337,6 +11645,20 @@ mod io { ) } + #[no_mangle] + pub extern "C" fn frbgen_breez_liquid_cst_new_list_String( + len: i32, + ) -> *mut wire_cst_list_String { + let wrap = wire_cst_list_String { + ptr: flutter_rust_bridge::for_generated::new_leak_vec_ptr( + <*mut wire_cst_list_prim_u_8_strict>::new_with_null_ptr(), + len, + ), + len, + }; + flutter_rust_bridge::for_generated::new_leak_box_ptr(wrap) + } + #[no_mangle] pub extern "C" fn frbgen_breez_liquid_cst_new_list_fiat_currency( len: i32, @@ -11507,6 +11829,30 @@ mod io { } #[repr(C)] #[derive(Clone, Copy)] + pub struct wire_cst_amount { + tag: i32, + kind: AmountKind, + } + #[repr(C)] + #[derive(Clone, Copy)] + pub union AmountKind { + Bitcoin: wire_cst_Amount_Bitcoin, + Currency: wire_cst_Amount_Currency, + nil__: (), + } + #[repr(C)] + #[derive(Clone, Copy)] + pub struct wire_cst_Amount_Bitcoin { + amount_msat: u64, + } + #[repr(C)] + #[derive(Clone, Copy)] + pub struct wire_cst_Amount_Currency { + iso4217_code: *mut wire_cst_list_prim_u_8_strict, + fractional_amount: u64, + } + #[repr(C)] + #[derive(Clone, Copy)] pub struct wire_cst_backup_request { backup_path: *mut wire_cst_list_prim_u_8_strict, } @@ -11643,7 +11989,7 @@ mod io { #[repr(C)] #[derive(Clone, Copy)] pub struct wire_cst_InputType_Bolt12Offer { - offer: *mut wire_cst_list_prim_u_8_strict, + offer: *mut wire_cst_ln_offer, } #[repr(C)] #[derive(Clone, Copy)] @@ -11700,6 +12046,12 @@ mod io { } #[repr(C)] #[derive(Clone, Copy)] + pub struct wire_cst_list_String { + ptr: *mut *mut wire_cst_list_prim_u_8_strict, + len: i32, + } + #[repr(C)] + #[derive(Clone, Copy)] pub struct wire_cst_list_fiat_currency { ptr: *mut wire_cst_fiat_currency, len: i32, @@ -11809,6 +12161,17 @@ mod io { } #[repr(C)] #[derive(Clone, Copy)] + pub struct wire_cst_ln_offer { + bolt12: *mut wire_cst_list_prim_u_8_strict, + chains: *mut wire_cst_list_String, + amount: *mut wire_cst_amount, + description: *mut wire_cst_list_prim_u_8_strict, + absolute_expiry: *mut u64, + issuer: *mut wire_cst_list_prim_u_8_strict, + signing_pubkey: *mut wire_cst_list_prim_u_8_strict, + } + #[repr(C)] + #[derive(Clone, Copy)] pub struct wire_cst_ln_url_auth_error { tag: i32, kind: LnUrlAuthErrorKind, diff --git a/packages/dart/lib/src/bindings.dart b/packages/dart/lib/src/bindings.dart index edd4b4899..47b2144a6 100644 --- a/packages/dart/lib/src/bindings.dart +++ b/packages/dart/lib/src/bindings.dart @@ -157,6 +157,19 @@ sealed class AesSuccessActionDataResult with _$AesSuccessActionDataResult { }) = AesSuccessActionDataResult_ErrorStatus; } +@freezed +sealed class Amount with _$Amount { + const Amount._(); + + const factory Amount.bitcoin({ + required BigInt amountMsat, + }) = Amount_Bitcoin; + const factory Amount.currency({ + required String iso4217Code, + required BigInt fractionalAmount, + }) = Amount_Currency; +} + class BindingEventListener { final RustStreamSink stream; @@ -282,7 +295,7 @@ sealed class InputType with _$InputType { required LNInvoice invoice, }) = InputType_Bolt11; const factory InputType.bolt12Offer({ - required String offer, + required LNOffer offer, }) = InputType_Bolt12Offer; const factory InputType.nodeId({ required String nodeId, @@ -406,6 +419,49 @@ class LNInvoice { minFinalCltvExpiryDelta == other.minFinalCltvExpiryDelta; } +class LNOffer { + final String bolt12; + final List chains; + final Amount? amount; + final String? description; + final BigInt? absoluteExpiry; + final String? issuer; + final String? signingPubkey; + + const LNOffer({ + required this.bolt12, + required this.chains, + this.amount, + this.description, + this.absoluteExpiry, + this.issuer, + this.signingPubkey, + }); + + @override + int get hashCode => + bolt12.hashCode ^ + chains.hashCode ^ + amount.hashCode ^ + description.hashCode ^ + absoluteExpiry.hashCode ^ + issuer.hashCode ^ + signingPubkey.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is LNOffer && + runtimeType == other.runtimeType && + bolt12 == other.bolt12 && + chains == other.chains && + amount == other.amount && + description == other.description && + absoluteExpiry == other.absoluteExpiry && + issuer == other.issuer && + signingPubkey == other.signingPubkey; +} + class LnUrlAuthRequestData { final String k1; final String? action; diff --git a/packages/dart/lib/src/bindings.freezed.dart b/packages/dart/lib/src/bindings.freezed.dart index 3db72819b..cf786c06e 100644 --- a/packages/dart/lib/src/bindings.freezed.dart +++ b/packages/dart/lib/src/bindings.freezed.dart @@ -203,6 +203,191 @@ abstract class AesSuccessActionDataResult_ErrorStatus extends AesSuccessActionDa get copyWith => throw _privateConstructorUsedError; } +/// @nodoc +mixin _$Amount {} + +/// @nodoc +abstract class $AmountCopyWith<$Res> { + factory $AmountCopyWith(Amount value, $Res Function(Amount) then) = _$AmountCopyWithImpl<$Res, Amount>; +} + +/// @nodoc +class _$AmountCopyWithImpl<$Res, $Val extends Amount> implements $AmountCopyWith<$Res> { + _$AmountCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of Amount + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc +abstract class _$$Amount_BitcoinImplCopyWith<$Res> { + factory _$$Amount_BitcoinImplCopyWith( + _$Amount_BitcoinImpl value, $Res Function(_$Amount_BitcoinImpl) then) = + __$$Amount_BitcoinImplCopyWithImpl<$Res>; + @useResult + $Res call({BigInt amountMsat}); +} + +/// @nodoc +class __$$Amount_BitcoinImplCopyWithImpl<$Res> extends _$AmountCopyWithImpl<$Res, _$Amount_BitcoinImpl> + implements _$$Amount_BitcoinImplCopyWith<$Res> { + __$$Amount_BitcoinImplCopyWithImpl(_$Amount_BitcoinImpl _value, $Res Function(_$Amount_BitcoinImpl) _then) + : super(_value, _then); + + /// Create a copy of Amount + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? amountMsat = null, + }) { + return _then(_$Amount_BitcoinImpl( + amountMsat: null == amountMsat + ? _value.amountMsat + : amountMsat // ignore: cast_nullable_to_non_nullable + as BigInt, + )); + } +} + +/// @nodoc + +class _$Amount_BitcoinImpl extends Amount_Bitcoin { + const _$Amount_BitcoinImpl({required this.amountMsat}) : super._(); + + @override + final BigInt amountMsat; + + @override + String toString() { + return 'Amount.bitcoin(amountMsat: $amountMsat)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$Amount_BitcoinImpl && + (identical(other.amountMsat, amountMsat) || other.amountMsat == amountMsat)); + } + + @override + int get hashCode => Object.hash(runtimeType, amountMsat); + + /// Create a copy of Amount + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$Amount_BitcoinImplCopyWith<_$Amount_BitcoinImpl> get copyWith => + __$$Amount_BitcoinImplCopyWithImpl<_$Amount_BitcoinImpl>(this, _$identity); +} + +abstract class Amount_Bitcoin extends Amount { + const factory Amount_Bitcoin({required final BigInt amountMsat}) = _$Amount_BitcoinImpl; + const Amount_Bitcoin._() : super._(); + + BigInt get amountMsat; + + /// Create a copy of Amount + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$Amount_BitcoinImplCopyWith<_$Amount_BitcoinImpl> get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$Amount_CurrencyImplCopyWith<$Res> { + factory _$$Amount_CurrencyImplCopyWith( + _$Amount_CurrencyImpl value, $Res Function(_$Amount_CurrencyImpl) then) = + __$$Amount_CurrencyImplCopyWithImpl<$Res>; + @useResult + $Res call({String iso4217Code, BigInt fractionalAmount}); +} + +/// @nodoc +class __$$Amount_CurrencyImplCopyWithImpl<$Res> extends _$AmountCopyWithImpl<$Res, _$Amount_CurrencyImpl> + implements _$$Amount_CurrencyImplCopyWith<$Res> { + __$$Amount_CurrencyImplCopyWithImpl( + _$Amount_CurrencyImpl _value, $Res Function(_$Amount_CurrencyImpl) _then) + : super(_value, _then); + + /// Create a copy of Amount + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? iso4217Code = null, + Object? fractionalAmount = null, + }) { + return _then(_$Amount_CurrencyImpl( + iso4217Code: null == iso4217Code + ? _value.iso4217Code + : iso4217Code // ignore: cast_nullable_to_non_nullable + as String, + fractionalAmount: null == fractionalAmount + ? _value.fractionalAmount + : fractionalAmount // ignore: cast_nullable_to_non_nullable + as BigInt, + )); + } +} + +/// @nodoc + +class _$Amount_CurrencyImpl extends Amount_Currency { + const _$Amount_CurrencyImpl({required this.iso4217Code, required this.fractionalAmount}) : super._(); + + @override + final String iso4217Code; + @override + final BigInt fractionalAmount; + + @override + String toString() { + return 'Amount.currency(iso4217Code: $iso4217Code, fractionalAmount: $fractionalAmount)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$Amount_CurrencyImpl && + (identical(other.iso4217Code, iso4217Code) || other.iso4217Code == iso4217Code) && + (identical(other.fractionalAmount, fractionalAmount) || + other.fractionalAmount == fractionalAmount)); + } + + @override + int get hashCode => Object.hash(runtimeType, iso4217Code, fractionalAmount); + + /// Create a copy of Amount + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$Amount_CurrencyImplCopyWith<_$Amount_CurrencyImpl> get copyWith => + __$$Amount_CurrencyImplCopyWithImpl<_$Amount_CurrencyImpl>(this, _$identity); +} + +abstract class Amount_Currency extends Amount { + const factory Amount_Currency({required final String iso4217Code, required final BigInt fractionalAmount}) = + _$Amount_CurrencyImpl; + const Amount_Currency._() : super._(); + + String get iso4217Code; + BigInt get fractionalAmount; + + /// Create a copy of Amount + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$Amount_CurrencyImplCopyWith<_$Amount_CurrencyImpl> get copyWith => throw _privateConstructorUsedError; +} + /// @nodoc mixin _$InputType {} @@ -468,7 +653,7 @@ abstract class _$$InputType_Bolt12OfferImplCopyWith<$Res> { _$InputType_Bolt12OfferImpl value, $Res Function(_$InputType_Bolt12OfferImpl) then) = __$$InputType_Bolt12OfferImplCopyWithImpl<$Res>; @useResult - $Res call({String offer}); + $Res call({LNOffer offer}); } /// @nodoc @@ -490,7 +675,7 @@ class __$$InputType_Bolt12OfferImplCopyWithImpl<$Res> offer: null == offer ? _value.offer : offer // ignore: cast_nullable_to_non_nullable - as String, + as LNOffer, )); } } @@ -501,7 +686,7 @@ class _$InputType_Bolt12OfferImpl extends InputType_Bolt12Offer { const _$InputType_Bolt12OfferImpl({required this.offer}) : super._(); @override - final String offer; + final LNOffer offer; @override String toString() { @@ -529,10 +714,10 @@ class _$InputType_Bolt12OfferImpl extends InputType_Bolt12Offer { } abstract class InputType_Bolt12Offer extends InputType { - const factory InputType_Bolt12Offer({required final String offer}) = _$InputType_Bolt12OfferImpl; + const factory InputType_Bolt12Offer({required final LNOffer offer}) = _$InputType_Bolt12OfferImpl; const InputType_Bolt12Offer._() : super._(); - String get offer; + LNOffer get offer; /// Create a copy of InputType /// with the given fields replaced by the non-null parameter values. diff --git a/packages/dart/lib/src/frb_generated.dart b/packages/dart/lib/src/frb_generated.dart index 67832e298..4c88030dd 100644 --- a/packages/dart/lib/src/frb_generated.dart +++ b/packages/dart/lib/src/frb_generated.dart @@ -1287,6 +1287,24 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } + @protected + Amount dco_decode_amount(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + switch (raw[0]) { + case 0: + return Amount_Bitcoin( + amountMsat: dco_decode_u_64(raw[1]), + ); + case 1: + return Amount_Currency( + iso4217Code: dco_decode_String(raw[1]), + fractionalAmount: dco_decode_u_64(raw[2]), + ); + default: + throw Exception("unreachable"); + } + } + @protected BackupRequest dco_decode_backup_request(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -1345,6 +1363,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return dco_decode_aes_success_action_data_result(raw); } + @protected + Amount dco_decode_box_autoadd_amount(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return dco_decode_amount(raw); + } + @protected BackupRequest dco_decode_box_autoadd_backup_request(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -1423,6 +1447,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return dco_decode_ln_invoice(raw); } + @protected + LNOffer dco_decode_box_autoadd_ln_offer(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return dco_decode_ln_offer(raw); + } + @protected LnUrlAuthRequestData dco_decode_box_autoadd_ln_url_auth_request_data(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -1767,7 +1797,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); case 3: return InputType_Bolt12Offer( - offer: dco_decode_String(raw[1]), + offer: dco_decode_box_autoadd_ln_offer(raw[1]), ); case 4: return InputType_NodeId( @@ -1842,6 +1872,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return LiquidNetwork.values[raw as int]; } + @protected + List dco_decode_list_String(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return (raw as List).map(dco_decode_String).toList(); + } + @protected List dco_decode_list_fiat_currency(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -1955,6 +1991,22 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } + @protected + LNOffer dco_decode_ln_offer(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 7) throw Exception('unexpected arr length: expect 7 but see ${arr.length}'); + return LNOffer( + bolt12: dco_decode_String(arr[0]), + chains: dco_decode_list_String(arr[1]), + amount: dco_decode_opt_box_autoadd_amount(arr[2]), + description: dco_decode_opt_String(arr[3]), + absoluteExpiry: dco_decode_opt_box_autoadd_u_64(arr[4]), + issuer: dco_decode_opt_String(arr[5]), + signingPubkey: dco_decode_opt_String(arr[6]), + ); + } + @protected LnUrlAuthError dco_decode_ln_url_auth_error(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -2297,6 +2349,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw == null ? null : dco_decode_String(raw); } + @protected + Amount? dco_decode_opt_box_autoadd_amount(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw == null ? null : dco_decode_box_autoadd_amount(raw); + } + @protected bool? dco_decode_opt_box_autoadd_bool(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -3091,6 +3149,24 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } + @protected + Amount sse_decode_amount(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + var tag_ = sse_decode_i_32(deserializer); + switch (tag_) { + case 0: + var var_amountMsat = sse_decode_u_64(deserializer); + return Amount_Bitcoin(amountMsat: var_amountMsat); + case 1: + var var_iso4217Code = sse_decode_String(deserializer); + var var_fractionalAmount = sse_decode_u_64(deserializer); + return Amount_Currency(iso4217Code: var_iso4217Code, fractionalAmount: var_fractionalAmount); + default: + throw UnimplementedError(''); + } + } + @protected BackupRequest sse_decode_backup_request(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -3147,6 +3223,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return (sse_decode_aes_success_action_data_result(deserializer)); } + @protected + Amount sse_decode_box_autoadd_amount(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return (sse_decode_amount(deserializer)); + } + @protected BackupRequest sse_decode_box_autoadd_backup_request(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -3225,6 +3307,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return (sse_decode_ln_invoice(deserializer)); } + @protected + LNOffer sse_decode_box_autoadd_ln_offer(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return (sse_decode_ln_offer(deserializer)); + } + @protected LnUrlAuthRequestData sse_decode_box_autoadd_ln_url_auth_request_data(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -3566,7 +3654,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_invoice = sse_decode_box_autoadd_ln_invoice(deserializer); return InputType_Bolt11(invoice: var_invoice); case 3: - var var_offer = sse_decode_String(deserializer); + var var_offer = sse_decode_box_autoadd_ln_offer(deserializer); return InputType_Bolt12Offer(offer: var_offer); case 4: var var_nodeId = sse_decode_String(deserializer); @@ -3633,6 +3721,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return LiquidNetwork.values[inner]; } + @protected + List sse_decode_list_String(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + var len_ = sse_decode_i_32(deserializer); + var ans_ = []; + for (var idx_ = 0; idx_ < len_; ++idx_) { + ans_.add(sse_decode_String(deserializer)); + } + return ans_; + } + @protected List sse_decode_list_fiat_currency(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -3813,6 +3913,26 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { minFinalCltvExpiryDelta: var_minFinalCltvExpiryDelta); } + @protected + LNOffer sse_decode_ln_offer(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + var var_bolt12 = sse_decode_String(deserializer); + var var_chains = sse_decode_list_String(deserializer); + var var_amount = sse_decode_opt_box_autoadd_amount(deserializer); + var var_description = sse_decode_opt_String(deserializer); + var var_absoluteExpiry = sse_decode_opt_box_autoadd_u_64(deserializer); + var var_issuer = sse_decode_opt_String(deserializer); + var var_signingPubkey = sse_decode_opt_String(deserializer); + return LNOffer( + bolt12: var_bolt12, + chains: var_chains, + amount: var_amount, + description: var_description, + absoluteExpiry: var_absoluteExpiry, + issuer: var_issuer, + signingPubkey: var_signingPubkey); + } + @protected LnUrlAuthError sse_decode_ln_url_auth_error(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -4118,6 +4238,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } + @protected + Amount? sse_decode_opt_box_autoadd_amount(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + if (sse_decode_bool(deserializer)) { + return (sse_decode_box_autoadd_amount(deserializer)); + } else { + return null; + } + } + @protected bool? sse_decode_opt_box_autoadd_bool(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -5018,6 +5149,22 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } + @protected + void sse_encode_amount(Amount self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + switch (self) { + case Amount_Bitcoin(amountMsat: final amountMsat): + sse_encode_i_32(0, serializer); + sse_encode_u_64(amountMsat, serializer); + case Amount_Currency(iso4217Code: final iso4217Code, fractionalAmount: final fractionalAmount): + sse_encode_i_32(1, serializer); + sse_encode_String(iso4217Code, serializer); + sse_encode_u_64(fractionalAmount, serializer); + default: + throw UnimplementedError(''); + } + } + @protected void sse_encode_backup_request(BackupRequest self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -5066,6 +5213,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_aes_success_action_data_result(self, serializer); } + @protected + void sse_encode_box_autoadd_amount(Amount self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_amount(self, serializer); + } + @protected void sse_encode_box_autoadd_backup_request(BackupRequest self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -5144,6 +5297,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_ln_invoice(self, serializer); } + @protected + void sse_encode_box_autoadd_ln_offer(LNOffer self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_ln_offer(self, serializer); + } + @protected void sse_encode_box_autoadd_ln_url_auth_request_data(LnUrlAuthRequestData self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -5459,7 +5618,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_box_autoadd_ln_invoice(invoice, serializer); case InputType_Bolt12Offer(offer: final offer): sse_encode_i_32(3, serializer); - sse_encode_String(offer, serializer); + sse_encode_box_autoadd_ln_offer(offer, serializer); case InputType_NodeId(nodeId: final nodeId): sse_encode_i_32(4, serializer); sse_encode_String(nodeId, serializer); @@ -5516,6 +5675,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_i_32(self.index, serializer); } + @protected + void sse_encode_list_String(List self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_i_32(self.length, serializer); + for (final item in self) { + sse_encode_String(item, serializer); + } + } + @protected void sse_encode_list_fiat_currency(List self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -5647,6 +5815,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_64(self.minFinalCltvExpiryDelta, serializer); } + @protected + void sse_encode_ln_offer(LNOffer self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_String(self.bolt12, serializer); + sse_encode_list_String(self.chains, serializer); + sse_encode_opt_box_autoadd_amount(self.amount, serializer); + sse_encode_opt_String(self.description, serializer); + sse_encode_opt_box_autoadd_u_64(self.absoluteExpiry, serializer); + sse_encode_opt_String(self.issuer, serializer); + sse_encode_opt_String(self.signingPubkey, serializer); + } + @protected void sse_encode_ln_url_auth_error(LnUrlAuthError self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -5911,6 +6091,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } + @protected + void sse_encode_opt_box_autoadd_amount(Amount? self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + sse_encode_bool(self != null, serializer); + if (self != null) { + sse_encode_box_autoadd_amount(self, serializer); + } + } + @protected void sse_encode_opt_box_autoadd_bool(bool? self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs diff --git a/packages/dart/lib/src/frb_generated.io.dart b/packages/dart/lib/src/frb_generated.io.dart index a115791b2..080e412ec 100644 --- a/packages/dart/lib/src/frb_generated.io.dart +++ b/packages/dart/lib/src/frb_generated.io.dart @@ -59,6 +59,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected AesSuccessActionDataResult dco_decode_aes_success_action_data_result(dynamic raw); + @protected + Amount dco_decode_amount(dynamic raw); + @protected BackupRequest dco_decode_backup_request(dynamic raw); @@ -80,6 +83,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected AesSuccessActionDataResult dco_decode_box_autoadd_aes_success_action_data_result(dynamic raw); + @protected + Amount dco_decode_box_autoadd_amount(dynamic raw); + @protected BackupRequest dco_decode_box_autoadd_backup_request(dynamic raw); @@ -119,6 +125,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected LNInvoice dco_decode_box_autoadd_ln_invoice(dynamic raw); + @protected + LNOffer dco_decode_box_autoadd_ln_offer(dynamic raw); + @protected LnUrlAuthRequestData dco_decode_box_autoadd_ln_url_auth_request_data(dynamic raw); @@ -266,6 +275,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected LiquidNetwork dco_decode_liquid_network(dynamic raw); + @protected + List dco_decode_list_String(dynamic raw); + @protected List dco_decode_list_fiat_currency(dynamic raw); @@ -305,6 +317,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected LNInvoice dco_decode_ln_invoice(dynamic raw); + @protected + LNOffer dco_decode_ln_offer(dynamic raw); + @protected LnUrlAuthError dco_decode_ln_url_auth_error(dynamic raw); @@ -371,6 +386,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected String? dco_decode_opt_String(dynamic raw); + @protected + Amount? dco_decode_opt_box_autoadd_amount(dynamic raw); + @protected bool? dco_decode_opt_box_autoadd_bool(dynamic raw); @@ -580,6 +598,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected AesSuccessActionDataResult sse_decode_aes_success_action_data_result(SseDeserializer deserializer); + @protected + Amount sse_decode_amount(SseDeserializer deserializer); + @protected BackupRequest sse_decode_backup_request(SseDeserializer deserializer); @@ -603,6 +624,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { AesSuccessActionDataResult sse_decode_box_autoadd_aes_success_action_data_result( SseDeserializer deserializer); + @protected + Amount sse_decode_box_autoadd_amount(SseDeserializer deserializer); + @protected BackupRequest sse_decode_box_autoadd_backup_request(SseDeserializer deserializer); @@ -642,6 +666,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected LNInvoice sse_decode_box_autoadd_ln_invoice(SseDeserializer deserializer); + @protected + LNOffer sse_decode_box_autoadd_ln_offer(SseDeserializer deserializer); + @protected LnUrlAuthRequestData sse_decode_box_autoadd_ln_url_auth_request_data(SseDeserializer deserializer); @@ -789,6 +816,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected LiquidNetwork sse_decode_liquid_network(SseDeserializer deserializer); + @protected + List sse_decode_list_String(SseDeserializer deserializer); + @protected List sse_decode_list_fiat_currency(SseDeserializer deserializer); @@ -828,6 +858,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected LNInvoice sse_decode_ln_invoice(SseDeserializer deserializer); + @protected + LNOffer sse_decode_ln_offer(SseDeserializer deserializer); + @protected LnUrlAuthError sse_decode_ln_url_auth_error(SseDeserializer deserializer); @@ -894,6 +927,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected String? sse_decode_opt_String(SseDeserializer deserializer); + @protected + Amount? sse_decode_opt_box_autoadd_amount(SseDeserializer deserializer); + @protected bool? sse_decode_opt_box_autoadd_bool(SseDeserializer deserializer); @@ -1129,6 +1165,14 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { return ptr; } + @protected + ffi.Pointer cst_encode_box_autoadd_amount(Amount raw) { + // Codec=Cst (C-struct based), see doc to use other codecs + final ptr = wire.cst_new_box_autoadd_amount(); + cst_api_fill_to_wire_amount(raw, ptr.ref); + return ptr; + } + @protected ffi.Pointer cst_encode_box_autoadd_backup_request(BackupRequest raw) { // Codec=Cst (C-struct based), see doc to use other codecs @@ -1237,6 +1281,14 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { return ptr; } + @protected + ffi.Pointer cst_encode_box_autoadd_ln_offer(LNOffer raw) { + // Codec=Cst (C-struct based), see doc to use other codecs + final ptr = wire.cst_new_box_autoadd_ln_offer(); + cst_api_fill_to_wire_ln_offer(raw, ptr.ref); + return ptr; + } + @protected ffi.Pointer cst_encode_box_autoadd_ln_url_auth_request_data( LnUrlAuthRequestData raw) { @@ -1507,6 +1559,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { return raw.toInt(); } + @protected + ffi.Pointer cst_encode_list_String(List raw) { + // Codec=Cst (C-struct based), see doc to use other codecs + final ans = wire.cst_new_list_String(raw.length); + for (var i = 0; i < raw.length; ++i) { + ans.ref.ptr[i] = cst_encode_String(raw[i]); + } + return ans; + } + @protected ffi.Pointer cst_encode_list_fiat_currency(List raw) { // Codec=Cst (C-struct based), see doc to use other codecs @@ -1611,6 +1673,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { return raw == null ? ffi.nullptr : cst_encode_String(raw); } + @protected + ffi.Pointer cst_encode_opt_box_autoadd_amount(Amount? raw) { + // Codec=Cst (C-struct based), see doc to use other codecs + return raw == null ? ffi.nullptr : cst_encode_box_autoadd_amount(raw); + } + @protected ffi.Pointer cst_encode_opt_box_autoadd_bool(bool? raw) { // Codec=Cst (C-struct based), see doc to use other codecs @@ -1723,6 +1791,24 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { } } + @protected + void cst_api_fill_to_wire_amount(Amount apiObj, wire_cst_amount wireObj) { + if (apiObj is Amount_Bitcoin) { + var pre_amount_msat = cst_encode_u_64(apiObj.amountMsat); + wireObj.tag = 0; + wireObj.kind.Bitcoin.amount_msat = pre_amount_msat; + return; + } + if (apiObj is Amount_Currency) { + var pre_iso4217_code = cst_encode_String(apiObj.iso4217Code); + var pre_fractional_amount = cst_encode_u_64(apiObj.fractionalAmount); + wireObj.tag = 1; + wireObj.kind.Currency.iso4217_code = pre_iso4217_code; + wireObj.kind.Currency.fractional_amount = pre_fractional_amount; + return; + } + } + @protected void cst_api_fill_to_wire_backup_request(BackupRequest apiObj, wire_cst_backup_request wireObj) { wireObj.backup_path = cst_encode_opt_String(apiObj.backupPath); @@ -1762,6 +1848,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { cst_api_fill_to_wire_aes_success_action_data_result(apiObj, wireObj.ref); } + @protected + void cst_api_fill_to_wire_box_autoadd_amount(Amount apiObj, ffi.Pointer wireObj) { + cst_api_fill_to_wire_amount(apiObj, wireObj.ref); + } + @protected void cst_api_fill_to_wire_box_autoadd_backup_request( BackupRequest apiObj, ffi.Pointer wireObj) { @@ -1828,6 +1919,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { cst_api_fill_to_wire_ln_invoice(apiObj, wireObj.ref); } + @protected + void cst_api_fill_to_wire_box_autoadd_ln_offer(LNOffer apiObj, ffi.Pointer wireObj) { + cst_api_fill_to_wire_ln_offer(apiObj, wireObj.ref); + } + @protected void cst_api_fill_to_wire_box_autoadd_ln_url_auth_request_data( LnUrlAuthRequestData apiObj, ffi.Pointer wireObj) { @@ -2097,7 +2193,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { return; } if (apiObj is InputType_Bolt12Offer) { - var pre_offer = cst_encode_String(apiObj.offer); + var pre_offer = cst_encode_box_autoadd_ln_offer(apiObj.offer); wireObj.tag = 3; wireObj.kind.Bolt12Offer.offer = pre_offer; return; @@ -2209,6 +2305,17 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { wireObj.min_final_cltv_expiry_delta = cst_encode_u_64(apiObj.minFinalCltvExpiryDelta); } + @protected + void cst_api_fill_to_wire_ln_offer(LNOffer apiObj, wire_cst_ln_offer wireObj) { + wireObj.bolt12 = cst_encode_String(apiObj.bolt12); + wireObj.chains = cst_encode_list_String(apiObj.chains); + wireObj.amount = cst_encode_opt_box_autoadd_amount(apiObj.amount); + wireObj.description = cst_encode_opt_String(apiObj.description); + wireObj.absolute_expiry = cst_encode_opt_box_autoadd_u_64(apiObj.absoluteExpiry); + wireObj.issuer = cst_encode_opt_String(apiObj.issuer); + wireObj.signing_pubkey = cst_encode_opt_String(apiObj.signingPubkey); + } + @protected void cst_api_fill_to_wire_ln_url_auth_error(LnUrlAuthError apiObj, wire_cst_ln_url_auth_error wireObj) { if (apiObj is LnUrlAuthError_Generic) { @@ -3117,6 +3224,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_aes_success_action_data_result(AesSuccessActionDataResult self, SseSerializer serializer); + @protected + void sse_encode_amount(Amount self, SseSerializer serializer); + @protected void sse_encode_backup_request(BackupRequest self, SseSerializer serializer); @@ -3140,6 +3250,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_box_autoadd_aes_success_action_data_result( AesSuccessActionDataResult self, SseSerializer serializer); + @protected + void sse_encode_box_autoadd_amount(Amount self, SseSerializer serializer); + @protected void sse_encode_box_autoadd_backup_request(BackupRequest self, SseSerializer serializer); @@ -3179,6 +3292,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_box_autoadd_ln_invoice(LNInvoice self, SseSerializer serializer); + @protected + void sse_encode_box_autoadd_ln_offer(LNOffer self, SseSerializer serializer); + @protected void sse_encode_box_autoadd_ln_url_auth_request_data(LnUrlAuthRequestData self, SseSerializer serializer); @@ -3333,6 +3449,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_liquid_network(LiquidNetwork self, SseSerializer serializer); + @protected + void sse_encode_list_String(List self, SseSerializer serializer); + @protected void sse_encode_list_fiat_currency(List self, SseSerializer serializer); @@ -3372,6 +3491,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_ln_invoice(LNInvoice self, SseSerializer serializer); + @protected + void sse_encode_ln_offer(LNOffer self, SseSerializer serializer); + @protected void sse_encode_ln_url_auth_error(LnUrlAuthError self, SseSerializer serializer); @@ -3439,6 +3561,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_opt_String(String? self, SseSerializer serializer); + @protected + void sse_encode_opt_box_autoadd_amount(Amount? self, SseSerializer serializer); + @protected void sse_encode_opt_box_autoadd_bool(bool? self, SseSerializer serializer); @@ -4440,6 +4565,16 @@ class RustLibWire implements BaseWire { _cst_new_box_autoadd_aes_success_action_data_resultPtr .asFunction Function()>(); + ffi.Pointer cst_new_box_autoadd_amount() { + return _cst_new_box_autoadd_amount(); + } + + late final _cst_new_box_autoadd_amountPtr = + _lookup Function()>>( + 'frbgen_breez_liquid_cst_new_box_autoadd_amount'); + late final _cst_new_box_autoadd_amount = + _cst_new_box_autoadd_amountPtr.asFunction Function()>(); + ffi.Pointer cst_new_box_autoadd_backup_request() { return _cst_new_box_autoadd_backup_request(); } @@ -4578,6 +4713,16 @@ class RustLibWire implements BaseWire { late final _cst_new_box_autoadd_ln_invoice = _cst_new_box_autoadd_ln_invoicePtr.asFunction Function()>(); + ffi.Pointer cst_new_box_autoadd_ln_offer() { + return _cst_new_box_autoadd_ln_offer(); + } + + late final _cst_new_box_autoadd_ln_offerPtr = + _lookup Function()>>( + 'frbgen_breez_liquid_cst_new_box_autoadd_ln_offer'); + late final _cst_new_box_autoadd_ln_offer = + _cst_new_box_autoadd_ln_offerPtr.asFunction Function()>(); + ffi.Pointer cst_new_box_autoadd_ln_url_auth_request_data() { return _cst_new_box_autoadd_ln_url_auth_request_data(); } @@ -4902,6 +5047,20 @@ class RustLibWire implements BaseWire { late final _cst_new_box_autoadd_url_success_action_data = _cst_new_box_autoadd_url_success_action_dataPtr .asFunction Function()>(); + ffi.Pointer cst_new_list_String( + int len, + ) { + return _cst_new_list_String( + len, + ); + } + + late final _cst_new_list_StringPtr = + _lookup Function(ffi.Int32)>>( + 'frbgen_breez_liquid_cst_new_list_String'); + late final _cst_new_list_String = + _cst_new_list_StringPtr.asFunction Function(int)>(); + ffi.Pointer cst_new_list_fiat_currency( int len, ) { @@ -5683,6 +5842,31 @@ final class wire_cst_aes_success_action_data_result extends ffi.Struct { external AesSuccessActionDataResultKind kind; } +final class wire_cst_Amount_Bitcoin extends ffi.Struct { + @ffi.Uint64() + external int amount_msat; +} + +final class wire_cst_Amount_Currency extends ffi.Struct { + external ffi.Pointer iso4217_code; + + @ffi.Uint64() + external int fractional_amount; +} + +final class AmountKind extends ffi.Union { + external wire_cst_Amount_Bitcoin Bitcoin; + + external wire_cst_Amount_Currency Currency; +} + +final class wire_cst_amount extends ffi.Struct { + @ffi.Int32() + external int tag; + + external AmountKind kind; +} + final class wire_cst_bitcoin_address_data extends ffi.Struct { external ffi.Pointer address; @@ -5696,6 +5880,29 @@ final class wire_cst_bitcoin_address_data extends ffi.Struct { external ffi.Pointer message; } +final class wire_cst_list_String extends ffi.Struct { + external ffi.Pointer> ptr; + + @ffi.Int32() + external int len; +} + +final class wire_cst_ln_offer extends ffi.Struct { + external ffi.Pointer bolt12; + + external ffi.Pointer chains; + + external ffi.Pointer amount; + + external ffi.Pointer description; + + external ffi.Pointer absolute_expiry; + + external ffi.Pointer issuer; + + external ffi.Pointer signing_pubkey; +} + final class wire_cst_ln_url_error_data extends ffi.Struct { external ffi.Pointer reason; } @@ -5882,7 +6089,7 @@ final class wire_cst_InputType_Bolt11 extends ffi.Struct { } final class wire_cst_InputType_Bolt12Offer extends ffi.Struct { - external ffi.Pointer offer; + external ffi.Pointer offer; } final class wire_cst_InputType_NodeId extends ffi.Struct { diff --git a/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart b/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart index e62f7b567..2a515e3e1 100644 --- a/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart +++ b/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart @@ -846,6 +846,17 @@ class FlutterBreezLiquidBindings { _frbgen_breez_liquid_cst_new_box_autoadd_aes_success_action_data_resultPtr .asFunction Function()>(); + ffi.Pointer frbgen_breez_liquid_cst_new_box_autoadd_amount() { + return _frbgen_breez_liquid_cst_new_box_autoadd_amount(); + } + + late final _frbgen_breez_liquid_cst_new_box_autoadd_amountPtr = + _lookup Function()>>( + 'frbgen_breez_liquid_cst_new_box_autoadd_amount'); + late final _frbgen_breez_liquid_cst_new_box_autoadd_amount = + _frbgen_breez_liquid_cst_new_box_autoadd_amountPtr + .asFunction Function()>(); + ffi.Pointer frbgen_breez_liquid_cst_new_box_autoadd_backup_request() { return _frbgen_breez_liquid_cst_new_box_autoadd_backup_request(); } @@ -998,6 +1009,17 @@ class FlutterBreezLiquidBindings { _frbgen_breez_liquid_cst_new_box_autoadd_ln_invoicePtr .asFunction Function()>(); + ffi.Pointer frbgen_breez_liquid_cst_new_box_autoadd_ln_offer() { + return _frbgen_breez_liquid_cst_new_box_autoadd_ln_offer(); + } + + late final _frbgen_breez_liquid_cst_new_box_autoadd_ln_offerPtr = + _lookup Function()>>( + 'frbgen_breez_liquid_cst_new_box_autoadd_ln_offer'); + late final _frbgen_breez_liquid_cst_new_box_autoadd_ln_offer = + _frbgen_breez_liquid_cst_new_box_autoadd_ln_offerPtr + .asFunction Function()>(); + ffi.Pointer frbgen_breez_liquid_cst_new_box_autoadd_ln_url_auth_request_data() { return _frbgen_breez_liquid_cst_new_box_autoadd_ln_url_auth_request_data(); @@ -1361,6 +1383,20 @@ class FlutterBreezLiquidBindings { _frbgen_breez_liquid_cst_new_box_autoadd_url_success_action_dataPtr .asFunction Function()>(); + ffi.Pointer frbgen_breez_liquid_cst_new_list_String( + int len, + ) { + return _frbgen_breez_liquid_cst_new_list_String( + len, + ); + } + + late final _frbgen_breez_liquid_cst_new_list_StringPtr = + _lookup Function(ffi.Int32)>>( + 'frbgen_breez_liquid_cst_new_list_String'); + late final _frbgen_breez_liquid_cst_new_list_String = _frbgen_breez_liquid_cst_new_list_StringPtr + .asFunction Function(int)>(); + ffi.Pointer frbgen_breez_liquid_cst_new_list_fiat_currency( int len, ) { @@ -4428,6 +4464,31 @@ final class wire_cst_aes_success_action_data_result extends ffi.Struct { external AesSuccessActionDataResultKind kind; } +final class wire_cst_Amount_Bitcoin extends ffi.Struct { + @ffi.Uint64() + external int amount_msat; +} + +final class wire_cst_Amount_Currency extends ffi.Struct { + external ffi.Pointer iso4217_code; + + @ffi.Uint64() + external int fractional_amount; +} + +final class AmountKind extends ffi.Union { + external wire_cst_Amount_Bitcoin Bitcoin; + + external wire_cst_Amount_Currency Currency; +} + +final class wire_cst_amount extends ffi.Struct { + @ffi.Int32() + external int tag; + + external AmountKind kind; +} + final class wire_cst_bitcoin_address_data extends ffi.Struct { external ffi.Pointer address; @@ -4441,6 +4502,29 @@ final class wire_cst_bitcoin_address_data extends ffi.Struct { external ffi.Pointer message; } +final class wire_cst_list_String extends ffi.Struct { + external ffi.Pointer> ptr; + + @ffi.Int32() + external int len; +} + +final class wire_cst_ln_offer extends ffi.Struct { + external ffi.Pointer bolt12; + + external ffi.Pointer chains; + + external ffi.Pointer amount; + + external ffi.Pointer description; + + external ffi.Pointer absolute_expiry; + + external ffi.Pointer issuer; + + external ffi.Pointer signing_pubkey; +} + final class wire_cst_ln_url_error_data extends ffi.Struct { external ffi.Pointer reason; } @@ -4627,7 +4711,7 @@ final class wire_cst_InputType_Bolt11 extends ffi.Struct { } final class wire_cst_InputType_Bolt12Offer extends ffi.Struct { - external ffi.Pointer offer; + external ffi.Pointer offer; } final class wire_cst_InputType_NodeId 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 fa6549446..fb611f6f8 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 @@ -564,6 +564,49 @@ fun asLnInvoiceList(arr: ReadableArray): List { return list } +fun asLnOffer(lnOffer: ReadableMap): LnOffer? { + if (!validateMandatoryFields( + lnOffer, + arrayOf( + "bolt12", + "chains", + ), + ) + ) { + return null + } + val bolt12 = lnOffer.getString("bolt12")!! + val chains = lnOffer.getArray("chains")?.let { asStringList(it) }!! + val description = if (hasNonNullKey(lnOffer, "description")) lnOffer.getString("description") else null + val signingPubkey = if (hasNonNullKey(lnOffer, "signingPubkey")) lnOffer.getString("signingPubkey") else null + val amount = if (hasNonNullKey(lnOffer, "amount")) lnOffer.getMap("amount")?.let { asAmount(it) } else null + val absoluteExpiry = if (hasNonNullKey(lnOffer, "absoluteExpiry")) lnOffer.getDouble("absoluteExpiry").toULong() else null + val issuer = if (hasNonNullKey(lnOffer, "issuer")) lnOffer.getString("issuer") else null + return LnOffer(bolt12, chains, description, signingPubkey, amount, absoluteExpiry, issuer) +} + +fun readableMapOf(lnOffer: LnOffer): ReadableMap = + readableMapOf( + "bolt12" to lnOffer.bolt12, + "chains" to readableArrayOf(lnOffer.chains), + "description" to lnOffer.description, + "signingPubkey" to lnOffer.signingPubkey, + "amount" to lnOffer.amount?.let { readableMapOf(it) }, + "absoluteExpiry" to lnOffer.absoluteExpiry, + "issuer" to lnOffer.issuer, + ) + +fun asLnOfferList(arr: ReadableArray): List { + val list = ArrayList() + for (value in arr.toList()) { + when (value) { + is ReadableMap -> list.add(asLnOffer(value)!!) + else -> throw SdkException.Generic(errUnexpectedType(value)) + } + } + return list +} + fun asLightningPaymentLimitsResponse(lightningPaymentLimitsResponse: ReadableMap): LightningPaymentLimitsResponse? { if (!validateMandatoryFields( lightningPaymentLimitsResponse, @@ -2367,6 +2410,48 @@ fun asAesSuccessActionDataResultList(arr: ReadableArray): List { + pushToMap(map, "type", "bitcoin") + pushToMap(map, "amountMsat", amount.amountMsat) + } + is Amount.Currency -> { + pushToMap(map, "type", "currency") + pushToMap(map, "iso4217Code", amount.iso4217Code) + pushToMap(map, "fractionalAmount", amount.fractionalAmount) + } + } + return map +} + +fun asAmountList(arr: ReadableArray): List { + val list = ArrayList() + for (value in arr.toList()) { + when (value) { + is ReadableMap -> list.add(asAmount(value)!!) + else -> throw SdkException.Generic(errUnexpectedType(value)) + } + } + return list +} + fun asBuyBitcoinProvider(type: String): BuyBitcoinProvider = BuyBitcoinProvider.valueOf(camelToUpperSnakeCase(type)) fun asBuyBitcoinProviderList(arr: ReadableArray): List { @@ -2428,7 +2513,7 @@ fun asInputType(inputType: ReadableMap): InputType? { return InputType.Bolt11(invoice) } if (type == "bolt12Offer") { - val offer = inputType.getString("offer")!! + val offer = inputType.getMap("offer")?.let { asLnOffer(it) }!! return InputType.Bolt12Offer(offer) } if (type == "nodeId") { @@ -2475,7 +2560,7 @@ fun readableMapOf(inputType: InputType): ReadableMap? { } is InputType.Bolt12Offer -> { pushToMap(map, "type", "bolt12Offer") - pushToMap(map, "offer", inputType.offer) + pushToMap(map, "offer", readableMapOf(inputType.offer)) } is InputType.NodeId -> { pushToMap(map, "type", "nodeId") @@ -3142,6 +3227,7 @@ fun pushToArray( is RefundableSwap -> array.pushMap(readableMapOf(value)) is RouteHint -> array.pushMap(readableMapOf(value)) is RouteHintHop -> array.pushMap(readableMapOf(value)) + is String -> array.pushString(value) is UByte -> array.pushInt(value.toInt()) is Array<*> -> array.pushArray(readableArrayOf(value.asIterable())) is List<*> -> array.pushArray(readableArrayOf(value)) diff --git a/packages/react-native/ios/BreezSDKLiquidMapper.swift b/packages/react-native/ios/BreezSDKLiquidMapper.swift index dc076c0fb..ab80b1415 100644 --- a/packages/react-native/ios/BreezSDKLiquidMapper.swift +++ b/packages/react-native/ios/BreezSDKLiquidMapper.swift @@ -665,6 +665,79 @@ enum BreezSDKLiquidMapper { return lnInvoiceList.map { v -> [String: Any?] in return dictionaryOf(lnInvoice: v) } } + static func asLnOffer(lnOffer: [String: Any?]) throws -> LnOffer { + guard let bolt12 = lnOffer["bolt12"] as? String else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "bolt12", typeName: "LnOffer")) + } + guard let chains = lnOffer["chains"] as? [String] else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "chains", typeName: "LnOffer")) + } + var description: String? + if hasNonNilKey(data: lnOffer, key: "description") { + guard let descriptionTmp = lnOffer["description"] as? String else { + throw SdkError.Generic(message: errUnexpectedValue(fieldName: "description")) + } + description = descriptionTmp + } + var signingPubkey: String? + if hasNonNilKey(data: lnOffer, key: "signingPubkey") { + guard let signingPubkeyTmp = lnOffer["signingPubkey"] as? String else { + throw SdkError.Generic(message: errUnexpectedValue(fieldName: "signingPubkey")) + } + signingPubkey = signingPubkeyTmp + } + var amount: Amount? + if let amountTmp = lnOffer["amount"] as? [String: Any?] { + amount = try asAmount(amount: amountTmp) + } + + var absoluteExpiry: UInt64? + if hasNonNilKey(data: lnOffer, key: "absoluteExpiry") { + guard let absoluteExpiryTmp = lnOffer["absoluteExpiry"] as? UInt64 else { + throw SdkError.Generic(message: errUnexpectedValue(fieldName: "absoluteExpiry")) + } + absoluteExpiry = absoluteExpiryTmp + } + var issuer: String? + if hasNonNilKey(data: lnOffer, key: "issuer") { + guard let issuerTmp = lnOffer["issuer"] as? String else { + throw SdkError.Generic(message: errUnexpectedValue(fieldName: "issuer")) + } + issuer = issuerTmp + } + + return LnOffer(bolt12: bolt12, chains: chains, description: description, signingPubkey: signingPubkey, amount: amount, absoluteExpiry: absoluteExpiry, issuer: issuer) + } + + static func dictionaryOf(lnOffer: LnOffer) -> [String: Any?] { + return [ + "bolt12": lnOffer.bolt12, + "chains": lnOffer.chains, + "description": lnOffer.description == nil ? nil : lnOffer.description, + "signingPubkey": lnOffer.signingPubkey == nil ? nil : lnOffer.signingPubkey, + "amount": lnOffer.amount == nil ? nil : dictionaryOf(amount: lnOffer.amount!), + "absoluteExpiry": lnOffer.absoluteExpiry == nil ? nil : lnOffer.absoluteExpiry, + "issuer": lnOffer.issuer == nil ? nil : lnOffer.issuer, + ] + } + + static func asLnOfferList(arr: [Any]) throws -> [LnOffer] { + var list = [LnOffer]() + for value in arr { + if let val = value as? [String: Any?] { + var lnOffer = try asLnOffer(lnOffer: val) + list.append(lnOffer) + } else { + throw SdkError.Generic(message: errUnexpectedType(typeName: "LnOffer")) + } + } + return list + } + + static func arrayOf(lnOfferList: [LnOffer]) -> [Any] { + return lnOfferList.map { v -> [String: Any?] in return dictionaryOf(lnOffer: v) } + } + static func asLightningPaymentLimitsResponse(lightningPaymentLimitsResponse: [String: Any?]) throws -> LightningPaymentLimitsResponse { guard let sendTmp = lightningPaymentLimitsResponse["send"] as? [String: Any?] else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "send", typeName: "LightningPaymentLimitsResponse")) @@ -2727,6 +2800,65 @@ enum BreezSDKLiquidMapper { return list } + static func asAmount(amount: [String: Any?]) throws -> Amount { + let type = amount["type"] as! String + if type == "bitcoin" { + guard let _amountMsat = amount["amountMsat"] as? UInt64 else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "amountMsat", typeName: "Amount")) + } + return Amount.bitcoin(amountMsat: _amountMsat) + } + if type == "currency" { + guard let _iso4217Code = amount["iso4217Code"] as? String else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "iso4217Code", typeName: "Amount")) + } + guard let _fractionalAmount = amount["fractionalAmount"] as? UInt64 else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "fractionalAmount", typeName: "Amount")) + } + return Amount.currency(iso4217Code: _iso4217Code, fractionalAmount: _fractionalAmount) + } + + throw SdkError.Generic(message: "Unexpected type \(type) for enum Amount") + } + + static func dictionaryOf(amount: Amount) -> [String: Any?] { + switch amount { + case let .bitcoin( + amountMsat + ): + return [ + "type": "bitcoin", + "amountMsat": amountMsat, + ] + + case let .currency( + iso4217Code, fractionalAmount + ): + return [ + "type": "currency", + "iso4217Code": iso4217Code, + "fractionalAmount": fractionalAmount, + ] + } + } + + static func arrayOf(amountList: [Amount]) -> [Any] { + return amountList.map { v -> [String: Any?] in return dictionaryOf(amount: v) } + } + + static func asAmountList(arr: [Any]) throws -> [Amount] { + var list = [Amount]() + for value in arr { + if let val = value as? [String: Any?] { + var amount = try asAmount(amount: val) + list.append(amount) + } else { + throw SdkError.Generic(message: errUnexpectedType(typeName: "Amount")) + } + } + return list + } + static func asBuyBitcoinProvider(buyBitcoinProvider: String) throws -> BuyBitcoinProvider { switch buyBitcoinProvider { case "moonpay": @@ -2828,9 +2960,11 @@ enum BreezSDKLiquidMapper { return InputType.bolt11(invoice: _invoice) } if type == "bolt12Offer" { - guard let _offer = inputType["offer"] as? String else { + guard let offerTmp = inputType["offer"] as? [String: Any?] else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "offer", typeName: "InputType")) } + let _offer = try asLnOffer(lnOffer: offerTmp) + return InputType.bolt12Offer(offer: _offer) } if type == "nodeId" { @@ -2912,7 +3046,7 @@ enum BreezSDKLiquidMapper { ): return [ "type": "bolt12Offer", - "offer": offer, + "offer": dictionaryOf(lnOffer: offer), ] case let .nodeId( diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index cd425df65..530aa1da4 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -116,6 +116,16 @@ export interface LnInvoice { minFinalCltvExpiryDelta: number } +export interface LnOffer { + bolt12: string + chains: string[] + description?: string + signingPubkey?: string + amount?: Amount + absoluteExpiry?: number + issuer?: string +} + export interface LightningPaymentLimitsResponse { send: Limits receive: Limits @@ -408,6 +418,20 @@ export type AesSuccessActionDataResult = { reason: string } +export enum AmountVariant { + BITCOIN = "bitcoin", + CURRENCY = "currency" +} + +export type Amount = { + type: AmountVariant.BITCOIN, + amountMsat: number +} | { + type: AmountVariant.CURRENCY, + iso4217Code: string + fractionalAmount: number +} + export enum BuyBitcoinProvider { MOONPAY = "moonpay" } @@ -445,7 +469,7 @@ export type InputType = { invoice: LnInvoice } | { type: InputTypeVariant.BOLT12_OFFER, - offer: string + offer: LnOffer } | { type: InputTypeVariant.NODE_ID, nodeId: string From 690e2e6537050525f5b5c5863eec7a7f1862446c Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:42:10 +0100 Subject: [PATCH 12/22] Cargo fmt --- lib/core/src/bindings.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/core/src/bindings.rs b/lib/core/src/bindings.rs index 483cac58e..5c25d9c43 100644 --- a/lib/core/src/bindings.rs +++ b/lib/core/src/bindings.rs @@ -329,7 +329,6 @@ pub struct _RouteHintHop { pub htlc_maximum_msat: Option, } - #[frb(mirror(Amount))] pub enum _Amount { Bitcoin { From 43a0bfed9df4631fff9246a58123952c04e118bb Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Wed, 13 Nov 2024 17:18:51 +0100 Subject: [PATCH 13/22] Bump sdk-common to include fixed type reference --- cli/Cargo.lock | 2 +- lib/Cargo.lock | 2 +- lib/core/src/sdk.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 40a7c943e..204a595a7 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -3046,7 +3046,7 @@ dependencies = [ [[package]] name = "sdk-common" version = "0.6.2" -source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#a8a7057320aca4f5b03708a11de0ea66dc931d80" +source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#11c6812c81407881a014390cb3da68cea169bcf1" dependencies = [ "aes 0.8.4", "anyhow", diff --git a/lib/Cargo.lock b/lib/Cargo.lock index 9568faf09..7e67fe799 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -3294,7 +3294,7 @@ dependencies = [ [[package]] name = "sdk-common" version = "0.6.2" -source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#a8a7057320aca4f5b03708a11de0ea66dc931d80" +source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#11c6812c81407881a014390cb3da68cea169bcf1" dependencies = [ "aes 0.8.4", "anyhow", diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index 01da81b79..798493ae0 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -860,7 +860,7 @@ impl LiquidSdk { }; payment_destination = SendDestination::Bolt11 { invoice }; } - Ok(InputType::Bolt12Offer { offer }) => { + Ok(InputType::Bolt12Offer { offer: _ }) => { receiver_amount_sat = match req.amount { Some(PayAmount::Receiver { amount_sat }) => Ok(amount_sat), _ => Err(PaymentError::amount_missing( @@ -877,7 +877,7 @@ impl LiquidSdk { fees_sat = boltz_fees_total + lockup_fees_sat; payment_destination = SendDestination::Bolt12 { - offer, + offer: req.destination.clone(), receiver_amount_sat, }; } From bb18d107be62f90a2972ee834c375e718e4ab302 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Wed, 13 Nov 2024 18:20:00 +0100 Subject: [PATCH 14/22] Update SendDestination::Bolt12 to include LNOffer --- lib/Cargo.lock | 2 +- .../include/breez_sdk_liquid.h | 70 ++++++------ lib/bindings/src/breez_sdk_liquid.udl | 2 +- lib/core/src/frb_generated.rs | 6 +- lib/core/src/model.rs | 2 +- lib/core/src/sdk.rs | 52 +++++++-- packages/dart/lib/src/frb_generated.dart | 6 +- packages/dart/lib/src/frb_generated.io.dart | 100 +++++++++--------- packages/dart/lib/src/model.dart | 2 +- packages/dart/lib/src/model.freezed.dart | 10 +- 10 files changed, 144 insertions(+), 108 deletions(-) diff --git a/lib/Cargo.lock b/lib/Cargo.lock index 7e67fe799..cb917d4f8 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -3294,7 +3294,7 @@ dependencies = [ [[package]] name = "sdk-common" version = "0.6.2" -source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#11c6812c81407881a014390cb3da68cea169bcf1" +source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#8da4455e4a5a077e40c440d6c875236d666fffb0" dependencies = [ "aes 0.8.4", "anyhow", 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 1994f6740..1aa4f4614 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 @@ -171,8 +171,42 @@ typedef struct wire_cst_SendDestination_Bolt11 { struct wire_cst_ln_invoice *invoice; } wire_cst_SendDestination_Bolt11; +typedef struct wire_cst_list_String { + struct wire_cst_list_prim_u_8_strict **ptr; + int32_t len; +} wire_cst_list_String; + +typedef struct wire_cst_Amount_Bitcoin { + uint64_t amount_msat; +} wire_cst_Amount_Bitcoin; + +typedef struct wire_cst_Amount_Currency { + struct wire_cst_list_prim_u_8_strict *iso4217_code; + uint64_t fractional_amount; +} wire_cst_Amount_Currency; + +typedef union AmountKind { + struct wire_cst_Amount_Bitcoin Bitcoin; + struct wire_cst_Amount_Currency Currency; +} AmountKind; + +typedef struct wire_cst_amount { + int32_t tag; + union AmountKind kind; +} wire_cst_amount; + +typedef struct wire_cst_ln_offer { + struct wire_cst_list_prim_u_8_strict *bolt12; + struct wire_cst_list_String *chains; + struct wire_cst_amount *amount; + struct wire_cst_list_prim_u_8_strict *description; + uint64_t *absolute_expiry; + struct wire_cst_list_prim_u_8_strict *issuer; + struct wire_cst_list_prim_u_8_strict *signing_pubkey; +} wire_cst_ln_offer; + typedef struct wire_cst_SendDestination_Bolt12 { - struct wire_cst_list_prim_u_8_strict *offer; + struct wire_cst_ln_offer *offer; uint64_t receiver_amount_sat; } wire_cst_SendDestination_Bolt12; @@ -480,25 +514,6 @@ typedef struct wire_cst_aes_success_action_data_result { union AesSuccessActionDataResultKind kind; } wire_cst_aes_success_action_data_result; -typedef struct wire_cst_Amount_Bitcoin { - uint64_t amount_msat; -} wire_cst_Amount_Bitcoin; - -typedef struct wire_cst_Amount_Currency { - struct wire_cst_list_prim_u_8_strict *iso4217_code; - uint64_t fractional_amount; -} wire_cst_Amount_Currency; - -typedef union AmountKind { - struct wire_cst_Amount_Bitcoin Bitcoin; - struct wire_cst_Amount_Currency Currency; -} AmountKind; - -typedef struct wire_cst_amount { - int32_t tag; - union AmountKind kind; -} wire_cst_amount; - typedef struct wire_cst_bitcoin_address_data { struct wire_cst_list_prim_u_8_strict *address; int32_t network; @@ -507,21 +522,6 @@ typedef struct wire_cst_bitcoin_address_data { struct wire_cst_list_prim_u_8_strict *message; } wire_cst_bitcoin_address_data; -typedef struct wire_cst_list_String { - struct wire_cst_list_prim_u_8_strict **ptr; - int32_t len; -} wire_cst_list_String; - -typedef struct wire_cst_ln_offer { - struct wire_cst_list_prim_u_8_strict *bolt12; - struct wire_cst_list_String *chains; - struct wire_cst_amount *amount; - struct wire_cst_list_prim_u_8_strict *description; - uint64_t *absolute_expiry; - struct wire_cst_list_prim_u_8_strict *issuer; - struct wire_cst_list_prim_u_8_strict *signing_pubkey; -} wire_cst_ln_offer; - typedef struct wire_cst_ln_url_error_data { struct wire_cst_list_prim_u_8_strict *reason; } wire_cst_ln_url_error_data; diff --git a/lib/bindings/src/breez_sdk_liquid.udl b/lib/bindings/src/breez_sdk_liquid.udl index ecae1d4ae..8a8e28621 100644 --- a/lib/bindings/src/breez_sdk_liquid.udl +++ b/lib/bindings/src/breez_sdk_liquid.udl @@ -397,7 +397,7 @@ dictionary PrepareSendRequest { interface SendDestination { LiquidAddress(LiquidAddressData address_data); Bolt11(LNInvoice invoice); - Bolt12(string offer, u64 receiver_amount_sat); + Bolt12(LNOffer offer, u64 receiver_amount_sat); }; dictionary PrepareSendResponse { diff --git a/lib/core/src/frb_generated.rs b/lib/core/src/frb_generated.rs index d361c26ad..2aaa2374a 100644 --- a/lib/core/src/frb_generated.rs +++ b/lib/core/src/frb_generated.rs @@ -3999,7 +3999,7 @@ impl SseDecode for crate::model::SendDestination { }; } 2 => { - let mut var_offer = ::sse_decode(deserializer); + let mut var_offer = ::sse_decode(deserializer); let mut var_receiverAmountSat = ::sse_decode(deserializer); return crate::model::SendDestination::Bolt12 { offer: var_offer, @@ -7803,7 +7803,7 @@ impl SseEncode for crate::model::SendDestination { receiver_amount_sat, } => { ::sse_encode(2, serializer); - ::sse_encode(offer, serializer); + ::sse_encode(offer, serializer); ::sse_encode(receiver_amount_sat, serializer); } _ => { @@ -12882,7 +12882,7 @@ mod io { #[repr(C)] #[derive(Clone, Copy)] pub struct wire_cst_SendDestination_Bolt12 { - offer: *mut wire_cst_list_prim_u_8_strict, + offer: *mut wire_cst_ln_offer, receiver_amount_sat: u64, } #[repr(C)] diff --git a/lib/core/src/model.rs b/lib/core/src/model.rs index 826cc2437..033531419 100644 --- a/lib/core/src/model.rs +++ b/lib/core/src/model.rs @@ -363,7 +363,7 @@ pub enum SendDestination { invoice: LNInvoice, }, Bolt12 { - offer: String, + offer: LNOffer, receiver_amount_sat: u64, }, } diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index 798493ae0..b2f2bf500 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -1,3 +1,7 @@ +use std::collections::HashMap; +use std::time::Instant; +use std::{fs, path::PathBuf, str::FromStr, sync::Arc, time::Duration}; + use anyhow::{anyhow, Result}; use boltz_client::{swaps::boltz::*, util::secrets::Preimage}; use buy::{BuyBitcoinApi, BuyBitcoinService}; @@ -19,9 +23,6 @@ use sdk_common::input_parser::InputType; use sdk_common::liquid::LiquidAddressData; use sdk_common::prelude::{FiatAPI, FiatCurrency, LnUrlPayError, LnUrlWithdrawError, Rate}; use signer::SdkSigner; -use std::collections::HashMap; -use std::time::Instant; -use std::{fs, path::PathBuf, str::FromStr, sync::Arc, time::Duration}; use tokio::sync::{watch, Mutex, RwLock}; use tokio::time::MissedTickBehavior; use tokio_stream::wrappers::BroadcastStream; @@ -45,6 +46,7 @@ use crate::{ persist::Persister, utils, *, }; +use ::lightning::offers::offer::Offer; pub const DEFAULT_DATA_DIR: &str = ".data"; /// Number of blocks to monitor a swap after its timeout block height @@ -766,7 +768,7 @@ impl LiquidSdk { let receiver_amount_sat; let payment_destination; - match sdk_common::input_parser::parse(&req.destination).await { + match Self::parse(&req.destination).await { Ok(InputType::LiquidAddress { address: mut liquid_address_data, }) => { @@ -860,7 +862,7 @@ impl LiquidSdk { }; payment_destination = SendDestination::Bolt11 { invoice }; } - Ok(InputType::Bolt12Offer { offer: _ }) => { + Ok(InputType::Bolt12Offer { offer }) => { receiver_amount_sat = match req.amount { Some(PayAmount::Receiver { amount_sat }) => Ok(amount_sat), _ => Err(PaymentError::amount_missing( @@ -877,7 +879,7 @@ impl LiquidSdk { fees_sat = boltz_fees_total + lockup_fees_sat; payment_destination = SendDestination::Bolt12 { - offer: req.destination.clone(), + offer, receiver_amount_sat, }; } @@ -968,7 +970,7 @@ impl LiquidSdk { } => { let bolt12_invoice = self .swapper - .get_bolt12_invoice(offer, *receiver_amount_sat)?; + .get_bolt12_invoice(&offer.bolt12, *receiver_amount_sat)?; self.pay_bolt12_invoice(&bolt12_invoice, *fees_sat).await } } @@ -2582,9 +2584,43 @@ impl LiquidSdk { /// Parses a string into an [InputType]. See [input_parser::parse]. pub async fn parse(input: &str) -> Result { + if let Ok(offer) = input.parse::() { + return Ok(InputType::Bolt12Offer { + offer: LNOffer { + bolt12: input.to_string(), + chains: offer + .chains() + .iter() + .map(|chain| chain.to_string()) + .collect(), + // TODO This conversion (between lightning-v0.0.125 to -v0.0.118 Amount types) + // won't be needed when Liquid SDK uses the same lightning crate version as sdk-common + amount: offer.amount().map(|amount| match amount { + ::lightning::offers::offer::Amount::Bitcoin { amount_msats } => { + Amount::Bitcoin { + amount_msat: amount_msats, + } + } + ::lightning::offers::offer::Amount::Currency { + iso4217_code, + amount, + } => Amount::Currency { + iso4217_code: String::from_utf8(iso4217_code.to_vec()) + .expect("Expecting a valid ISO 4217 character sequence"), + fractional_amount: amount, + }, + }), + description: offer.description().map(|d| d.to_string()), + absolute_expiry: offer.absolute_expiry().map(|expiry| expiry.as_secs()), + issuer: offer.issuer().map(|s| s.to_string()), + signing_pubkey: offer.signing_pubkey().map(|pk| pk.to_string()), + }, + }); + } + parse(input) .await - .map_err(|e| PaymentError::Generic { err: e.to_string() }) + .map_err(|e| PaymentError::generic(&e.to_string())) } /// Parses a string into an [LNInvoice]. See [invoice::parse_invoice]. diff --git a/packages/dart/lib/src/frb_generated.dart b/packages/dart/lib/src/frb_generated.dart index 4c88030dd..0a6a1c656 100644 --- a/packages/dart/lib/src/frb_generated.dart +++ b/packages/dart/lib/src/frb_generated.dart @@ -2912,7 +2912,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); case 2: return SendDestination_Bolt12( - offer: dco_decode_String(raw[1]), + offer: dco_decode_box_autoadd_ln_offer(raw[1]), receiverAmountSat: dco_decode_u_64(raw[2]), ); default: @@ -4828,7 +4828,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_invoice = sse_decode_box_autoadd_ln_invoice(deserializer); return SendDestination_Bolt11(invoice: var_invoice); case 2: - var var_offer = sse_decode_String(deserializer); + var var_offer = sse_decode_box_autoadd_ln_offer(deserializer); var var_receiverAmountSat = sse_decode_u_64(deserializer); return SendDestination_Bolt12(offer: var_offer, receiverAmountSat: var_receiverAmountSat); default: @@ -6596,7 +6596,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_box_autoadd_ln_invoice(invoice, serializer); case SendDestination_Bolt12(offer: final offer, receiverAmountSat: final receiverAmountSat): sse_encode_i_32(2, serializer); - sse_encode_String(offer, serializer); + sse_encode_box_autoadd_ln_offer(offer, serializer); sse_encode_u_64(receiverAmountSat, serializer); default: throw UnimplementedError(''); diff --git a/packages/dart/lib/src/frb_generated.io.dart b/packages/dart/lib/src/frb_generated.io.dart index 080e412ec..4ce0b5279 100644 --- a/packages/dart/lib/src/frb_generated.io.dart +++ b/packages/dart/lib/src/frb_generated.io.dart @@ -3045,7 +3045,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { return; } if (apiObj is SendDestination_Bolt12) { - var pre_offer = cst_encode_String(apiObj.offer); + var pre_offer = cst_encode_box_autoadd_ln_offer(apiObj.offer); var pre_receiver_amount_sat = cst_encode_u_64(apiObj.receiverAmountSat); wireObj.tag = 2; wireObj.kind.Bolt12.offer = pre_offer; @@ -5411,8 +5411,56 @@ final class wire_cst_SendDestination_Bolt11 extends ffi.Struct { external ffi.Pointer invoice; } +final class wire_cst_list_String extends ffi.Struct { + external ffi.Pointer> ptr; + + @ffi.Int32() + external int len; +} + +final class wire_cst_Amount_Bitcoin extends ffi.Struct { + @ffi.Uint64() + external int amount_msat; +} + +final class wire_cst_Amount_Currency extends ffi.Struct { + external ffi.Pointer iso4217_code; + + @ffi.Uint64() + external int fractional_amount; +} + +final class AmountKind extends ffi.Union { + external wire_cst_Amount_Bitcoin Bitcoin; + + external wire_cst_Amount_Currency Currency; +} + +final class wire_cst_amount extends ffi.Struct { + @ffi.Int32() + external int tag; + + external AmountKind kind; +} + +final class wire_cst_ln_offer extends ffi.Struct { + external ffi.Pointer bolt12; + + external ffi.Pointer chains; + + external ffi.Pointer amount; + + external ffi.Pointer description; + + external ffi.Pointer absolute_expiry; + + external ffi.Pointer issuer; + + external ffi.Pointer signing_pubkey; +} + final class wire_cst_SendDestination_Bolt12 extends ffi.Struct { - external ffi.Pointer offer; + external ffi.Pointer offer; @ffi.Uint64() external int receiver_amount_sat; @@ -5842,31 +5890,6 @@ final class wire_cst_aes_success_action_data_result extends ffi.Struct { external AesSuccessActionDataResultKind kind; } -final class wire_cst_Amount_Bitcoin extends ffi.Struct { - @ffi.Uint64() - external int amount_msat; -} - -final class wire_cst_Amount_Currency extends ffi.Struct { - external ffi.Pointer iso4217_code; - - @ffi.Uint64() - external int fractional_amount; -} - -final class AmountKind extends ffi.Union { - external wire_cst_Amount_Bitcoin Bitcoin; - - external wire_cst_Amount_Currency Currency; -} - -final class wire_cst_amount extends ffi.Struct { - @ffi.Int32() - external int tag; - - external AmountKind kind; -} - final class wire_cst_bitcoin_address_data extends ffi.Struct { external ffi.Pointer address; @@ -5880,29 +5903,6 @@ final class wire_cst_bitcoin_address_data extends ffi.Struct { external ffi.Pointer message; } -final class wire_cst_list_String extends ffi.Struct { - external ffi.Pointer> ptr; - - @ffi.Int32() - external int len; -} - -final class wire_cst_ln_offer extends ffi.Struct { - external ffi.Pointer bolt12; - - external ffi.Pointer chains; - - external ffi.Pointer amount; - - external ffi.Pointer description; - - external ffi.Pointer absolute_expiry; - - external ffi.Pointer issuer; - - external ffi.Pointer signing_pubkey; -} - final class wire_cst_ln_url_error_data extends ffi.Struct { external ffi.Pointer reason; } diff --git a/packages/dart/lib/src/model.dart b/packages/dart/lib/src/model.dart index 62dad616f..39fab7585 100644 --- a/packages/dart/lib/src/model.dart +++ b/packages/dart/lib/src/model.dart @@ -1258,7 +1258,7 @@ sealed class SendDestination with _$SendDestination { required LNInvoice invoice, }) = SendDestination_Bolt11; const factory SendDestination.bolt12({ - required String offer, + required LNOffer offer, required BigInt receiverAmountSat, }) = SendDestination_Bolt12; } diff --git a/packages/dart/lib/src/model.freezed.dart b/packages/dart/lib/src/model.freezed.dart index 5ffb02103..e0f93d351 100644 --- a/packages/dart/lib/src/model.freezed.dart +++ b/packages/dart/lib/src/model.freezed.dart @@ -1927,7 +1927,7 @@ abstract class _$$SendDestination_Bolt12ImplCopyWith<$Res> { _$SendDestination_Bolt12Impl value, $Res Function(_$SendDestination_Bolt12Impl) then) = __$$SendDestination_Bolt12ImplCopyWithImpl<$Res>; @useResult - $Res call({String offer, BigInt receiverAmountSat}); + $Res call({LNOffer offer, BigInt receiverAmountSat}); } /// @nodoc @@ -1950,7 +1950,7 @@ class __$$SendDestination_Bolt12ImplCopyWithImpl<$Res> offer: null == offer ? _value.offer : offer // ignore: cast_nullable_to_non_nullable - as String, + as LNOffer, receiverAmountSat: null == receiverAmountSat ? _value.receiverAmountSat : receiverAmountSat // ignore: cast_nullable_to_non_nullable @@ -1965,7 +1965,7 @@ class _$SendDestination_Bolt12Impl extends SendDestination_Bolt12 { const _$SendDestination_Bolt12Impl({required this.offer, required this.receiverAmountSat}) : super._(); @override - final String offer; + final LNOffer offer; @override final BigInt receiverAmountSat; @@ -1998,10 +1998,10 @@ class _$SendDestination_Bolt12Impl extends SendDestination_Bolt12 { abstract class SendDestination_Bolt12 extends SendDestination { const factory SendDestination_Bolt12( - {required final String offer, required final BigInt receiverAmountSat}) = _$SendDestination_Bolt12Impl; + {required final LNOffer offer, required final BigInt receiverAmountSat}) = _$SendDestination_Bolt12Impl; const SendDestination_Bolt12._() : super._(); - String get offer; + LNOffer get offer; BigInt get receiverAmountSat; /// Create a copy of SendDestination From 4a5317724b884de2d273edd67ec1bf47813884a7 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Wed, 13 Nov 2024 18:22:51 +0100 Subject: [PATCH 15/22] Re-generate RN bindings --- .../main/java/com/breezsdkliquid/BreezSDKLiquidMapper.kt | 4 ++-- packages/react-native/ios/BreezSDKLiquidMapper.swift | 6 ++++-- packages/react-native/src/index.ts | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) 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 fb611f6f8..daa9b006d 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 @@ -3054,7 +3054,7 @@ fun asSendDestination(sendDestination: ReadableMap): SendDestination? { return SendDestination.Bolt11(invoice) } if (type == "bolt12") { - val offer = sendDestination.getString("offer")!! + val offer = sendDestination.getMap("offer")?.let { asLnOffer(it) }!! val receiverAmountSat = sendDestination.getDouble("receiverAmountSat").toULong() return SendDestination.Bolt12(offer, receiverAmountSat) } @@ -3074,7 +3074,7 @@ fun readableMapOf(sendDestination: SendDestination): ReadableMap? { } is SendDestination.Bolt12 -> { pushToMap(map, "type", "bolt12") - pushToMap(map, "offer", sendDestination.offer) + pushToMap(map, "offer", readableMapOf(sendDestination.offer)) pushToMap(map, "receiverAmountSat", sendDestination.receiverAmountSat) } } diff --git a/packages/react-native/ios/BreezSDKLiquidMapper.swift b/packages/react-native/ios/BreezSDKLiquidMapper.swift index ab80b1415..a9b847cfd 100644 --- a/packages/react-native/ios/BreezSDKLiquidMapper.swift +++ b/packages/react-native/ios/BreezSDKLiquidMapper.swift @@ -3916,9 +3916,11 @@ enum BreezSDKLiquidMapper { return SendDestination.bolt11(invoice: _invoice) } if type == "bolt12" { - guard let _offer = sendDestination["offer"] as? String else { + guard let offerTmp = sendDestination["offer"] as? [String: Any?] else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "offer", typeName: "SendDestination")) } + let _offer = try asLnOffer(lnOffer: offerTmp) + guard let _receiverAmountSat = sendDestination["receiverAmountSat"] as? UInt64 else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "receiverAmountSat", typeName: "SendDestination")) } @@ -3951,7 +3953,7 @@ enum BreezSDKLiquidMapper { ): return [ "type": "bolt12", - "offer": offer, + "offer": dictionaryOf(lnOffer: offer), "receiverAmountSat": receiverAmountSat, ] } diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index 530aa1da4..bcd2dc11c 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -667,7 +667,7 @@ export type SendDestination = { invoice: LnInvoice } | { type: SendDestinationVariant.BOLT12, - offer: string + offer: LnOffer receiverAmountSat: number } From 2928ed909f85d7d56d3b2b3af29b3606c53781b8 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Thu, 14 Nov 2024 09:09:54 +0100 Subject: [PATCH 16/22] Bump sdk-common, propagate changes --- cli/Cargo.lock | 2 +- lib/Cargo.lock | 2 +- .../breez_sdk_liquid/include/breez_sdk_liquid.h | 2 +- lib/bindings/src/breez_sdk_liquid.udl | 2 +- lib/core/src/bindings.rs | 2 +- lib/core/src/frb_generated.rs | 16 ++++++++-------- lib/core/src/sdk.rs | 2 +- packages/dart/lib/src/bindings.dart | 8 ++++---- packages/dart/lib/src/frb_generated.dart | 8 ++++---- packages/dart/lib/src/frb_generated.io.dart | 4 ++-- .../com/breezsdkliquid/BreezSDKLiquidMapper.kt | 6 +++--- .../react-native/ios/BreezSDKLiquidMapper.swift | 10 +++++----- packages/react-native/src/index.ts | 2 +- 13 files changed, 33 insertions(+), 33 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 204a595a7..6ce53c996 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -3046,7 +3046,7 @@ dependencies = [ [[package]] name = "sdk-common" version = "0.6.2" -source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#11c6812c81407881a014390cb3da68cea169bcf1" +source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#3ffe4458d81e4d8f9c5d287e1b5dacf9735ec3d3" dependencies = [ "aes 0.8.4", "anyhow", diff --git a/lib/Cargo.lock b/lib/Cargo.lock index cb917d4f8..70efd04b0 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -3294,7 +3294,7 @@ dependencies = [ [[package]] name = "sdk-common" version = "0.6.2" -source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#8da4455e4a5a077e40c440d6c875236d666fffb0" +source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#3ffe4458d81e4d8f9c5d287e1b5dacf9735ec3d3" dependencies = [ "aes 0.8.4", "anyhow", 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 1aa4f4614..d1780fdb7 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 @@ -198,7 +198,7 @@ typedef struct wire_cst_amount { typedef struct wire_cst_ln_offer { struct wire_cst_list_prim_u_8_strict *bolt12; struct wire_cst_list_String *chains; - struct wire_cst_amount *amount; + struct wire_cst_amount *min_amount; struct wire_cst_list_prim_u_8_strict *description; uint64_t *absolute_expiry; struct wire_cst_list_prim_u_8_strict *issuer; diff --git a/lib/bindings/src/breez_sdk_liquid.udl b/lib/bindings/src/breez_sdk_liquid.udl index 8a8e28621..33623e848 100644 --- a/lib/bindings/src/breez_sdk_liquid.udl +++ b/lib/bindings/src/breez_sdk_liquid.udl @@ -49,7 +49,7 @@ dictionary LNOffer { sequence chains; string? description; string? signing_pubkey; - Amount? amount; + Amount? min_amount; u64? absolute_expiry; string? issuer; }; diff --git a/lib/core/src/bindings.rs b/lib/core/src/bindings.rs index 5c25d9c43..bf02ff05b 100644 --- a/lib/core/src/bindings.rs +++ b/lib/core/src/bindings.rs @@ -344,7 +344,7 @@ pub enum _Amount { pub struct _LNOffer { pub bolt12: String, pub chains: Vec, - pub amount: Option, + pub min_amount: Option, pub description: Option, pub absolute_expiry: Option, pub issuer: Option, diff --git a/lib/core/src/frb_generated.rs b/lib/core/src/frb_generated.rs index 2aaa2374a..bac33fa4e 100644 --- a/lib/core/src/frb_generated.rs +++ b/lib/core/src/frb_generated.rs @@ -1860,7 +1860,7 @@ const _: fn() = || { let LNOffer = None::.unwrap(); let _: String = LNOffer.bolt12; let _: Vec = LNOffer.chains; - let _: Option = LNOffer.amount; + let _: Option = LNOffer.min_amount; let _: Option = LNOffer.description; let _: Option = LNOffer.absolute_expiry; let _: Option = LNOffer.issuer; @@ -2792,7 +2792,7 @@ impl SseDecode for crate::bindings::LNOffer { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { let mut var_bolt12 = ::sse_decode(deserializer); let mut var_chains = >::sse_decode(deserializer); - let mut var_amount = >::sse_decode(deserializer); + let mut var_minAmount = >::sse_decode(deserializer); let mut var_description = >::sse_decode(deserializer); let mut var_absoluteExpiry = >::sse_decode(deserializer); let mut var_issuer = >::sse_decode(deserializer); @@ -2800,7 +2800,7 @@ impl SseDecode for crate::bindings::LNOffer { return crate::bindings::LNOffer { bolt12: var_bolt12, chains: var_chains, - amount: var_amount, + min_amount: var_minAmount, description: var_description, absolute_expiry: var_absoluteExpiry, issuer: var_issuer, @@ -4816,7 +4816,7 @@ impl flutter_rust_bridge::IntoDart for FrbWrapper { [ self.0.bolt12.into_into_dart().into_dart(), self.0.chains.into_into_dart().into_dart(), - self.0.amount.into_into_dart().into_dart(), + self.0.min_amount.into_into_dart().into_dart(), self.0.description.into_into_dart().into_dart(), self.0.absolute_expiry.into_into_dart().into_dart(), self.0.issuer.into_into_dart().into_dart(), @@ -6839,7 +6839,7 @@ impl SseEncode for crate::bindings::LNOffer { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { ::sse_encode(self.bolt12, serializer); >::sse_encode(self.chains, serializer); - >::sse_encode(self.amount, serializer); + >::sse_encode(self.min_amount, serializer); >::sse_encode(self.description, serializer); >::sse_encode(self.absolute_expiry, serializer); >::sse_encode(self.issuer, serializer); @@ -8853,7 +8853,7 @@ mod io { crate::bindings::LNOffer { bolt12: self.bolt12.cst_decode(), chains: self.chains.cst_decode(), - amount: self.amount.cst_decode(), + min_amount: self.min_amount.cst_decode(), description: self.description.cst_decode(), absolute_expiry: self.absolute_expiry.cst_decode(), issuer: self.issuer.cst_decode(), @@ -10126,7 +10126,7 @@ mod io { Self { bolt12: core::ptr::null_mut(), chains: core::ptr::null_mut(), - amount: core::ptr::null_mut(), + min_amount: core::ptr::null_mut(), description: core::ptr::null_mut(), absolute_expiry: core::ptr::null_mut(), issuer: core::ptr::null_mut(), @@ -12164,7 +12164,7 @@ mod io { pub struct wire_cst_ln_offer { bolt12: *mut wire_cst_list_prim_u_8_strict, chains: *mut wire_cst_list_String, - amount: *mut wire_cst_amount, + min_amount: *mut wire_cst_amount, description: *mut wire_cst_list_prim_u_8_strict, absolute_expiry: *mut u64, issuer: *mut wire_cst_list_prim_u_8_strict, diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index b2f2bf500..d1e210d69 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -2595,7 +2595,7 @@ impl LiquidSdk { .collect(), // TODO This conversion (between lightning-v0.0.125 to -v0.0.118 Amount types) // won't be needed when Liquid SDK uses the same lightning crate version as sdk-common - amount: offer.amount().map(|amount| match amount { + min_amount: offer.amount().map(|amount| match amount { ::lightning::offers::offer::Amount::Bitcoin { amount_msats } => { Amount::Bitcoin { amount_msat: amount_msats, diff --git a/packages/dart/lib/src/bindings.dart b/packages/dart/lib/src/bindings.dart index 47b2144a6..c46ca6b57 100644 --- a/packages/dart/lib/src/bindings.dart +++ b/packages/dart/lib/src/bindings.dart @@ -422,7 +422,7 @@ class LNInvoice { class LNOffer { final String bolt12; final List chains; - final Amount? amount; + final Amount? minAmount; final String? description; final BigInt? absoluteExpiry; final String? issuer; @@ -431,7 +431,7 @@ class LNOffer { const LNOffer({ required this.bolt12, required this.chains, - this.amount, + this.minAmount, this.description, this.absoluteExpiry, this.issuer, @@ -442,7 +442,7 @@ class LNOffer { int get hashCode => bolt12.hashCode ^ chains.hashCode ^ - amount.hashCode ^ + minAmount.hashCode ^ description.hashCode ^ absoluteExpiry.hashCode ^ issuer.hashCode ^ @@ -455,7 +455,7 @@ class LNOffer { runtimeType == other.runtimeType && bolt12 == other.bolt12 && chains == other.chains && - amount == other.amount && + minAmount == other.minAmount && description == other.description && absoluteExpiry == other.absoluteExpiry && issuer == other.issuer && diff --git a/packages/dart/lib/src/frb_generated.dart b/packages/dart/lib/src/frb_generated.dart index 0a6a1c656..358dc7563 100644 --- a/packages/dart/lib/src/frb_generated.dart +++ b/packages/dart/lib/src/frb_generated.dart @@ -1999,7 +1999,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return LNOffer( bolt12: dco_decode_String(arr[0]), chains: dco_decode_list_String(arr[1]), - amount: dco_decode_opt_box_autoadd_amount(arr[2]), + minAmount: dco_decode_opt_box_autoadd_amount(arr[2]), description: dco_decode_opt_String(arr[3]), absoluteExpiry: dco_decode_opt_box_autoadd_u_64(arr[4]), issuer: dco_decode_opt_String(arr[5]), @@ -3918,7 +3918,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { // Codec=Sse (Serialization based), see doc to use other codecs var var_bolt12 = sse_decode_String(deserializer); var var_chains = sse_decode_list_String(deserializer); - var var_amount = sse_decode_opt_box_autoadd_amount(deserializer); + var var_minAmount = sse_decode_opt_box_autoadd_amount(deserializer); var var_description = sse_decode_opt_String(deserializer); var var_absoluteExpiry = sse_decode_opt_box_autoadd_u_64(deserializer); var var_issuer = sse_decode_opt_String(deserializer); @@ -3926,7 +3926,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return LNOffer( bolt12: var_bolt12, chains: var_chains, - amount: var_amount, + minAmount: var_minAmount, description: var_description, absoluteExpiry: var_absoluteExpiry, issuer: var_issuer, @@ -5820,7 +5820,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_String(self.bolt12, serializer); sse_encode_list_String(self.chains, serializer); - sse_encode_opt_box_autoadd_amount(self.amount, serializer); + sse_encode_opt_box_autoadd_amount(self.minAmount, serializer); sse_encode_opt_String(self.description, serializer); sse_encode_opt_box_autoadd_u_64(self.absoluteExpiry, serializer); sse_encode_opt_String(self.issuer, serializer); diff --git a/packages/dart/lib/src/frb_generated.io.dart b/packages/dart/lib/src/frb_generated.io.dart index 4ce0b5279..a4ae99366 100644 --- a/packages/dart/lib/src/frb_generated.io.dart +++ b/packages/dart/lib/src/frb_generated.io.dart @@ -2309,7 +2309,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void cst_api_fill_to_wire_ln_offer(LNOffer apiObj, wire_cst_ln_offer wireObj) { wireObj.bolt12 = cst_encode_String(apiObj.bolt12); wireObj.chains = cst_encode_list_String(apiObj.chains); - wireObj.amount = cst_encode_opt_box_autoadd_amount(apiObj.amount); + wireObj.min_amount = cst_encode_opt_box_autoadd_amount(apiObj.minAmount); wireObj.description = cst_encode_opt_String(apiObj.description); wireObj.absolute_expiry = cst_encode_opt_box_autoadd_u_64(apiObj.absoluteExpiry); wireObj.issuer = cst_encode_opt_String(apiObj.issuer); @@ -5448,7 +5448,7 @@ final class wire_cst_ln_offer extends ffi.Struct { external ffi.Pointer chains; - external ffi.Pointer amount; + external ffi.Pointer min_amount; external ffi.Pointer description; 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 daa9b006d..be494c112 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 @@ -579,10 +579,10 @@ fun asLnOffer(lnOffer: ReadableMap): LnOffer? { val chains = lnOffer.getArray("chains")?.let { asStringList(it) }!! val description = if (hasNonNullKey(lnOffer, "description")) lnOffer.getString("description") else null val signingPubkey = if (hasNonNullKey(lnOffer, "signingPubkey")) lnOffer.getString("signingPubkey") else null - val amount = if (hasNonNullKey(lnOffer, "amount")) lnOffer.getMap("amount")?.let { asAmount(it) } else null + val minAmount = if (hasNonNullKey(lnOffer, "minAmount")) lnOffer.getMap("minAmount")?.let { asAmount(it) } else null val absoluteExpiry = if (hasNonNullKey(lnOffer, "absoluteExpiry")) lnOffer.getDouble("absoluteExpiry").toULong() else null val issuer = if (hasNonNullKey(lnOffer, "issuer")) lnOffer.getString("issuer") else null - return LnOffer(bolt12, chains, description, signingPubkey, amount, absoluteExpiry, issuer) + return LnOffer(bolt12, chains, description, signingPubkey, minAmount, absoluteExpiry, issuer) } fun readableMapOf(lnOffer: LnOffer): ReadableMap = @@ -591,7 +591,7 @@ fun readableMapOf(lnOffer: LnOffer): ReadableMap = "chains" to readableArrayOf(lnOffer.chains), "description" to lnOffer.description, "signingPubkey" to lnOffer.signingPubkey, - "amount" to lnOffer.amount?.let { readableMapOf(it) }, + "minAmount" to lnOffer.minAmount?.let { readableMapOf(it) }, "absoluteExpiry" to lnOffer.absoluteExpiry, "issuer" to lnOffer.issuer, ) diff --git a/packages/react-native/ios/BreezSDKLiquidMapper.swift b/packages/react-native/ios/BreezSDKLiquidMapper.swift index a9b847cfd..c9a3028d8 100644 --- a/packages/react-native/ios/BreezSDKLiquidMapper.swift +++ b/packages/react-native/ios/BreezSDKLiquidMapper.swift @@ -686,9 +686,9 @@ enum BreezSDKLiquidMapper { } signingPubkey = signingPubkeyTmp } - var amount: Amount? - if let amountTmp = lnOffer["amount"] as? [String: Any?] { - amount = try asAmount(amount: amountTmp) + var minAmount: Amount? + if let minAmountTmp = lnOffer["minAmount"] as? [String: Any?] { + minAmount = try asAmount(amount: minAmountTmp) } var absoluteExpiry: UInt64? @@ -706,7 +706,7 @@ enum BreezSDKLiquidMapper { issuer = issuerTmp } - return LnOffer(bolt12: bolt12, chains: chains, description: description, signingPubkey: signingPubkey, amount: amount, absoluteExpiry: absoluteExpiry, issuer: issuer) + return LnOffer(bolt12: bolt12, chains: chains, description: description, signingPubkey: signingPubkey, minAmount: minAmount, absoluteExpiry: absoluteExpiry, issuer: issuer) } static func dictionaryOf(lnOffer: LnOffer) -> [String: Any?] { @@ -715,7 +715,7 @@ enum BreezSDKLiquidMapper { "chains": lnOffer.chains, "description": lnOffer.description == nil ? nil : lnOffer.description, "signingPubkey": lnOffer.signingPubkey == nil ? nil : lnOffer.signingPubkey, - "amount": lnOffer.amount == nil ? nil : dictionaryOf(amount: lnOffer.amount!), + "minAmount": lnOffer.minAmount == nil ? nil : dictionaryOf(amount: lnOffer.minAmount!), "absoluteExpiry": lnOffer.absoluteExpiry == nil ? nil : lnOffer.absoluteExpiry, "issuer": lnOffer.issuer == nil ? nil : lnOffer.issuer, ] diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index bcd2dc11c..329698b05 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -121,7 +121,7 @@ export interface LnOffer { chains: string[] description?: string signingPubkey?: string - amount?: Amount + minAmount?: Amount absoluteExpiry?: number issuer?: string } From f9b9d5493f7e7b42a044cb6f3e36e02569ca0f33 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Thu, 14 Nov 2024 08:20:49 +0000 Subject: [PATCH 17/22] Update cli/src/commands.rs Co-authored-by: Ross Savage <551697+dangeross@users.noreply.github.com> --- cli/src/commands.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/commands.rs b/cli/src/commands.rs index 5cdaef4ba..d021f30c6 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -327,7 +327,7 @@ pub(crate) async fn handle_command( (None, Some(offer), None) => match amount_sat { Some(_) => Ok(offer), None => Err(anyhow!( - "Must specify either a BOLT11 invoice, a BOLT12 offer or a direct/BIP21 address." + "Must specify an amount for a BOLT12 offer." )) }, (None, None, Some(address)) => Ok(address), From 10fbefa660b3218bb399d5c3ee0fe259c5f2798a Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Thu, 14 Nov 2024 14:54:12 +0100 Subject: [PATCH 18/22] Bump sdk-common, propagate changes --- cli/Cargo.lock | 2 +- lib/Cargo.lock | 2 +- .../include/breez_sdk_liquid.h | 2 +- lib/bindings/src/breez_sdk_liquid.udl | 2 +- lib/core/src/bindings.rs | 2 +- lib/core/src/frb_generated.rs | 16 +++---- lib/core/src/sdk.rs | 47 +++++++++++-------- packages/dart/lib/src/bindings.dart | 8 ++-- packages/dart/lib/src/frb_generated.dart | 8 ++-- packages/dart/lib/src/frb_generated.io.dart | 4 +- .../breezsdkliquid/BreezSDKLiquidMapper.kt | 8 ++-- .../ios/BreezSDKLiquidMapper.swift | 8 ++-- packages/react-native/src/index.ts | 2 +- 13 files changed, 60 insertions(+), 51 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 6ce53c996..5a2e1bcf9 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -3046,7 +3046,7 @@ dependencies = [ [[package]] name = "sdk-common" version = "0.6.2" -source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#3ffe4458d81e4d8f9c5d287e1b5dacf9735ec3d3" +source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#caf483b6eebf860e079c239858f2900c82302955" dependencies = [ "aes 0.8.4", "anyhow", diff --git a/lib/Cargo.lock b/lib/Cargo.lock index 70efd04b0..bb064bb27 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -3294,7 +3294,7 @@ dependencies = [ [[package]] name = "sdk-common" version = "0.6.2" -source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#3ffe4458d81e4d8f9c5d287e1b5dacf9735ec3d3" +source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#caf483b6eebf860e079c239858f2900c82302955" dependencies = [ "aes 0.8.4", "anyhow", 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 d1780fdb7..76701b415 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 @@ -196,7 +196,7 @@ typedef struct wire_cst_amount { } wire_cst_amount; typedef struct wire_cst_ln_offer { - struct wire_cst_list_prim_u_8_strict *bolt12; + struct wire_cst_list_prim_u_8_strict *offer; struct wire_cst_list_String *chains; struct wire_cst_amount *min_amount; struct wire_cst_list_prim_u_8_strict *description; diff --git a/lib/bindings/src/breez_sdk_liquid.udl b/lib/bindings/src/breez_sdk_liquid.udl index 33623e848..f7f9b942e 100644 --- a/lib/bindings/src/breez_sdk_liquid.udl +++ b/lib/bindings/src/breez_sdk_liquid.udl @@ -45,7 +45,7 @@ interface Amount { }; dictionary LNOffer { - string bolt12; + string offer; sequence chains; string? description; string? signing_pubkey; diff --git a/lib/core/src/bindings.rs b/lib/core/src/bindings.rs index bf02ff05b..53b99de1d 100644 --- a/lib/core/src/bindings.rs +++ b/lib/core/src/bindings.rs @@ -342,7 +342,7 @@ pub enum _Amount { #[frb(mirror(LNOffer))] pub struct _LNOffer { - pub bolt12: String, + pub offer: String, pub chains: Vec, pub min_amount: Option, pub description: Option, diff --git a/lib/core/src/frb_generated.rs b/lib/core/src/frb_generated.rs index bac33fa4e..18f8da196 100644 --- a/lib/core/src/frb_generated.rs +++ b/lib/core/src/frb_generated.rs @@ -1858,7 +1858,7 @@ const _: fn() = || { } { let LNOffer = None::.unwrap(); - let _: String = LNOffer.bolt12; + let _: String = LNOffer.offer; let _: Vec = LNOffer.chains; let _: Option = LNOffer.min_amount; let _: Option = LNOffer.description; @@ -2790,7 +2790,7 @@ impl SseDecode for crate::bindings::LNInvoice { impl SseDecode for crate::bindings::LNOffer { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - let mut var_bolt12 = ::sse_decode(deserializer); + let mut var_offer = ::sse_decode(deserializer); let mut var_chains = >::sse_decode(deserializer); let mut var_minAmount = >::sse_decode(deserializer); let mut var_description = >::sse_decode(deserializer); @@ -2798,7 +2798,7 @@ impl SseDecode for crate::bindings::LNOffer { let mut var_issuer = >::sse_decode(deserializer); let mut var_signingPubkey = >::sse_decode(deserializer); return crate::bindings::LNOffer { - bolt12: var_bolt12, + offer: var_offer, chains: var_chains, min_amount: var_minAmount, description: var_description, @@ -4814,7 +4814,7 @@ impl flutter_rust_bridge::IntoIntoDart> impl flutter_rust_bridge::IntoDart for FrbWrapper { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { [ - self.0.bolt12.into_into_dart().into_dart(), + self.0.offer.into_into_dart().into_dart(), self.0.chains.into_into_dart().into_dart(), self.0.min_amount.into_into_dart().into_dart(), self.0.description.into_into_dart().into_dart(), @@ -6837,7 +6837,7 @@ impl SseEncode for crate::bindings::LNInvoice { impl SseEncode for crate::bindings::LNOffer { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - ::sse_encode(self.bolt12, serializer); + ::sse_encode(self.offer, serializer); >::sse_encode(self.chains, serializer); >::sse_encode(self.min_amount, serializer); >::sse_encode(self.description, serializer); @@ -8851,7 +8851,7 @@ mod io { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> crate::bindings::LNOffer { crate::bindings::LNOffer { - bolt12: self.bolt12.cst_decode(), + offer: self.offer.cst_decode(), chains: self.chains.cst_decode(), min_amount: self.min_amount.cst_decode(), description: self.description.cst_decode(), @@ -10124,7 +10124,7 @@ mod io { impl NewWithNullPtr for wire_cst_ln_offer { fn new_with_null_ptr() -> Self { Self { - bolt12: core::ptr::null_mut(), + offer: core::ptr::null_mut(), chains: core::ptr::null_mut(), min_amount: core::ptr::null_mut(), description: core::ptr::null_mut(), @@ -12162,7 +12162,7 @@ mod io { #[repr(C)] #[derive(Clone, Copy)] pub struct wire_cst_ln_offer { - bolt12: *mut wire_cst_list_prim_u_8_strict, + offer: *mut wire_cst_list_prim_u_8_strict, chains: *mut wire_cst_list_String, min_amount: *mut wire_cst_amount, description: *mut wire_cst_list_prim_u_8_strict, diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index d1e210d69..b8c0b1294 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -970,7 +970,7 @@ impl LiquidSdk { } => { let bolt12_invoice = self .swapper - .get_bolt12_invoice(&offer.bolt12, *receiver_amount_sat)?; + .get_bolt12_invoice(&offer.offer, *receiver_amount_sat)?; self.pay_bolt12_invoice(&bolt12_invoice, *fees_sat).await } } @@ -2585,31 +2585,40 @@ impl LiquidSdk { /// Parses a string into an [InputType]. See [input_parser::parse]. pub async fn parse(input: &str) -> Result { if let Ok(offer) = input.parse::() { + // TODO This conversion (between lightning-v0.0.125 to -v0.0.118 Amount types) + // won't be needed when Liquid SDK uses the same lightning crate version as sdk-common + let min_amount = offer + .amount() + .map(|amount| match amount { + ::lightning::offers::offer::Amount::Bitcoin { amount_msats } => { + Ok(Amount::Bitcoin { + amount_msat: amount_msats, + }) + } + ::lightning::offers::offer::Amount::Currency { + iso4217_code, + amount, + } => Ok(Amount::Currency { + iso4217_code: String::from_utf8(iso4217_code.to_vec()).map_err(|_| { + anyhow!("Expecting a valid ISO 4217 character sequence") + })?, + fractional_amount: amount, + }), + }) + .transpose() + .map_err(|e: anyhow::Error| { + PaymentError::generic(&format!("Failed to reconstruct amount: {e:?}")) + })?; + return Ok(InputType::Bolt12Offer { offer: LNOffer { - bolt12: input.to_string(), + offer: input.to_string(), chains: offer .chains() .iter() .map(|chain| chain.to_string()) .collect(), - // TODO This conversion (between lightning-v0.0.125 to -v0.0.118 Amount types) - // won't be needed when Liquid SDK uses the same lightning crate version as sdk-common - min_amount: offer.amount().map(|amount| match amount { - ::lightning::offers::offer::Amount::Bitcoin { amount_msats } => { - Amount::Bitcoin { - amount_msat: amount_msats, - } - } - ::lightning::offers::offer::Amount::Currency { - iso4217_code, - amount, - } => Amount::Currency { - iso4217_code: String::from_utf8(iso4217_code.to_vec()) - .expect("Expecting a valid ISO 4217 character sequence"), - fractional_amount: amount, - }, - }), + min_amount, description: offer.description().map(|d| d.to_string()), absolute_expiry: offer.absolute_expiry().map(|expiry| expiry.as_secs()), issuer: offer.issuer().map(|s| s.to_string()), diff --git a/packages/dart/lib/src/bindings.dart b/packages/dart/lib/src/bindings.dart index c46ca6b57..23ac294ed 100644 --- a/packages/dart/lib/src/bindings.dart +++ b/packages/dart/lib/src/bindings.dart @@ -420,7 +420,7 @@ class LNInvoice { } class LNOffer { - final String bolt12; + final String offer; final List chains; final Amount? minAmount; final String? description; @@ -429,7 +429,7 @@ class LNOffer { final String? signingPubkey; const LNOffer({ - required this.bolt12, + required this.offer, required this.chains, this.minAmount, this.description, @@ -440,7 +440,7 @@ class LNOffer { @override int get hashCode => - bolt12.hashCode ^ + offer.hashCode ^ chains.hashCode ^ minAmount.hashCode ^ description.hashCode ^ @@ -453,7 +453,7 @@ class LNOffer { identical(this, other) || other is LNOffer && runtimeType == other.runtimeType && - bolt12 == other.bolt12 && + offer == other.offer && chains == other.chains && minAmount == other.minAmount && description == other.description && diff --git a/packages/dart/lib/src/frb_generated.dart b/packages/dart/lib/src/frb_generated.dart index 358dc7563..50e794d0c 100644 --- a/packages/dart/lib/src/frb_generated.dart +++ b/packages/dart/lib/src/frb_generated.dart @@ -1997,7 +1997,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final arr = raw as List; if (arr.length != 7) throw Exception('unexpected arr length: expect 7 but see ${arr.length}'); return LNOffer( - bolt12: dco_decode_String(arr[0]), + offer: dco_decode_String(arr[0]), chains: dco_decode_list_String(arr[1]), minAmount: dco_decode_opt_box_autoadd_amount(arr[2]), description: dco_decode_opt_String(arr[3]), @@ -3916,7 +3916,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected LNOffer sse_decode_ln_offer(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs - var var_bolt12 = sse_decode_String(deserializer); + var var_offer = sse_decode_String(deserializer); var var_chains = sse_decode_list_String(deserializer); var var_minAmount = sse_decode_opt_box_autoadd_amount(deserializer); var var_description = sse_decode_opt_String(deserializer); @@ -3924,7 +3924,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_issuer = sse_decode_opt_String(deserializer); var var_signingPubkey = sse_decode_opt_String(deserializer); return LNOffer( - bolt12: var_bolt12, + offer: var_offer, chains: var_chains, minAmount: var_minAmount, description: var_description, @@ -5818,7 +5818,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected void sse_encode_ln_offer(LNOffer self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_String(self.bolt12, serializer); + sse_encode_String(self.offer, serializer); sse_encode_list_String(self.chains, serializer); sse_encode_opt_box_autoadd_amount(self.minAmount, serializer); sse_encode_opt_String(self.description, serializer); diff --git a/packages/dart/lib/src/frb_generated.io.dart b/packages/dart/lib/src/frb_generated.io.dart index a4ae99366..37bfcd8e5 100644 --- a/packages/dart/lib/src/frb_generated.io.dart +++ b/packages/dart/lib/src/frb_generated.io.dart @@ -2307,7 +2307,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void cst_api_fill_to_wire_ln_offer(LNOffer apiObj, wire_cst_ln_offer wireObj) { - wireObj.bolt12 = cst_encode_String(apiObj.bolt12); + wireObj.offer = cst_encode_String(apiObj.offer); wireObj.chains = cst_encode_list_String(apiObj.chains); wireObj.min_amount = cst_encode_opt_box_autoadd_amount(apiObj.minAmount); wireObj.description = cst_encode_opt_String(apiObj.description); @@ -5444,7 +5444,7 @@ final class wire_cst_amount extends ffi.Struct { } final class wire_cst_ln_offer extends ffi.Struct { - external ffi.Pointer bolt12; + external ffi.Pointer offer; external ffi.Pointer chains; 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 be494c112..4be029222 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 @@ -568,26 +568,26 @@ fun asLnOffer(lnOffer: ReadableMap): LnOffer? { if (!validateMandatoryFields( lnOffer, arrayOf( - "bolt12", + "offer", "chains", ), ) ) { return null } - val bolt12 = lnOffer.getString("bolt12")!! + val offer = lnOffer.getString("offer")!! val chains = lnOffer.getArray("chains")?.let { asStringList(it) }!! val description = if (hasNonNullKey(lnOffer, "description")) lnOffer.getString("description") else null val signingPubkey = if (hasNonNullKey(lnOffer, "signingPubkey")) lnOffer.getString("signingPubkey") else null val minAmount = if (hasNonNullKey(lnOffer, "minAmount")) lnOffer.getMap("minAmount")?.let { asAmount(it) } else null val absoluteExpiry = if (hasNonNullKey(lnOffer, "absoluteExpiry")) lnOffer.getDouble("absoluteExpiry").toULong() else null val issuer = if (hasNonNullKey(lnOffer, "issuer")) lnOffer.getString("issuer") else null - return LnOffer(bolt12, chains, description, signingPubkey, minAmount, absoluteExpiry, issuer) + return LnOffer(offer, chains, description, signingPubkey, minAmount, absoluteExpiry, issuer) } fun readableMapOf(lnOffer: LnOffer): ReadableMap = readableMapOf( - "bolt12" to lnOffer.bolt12, + "offer" to lnOffer.offer, "chains" to readableArrayOf(lnOffer.chains), "description" to lnOffer.description, "signingPubkey" to lnOffer.signingPubkey, diff --git a/packages/react-native/ios/BreezSDKLiquidMapper.swift b/packages/react-native/ios/BreezSDKLiquidMapper.swift index c9a3028d8..ec212d5f8 100644 --- a/packages/react-native/ios/BreezSDKLiquidMapper.swift +++ b/packages/react-native/ios/BreezSDKLiquidMapper.swift @@ -666,8 +666,8 @@ enum BreezSDKLiquidMapper { } static func asLnOffer(lnOffer: [String: Any?]) throws -> LnOffer { - guard let bolt12 = lnOffer["bolt12"] as? String else { - throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "bolt12", typeName: "LnOffer")) + guard let offer = lnOffer["offer"] as? String else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "offer", typeName: "LnOffer")) } guard let chains = lnOffer["chains"] as? [String] else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "chains", typeName: "LnOffer")) @@ -706,12 +706,12 @@ enum BreezSDKLiquidMapper { issuer = issuerTmp } - return LnOffer(bolt12: bolt12, chains: chains, description: description, signingPubkey: signingPubkey, minAmount: minAmount, absoluteExpiry: absoluteExpiry, issuer: issuer) + return LnOffer(offer: offer, chains: chains, description: description, signingPubkey: signingPubkey, minAmount: minAmount, absoluteExpiry: absoluteExpiry, issuer: issuer) } static func dictionaryOf(lnOffer: LnOffer) -> [String: Any?] { return [ - "bolt12": lnOffer.bolt12, + "offer": lnOffer.offer, "chains": lnOffer.chains, "description": lnOffer.description == nil ? nil : lnOffer.description, "signingPubkey": lnOffer.signingPubkey == nil ? nil : lnOffer.signingPubkey, diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index 329698b05..94d268a0e 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -117,7 +117,7 @@ export interface LnInvoice { } export interface LnOffer { - bolt12: string + offer: string chains: string[] description?: string signingPubkey?: string From 9ae1f6cedac617b3dcb707f3987823316a92b5cc Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:16:42 +0100 Subject: [PATCH 19/22] Clarify CLI invoice description --- cli/src/commands.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/commands.rs b/cli/src/commands.rs index d021f30c6..78cf7422a 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -21,7 +21,7 @@ use serde_json::to_string_pretty; pub(crate) enum Command { /// Send a payment directly or via a swap SendPayment { - /// Invoice which has to be paid (BOLT11 or BOLT12) + /// Invoice which has to be paid (BOLT11) #[arg(long)] invoice: Option, From 83ccce0e2dd40d1014b88644b783d2dd67008ebc Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:17:48 +0100 Subject: [PATCH 20/22] Address MRH TODO MRH with Bolt12 is not possible because receive is not yet supported. --- lib/core/src/sdk.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index b8c0b1294..15a3e918a 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -1046,8 +1046,6 @@ impl LiquidSdk { PaymentError::InsufficientFunds ); - // TODO CHeck for MRH (if present, pay via Liquid) - self.send_payment_via_swap( invoice, &invoice_parsed.payment_hash().to_string(), From b54da49c0f98e972976f3bc1a20a2b482aba3cd4 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:18:33 +0100 Subject: [PATCH 21/22] Address self-transfer TODO Removed self-transfer check TODO, because self-transfers are not possible with Bolt12 when receive is not supported. --- lib/core/src/sdk.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index 15a3e918a..7cd797243 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -1034,7 +1034,6 @@ impl LiquidSdk { invoice: &str, fees_sat: u64, ) -> Result { - // TODO Ensure it's not self-transfer // TODO Validate invoice let invoice_parsed = utils::parse_bolt12_invoice(invoice)?; From 6be99dec40178721c12be04eb7b280ecbf847de4 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Fri, 15 Nov 2024 18:01:52 +0100 Subject: [PATCH 22/22] Add Bolt12 invoice validation before paying --- cli/Cargo.lock | 2 +- lib/Cargo.lock | 2 +- .../include/breez_sdk_liquid.h | 13 ++ lib/bindings/src/breez_sdk_liquid.udl | 5 + lib/core/src/bindings.rs | 6 + lib/core/src/frb_generated.rs | 127 ++++++++++++++++++ lib/core/src/sdk.rs | 58 +++++++- packages/dart/lib/src/bindings.dart | 24 +++- packages/dart/lib/src/frb_generated.dart | 58 +++++++- packages/dart/lib/src/frb_generated.io.dart | 63 +++++++++ .../breezsdkliquid/BreezSDKLiquidMapper.kt | 36 ++++- .../ios/BreezSDKLiquidMapper.swift | 39 +++++- packages/react-native/src/index.ts | 5 + 13 files changed, 424 insertions(+), 14 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 5a2e1bcf9..03d7e1a2a 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -3046,7 +3046,7 @@ dependencies = [ [[package]] name = "sdk-common" version = "0.6.2" -source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#caf483b6eebf860e079c239858f2900c82302955" +source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#53ba77bce27b49a5c9b47698b5ce3cbded975572" dependencies = [ "aes 0.8.4", "anyhow", diff --git a/lib/Cargo.lock b/lib/Cargo.lock index bb064bb27..2a0d07789 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -3294,7 +3294,7 @@ dependencies = [ [[package]] name = "sdk-common" version = "0.6.2" -source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#caf483b6eebf860e079c239858f2900c82302955" +source = "git+https://github.com/breez/breez-sdk?branch=ok300-sdk-common-parse-bolt12-offer#53ba77bce27b49a5c9b47698b5ce3cbded975572" dependencies = [ "aes 0.8.4", "anyhow", 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 76701b415..e5f9d67e0 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 @@ -195,6 +195,15 @@ typedef struct wire_cst_amount { union AmountKind kind; } wire_cst_amount; +typedef struct wire_cst_ln_offer_blinded_path { + struct wire_cst_list_String *blinded_hops; +} wire_cst_ln_offer_blinded_path; + +typedef struct wire_cst_list_ln_offer_blinded_path { + struct wire_cst_ln_offer_blinded_path *ptr; + int32_t len; +} wire_cst_list_ln_offer_blinded_path; + typedef struct wire_cst_ln_offer { struct wire_cst_list_prim_u_8_strict *offer; struct wire_cst_list_String *chains; @@ -203,6 +212,7 @@ typedef struct wire_cst_ln_offer { uint64_t *absolute_expiry; struct wire_cst_list_prim_u_8_strict *issuer; struct wire_cst_list_prim_u_8_strict *signing_pubkey; + struct wire_cst_list_ln_offer_blinded_path *paths; } wire_cst_ln_offer; typedef struct wire_cst_SendDestination_Bolt12 { @@ -1261,6 +1271,8 @@ struct wire_cst_list_String *frbgen_breez_liquid_cst_new_list_String(int32_t len struct wire_cst_list_fiat_currency *frbgen_breez_liquid_cst_new_list_fiat_currency(int32_t len); +struct wire_cst_list_ln_offer_blinded_path *frbgen_breez_liquid_cst_new_list_ln_offer_blinded_path(int32_t len); + struct wire_cst_list_locale_overrides *frbgen_breez_liquid_cst_new_list_locale_overrides(int32_t len); struct wire_cst_list_localized_name *frbgen_breez_liquid_cst_new_list_localized_name(int32_t len); @@ -1331,6 +1343,7 @@ static int64_t dummy_method_to_enforce_bundling(void) { dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_url_success_action_data); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_String); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_fiat_currency); + dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_ln_offer_blinded_path); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_locale_overrides); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_localized_name); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_payment); diff --git a/lib/bindings/src/breez_sdk_liquid.udl b/lib/bindings/src/breez_sdk_liquid.udl index f7f9b942e..6b55388ff 100644 --- a/lib/bindings/src/breez_sdk_liquid.udl +++ b/lib/bindings/src/breez_sdk_liquid.udl @@ -44,9 +44,14 @@ interface Amount { Currency(string iso4217_code, u64 fractional_amount); }; +dictionary LNOfferBlindedPath { + sequence blinded_hops; +}; + dictionary LNOffer { string offer; sequence chains; + sequence paths; string? description; string? signing_pubkey; Amount? min_amount; diff --git a/lib/core/src/bindings.rs b/lib/core/src/bindings.rs index 53b99de1d..b1ad7b809 100644 --- a/lib/core/src/bindings.rs +++ b/lib/core/src/bindings.rs @@ -340,6 +340,11 @@ pub enum _Amount { }, } +#[frb(mirror(LNOfferBlindedPath))] +pub struct _LNOfferBlindedPath { + pub blinded_hops: Vec, +} + #[frb(mirror(LNOffer))] pub struct _LNOffer { pub offer: String, @@ -349,6 +354,7 @@ pub struct _LNOffer { pub absolute_expiry: Option, pub issuer: Option, pub signing_pubkey: Option, + pub paths: Vec, } #[frb(mirror(InputType))] diff --git a/lib/core/src/frb_generated.rs b/lib/core/src/frb_generated.rs index 18f8da196..ba96a9cfb 100644 --- a/lib/core/src/frb_generated.rs +++ b/lib/core/src/frb_generated.rs @@ -1865,6 +1865,11 @@ const _: fn() = || { let _: Option = LNOffer.absolute_expiry; let _: Option = LNOffer.issuer; let _: Option = LNOffer.signing_pubkey; + let _: Vec = LNOffer.paths; + } + { + let LNOfferBlindedPath = None::.unwrap(); + let _: Vec = LNOfferBlindedPath.blinded_hops; } { let LnUrlAuthRequestData = None::.unwrap(); @@ -2603,6 +2608,20 @@ impl SseDecode for Vec { } } +impl SseDecode for Vec { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut len_ = ::sse_decode(deserializer); + let mut ans_ = vec![]; + for idx_ in 0..len_ { + ans_.push(::sse_decode( + deserializer, + )); + } + return ans_; + } +} + impl SseDecode for Vec { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -2797,6 +2816,7 @@ impl SseDecode for crate::bindings::LNOffer { let mut var_absoluteExpiry = >::sse_decode(deserializer); let mut var_issuer = >::sse_decode(deserializer); let mut var_signingPubkey = >::sse_decode(deserializer); + let mut var_paths = >::sse_decode(deserializer); return crate::bindings::LNOffer { offer: var_offer, chains: var_chains, @@ -2805,6 +2825,17 @@ impl SseDecode for crate::bindings::LNOffer { absolute_expiry: var_absoluteExpiry, issuer: var_issuer, signing_pubkey: var_signingPubkey, + paths: var_paths, + }; + } +} + +impl SseDecode for crate::bindings::LNOfferBlindedPath { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_blindedHops = >::sse_decode(deserializer); + return crate::bindings::LNOfferBlindedPath { + blinded_hops: var_blindedHops, }; } } @@ -4821,6 +4852,7 @@ impl flutter_rust_bridge::IntoDart for FrbWrapper { self.0.absolute_expiry.into_into_dart().into_dart(), self.0.issuer.into_into_dart().into_dart(), self.0.signing_pubkey.into_into_dart().into_dart(), + self.0.paths.into_into_dart().into_dart(), ] .into_dart() } @@ -4837,6 +4869,23 @@ impl flutter_rust_bridge::IntoIntoDart> } } // Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for FrbWrapper { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + [self.0.blinded_hops.into_into_dart().into_dart()].into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive + for FrbWrapper +{ +} +impl flutter_rust_bridge::IntoIntoDart> + for crate::bindings::LNOfferBlindedPath +{ + fn into_into_dart(self) -> FrbWrapper { + self.into() + } +} +// Codec=Dco (DartCObject based), see doc to use other codecs impl flutter_rust_bridge::IntoDart for crate::bindings::duplicates::LnUrlAuthError { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { match self { @@ -6695,6 +6744,16 @@ impl SseEncode for Vec { } } +impl SseEncode for Vec { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.len() as _, serializer); + for item in self { + ::sse_encode(item, serializer); + } + } +} + impl SseEncode for Vec { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -6844,6 +6903,14 @@ impl SseEncode for crate::bindings::LNOffer { >::sse_encode(self.absolute_expiry, serializer); >::sse_encode(self.issuer, serializer); >::sse_encode(self.signing_pubkey, serializer); + >::sse_encode(self.paths, serializer); + } +} + +impl SseEncode for crate::bindings::LNOfferBlindedPath { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + >::sse_encode(self.blinded_hops, serializer); } } @@ -8706,6 +8773,18 @@ mod io { vec.into_iter().map(CstDecode::cst_decode).collect() } } + impl CstDecode> + for *mut wire_cst_list_ln_offer_blinded_path + { + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> Vec { + let vec = unsafe { + let wrap = flutter_rust_bridge::for_generated::box_from_leak_ptr(self); + flutter_rust_bridge::for_generated::vec_from_leak_ptr(wrap.ptr, wrap.len) + }; + vec.into_iter().map(CstDecode::cst_decode).collect() + } + } impl CstDecode> for *mut wire_cst_list_locale_overrides { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> Vec { @@ -8858,6 +8937,15 @@ mod io { absolute_expiry: self.absolute_expiry.cst_decode(), issuer: self.issuer.cst_decode(), signing_pubkey: self.signing_pubkey.cst_decode(), + paths: self.paths.cst_decode(), + } + } + } + impl CstDecode for wire_cst_ln_offer_blinded_path { + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> crate::bindings::LNOfferBlindedPath { + crate::bindings::LNOfferBlindedPath { + blinded_hops: self.blinded_hops.cst_decode(), } } } @@ -10131,6 +10219,7 @@ mod io { absolute_expiry: core::ptr::null_mut(), issuer: core::ptr::null_mut(), signing_pubkey: core::ptr::null_mut(), + paths: core::ptr::null_mut(), } } } @@ -10139,6 +10228,18 @@ mod io { Self::new_with_null_ptr() } } + impl NewWithNullPtr for wire_cst_ln_offer_blinded_path { + fn new_with_null_ptr() -> Self { + Self { + blinded_hops: core::ptr::null_mut(), + } + } + } + impl Default for wire_cst_ln_offer_blinded_path { + fn default() -> Self { + Self::new_with_null_ptr() + } + } impl NewWithNullPtr for wire_cst_ln_url_auth_error { fn new_with_null_ptr() -> Self { Self { @@ -11673,6 +11774,20 @@ mod io { flutter_rust_bridge::for_generated::new_leak_box_ptr(wrap) } + #[no_mangle] + pub extern "C" fn frbgen_breez_liquid_cst_new_list_ln_offer_blinded_path( + len: i32, + ) -> *mut wire_cst_list_ln_offer_blinded_path { + let wrap = wire_cst_list_ln_offer_blinded_path { + ptr: flutter_rust_bridge::for_generated::new_leak_vec_ptr( + ::new_with_null_ptr(), + len, + ), + len, + }; + flutter_rust_bridge::for_generated::new_leak_box_ptr(wrap) + } + #[no_mangle] pub extern "C" fn frbgen_breez_liquid_cst_new_list_locale_overrides( len: i32, @@ -12058,6 +12173,12 @@ mod io { } #[repr(C)] #[derive(Clone, Copy)] + pub struct wire_cst_list_ln_offer_blinded_path { + ptr: *mut wire_cst_ln_offer_blinded_path, + len: i32, + } + #[repr(C)] + #[derive(Clone, Copy)] pub struct wire_cst_list_locale_overrides { ptr: *mut wire_cst_locale_overrides, len: i32, @@ -12169,6 +12290,12 @@ mod io { absolute_expiry: *mut u64, issuer: *mut wire_cst_list_prim_u_8_strict, signing_pubkey: *mut wire_cst_list_prim_u_8_strict, + paths: *mut wire_cst_list_ln_offer_blinded_path, + } + #[repr(C)] + #[derive(Clone, Copy)] + pub struct wire_cst_ln_offer_blinded_path { + blinded_hops: *mut wire_cst_list_String, } #[repr(C)] #[derive(Clone, Copy)] diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index 7cd797243..f99e3d8a1 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -971,7 +971,8 @@ impl LiquidSdk { let bolt12_invoice = self .swapper .get_bolt12_invoice(&offer.offer, *receiver_amount_sat)?; - self.pay_bolt12_invoice(&bolt12_invoice, *fees_sat).await + self.pay_bolt12_invoice(offer, *receiver_amount_sat, &bolt12_invoice, *fees_sat) + .await } } } @@ -1031,15 +1032,49 @@ impl LiquidSdk { async fn pay_bolt12_invoice( &self, + offer: &LNOffer, + user_specified_receiver_amount_sat: u64, invoice: &str, fees_sat: u64, ) -> Result { - // TODO Validate invoice - let invoice_parsed = utils::parse_bolt12_invoice(invoice)?; + let invoice_signing_pubkey = invoice_parsed.signing_pubkey().to_hex(); - let amount_sat = invoice_parsed.amount_msats() / 1_000; - let payer_amount_sat = amount_sat + fees_sat; + // Check if the invoice is signed by same key as the offer + match &offer.signing_pubkey { + None => { + ensure_sdk!( + &offer + .paths + .iter() + .filter_map(|path| path.blinded_hops.last()) + .any(|last_hop| &invoice_signing_pubkey == last_hop), + PaymentError::invalid_invoice( + "Invalid Bolt12 invoice signing key when using blinded path" + ) + ); + } + Some(offer_signing_pubkey) => { + ensure_sdk!( + offer_signing_pubkey == &invoice_signing_pubkey, + PaymentError::invalid_invoice("Invalid Bolt12 invoice signing key") + ); + } + } + + let receiver_amount_sat = invoice_parsed.amount_msats() / 1_000; + ensure_sdk!( + receiver_amount_sat == user_specified_receiver_amount_sat, + PaymentError::invalid_invoice("Invalid Bolt12 invoice amount") + ); + if let Some(Amount::Bitcoin { amount_msat }) = &offer.min_amount { + ensure_sdk!( + receiver_amount_sat >= amount_msat / 1_000, + PaymentError::invalid_invoice("Invalid Bolt12 invoice amount: below offer minimum") + ); + } + + let payer_amount_sat = receiver_amount_sat + fees_sat; ensure_sdk!( payer_amount_sat <= self.get_info().await?.balance_sat, PaymentError::InsufficientFunds @@ -1049,7 +1084,7 @@ impl LiquidSdk { invoice, &invoice_parsed.payment_hash().to_string(), invoice_parsed.description().map(|desc| desc.to_string()), - amount_sat, + receiver_amount_sat, fees_sat, ) .await @@ -2620,6 +2655,17 @@ impl LiquidSdk { absolute_expiry: offer.absolute_expiry().map(|expiry| expiry.as_secs()), issuer: offer.issuer().map(|s| s.to_string()), signing_pubkey: offer.signing_pubkey().map(|pk| pk.to_string()), + paths: offer + .paths() + .iter() + .map(|path| LNOfferBlindedPath { + blinded_hops: path + .blinded_hops() + .iter() + .map(|hop| hop.blinded_node_id.to_hex()) + .collect(), + }) + .collect::>(), }, }); } diff --git a/packages/dart/lib/src/bindings.dart b/packages/dart/lib/src/bindings.dart index 23ac294ed..d2daac318 100644 --- a/packages/dart/lib/src/bindings.dart +++ b/packages/dart/lib/src/bindings.dart @@ -427,6 +427,7 @@ class LNOffer { final BigInt? absoluteExpiry; final String? issuer; final String? signingPubkey; + final List paths; const LNOffer({ required this.offer, @@ -436,6 +437,7 @@ class LNOffer { this.absoluteExpiry, this.issuer, this.signingPubkey, + required this.paths, }); @override @@ -446,7 +448,8 @@ class LNOffer { description.hashCode ^ absoluteExpiry.hashCode ^ issuer.hashCode ^ - signingPubkey.hashCode; + signingPubkey.hashCode ^ + paths.hashCode; @override bool operator ==(Object other) => @@ -459,7 +462,24 @@ class LNOffer { description == other.description && absoluteExpiry == other.absoluteExpiry && issuer == other.issuer && - signingPubkey == other.signingPubkey; + signingPubkey == other.signingPubkey && + paths == other.paths; +} + +class LNOfferBlindedPath { + final List blindedHops; + + const LNOfferBlindedPath({ + required this.blindedHops, + }); + + @override + int get hashCode => blindedHops.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is LNOfferBlindedPath && runtimeType == other.runtimeType && blindedHops == other.blindedHops; } class LnUrlAuthRequestData { diff --git a/packages/dart/lib/src/frb_generated.dart b/packages/dart/lib/src/frb_generated.dart index 50e794d0c..3bfaa7b98 100644 --- a/packages/dart/lib/src/frb_generated.dart +++ b/packages/dart/lib/src/frb_generated.dart @@ -1884,6 +1884,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return (raw as List).map(dco_decode_fiat_currency).toList(); } + @protected + List dco_decode_list_ln_offer_blinded_path(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return (raw as List).map(dco_decode_ln_offer_blinded_path).toList(); + } + @protected List dco_decode_list_locale_overrides(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -1995,7 +2001,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { LNOffer dco_decode_ln_offer(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 7) throw Exception('unexpected arr length: expect 7 but see ${arr.length}'); + if (arr.length != 8) throw Exception('unexpected arr length: expect 8 but see ${arr.length}'); return LNOffer( offer: dco_decode_String(arr[0]), chains: dco_decode_list_String(arr[1]), @@ -2004,6 +2010,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { absoluteExpiry: dco_decode_opt_box_autoadd_u_64(arr[4]), issuer: dco_decode_opt_String(arr[5]), signingPubkey: dco_decode_opt_String(arr[6]), + paths: dco_decode_list_ln_offer_blinded_path(arr[7]), + ); + } + + @protected + LNOfferBlindedPath dco_decode_ln_offer_blinded_path(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 1) throw Exception('unexpected arr length: expect 1 but see ${arr.length}'); + return LNOfferBlindedPath( + blindedHops: dco_decode_list_String(arr[0]), ); } @@ -3745,6 +3762,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return ans_; } + @protected + List sse_decode_list_ln_offer_blinded_path(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + var len_ = sse_decode_i_32(deserializer); + var ans_ = []; + for (var idx_ = 0; idx_ < len_; ++idx_) { + ans_.add(sse_decode_ln_offer_blinded_path(deserializer)); + } + return ans_; + } + @protected List sse_decode_list_locale_overrides(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -3923,6 +3952,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_absoluteExpiry = sse_decode_opt_box_autoadd_u_64(deserializer); var var_issuer = sse_decode_opt_String(deserializer); var var_signingPubkey = sse_decode_opt_String(deserializer); + var var_paths = sse_decode_list_ln_offer_blinded_path(deserializer); return LNOffer( offer: var_offer, chains: var_chains, @@ -3930,7 +3960,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { description: var_description, absoluteExpiry: var_absoluteExpiry, issuer: var_issuer, - signingPubkey: var_signingPubkey); + signingPubkey: var_signingPubkey, + paths: var_paths); + } + + @protected + LNOfferBlindedPath sse_decode_ln_offer_blinded_path(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + var var_blindedHops = sse_decode_list_String(deserializer); + return LNOfferBlindedPath(blindedHops: var_blindedHops); } @protected @@ -5693,6 +5731,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } + @protected + void sse_encode_list_ln_offer_blinded_path(List self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_i_32(self.length, serializer); + for (final item in self) { + sse_encode_ln_offer_blinded_path(item, serializer); + } + } + @protected void sse_encode_list_locale_overrides(List self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -5825,6 +5872,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_64(self.absoluteExpiry, serializer); sse_encode_opt_String(self.issuer, serializer); sse_encode_opt_String(self.signingPubkey, serializer); + sse_encode_list_ln_offer_blinded_path(self.paths, serializer); + } + + @protected + void sse_encode_ln_offer_blinded_path(LNOfferBlindedPath self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_list_String(self.blindedHops, serializer); } @protected diff --git a/packages/dart/lib/src/frb_generated.io.dart b/packages/dart/lib/src/frb_generated.io.dart index 37bfcd8e5..a2d30beec 100644 --- a/packages/dart/lib/src/frb_generated.io.dart +++ b/packages/dart/lib/src/frb_generated.io.dart @@ -281,6 +281,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected List dco_decode_list_fiat_currency(dynamic raw); + @protected + List dco_decode_list_ln_offer_blinded_path(dynamic raw); + @protected List dco_decode_list_locale_overrides(dynamic raw); @@ -320,6 +323,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected LNOffer dco_decode_ln_offer(dynamic raw); + @protected + LNOfferBlindedPath dco_decode_ln_offer_blinded_path(dynamic raw); + @protected LnUrlAuthError dco_decode_ln_url_auth_error(dynamic raw); @@ -822,6 +828,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected List sse_decode_list_fiat_currency(SseDeserializer deserializer); + @protected + List sse_decode_list_ln_offer_blinded_path(SseDeserializer deserializer); + @protected List sse_decode_list_locale_overrides(SseDeserializer deserializer); @@ -861,6 +870,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected LNOffer sse_decode_ln_offer(SseDeserializer deserializer); + @protected + LNOfferBlindedPath sse_decode_ln_offer_blinded_path(SseDeserializer deserializer); + @protected LnUrlAuthError sse_decode_ln_url_auth_error(SseDeserializer deserializer); @@ -1579,6 +1591,17 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { return ans; } + @protected + ffi.Pointer cst_encode_list_ln_offer_blinded_path( + List raw) { + // Codec=Cst (C-struct based), see doc to use other codecs + final ans = wire.cst_new_list_ln_offer_blinded_path(raw.length); + for (var i = 0; i < raw.length; ++i) { + cst_api_fill_to_wire_ln_offer_blinded_path(raw[i], ans.ref.ptr[i]); + } + return ans; + } + @protected ffi.Pointer cst_encode_list_locale_overrides(List raw) { // Codec=Cst (C-struct based), see doc to use other codecs @@ -2314,6 +2337,13 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { wireObj.absolute_expiry = cst_encode_opt_box_autoadd_u_64(apiObj.absoluteExpiry); wireObj.issuer = cst_encode_opt_String(apiObj.issuer); wireObj.signing_pubkey = cst_encode_opt_String(apiObj.signingPubkey); + wireObj.paths = cst_encode_list_ln_offer_blinded_path(apiObj.paths); + } + + @protected + void cst_api_fill_to_wire_ln_offer_blinded_path( + LNOfferBlindedPath apiObj, wire_cst_ln_offer_blinded_path wireObj) { + wireObj.blinded_hops = cst_encode_list_String(apiObj.blindedHops); } @protected @@ -3455,6 +3485,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_list_fiat_currency(List self, SseSerializer serializer); + @protected + void sse_encode_list_ln_offer_blinded_path(List self, SseSerializer serializer); + @protected void sse_encode_list_locale_overrides(List self, SseSerializer serializer); @@ -3494,6 +3527,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_ln_offer(LNOffer self, SseSerializer serializer); + @protected + void sse_encode_ln_offer_blinded_path(LNOfferBlindedPath self, SseSerializer serializer); + @protected void sse_encode_ln_url_auth_error(LnUrlAuthError self, SseSerializer serializer); @@ -5075,6 +5111,20 @@ class RustLibWire implements BaseWire { late final _cst_new_list_fiat_currency = _cst_new_list_fiat_currencyPtr.asFunction Function(int)>(); + ffi.Pointer cst_new_list_ln_offer_blinded_path( + int len, + ) { + return _cst_new_list_ln_offer_blinded_path( + len, + ); + } + + late final _cst_new_list_ln_offer_blinded_pathPtr = + _lookup Function(ffi.Int32)>>( + 'frbgen_breez_liquid_cst_new_list_ln_offer_blinded_path'); + late final _cst_new_list_ln_offer_blinded_path = _cst_new_list_ln_offer_blinded_pathPtr + .asFunction Function(int)>(); + ffi.Pointer cst_new_list_locale_overrides( int len, ) { @@ -5443,6 +5493,17 @@ final class wire_cst_amount extends ffi.Struct { external AmountKind kind; } +final class wire_cst_ln_offer_blinded_path extends ffi.Struct { + external ffi.Pointer blinded_hops; +} + +final class wire_cst_list_ln_offer_blinded_path extends ffi.Struct { + external ffi.Pointer ptr; + + @ffi.Int32() + external int len; +} + final class wire_cst_ln_offer extends ffi.Struct { external ffi.Pointer offer; @@ -5457,6 +5518,8 @@ final class wire_cst_ln_offer extends ffi.Struct { external ffi.Pointer issuer; external ffi.Pointer signing_pubkey; + + external ffi.Pointer paths; } final class wire_cst_SendDestination_Bolt12 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 4be029222..0f455f5b5 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 @@ -570,6 +570,7 @@ fun asLnOffer(lnOffer: ReadableMap): LnOffer? { arrayOf( "offer", "chains", + "paths", ), ) ) { @@ -577,18 +578,20 @@ fun asLnOffer(lnOffer: ReadableMap): LnOffer? { } val offer = lnOffer.getString("offer")!! val chains = lnOffer.getArray("chains")?.let { asStringList(it) }!! + val paths = lnOffer.getArray("paths")?.let { asLnOfferBlindedPathList(it) }!! val description = if (hasNonNullKey(lnOffer, "description")) lnOffer.getString("description") else null val signingPubkey = if (hasNonNullKey(lnOffer, "signingPubkey")) lnOffer.getString("signingPubkey") else null val minAmount = if (hasNonNullKey(lnOffer, "minAmount")) lnOffer.getMap("minAmount")?.let { asAmount(it) } else null val absoluteExpiry = if (hasNonNullKey(lnOffer, "absoluteExpiry")) lnOffer.getDouble("absoluteExpiry").toULong() else null val issuer = if (hasNonNullKey(lnOffer, "issuer")) lnOffer.getString("issuer") else null - return LnOffer(offer, chains, description, signingPubkey, minAmount, absoluteExpiry, issuer) + return LnOffer(offer, chains, paths, description, signingPubkey, minAmount, absoluteExpiry, issuer) } fun readableMapOf(lnOffer: LnOffer): ReadableMap = readableMapOf( "offer" to lnOffer.offer, "chains" to readableArrayOf(lnOffer.chains), + "paths" to readableArrayOf(lnOffer.paths), "description" to lnOffer.description, "signingPubkey" to lnOffer.signingPubkey, "minAmount" to lnOffer.minAmount?.let { readableMapOf(it) }, @@ -778,6 +781,36 @@ fun asListPaymentsRequestList(arr: ReadableArray): List { return list } +fun asLnOfferBlindedPath(lnOfferBlindedPath: ReadableMap): LnOfferBlindedPath? { + if (!validateMandatoryFields( + lnOfferBlindedPath, + arrayOf( + "blindedHops", + ), + ) + ) { + return null + } + val blindedHops = lnOfferBlindedPath.getArray("blindedHops")?.let { asStringList(it) }!! + return LnOfferBlindedPath(blindedHops) +} + +fun readableMapOf(lnOfferBlindedPath: LnOfferBlindedPath): ReadableMap = + readableMapOf( + "blindedHops" to readableArrayOf(lnOfferBlindedPath.blindedHops), + ) + +fun asLnOfferBlindedPathList(arr: ReadableArray): List { + val list = ArrayList() + for (value in arr.toList()) { + when (value) { + is ReadableMap -> list.add(asLnOfferBlindedPath(value)!!) + else -> throw SdkException.Generic(errUnexpectedType(value)) + } + } + return list +} + fun asLnUrlAuthRequestData(lnUrlAuthRequestData: ReadableMap): LnUrlAuthRequestData? { if (!validateMandatoryFields( lnUrlAuthRequestData, @@ -3219,6 +3252,7 @@ fun pushToArray( when (value) { null -> array.pushNull() is FiatCurrency -> array.pushMap(readableMapOf(value)) + is LnOfferBlindedPath -> array.pushMap(readableMapOf(value)) is LocaleOverrides -> array.pushMap(readableMapOf(value)) is LocalizedName -> array.pushMap(readableMapOf(value)) is Payment -> array.pushMap(readableMapOf(value)) diff --git a/packages/react-native/ios/BreezSDKLiquidMapper.swift b/packages/react-native/ios/BreezSDKLiquidMapper.swift index ec212d5f8..9c01b8791 100644 --- a/packages/react-native/ios/BreezSDKLiquidMapper.swift +++ b/packages/react-native/ios/BreezSDKLiquidMapper.swift @@ -672,6 +672,11 @@ enum BreezSDKLiquidMapper { guard let chains = lnOffer["chains"] as? [String] else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "chains", typeName: "LnOffer")) } + guard let pathsTmp = lnOffer["paths"] as? [[String: Any?]] else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "paths", typeName: "LnOffer")) + } + let paths = try asLnOfferBlindedPathList(arr: pathsTmp) + var description: String? if hasNonNilKey(data: lnOffer, key: "description") { guard let descriptionTmp = lnOffer["description"] as? String else { @@ -706,13 +711,14 @@ enum BreezSDKLiquidMapper { issuer = issuerTmp } - return LnOffer(offer: offer, chains: chains, description: description, signingPubkey: signingPubkey, minAmount: minAmount, absoluteExpiry: absoluteExpiry, issuer: issuer) + return LnOffer(offer: offer, chains: chains, paths: paths, description: description, signingPubkey: signingPubkey, minAmount: minAmount, absoluteExpiry: absoluteExpiry, issuer: issuer) } static func dictionaryOf(lnOffer: LnOffer) -> [String: Any?] { return [ "offer": lnOffer.offer, "chains": lnOffer.chains, + "paths": arrayOf(lnOfferBlindedPathList: lnOffer.paths), "description": lnOffer.description == nil ? nil : lnOffer.description, "signingPubkey": lnOffer.signingPubkey == nil ? nil : lnOffer.signingPubkey, "minAmount": lnOffer.minAmount == nil ? nil : dictionaryOf(amount: lnOffer.minAmount!), @@ -954,6 +960,37 @@ enum BreezSDKLiquidMapper { return listPaymentsRequestList.map { v -> [String: Any?] in return dictionaryOf(listPaymentsRequest: v) } } + static func asLnOfferBlindedPath(lnOfferBlindedPath: [String: Any?]) throws -> LnOfferBlindedPath { + guard let blindedHops = lnOfferBlindedPath["blindedHops"] as? [String] else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "blindedHops", typeName: "LnOfferBlindedPath")) + } + + return LnOfferBlindedPath(blindedHops: blindedHops) + } + + static func dictionaryOf(lnOfferBlindedPath: LnOfferBlindedPath) -> [String: Any?] { + return [ + "blindedHops": lnOfferBlindedPath.blindedHops, + ] + } + + static func asLnOfferBlindedPathList(arr: [Any]) throws -> [LnOfferBlindedPath] { + var list = [LnOfferBlindedPath]() + for value in arr { + if let val = value as? [String: Any?] { + var lnOfferBlindedPath = try asLnOfferBlindedPath(lnOfferBlindedPath: val) + list.append(lnOfferBlindedPath) + } else { + throw SdkError.Generic(message: errUnexpectedType(typeName: "LnOfferBlindedPath")) + } + } + return list + } + + static func arrayOf(lnOfferBlindedPathList: [LnOfferBlindedPath]) -> [Any] { + return lnOfferBlindedPathList.map { v -> [String: Any?] in return dictionaryOf(lnOfferBlindedPath: v) } + } + static func asLnUrlAuthRequestData(lnUrlAuthRequestData: [String: Any?]) throws -> LnUrlAuthRequestData { guard let k1 = lnUrlAuthRequestData["k1"] as? String else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "k1", typeName: "LnUrlAuthRequestData")) diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index 94d268a0e..a0f126379 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -119,6 +119,7 @@ export interface LnInvoice { export interface LnOffer { offer: string chains: string[] + paths: LnOfferBlindedPath[] description?: string signingPubkey?: string minAmount?: Amount @@ -155,6 +156,10 @@ export interface ListPaymentsRequest { details?: ListPaymentDetails } +export interface LnOfferBlindedPath { + blindedHops: string[] +} + export interface LnUrlAuthRequestData { k1: string domain: string