diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 4d0b8f500..9b2448d61 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/SatoshiPortal/boltz-rust?branch=trunk#19e01071f5722c21f80ae9b34edef5c3972e9e0b" 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]] @@ -2931,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?rev=5955216ec4ed003972b4473a77207dfb744da882#5955216ec4ed003972b4473a77207dfb744da882" dependencies = [ "aes 0.8.4", "anyhow", @@ -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 766feb5dd..78cf7422a 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) #[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, @@ -290,12 +294,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:?}") })?)) } _ => {} @@ -312,25 +316,30 @@ pub(crate) async fn handle_command( } Command::SendPayment { invoice, + offer, address, amount_sat, drain, delay, } => { - let destination = match (invoice, address) { - (None, None) => { - return Err(anyhow::anyhow!( - "Must specify either an invoice or a direct/BIP21 address." + 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 an amount for a BOLT12 offer." )) - } - (Some(invoice), None) => invoice, - (None, Some(address)) => address, - (Some(_), Some(_)) => { - return Err(anyhow::anyhow!( + }, + (None, None, Some(address)) => Ok(address), + (Some(_), _, Some(_)) => { + Err(anyhow::anyhow!( "Cannot specify both invoice and address at the same time." )) } - }; + _ => Err(anyhow!( + "Must specify either a BOLT11 invoice, a BOLT12 offer or a direct/BIP21 address." + )) + }?; let amount = match (amount_sat, drain.unwrap_or(false)) { (Some(amount_sat), _) => Some(PayAmount::Receiver { amount_sat }), (_, true) => Some(PayAmount::Drain), @@ -492,7 +501,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.")); } } } @@ -595,7 +604,7 @@ pub(crate) async fn handle_command( .await?; Ok(pay_res) } - _ => Err(anyhow::anyhow!("Invalid input")), + _ => Err(anyhow!("Invalid input")), }?; command_result!(res) @@ -619,7 +628,7 @@ pub(crate) async fn handle_command( .await?; Ok(withdraw_res) } - _ => Err(anyhow::anyhow!("Invalid input")), + _ => Err(anyhow!("Invalid input")), }?; command_result!(res) @@ -632,7 +641,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..c4aaf038d 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/SatoshiPortal/boltz-rust?branch=trunk#19e01071f5722c21f80ae9b34edef5c3972e9e0b" 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]] @@ -3179,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?rev=5955216ec4ed003972b4473a77207dfb744da882#5955216ec4ed003972b4473a77207dfb744da882" dependencies = [ "aes 0.8.4", "anyhow", @@ -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 a940394ab..e17d67b3b 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,59 @@ 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_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; + 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; + 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 { + struct wire_cst_ln_offer *offer; + uint64_t receiver_amount_sat; +} 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 { @@ -357,6 +407,7 @@ typedef struct wire_cst_PaymentDetails_Lightning { struct wire_cst_list_prim_u_8_strict *description; struct wire_cst_list_prim_u_8_strict *preimage; struct wire_cst_list_prim_u_8_strict *bolt11; + struct wire_cst_list_prim_u_8_strict *bolt12_offer; struct wire_cst_list_prim_u_8_strict *payment_hash; struct wire_cst_list_prim_u_8_strict *refund_tx_id; uint64_t *refund_tx_amount_sat; @@ -622,6 +673,10 @@ typedef struct wire_cst_InputType_Bolt11 { struct wire_cst_ln_invoice *invoice; } wire_cst_InputType_Bolt11; +typedef struct wire_cst_InputType_Bolt12Offer { + struct wire_cst_ln_offer *offer; +} wire_cst_InputType_Bolt12Offer; + typedef struct wire_cst_InputType_NodeId { struct wire_cst_list_prim_u_8_strict *node_id; } wire_cst_InputType_NodeId; @@ -650,6 +705,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_Bolt12Offer Bolt12Offer; struct wire_cst_InputType_NodeId NodeId; struct wire_cst_InputType_Url Url; struct wire_cst_InputType_LnUrlPay LnUrlPay; @@ -1121,6 +1177,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); @@ -1147,6 +1205,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); @@ -1209,8 +1269,12 @@ 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_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); @@ -1233,6 +1297,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); @@ -1246,6 +1311,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); @@ -1277,7 +1343,9 @@ 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_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/langs/swift/Sources/BreezSDKLiquid/Task/SwapUpdated.swift b/lib/bindings/langs/swift/Sources/BreezSDKLiquid/Task/SwapUpdated.swift index 5ec6c7ead..49ea38709 100644 --- a/lib/bindings/langs/swift/Sources/BreezSDKLiquid/Task/SwapUpdated.swift +++ b/lib/bindings/langs/swift/Sources/BreezSDKLiquid/Task/SwapUpdated.swift @@ -54,7 +54,7 @@ class SwapUpdatedTask : TaskProtocol { switch details { case let .bitcoin(swapId, _, _, _): return swapId - case let .lightning(swapId, _, _, _, _, _, _): + case let .lightning(swapId, _, _, _, _, _, _, _): return swapId default: break diff --git a/lib/bindings/src/breez_sdk_liquid.udl b/lib/bindings/src/breez_sdk_liquid.udl index 59dd17649..9513c60a6 100644 --- a/lib/bindings/src/breez_sdk_liquid.udl +++ b/lib/bindings/src/breez_sdk_liquid.udl @@ -38,11 +38,33 @@ dictionary RouteHintHop { u64? htlc_maximum_msat; }; +[Enum] +interface Amount { + Bitcoin(u64 amount_msat); + 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; + u64? absolute_expiry; + string? issuer; +}; + [Enum] interface InputType { BitcoinAddress(BitcoinAddressData address); LiquidAddress(LiquidAddressData address); Bolt11(LNInvoice invoice); + Bolt12Offer(LNOffer offer); NodeId(string node_id); Url(string url); LnUrlPay(LnUrlPayRequestData data); @@ -381,6 +403,7 @@ dictionary PrepareSendRequest { interface SendDestination { LiquidAddress(LiquidAddressData address_data); Bolt11(LNInvoice invoice); + Bolt12(LNOffer offer, u64 receiver_amount_sat); }; dictionary PrepareSendResponse { @@ -511,7 +534,7 @@ interface GetPaymentRequest { [Enum] interface PaymentDetails { - Lightning(string swap_id, string description, string? preimage, string? bolt11, string? payment_hash, string? refund_tx_id, u64? refund_tx_amount_sat); + Lightning(string swap_id, string description, string? preimage, string? bolt11, string? bolt12_offer, string? payment_hash, string? refund_tx_id, u64? refund_tx_amount_sat); Liquid(string destination, string description); Bitcoin(string swap_id, string description, string? refund_tx_id, u64? refund_tx_amount_sat); }; diff --git a/lib/core/Cargo.toml b/lib/core/Cargo.toml index 9a9fb5add..7f0e20d9f 100644 --- a/lib/core/Cargo.toml +++ b/lib/core/Cargo.toml @@ -20,6 +20,8 @@ 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" @@ -27,7 +29,7 @@ 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 = "5955216ec4ed003972b4473a77207dfb744da882", 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..f57801826 100644 --- a/lib/core/src/bindings.rs +++ b/lib/core/src/bindings.rs @@ -329,11 +329,40 @@ 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(LnOfferBlindedPath))] +pub struct _LnOfferBlindedPath { + pub blinded_hops: Vec, +} + +#[frb(mirror(LNOffer))] +pub struct _LNOffer { + pub offer: String, + pub chains: Vec, + pub min_amount: Option, + pub description: Option, + pub absolute_expiry: Option, + pub issuer: Option, + pub signing_pubkey: Option, + pub paths: Vec, +} + #[frb(mirror(InputType))] pub enum _InputType { BitcoinAddress { address: BitcoinAddressData }, LiquidAddress { address: LiquidAddressData }, Bolt11 { invoice: LNInvoice }, + Bolt12Offer { offer: LNOffer }, NodeId { node_id: String }, Url { url: String }, LnUrlPay { data: LnUrlPayRequestData }, diff --git a/lib/core/src/error.rs b/lib/core/src/error.rs index 5a0c3bec6..ae5a3cc30 100644 --- a/lib/core/src/error.rs +++ b/lib/core/src/error.rs @@ -125,6 +125,12 @@ pub enum PaymentError { SignerError { err: String }, } impl PaymentError { + pub(crate) fn generic(err: &str) -> Self { + Self::Generic { + err: err.to_string(), + } + } + pub(crate) fn invalid_invoice(err: &str) -> Self { Self::InvalidInvoice { err: err.to_string(), @@ -136,6 +142,12 @@ impl PaymentError { err: err.to_string(), } } + + pub(crate) fn amount_missing(err: &str) -> Self { + Self::AmountMissing { + err: err.to_string(), + } + } } impl From for PaymentError { diff --git a/lib/core/src/frb_generated.rs b/lib/core/src/frb_generated.rs index e1c5b83e0..6084b6853 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; @@ -1798,6 +1810,9 @@ const _: fn() = || { crate::bindings::InputType::Bolt11 { invoice } => { let _: crate::bindings::LNInvoice = invoice; } + crate::bindings::InputType::Bolt12Offer { offer } => { + let _: crate::bindings::LNOffer = offer; + } crate::bindings::InputType::NodeId { node_id } => { let _: String = node_id; } @@ -1841,6 +1856,21 @@ const _: fn() = || { let _: Vec = LNInvoice.payment_secret; let _: u64 = LNInvoice.min_final_cltv_expiry_delta; } + { + let LNOffer = None::.unwrap(); + let _: String = LNOffer.offer; + let _: Vec = LNOffer.chains; + let _: Option = LNOffer.min_amount; + let _: Option = LNOffer.description; + 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(); let _: String = LnUrlAuthRequestData.k1; @@ -2182,6 +2212,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 { @@ -2434,30 +2490,34 @@ impl SseDecode for crate::bindings::InputType { }; } 3 => { + let mut var_offer = ::sse_decode(deserializer); + return crate::bindings::InputType::Bolt12Offer { 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 }; } @@ -2526,6 +2586,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 { @@ -2538,6 +2610,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 { @@ -2722,6 +2808,40 @@ 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_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); + 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, + min_amount: var_minAmount, + description: var_description, + 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, + }; + } +} + 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 { @@ -3149,6 +3269,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 { @@ -3340,6 +3471,7 @@ impl SseDecode for crate::model::PaymentDetails { let mut var_description = ::sse_decode(deserializer); let mut var_preimage = >::sse_decode(deserializer); let mut var_bolt11 = >::sse_decode(deserializer); + let mut var_bolt12Offer = >::sse_decode(deserializer); let mut var_paymentHash = >::sse_decode(deserializer); let mut var_refundTxId = >::sse_decode(deserializer); let mut var_refundTxAmountSat = >::sse_decode(deserializer); @@ -3348,6 +3480,7 @@ impl SseDecode for crate::model::PaymentDetails { description: var_description, preimage: var_preimage, bolt11: var_bolt11, + bolt12_offer: var_bolt12Offer, payment_hash: var_paymentHash, refund_tx_id: var_refundTxId, refund_tx_amount_sat: var_refundTxAmountSat, @@ -3900,6 +4033,14 @@ impl SseDecode for crate::model::SendDestination { invoice: var_invoice, }; } + 2 => { + let mut var_offer = ::sse_decode(deserializer); + let mut var_receiverAmountSat = ::sse_decode(deserializer); + return crate::model::SendDestination::Bolt12 { + offer: var_offer, + receiver_amount_sat: var_receiverAmountSat, + }; + } _ => { unimplemented!(""); } @@ -4184,6 +4325,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() @@ -4467,23 +4641,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::Bolt12Offer { 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!(""); @@ -4670,6 +4847,50 @@ 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.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(), + 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() + } +} +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 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 { @@ -5254,6 +5475,7 @@ impl flutter_rust_bridge::IntoDart for crate::model::PaymentDetails { description, preimage, bolt11, + bolt12_offer, payment_hash, refund_tx_id, refund_tx_amount_sat, @@ -5263,6 +5485,7 @@ impl flutter_rust_bridge::IntoDart for crate::model::PaymentDetails { description.into_into_dart().into_dart(), preimage.into_into_dart().into_dart(), bolt11.into_into_dart().into_dart(), + bolt12_offer.into_into_dart().into_dart(), payment_hash.into_into_dart().into_dart(), refund_tx_id.into_into_dart().into_dart(), refund_tx_amount_sat.into_into_dart().into_dart(), @@ -5944,6 +6167,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, + receiver_amount_sat, + } => [ + 2.into_dart(), + offer.into_into_dart().into_dart(), + receiver_amount_sat.into_into_dart().into_dart(), + ] + .into_dart(), _ => { unimplemented!(""); } @@ -6218,6 +6450,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) { @@ -6397,28 +6652,32 @@ impl SseEncode for crate::bindings::InputType { ::sse_encode(2, serializer); ::sse_encode(invoice, serializer); } - crate::bindings::InputType::NodeId { node_id } => { + crate::bindings::InputType::Bolt12Offer { 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); } _ => { @@ -6473,6 +6732,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) { @@ -6483,6 +6752,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) { @@ -6622,6 +6901,27 @@ 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.offer, serializer); + >::sse_encode(self.chains, serializer); + >::sse_encode(self.min_amount, serializer); + >::sse_encode(self.description, serializer); + >::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); + } +} + 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) { @@ -6959,6 +7259,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) { @@ -7118,6 +7428,7 @@ impl SseEncode for crate::model::PaymentDetails { description, preimage, bolt11, + bolt12_offer, payment_hash, refund_tx_id, refund_tx_amount_sat, @@ -7127,6 +7438,7 @@ impl SseEncode for crate::model::PaymentDetails { ::sse_encode(description, serializer); >::sse_encode(preimage, serializer); >::sse_encode(bolt11, serializer); + >::sse_encode(bolt12_offer, serializer); >::sse_encode(payment_hash, serializer); >::sse_encode(refund_tx_id, serializer); >::sse_encode(refund_tx_amount_sat, serializer); @@ -7563,6 +7875,14 @@ impl SseEncode for crate::model::SendDestination { ::sse_encode(1, serializer); ::sse_encode(invoice, serializer); } + crate::model::SendDestination::Bolt12 { + offer, + receiver_amount_sat, + } => { + ::sse_encode(2, serializer); + ::sse_encode(offer, serializer); + ::sse_encode(receiver_amount_sat, serializer); + } _ => { unimplemented!(""); } @@ -7837,6 +8157,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 { @@ -7890,6 +8231,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 { @@ -7979,6 +8327,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 { @@ -8330,36 +8685,42 @@ mod io { } } 3 => { + let ans = unsafe { self.kind.Bolt12Offer }; + crate::bindings::InputType::Bolt12Offer { + 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(), @@ -8403,6 +8764,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 { @@ -8413,6 +8784,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 { @@ -8554,6 +8937,29 @@ 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 { + offer: self.offer.cst_decode(), + chains: self.chains.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(), + 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(), + } + } + } 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 { @@ -8962,6 +9368,7 @@ mod io { description: ans.description.cst_decode(), preimage: ans.preimage.cst_decode(), bolt11: ans.bolt11.cst_decode(), + bolt12_offer: ans.bolt12_offer.cst_decode(), payment_hash: ans.payment_hash.cst_decode(), refund_tx_id: ans.refund_tx_id.cst_decode(), refund_tx_amount_sat: ans.refund_tx_amount_sat.cst_decode(), @@ -9363,6 +9770,13 @@ mod io { invoice: ans.invoice.cst_decode(), } } + 2 => { + 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!(), } } @@ -9512,6 +9926,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 { @@ -9795,6 +10222,37 @@ mod io { Self::new_with_null_ptr() } } + impl NewWithNullPtr for wire_cst_ln_offer { + fn new_with_null_ptr() -> Self { + Self { + offer: core::ptr::null_mut(), + chains: 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(), + signing_pubkey: core::ptr::null_mut(), + paths: 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_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 { @@ -10959,6 +11417,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 { @@ -11057,6 +11520,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 { @@ -11291,6 +11759,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, @@ -11305,6 +11787,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, @@ -11461,6 +11957,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, } @@ -11571,6 +12091,7 @@ mod io { BitcoinAddress: wire_cst_InputType_BitcoinAddress, LiquidAddress: wire_cst_InputType_LiquidAddress, Bolt11: wire_cst_InputType_Bolt11, + Bolt12Offer: wire_cst_InputType_Bolt12Offer, NodeId: wire_cst_InputType_NodeId, Url: wire_cst_InputType_Url, LnUrlPay: wire_cst_InputType_LnUrlPay, @@ -11596,6 +12117,11 @@ mod io { } #[repr(C)] #[derive(Clone, Copy)] + pub struct wire_cst_InputType_Bolt12Offer { + offer: *mut wire_cst_ln_offer, + } + #[repr(C)] + #[derive(Clone, Copy)] pub struct wire_cst_InputType_NodeId { node_id: *mut wire_cst_list_prim_u_8_strict, } @@ -11649,12 +12175,24 @@ 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, } #[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, @@ -11758,6 +12296,23 @@ mod io { } #[repr(C)] #[derive(Clone, Copy)] + pub struct wire_cst_ln_offer { + 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, + 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)] pub struct wire_cst_ln_url_auth_error { tag: i32, kind: LnUrlAuthErrorKind, @@ -12134,6 +12689,7 @@ mod io { description: *mut wire_cst_list_prim_u_8_strict, preimage: *mut wire_cst_list_prim_u_8_strict, bolt11: *mut wire_cst_list_prim_u_8_strict, + bolt12_offer: *mut wire_cst_list_prim_u_8_strict, payment_hash: *mut wire_cst_list_prim_u_8_strict, refund_tx_id: *mut wire_cst_list_prim_u_8_strict, refund_tx_amount_sat: *mut u64, @@ -12452,6 +13008,7 @@ mod io { pub union SendDestinationKind { LiquidAddress: wire_cst_SendDestination_LiquidAddress, Bolt11: wire_cst_SendDestination_Bolt11, + Bolt12: wire_cst_SendDestination_Bolt12, nil__: (), } #[repr(C)] @@ -12466,6 +13023,12 @@ mod io { } #[repr(C)] #[derive(Clone, Copy)] + pub struct wire_cst_SendDestination_Bolt12 { + offer: *mut wire_cst_ln_offer, + receiver_amount_sat: u64, + } + #[repr(C)] + #[derive(Clone, Copy)] pub struct wire_cst_send_payment_request { prepare_response: wire_cst_prepare_send_response, } diff --git a/lib/core/src/model.rs b/lib/core/src/model.rs index 01d832356..90353d530 100644 --- a/lib/core/src/model.rs +++ b/lib/core/src/model.rs @@ -353,7 +353,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 @@ -370,6 +370,10 @@ pub enum SendDestination { Bolt11 { invoice: LNInvoice, }, + Bolt12 { + offer: LNOffer, + receiver_amount_sat: u64, + }, } /// Returned when calling [crate::sdk::LiquidSdk::prepare_send_payment]. @@ -747,7 +751,10 @@ impl ChainSwap { #[derive(Clone, Debug)] pub(crate) struct SendSwap { pub(crate) id: String, + /// Bolt11 or Bolt12 invoice. This is determined by whether `bolt12_offer` is set or not. pub(crate) invoice: String, + /// The bolt12 offer, if this swap sends to a Bolt12 offer + pub(crate) bolt12_offer: Option, pub(crate) payment_hash: Option, pub(crate) description: Option, pub(crate) preimage: Option, @@ -1119,6 +1126,7 @@ pub struct PaymentSwapData { pub preimage: Option, pub bolt11: Option, + pub bolt12_offer: Option, pub payment_hash: Option, pub description: String, @@ -1153,11 +1161,13 @@ pub enum PaymentDetails { /// In case of a Send swap, this is the preimage of the paid invoice (proof of payment). preimage: Option, - /// Represents the invoice associated with a payment + /// Represents the Bolt11 invoice associated with a payment /// In the case of a Send payment, this is the invoice paid by the swapper /// In the case of a Receive payment, this is the invoice paid by the user bolt11: Option, + bolt12_offer: Option, + /// The payment hash of the invoice payment_hash: Option, @@ -1286,6 +1296,7 @@ impl Payment { swap_id: swap.swap_id, preimage: swap.preimage, bolt11: swap.bolt11, + bolt12_offer: swap.bolt12_offer, payment_hash: swap.payment_hash, description: swap.description, refund_tx_id: swap.refund_tx_id, @@ -1305,18 +1316,17 @@ impl Payment { // If it's a chain swap instead, we use the `claim_address` field from the swap data (either pure Bitcoin or Liquid address). // Otherwise, we specify the Liquid address (BIP21 or pure), set in `payment_details.address`. destination: match &swap { - Some( - PaymentSwapData { - swap_type: PaymentSwapType::Receive, - bolt11, - .. - } - | PaymentSwapData { - swap_type: PaymentSwapType::Send, - bolt11, - .. - }, - ) => bolt11.clone(), + Some(PaymentSwapData { + swap_type: PaymentSwapType::Receive, + bolt11, + .. + }) => bolt11.clone(), + Some(PaymentSwapData { + swap_type: PaymentSwapType::Send, + bolt11, + bolt12_offer, + .. + }) => bolt11.clone().or(bolt12_offer.clone()), Some(PaymentSwapData { swap_type: PaymentSwapType::Chain, claim_address, 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/persist/migrations.rs b/lib/core/src/persist/migrations.rs index 279d987f4..7bc55857e 100644 --- a/lib/core/src/persist/migrations.rs +++ b/lib/core/src/persist/migrations.rs @@ -183,5 +183,6 @@ pub(crate) fn current_migrations() -> Vec<&'static str> { DROP TABLE old_chain_swaps; ", + "ALTER TABLE send_swaps ADD COLUMN bolt12_offer TEXT;", ] } diff --git a/lib/core/src/persist/mod.rs b/lib/core/src/persist/mod.rs index 3a68af63f..7ca35a6d6 100644 --- a/lib/core/src/persist/mod.rs +++ b/lib/core/src/persist/mod.rs @@ -180,6 +180,7 @@ impl Persister { ss.id, ss.created_at, ss.invoice, + ss.bolt12_offer, ss.payment_hash, ss.description, ss.preimage, @@ -256,29 +257,30 @@ impl Persister { let maybe_send_swap_id: Option = row.get(14)?; let maybe_send_swap_created_at: Option = row.get(15)?; let maybe_send_swap_invoice: Option = row.get(16)?; - let maybe_send_swap_payment_hash: Option = row.get(17)?; - let maybe_send_swap_description: Option = row.get(18)?; - let maybe_send_swap_preimage: Option = row.get(19)?; - let maybe_send_swap_refund_tx_id: Option = row.get(20)?; - let maybe_send_swap_payer_amount_sat: Option = row.get(21)?; - let maybe_send_swap_receiver_amount_sat: Option = row.get(22)?; - let maybe_send_swap_state: Option = row.get(23)?; - - let maybe_chain_swap_id: Option = row.get(24)?; - let maybe_chain_swap_created_at: Option = row.get(25)?; - let maybe_chain_swap_direction: Option = row.get(26)?; - let maybe_chain_swap_preimage: Option = row.get(27)?; - let maybe_chain_swap_description: Option = row.get(28)?; - let maybe_chain_swap_refund_tx_id: Option = row.get(29)?; - let maybe_chain_swap_payer_amount_sat: Option = row.get(30)?; - let maybe_chain_swap_receiver_amount_sat: Option = row.get(31)?; - let maybe_chain_swap_claim_address: Option = row.get(32)?; - let maybe_chain_swap_state: Option = row.get(33)?; - - let maybe_swap_refund_tx_amount_sat: Option = row.get(34)?; - - let maybe_payment_details_destination: Option = row.get(35)?; - let maybe_payment_details_description: Option = row.get(36)?; + let maybe_send_swap_bolt12_offer: Option = row.get(17)?; + let maybe_send_swap_payment_hash: Option = row.get(18)?; + let maybe_send_swap_description: Option = row.get(19)?; + let maybe_send_swap_preimage: Option = row.get(20)?; + let maybe_send_swap_refund_tx_id: Option = row.get(21)?; + let maybe_send_swap_payer_amount_sat: Option = row.get(22)?; + let maybe_send_swap_receiver_amount_sat: Option = row.get(23)?; + let maybe_send_swap_state: Option = row.get(24)?; + + let maybe_chain_swap_id: Option = row.get(25)?; + let maybe_chain_swap_created_at: Option = row.get(26)?; + let maybe_chain_swap_direction: Option = row.get(27)?; + let maybe_chain_swap_preimage: Option = row.get(28)?; + let maybe_chain_swap_description: Option = row.get(29)?; + let maybe_chain_swap_refund_tx_id: Option = row.get(30)?; + let maybe_chain_swap_payer_amount_sat: Option = row.get(31)?; + let maybe_chain_swap_receiver_amount_sat: Option = row.get(32)?; + let maybe_chain_swap_claim_address: Option = row.get(33)?; + let maybe_chain_swap_state: Option = row.get(34)?; + + let maybe_swap_refund_tx_amount_sat: Option = row.get(35)?; + + let maybe_payment_details_destination: Option = row.get(36)?; + let maybe_payment_details_description: Option = row.get(37)?; let (swap, payment_type) = match maybe_receive_swap_id { Some(receive_swap_id) => ( @@ -288,6 +290,7 @@ impl Persister { created_at: maybe_receive_swap_created_at.unwrap_or(utils::now()), preimage: None, bolt11: maybe_receive_swap_invoice.clone(), + bolt12_offer: None, // Bolt12 not supported for Receive Swaps payment_hash: maybe_receive_swap_payment_hash, description: maybe_receive_swap_description.unwrap_or_else(|| { maybe_receive_swap_invoice @@ -310,13 +313,14 @@ impl Persister { swap_type: PaymentSwapType::Send, created_at: maybe_send_swap_created_at.unwrap_or(utils::now()), preimage: maybe_send_swap_preimage, - bolt11: maybe_send_swap_invoice.clone(), + bolt11: match maybe_send_swap_bolt12_offer.is_some() { + true => None, // We don't expose the Bolt12 invoice + false => maybe_send_swap_invoice, + }, + bolt12_offer: maybe_send_swap_bolt12_offer, 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: 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, @@ -334,6 +338,7 @@ impl Persister { created_at: maybe_chain_swap_created_at.unwrap_or(utils::now()), preimage: maybe_chain_swap_preimage, bolt11: None, + bolt12_offer: None, // Bolt12 not supported for Chain Swaps payment_hash: None, description: maybe_chain_swap_description .unwrap_or("Bitcoin transfer".to_string()), @@ -360,6 +365,7 @@ impl Persister { swap_type: PaymentSwapType::Receive, swap_id, bolt11, + bolt12_offer, payment_hash, refund_tx_id, preimage, @@ -370,6 +376,7 @@ impl Persister { swap_type: PaymentSwapType::Send, swap_id, bolt11, + bolt12_offer, payment_hash, preimage, refund_tx_id, @@ -380,6 +387,7 @@ impl Persister { swap_id, preimage, bolt11, + bolt12_offer, payment_hash, refund_tx_id, refund_tx_amount_sat, diff --git a/lib/core/src/persist/send.rs b/lib/core/src/persist/send.rs index f956d1714..2cd3f123b 100644 --- a/lib/core/src/persist/send.rs +++ b/lib/core/src/persist/send.rs @@ -21,6 +21,7 @@ impl Persister { id, id_hash, invoice, + bolt12_offer, payment_hash, description, payer_amount_sat, @@ -32,13 +33,14 @@ impl Persister { created_at, state ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", )?; let id_hash = sha256::Hash::hash(send_swap.id.as_bytes()).to_hex(); _ = stmt.execute(( &send_swap.id, &id_hash, &send_swap.invoice, + &send_swap.bolt12_offer, &send_swap.payment_hash, &send_swap.description, &send_swap.payer_amount_sat, @@ -88,6 +90,7 @@ impl Persister { SELECT id, invoice, + bolt12_offer, payment_hash, description, preimage, @@ -126,17 +129,18 @@ impl Persister { Ok(SendSwap { id: row.get(0)?, invoice: row.get(1)?, - payment_hash: row.get(2)?, - description: row.get(3)?, - preimage: row.get(4)?, - payer_amount_sat: row.get(5)?, - receiver_amount_sat: row.get(6)?, - create_response_json: row.get(7)?, - refund_private_key: row.get(8)?, - lockup_tx_id: row.get(9)?, - refund_tx_id: row.get(10)?, - created_at: row.get(11)?, - state: row.get(12)?, + bolt12_offer: row.get(2)?, + payment_hash: row.get(3)?, + description: row.get(4)?, + preimage: row.get(5)?, + payer_amount_sat: row.get(6)?, + receiver_amount_sat: row.get(7)?, + create_response_json: row.get(8)?, + refund_private_key: row.get(9)?, + lockup_tx_id: row.get(10)?, + refund_tx_id: row.get(11)?, + created_at: row.get(12)?, + state: row.get(13)?, }) } diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index 49cec9bff..002cef2c4 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,8 @@ use crate::{ persist::Persister, utils, *, }; +use ::lightning::offers::invoice::Bolt12Invoice; +use ::lightning::offers::offer::Offer; pub const DEFAULT_DATA_DIR: &str = ".data"; /// Number of blocks to monitor a swap after its timeout block height @@ -590,7 +593,7 @@ impl LiquidSdk { } } - fn validate_invoice(&self, invoice: &str) -> Result { + fn validate_bolt11_invoice(&self, invoice: &str) -> Result { let invoice = invoice .trim() .parse::() @@ -614,6 +617,46 @@ impl LiquidSdk { Ok(invoice) } + fn validate_bolt12_invoice( + &self, + offer: &LNOffer, + user_specified_receiver_amount_sat: u64, + invoice: &str, + ) -> Result { + let invoice_parsed = utils::parse_bolt12_invoice(invoice)?; + let invoice_signing_pubkey = invoice_parsed.signing_pubkey().to_hex(); + + // 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") + ); + + Ok(invoice_parsed) + } + /// For submarine swaps (Liquid -> LN), the output amount (invoice amount) is checked if it fits /// the pair limits. This is unlike all the other swap types, where the input amount is checked. fn validate_submarine_pairs( @@ -757,7 +800,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` - The optional amount of type [PayAmount]. Should only be specified /// when paying directly onchain or via amount-less BIP21. /// - [PayAmount::Drain] which uses all funds @@ -778,10 +821,10 @@ impl LiquidSdk { let receiver_amount_sat; let payment_destination; - match sdk_common::input_parser::parse(&req.destination).await? { - InputType::LiquidAddress { + match Self::parse(&req.destination).await { + Ok(InputType::LiquidAddress { address: mut liquid_address_data, - } => { + }) => { let amount = match (liquid_address_data.amount_sat, req.amount.clone()) { (None, None) => { return Err(PaymentError::AmountMissing { @@ -837,13 +880,13 @@ 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)?; + self.validate_bolt11_invoice(&invoice.bolt11)?; - receiver_amount_sat = invoice.amount_msat.ok_or(PaymentError::AmountMissing { - err: "Expected invoice with an amount".to_string(), - })? / 1000; + receiver_amount_sat = invoice.amount_msat.ok_or(PaymentError::amount_missing( + "Expected invoice with an amount", + ))? / 1000; if let Some(PayAmount::Receiver { amount_sat }) = req.amount { ensure_sdk!( @@ -872,10 +915,37 @@ impl LiquidSdk { }; payment_destination = SendDestination::Bolt11 { invoice }; } + Ok(InputType::Bolt12Offer { offer }) => { + receiver_amount_sat = match req.amount { + Some(PayAmount::Receiver { amount_sat }) => Ok(amount_sat), + _ => Err(PaymentError::amount_missing( + "Expected PayAmount of type Receiver when processing a Bolt12 offer", + )), + }?; + if let Some(Amount::Bitcoin { amount_msat }) = &offer.min_amount { + ensure_sdk!( + receiver_amount_sat >= amount_msat / 1_000, + PaymentError::invalid_invoice( + "Invalid receiver amount: below offer minimum" + ) + ); + } + + let lbtc_pair = self.validate_submarine_pairs(receiver_amount_sat)?; + + let boltz_fees_total = lbtc_pair.fees.total(receiver_amount_sat); + let lockup_fees_sat = self + .estimate_lockup_tx_or_drain_tx_fee(receiver_amount_sat + boltz_fees_total) + .await?; + fees_sat = boltz_fees_total + lockup_fees_sat; + + payment_destination = SendDestination::Bolt12 { + offer, + receiver_amount_sat, + }; + } _ => { - return Err(PaymentError::Generic { - err: "Destination is not valid".to_string(), - }); + return Err(PaymentError::generic("Destination is not valid")); } }; @@ -953,18 +1023,28 @@ 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, + receiver_amount_sat, + } => { + let bolt12_invoice = self + .swapper + .get_bolt12_invoice(&offer.offer, *receiver_amount_sat)?; + self.pay_bolt12_invoice(offer, *receiver_amount_sat, &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_bolt11_invoice(invoice)?; let amount_sat = get_invoice_amount!(invoice); let payer_amount_sat = amount_sat + fees_sat; @@ -973,6 +1053,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, _)) => { @@ -993,10 +1078,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, + None, + &bolt11_invoice.payment_hash().to_string(), + description, + amount_sat, + fees_sat, + ) + .await + } } } + async fn pay_bolt12_invoice( + &self, + offer: &LNOffer, + user_specified_receiver_amount_sat: u64, + invoice_str: &str, + fees_sat: u64, + ) -> Result { + let invoice = + self.validate_bolt12_invoice(offer, user_specified_receiver_amount_sat, invoice_str)?; + + let receiver_amount_sat = invoice.amount_msats() / 1_000; + let payer_amount_sat = receiver_amount_sat + fees_sat; + ensure_sdk!( + payer_amount_sat <= self.get_info().await?.balance_sat, + PaymentError::InsufficientFunds + ); + + self.send_payment_via_swap( + invoice_str, + Some(offer.offer.clone()), + &invoice.payment_hash().to_string(), + invoice.description().map(|desc| desc.to_string()), + receiver_amount_sat, + fees_sat, + ) + .await + } + /// Performs a Send Payment by doing an onchain tx to a L-BTC address async fn pay_liquid( &self, @@ -1056,23 +1179,17 @@ impl LiquidSdk { } /// Performs a Send Payment by doing a swap (create it, fund it, track it, etc). + /// + /// If `bolt12_offer` is set, `invoice` refers to a Bolt12 invoice, otherwise it's a Bolt11 one. async fn send_payment_via_swap( &self, invoice: &str, + bolt12_offer: Option, + 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 user_lockup_amount_sat = receiver_amount_sat + boltz_fees_total; @@ -1130,17 +1247,13 @@ 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), + bolt12_offer, + payment_hash: Some(payment_hash.to_string()), description, preimage: None, payer_amount_sat, @@ -2537,9 +2650,63 @@ 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 { + offer: input.to_string(), + chains: offer + .chains() + .iter() + .map(|chain| chain.to_string()) + .collect(), + 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()), + 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::>(), + }, + }); + } + 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/lib/core/src/swapper/boltz/liquid.rs b/lib/core/src/swapper/boltz/liquid.rs index 712288015..3f930bb4a 100644 --- a/lib/core/src/swapper/boltz/liquid.rs +++ b/lib/core/src/swapper/boltz/liquid.rs @@ -34,14 +34,23 @@ impl BoltzSwapper { pub(crate) fn verify_payment_hash(preimage: &str, invoice: &str) -> Result<(), PaymentError> { let preimage = Preimage::from_str(preimage)?; let preimage_hash = preimage.sha256.to_string(); - let invoice = Bolt11Invoice::from_str(invoice).map_err(|err| PaymentError::Generic { - err: format!("Could not parse invoice: {err:?}"), - })?; - let invoice_payment_hash = invoice.payment_hash(); - - (invoice_payment_hash.to_string() == preimage_hash) - .then_some(()) - .ok_or(PaymentError::InvalidPreimage) + + let invoice_payment_hash = match Bolt11Invoice::from_str(invoice) { + Ok(invoice) => Ok(invoice.payment_hash().to_string()), + Err(_) => match crate::utils::parse_bolt12_invoice(invoice) { + Ok(invoice) => Ok(invoice.payment_hash().to_string()), + Err(e) => Err(PaymentError::Generic { + err: format!("Could not parse invoice: {e:?}"), + }), + }, + }?; + + ensure_sdk!( + invoice_payment_hash == preimage_hash, + PaymentError::InvalidPreimage + ); + + Ok(()) } pub(crate) fn new_receive_claim_tx( 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/test_utils/persist.rs b/lib/core/src/test_utils/persist.rs index 4b6fac2e7..ce024bf00 100644 --- a/lib/core/src/test_utils/persist.rs +++ b/lib/core/src/test_utils/persist.rs @@ -42,6 +42,7 @@ pub(crate) fn new_send_swap(payment_state: Option) -> SendSwap { SendSwap { id: generate_random_string(4), invoice: invoice.to_string(), + bolt12_offer: None, payment_hash: Some(payment_hash.to_string()), description: Some("Send to BTC lightning".to_string()), preimage: None, 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!() + } } 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:?}")) +} diff --git a/packages/dart/lib/src/bindings.dart b/packages/dart/lib/src/bindings.dart index c97737fb2..655064b6f 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; @@ -281,6 +294,9 @@ sealed class InputType with _$InputType { const factory InputType.bolt11({ required LNInvoice invoice, }) = InputType_Bolt11; + const factory InputType.bolt12Offer({ + required LNOffer offer, + }) = InputType_Bolt12Offer; const factory InputType.nodeId({ required String nodeId, }) = InputType_NodeId; @@ -403,6 +419,69 @@ class LNInvoice { minFinalCltvExpiryDelta == other.minFinalCltvExpiryDelta; } +class LNOffer { + final String offer; + final List chains; + final Amount? minAmount; + final String? description; + final BigInt? absoluteExpiry; + final String? issuer; + final String? signingPubkey; + final List paths; + + const LNOffer({ + required this.offer, + required this.chains, + this.minAmount, + this.description, + this.absoluteExpiry, + this.issuer, + this.signingPubkey, + required this.paths, + }); + + @override + int get hashCode => + offer.hashCode ^ + chains.hashCode ^ + minAmount.hashCode ^ + description.hashCode ^ + absoluteExpiry.hashCode ^ + issuer.hashCode ^ + signingPubkey.hashCode ^ + paths.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is LNOffer && + runtimeType == other.runtimeType && + offer == other.offer && + chains == other.chains && + minAmount == other.minAmount && + description == other.description && + absoluteExpiry == other.absoluteExpiry && + issuer == other.issuer && + 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 { 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 bb26c07c2..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 {} @@ -462,6 +647,85 @@ abstract class InputType_Bolt11 extends InputType { _$$InputType_Bolt11ImplCopyWith<_$InputType_Bolt11Impl> get copyWith => throw _privateConstructorUsedError; } +/// @nodoc +abstract class _$$InputType_Bolt12OfferImplCopyWith<$Res> { + factory _$$InputType_Bolt12OfferImplCopyWith( + _$InputType_Bolt12OfferImpl value, $Res Function(_$InputType_Bolt12OfferImpl) then) = + __$$InputType_Bolt12OfferImplCopyWithImpl<$Res>; + @useResult + $Res call({LNOffer offer}); +} + +/// @nodoc +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 + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? offer = null, + }) { + return _then(_$InputType_Bolt12OfferImpl( + offer: null == offer + ? _value.offer + : offer // ignore: cast_nullable_to_non_nullable + as LNOffer, + )); + } +} + +/// @nodoc + +class _$InputType_Bolt12OfferImpl extends InputType_Bolt12Offer { + const _$InputType_Bolt12OfferImpl({required this.offer}) : super._(); + + @override + final LNOffer offer; + + @override + String toString() { + return 'InputType.bolt12Offer(offer: $offer)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$InputType_Bolt12OfferImpl && + (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_Bolt12OfferImplCopyWith<_$InputType_Bolt12OfferImpl> get copyWith => + __$$InputType_Bolt12OfferImplCopyWithImpl<_$InputType_Bolt12OfferImpl>(this, _$identity); +} + +abstract class InputType_Bolt12Offer extends InputType { + const factory InputType_Bolt12Offer({required final LNOffer offer}) = _$InputType_Bolt12OfferImpl; + const InputType_Bolt12Offer._() : super._(); + + LNOffer get offer; + + /// Create a copy of InputType + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$InputType_Bolt12OfferImplCopyWith<_$InputType_Bolt12OfferImpl> 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 fb347ce1f..d7fd63a98 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,26 +1797,30 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { invoice: dco_decode_box_autoadd_ln_invoice(raw[1]), ); case 3: + return InputType_Bolt12Offer( + offer: dco_decode_box_autoadd_ln_offer(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]), ); @@ -1839,12 +1873,24 @@ 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 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 @@ -1952,6 +1998,33 @@ 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 != 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]), + 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]), + 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]), + ); + } + @protected LnUrlAuthError dco_decode_ln_url_auth_error(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -2294,6 +2367,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 @@ -2413,9 +2492,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { description: dco_decode_String(raw[2]), preimage: dco_decode_opt_String(raw[3]), bolt11: dco_decode_opt_String(raw[4]), - paymentHash: dco_decode_opt_String(raw[5]), - refundTxId: dco_decode_opt_String(raw[6]), - refundTxAmountSat: dco_decode_opt_box_autoadd_u_64(raw[7]), + bolt12Offer: dco_decode_opt_String(raw[5]), + paymentHash: dco_decode_opt_String(raw[6]), + refundTxId: dco_decode_opt_String(raw[7]), + refundTxAmountSat: dco_decode_opt_box_autoadd_u_64(raw[8]), ); case 1: return PaymentDetails_Liquid( @@ -2849,6 +2929,11 @@ 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_box_autoadd_ln_offer(raw[1]), + receiverAmountSat: dco_decode_u_64(raw[2]), + ); default: throw Exception("unreachable"); } @@ -3083,6 +3168,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 @@ -3139,6 +3242,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 @@ -3217,6 +3326,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 @@ -3560,21 +3675,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_box_autoadd_ln_offer(deserializer); + return InputType_Bolt12Offer(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: @@ -3624,6 +3742,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 @@ -3636,6 +3766,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 @@ -3804,6 +3946,35 @@ 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_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); + 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, + minAmount: var_minAmount, + description: var_description, + absoluteExpiry: var_absoluteExpiry, + issuer: var_issuer, + 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 LnUrlAuthError sse_decode_ln_url_auth_error(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -4109,6 +4280,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 @@ -4287,6 +4469,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_description = sse_decode_String(deserializer); var var_preimage = sse_decode_opt_String(deserializer); var var_bolt11 = sse_decode_opt_String(deserializer); + var var_bolt12Offer = sse_decode_opt_String(deserializer); var var_paymentHash = sse_decode_opt_String(deserializer); var var_refundTxId = sse_decode_opt_String(deserializer); var var_refundTxAmountSat = sse_decode_opt_box_autoadd_u_64(deserializer); @@ -4295,6 +4478,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { description: var_description, preimage: var_preimage, bolt11: var_bolt11, + bolt12Offer: var_bolt12Offer, paymentHash: var_paymentHash, refundTxId: var_refundTxId, refundTxAmountSat: var_refundTxAmountSat); @@ -4687,6 +4871,10 @@ 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_box_autoadd_ln_offer(deserializer); + var var_receiverAmountSat = sse_decode_u_64(deserializer); + return SendDestination_Bolt12(offer: var_offer, receiverAmountSat: var_receiverAmountSat); default: throw UnimplementedError(''); } @@ -5005,6 +5193,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 @@ -5053,6 +5257,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 @@ -5131,6 +5341,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 @@ -5445,23 +5661,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_Bolt12Offer(offer: final offer): sse_encode_i_32(3, 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); 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(''); @@ -5501,6 +5720,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 @@ -5510,6 +5738,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 @@ -5632,6 +5869,25 @@ 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.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); + 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 void sse_encode_ln_url_auth_error(LnUrlAuthError self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -5896,6 +6152,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 @@ -6050,6 +6316,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { description: final description, preimage: final preimage, bolt11: final bolt11, + bolt12Offer: final bolt12Offer, paymentHash: final paymentHash, refundTxId: final refundTxId, refundTxAmountSat: final refundTxAmountSat @@ -6059,6 +6326,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_String(description, serializer); sse_encode_opt_String(preimage, serializer); sse_encode_opt_String(bolt11, serializer); + sse_encode_opt_String(bolt12Offer, serializer); sse_encode_opt_String(paymentHash, serializer); sse_encode_opt_String(refundTxId, serializer); sse_encode_opt_box_autoadd_u_64(refundTxAmountSat, serializer); @@ -6389,6 +6657,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, receiverAmountSat: final receiverAmountSat): + sse_encode_i_32(2, 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 44052efc7..85e594835 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,9 +275,15 @@ 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); + @protected + List dco_decode_list_ln_offer_blinded_path(dynamic raw); + @protected List dco_decode_list_locale_overrides(dynamic raw); @@ -305,6 +320,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected LNInvoice dco_decode_ln_invoice(dynamic raw); + @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); @@ -371,6 +392,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 +604,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 +630,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 +672,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,9 +822,15 @@ 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); + @protected + List sse_decode_list_ln_offer_blinded_path(SseDeserializer deserializer); + @protected List sse_decode_list_locale_overrides(SseDeserializer deserializer); @@ -828,6 +867,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected LNInvoice sse_decode_ln_invoice(SseDeserializer deserializer); + @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); @@ -894,6 +939,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 +1177,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 +1293,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 +1571,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 @@ -1517,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 @@ -1611,6 +1696,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 +1814,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 +1871,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 +1942,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,39 +2216,45 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { wireObj.kind.Bolt11.invoice = pre_invoice; return; } + if (apiObj is InputType_Bolt12Offer) { + var pre_offer = cst_encode_box_autoadd_ln_offer(apiObj.offer); + wireObj.tag = 3; + wireObj.kind.Bolt12Offer.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; } @@ -2204,6 +2329,24 @@ 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.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); + 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 void cst_api_fill_to_wire_ln_url_auth_error(LnUrlAuthError apiObj, wire_cst_ln_url_auth_error wireObj) { if (apiObj is LnUrlAuthError_Generic) { @@ -2544,6 +2687,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { var pre_description = cst_encode_String(apiObj.description); var pre_preimage = cst_encode_opt_String(apiObj.preimage); var pre_bolt11 = cst_encode_opt_String(apiObj.bolt11); + var pre_bolt12_offer = cst_encode_opt_String(apiObj.bolt12Offer); var pre_payment_hash = cst_encode_opt_String(apiObj.paymentHash); var pre_refund_tx_id = cst_encode_opt_String(apiObj.refundTxId); var pre_refund_tx_amount_sat = cst_encode_opt_box_autoadd_u_64(apiObj.refundTxAmountSat); @@ -2552,6 +2696,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { wireObj.kind.Lightning.description = pre_description; wireObj.kind.Lightning.preimage = pre_preimage; wireObj.kind.Lightning.bolt11 = pre_bolt11; + wireObj.kind.Lightning.bolt12_offer = pre_bolt12_offer; wireObj.kind.Lightning.payment_hash = pre_payment_hash; wireObj.kind.Lightning.refund_tx_id = pre_refund_tx_id; wireObj.kind.Lightning.refund_tx_amount_sat = pre_refund_tx_amount_sat; @@ -2932,6 +3077,14 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { wireObj.kind.Bolt11.invoice = pre_invoice; return; } + if (apiObj is SendDestination_Bolt12) { + 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; + wireObj.kind.Bolt12.receiver_amount_sat = pre_receiver_amount_sat; + return; + } } @protected @@ -3104,6 +3257,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); @@ -3127,6 +3283,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); @@ -3166,6 +3325,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); @@ -3320,9 +3482,15 @@ 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); + @protected + void sse_encode_list_ln_offer_blinded_path(List self, SseSerializer serializer); + @protected void sse_encode_list_locale_overrides(List self, SseSerializer serializer); @@ -3359,6 +3527,12 @@ 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_offer_blinded_path(LnOfferBlindedPath self, SseSerializer serializer); + @protected void sse_encode_ln_url_auth_error(LnUrlAuthError self, SseSerializer serializer); @@ -3426,6 +3600,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); @@ -4427,6 +4604,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(); } @@ -4565,6 +4752,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(); } @@ -4889,6 +5086,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, ) { @@ -4903,6 +5114,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, ) { @@ -5239,10 +5464,80 @@ 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_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; + + external ffi.Pointer chains; + + external ffi.Pointer min_amount; + + external ffi.Pointer description; + + external ffi.Pointer absolute_expiry; + + external ffi.Pointer issuer; + + external ffi.Pointer signing_pubkey; + + external ffi.Pointer paths; +} + +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 { 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 { @@ -5499,6 +5794,8 @@ final class wire_cst_PaymentDetails_Lightning extends ffi.Struct { external ffi.Pointer bolt11; + external ffi.Pointer bolt12_offer; + external ffi.Pointer payment_hash; external ffi.Pointer refund_tx_id; @@ -5861,6 +6158,10 @@ final class wire_cst_InputType_Bolt11 extends ffi.Struct { external ffi.Pointer invoice; } +final class wire_cst_InputType_Bolt12Offer extends ffi.Struct { + external ffi.Pointer offer; +} + final class wire_cst_InputType_NodeId extends ffi.Struct { external ffi.Pointer node_id; } @@ -5892,6 +6193,8 @@ final class InputTypeKind extends ffi.Union { external wire_cst_InputType_Bolt11 Bolt11; + external wire_cst_InputType_Bolt12Offer Bolt12Offer; + external wire_cst_InputType_NodeId NodeId; external wire_cst_InputType_Url Url; diff --git a/packages/dart/lib/src/lib.dart b/packages/dart/lib/src/lib.dart index fb0def6e9..2817799a7 100644 --- a/packages/dart/lib/src/lib.dart +++ b/packages/dart/lib/src/lib.dart @@ -6,5 +6,5 @@ import 'frb_generated.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; -// Rust type: RustOpaqueNom >>> -abstract class ArcBoxSigner implements RustOpaqueInterface {} +// Rust type: RustOpaqueNom> +abstract class LnOfferBlindedPath implements RustOpaqueInterface {} diff --git a/packages/dart/lib/src/model.dart b/packages/dart/lib/src/model.dart index 9649c58c2..87063eed6 100644 --- a/packages/dart/lib/src/model.dart +++ b/packages/dart/lib/src/model.dart @@ -617,10 +617,11 @@ sealed class PaymentDetails with _$PaymentDetails { /// In case of a Send swap, this is the preimage of the paid invoice (proof of payment). String? preimage, - /// Represents the invoice associated with a payment + /// Represents the Bolt11 invoice associated with a payment /// In the case of a Send payment, this is the invoice paid by the swapper /// In the case of a Receive payment, this is the invoice paid by the user String? bolt11, + String? bolt12Offer, /// The payment hash of the invoice String? paymentHash, @@ -1007,7 +1008,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 @@ -1263,6 +1264,10 @@ sealed class SendDestination with _$SendDestination { const factory SendDestination.bolt11({ required LNInvoice invoice, }) = SendDestination_Bolt11; + const factory SendDestination.bolt12({ + required LNOffer offer, + required BigInt receiverAmountSat, + }) = 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 37d19aaf8..c511a99f3 100644 --- a/packages/dart/lib/src/model.freezed.dart +++ b/packages/dart/lib/src/model.freezed.dart @@ -793,6 +793,7 @@ abstract class _$$PaymentDetails_LightningImplCopyWith<$Res> implements $Payment String description, String? preimage, String? bolt11, + String? bolt12Offer, String? paymentHash, String? refundTxId, BigInt? refundTxAmountSat}); @@ -815,6 +816,7 @@ class __$$PaymentDetails_LightningImplCopyWithImpl<$Res> Object? description = null, Object? preimage = freezed, Object? bolt11 = freezed, + Object? bolt12Offer = freezed, Object? paymentHash = freezed, Object? refundTxId = freezed, Object? refundTxAmountSat = freezed, @@ -836,6 +838,10 @@ class __$$PaymentDetails_LightningImplCopyWithImpl<$Res> ? _value.bolt11 : bolt11 // ignore: cast_nullable_to_non_nullable as String?, + bolt12Offer: freezed == bolt12Offer + ? _value.bolt12Offer + : bolt12Offer // ignore: cast_nullable_to_non_nullable + as String?, paymentHash: freezed == paymentHash ? _value.paymentHash : paymentHash // ignore: cast_nullable_to_non_nullable @@ -860,6 +866,7 @@ class _$PaymentDetails_LightningImpl extends PaymentDetails_Lightning { required this.description, this.preimage, this.bolt11, + this.bolt12Offer, this.paymentHash, this.refundTxId, this.refundTxAmountSat}) @@ -876,11 +883,13 @@ class _$PaymentDetails_LightningImpl extends PaymentDetails_Lightning { @override final String? preimage; - /// Represents the invoice associated with a payment + /// Represents the Bolt11 invoice associated with a payment /// In the case of a Send payment, this is the invoice paid by the swapper /// In the case of a Receive payment, this is the invoice paid by the user @override final String? bolt11; + @override + final String? bolt12Offer; /// The payment hash of the invoice @override @@ -896,7 +905,7 @@ class _$PaymentDetails_LightningImpl extends PaymentDetails_Lightning { @override String toString() { - return 'PaymentDetails.lightning(swapId: $swapId, description: $description, preimage: $preimage, bolt11: $bolt11, paymentHash: $paymentHash, refundTxId: $refundTxId, refundTxAmountSat: $refundTxAmountSat)'; + return 'PaymentDetails.lightning(swapId: $swapId, description: $description, preimage: $preimage, bolt11: $bolt11, bolt12Offer: $bolt12Offer, paymentHash: $paymentHash, refundTxId: $refundTxId, refundTxAmountSat: $refundTxAmountSat)'; } @override @@ -908,6 +917,7 @@ class _$PaymentDetails_LightningImpl extends PaymentDetails_Lightning { (identical(other.description, description) || other.description == description) && (identical(other.preimage, preimage) || other.preimage == preimage) && (identical(other.bolt11, bolt11) || other.bolt11 == bolt11) && + (identical(other.bolt12Offer, bolt12Offer) || other.bolt12Offer == bolt12Offer) && (identical(other.paymentHash, paymentHash) || other.paymentHash == paymentHash) && (identical(other.refundTxId, refundTxId) || other.refundTxId == refundTxId) && (identical(other.refundTxAmountSat, refundTxAmountSat) || @@ -915,8 +925,8 @@ class _$PaymentDetails_LightningImpl extends PaymentDetails_Lightning { } @override - int get hashCode => Object.hash( - runtimeType, swapId, description, preimage, bolt11, paymentHash, refundTxId, refundTxAmountSat); + int get hashCode => Object.hash(runtimeType, swapId, description, preimage, bolt11, bolt12Offer, + paymentHash, refundTxId, refundTxAmountSat); /// Create a copy of PaymentDetails /// with the given fields replaced by the non-null parameter values. @@ -933,6 +943,7 @@ abstract class PaymentDetails_Lightning extends PaymentDetails { required final String description, final String? preimage, final String? bolt11, + final String? bolt12Offer, final String? paymentHash, final String? refundTxId, final BigInt? refundTxAmountSat}) = _$PaymentDetails_LightningImpl; @@ -947,10 +958,11 @@ abstract class PaymentDetails_Lightning extends PaymentDetails { /// In case of a Send swap, this is the preimage of the paid invoice (proof of payment). String? get preimage; - /// Represents the invoice associated with a payment + /// Represents the Bolt11 invoice associated with a payment /// In the case of a Send payment, this is the invoice paid by the swapper /// In the case of a Receive payment, this is the invoice paid by the user String? get bolt11; + String? get bolt12Offer; /// The payment hash of the invoice String? get paymentHash; @@ -1920,3 +1932,93 @@ 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({LNOffer offer, BigInt receiverAmountSat}); +} + +/// @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, + Object? receiverAmountSat = null, + }) { + return _then(_$SendDestination_Bolt12Impl( + offer: null == offer + ? _value.offer + : offer // ignore: cast_nullable_to_non_nullable + as LNOffer, + receiverAmountSat: null == receiverAmountSat + ? _value.receiverAmountSat + : receiverAmountSat // ignore: cast_nullable_to_non_nullable + as BigInt, + )); + } +} + +/// @nodoc + +class _$SendDestination_Bolt12Impl extends SendDestination_Bolt12 { + const _$SendDestination_Bolt12Impl({required this.offer, required this.receiverAmountSat}) : super._(); + + @override + final LNOffer offer; + @override + final BigInt receiverAmountSat; + + @override + String toString() { + return 'SendDestination.bolt12(offer: $offer, receiverAmountSat: $receiverAmountSat)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SendDestination_Bolt12Impl && + (identical(other.offer, offer) || other.offer == offer) && + (identical(other.receiverAmountSat, receiverAmountSat) || + other.receiverAmountSat == receiverAmountSat)); + } + + @override + int get hashCode => Object.hash(runtimeType, offer, receiverAmountSat); + + /// 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 LNOffer offer, required final BigInt receiverAmountSat}) = _$SendDestination_Bolt12Impl; + const SendDestination_Bolt12._() : super._(); + + LNOffer get offer; + BigInt get receiverAmountSat; + + /// 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 2670768a3..3ec45f173 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, ) { @@ -1376,6 +1412,21 @@ class FlutterBreezLiquidBindings { _frbgen_breez_liquid_cst_new_list_fiat_currencyPtr .asFunction Function(int)>(); + ffi.Pointer frbgen_breez_liquid_cst_new_list_ln_offer_blinded_path( + int len, + ) { + return _frbgen_breez_liquid_cst_new_list_ln_offer_blinded_path( + len, + ); + } + + late final _frbgen_breez_liquid_cst_new_list_ln_offer_blinded_pathPtr = + _lookup Function(ffi.Int32)>>( + 'frbgen_breez_liquid_cst_new_list_ln_offer_blinded_path'); + late final _frbgen_breez_liquid_cst_new_list_ln_offer_blinded_path = + _frbgen_breez_liquid_cst_new_list_ln_offer_blinded_pathPtr + .asFunction Function(int)>(); + ffi.Pointer frbgen_breez_liquid_cst_new_list_locale_overrides( int len, ) { @@ -3997,10 +4048,80 @@ 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_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; + + external ffi.Pointer chains; + + external ffi.Pointer min_amount; + + external ffi.Pointer description; + + external ffi.Pointer absolute_expiry; + + external ffi.Pointer issuer; + + external ffi.Pointer signing_pubkey; + + external ffi.Pointer paths; +} + +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 { 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 { @@ -4257,6 +4378,8 @@ final class wire_cst_PaymentDetails_Lightning extends ffi.Struct { external ffi.Pointer bolt11; + external ffi.Pointer bolt12_offer; + external ffi.Pointer payment_hash; external ffi.Pointer refund_tx_id; @@ -4619,6 +4742,10 @@ final class wire_cst_InputType_Bolt11 extends ffi.Struct { external ffi.Pointer invoice; } +final class wire_cst_InputType_Bolt12Offer extends ffi.Struct { + external ffi.Pointer offer; +} + final class wire_cst_InputType_NodeId extends ffi.Struct { external ffi.Pointer node_id; } @@ -4650,6 +4777,8 @@ final class InputTypeKind extends ffi.Union { external wire_cst_InputType_Bolt11 Bolt11; + external wire_cst_InputType_Bolt12Offer Bolt12Offer; + external wire_cst_InputType_NodeId NodeId; external wire_cst_InputType_Url Url; 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 ffcae1c8e..d4ce62613 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 @@ -567,6 +567,52 @@ fun asLnInvoiceList(arr: ReadableArray): List { return list } +fun asLnOffer(lnOffer: ReadableMap): LnOffer? { + if (!validateMandatoryFields( + lnOffer, + arrayOf( + "offer", + "chains", + "paths", + ), + ) + ) { + return null + } + 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, 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) }, + "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, @@ -738,6 +784,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, @@ -2370,6 +2446,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 { @@ -2430,6 +2548,10 @@ fun asInputType(inputType: ReadableMap): InputType? { val invoice = inputType.getMap("invoice")?.let { asLnInvoice(it) }!! return InputType.Bolt11(invoice) } + if (type == "bolt12Offer") { + val offer = inputType.getMap("offer")?.let { asLnOffer(it) }!! + return InputType.Bolt12Offer(offer) + } if (type == "nodeId") { val nodeId = inputType.getString("nodeId")!! return InputType.NodeId(nodeId) @@ -2472,6 +2594,10 @@ fun readableMapOf(inputType: InputType): ReadableMap? { pushToMap(map, "type", "bolt11") pushToMap(map, "invoice", readableMapOf(inputType.invoice)) } + is InputType.Bolt12Offer -> { + pushToMap(map, "type", "bolt12Offer") + pushToMap(map, "offer", readableMapOf(inputType.offer)) + } is InputType.NodeId -> { pushToMap(map, "type", "nodeId") pushToMap(map, "nodeId", inputType.nodeId) @@ -2757,6 +2883,7 @@ fun asPaymentDetails(paymentDetails: ReadableMap): PaymentDetails? { val description = paymentDetails.getString("description")!! val preimage = if (hasNonNullKey(paymentDetails, "preimage")) paymentDetails.getString("preimage") else null val bolt11 = if (hasNonNullKey(paymentDetails, "bolt11")) paymentDetails.getString("bolt11") else null + val bolt12Offer = if (hasNonNullKey(paymentDetails, "bolt12Offer")) paymentDetails.getString("bolt12Offer") else null val paymentHash = if (hasNonNullKey(paymentDetails, "paymentHash")) paymentDetails.getString("paymentHash") else null val refundTxId = if (hasNonNullKey(paymentDetails, "refundTxId")) paymentDetails.getString("refundTxId") else null val refundTxAmountSat = @@ -2769,7 +2896,7 @@ fun asPaymentDetails(paymentDetails: ReadableMap): PaymentDetails? { } else { null } - return PaymentDetails.Lightning(swapId, description, preimage, bolt11, paymentHash, refundTxId, refundTxAmountSat) + return PaymentDetails.Lightning(swapId, description, preimage, bolt11, bolt12Offer, paymentHash, refundTxId, refundTxAmountSat) } if (type == "liquid") { val destination = paymentDetails.getString("destination")!! @@ -2804,6 +2931,7 @@ fun readableMapOf(paymentDetails: PaymentDetails): ReadableMap? { pushToMap(map, "description", paymentDetails.description) pushToMap(map, "preimage", paymentDetails.preimage) pushToMap(map, "bolt11", paymentDetails.bolt11) + pushToMap(map, "bolt12Offer", paymentDetails.bolt12Offer) pushToMap(map, "paymentHash", paymentDetails.paymentHash) pushToMap(map, "refundTxId", paymentDetails.refundTxId) pushToMap(map, "refundTxAmountSat", paymentDetails.refundTxAmountSat) @@ -2963,6 +3091,11 @@ fun asSendDestination(sendDestination: ReadableMap): SendDestination? { val invoice = sendDestination.getMap("invoice")?.let { asLnInvoice(it) }!! return SendDestination.Bolt11(invoice) } + if (type == "bolt12") { + val offer = sendDestination.getMap("offer")?.let { asLnOffer(it) }!! + val receiverAmountSat = sendDestination.getDouble("receiverAmountSat").toULong() + return SendDestination.Bolt12(offer, receiverAmountSat) + } return null } @@ -2977,6 +3110,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", readableMapOf(sendDestination.offer)) + pushToMap(map, "receiverAmountSat", sendDestination.receiverAmountSat) + } } return map } @@ -3119,6 +3257,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)) @@ -3127,6 +3266,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 9fa12a549..2a647dc68 100644 --- a/packages/react-native/ios/BreezSDKLiquidMapper.swift +++ b/packages/react-native/ios/BreezSDKLiquidMapper.swift @@ -673,6 +673,85 @@ enum BreezSDKLiquidMapper { return lnInvoiceList.map { v -> [String: Any?] in return dictionaryOf(lnInvoice: v) } } + static func asLnOffer(lnOffer: [String: Any?]) throws -> 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")) + } + 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 { + 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 minAmount: Amount? + if let minAmountTmp = lnOffer["minAmount"] as? [String: Any?] { + minAmount = try asAmount(amount: minAmountTmp) + } + + 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(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!), + "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")) @@ -889,6 +968,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")) @@ -2735,6 +2845,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": @@ -2835,6 +3004,14 @@ enum BreezSDKLiquidMapper { return InputType.bolt11(invoice: _invoice) } + if type == "bolt12Offer" { + 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" { guard let _nodeId = inputType["nodeId"] as? String else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "nodeId", typeName: "InputType")) @@ -2909,6 +3086,14 @@ enum BreezSDKLiquidMapper { "invoice": dictionaryOf(lnInvoice: invoice), ] + case let .bolt12Offer( + offer + ): + return [ + "type": "bolt12Offer", + "offer": dictionaryOf(lnOffer: offer), + ] + case let .nodeId( nodeId ): @@ -3384,13 +3569,15 @@ enum BreezSDKLiquidMapper { let _bolt11 = paymentDetails["bolt11"] as? String + let _bolt12Offer = paymentDetails["bolt12Offer"] as? String + let _paymentHash = paymentDetails["paymentHash"] as? String let _refundTxId = paymentDetails["refundTxId"] as? String let _refundTxAmountSat = paymentDetails["refundTxAmountSat"] as? UInt64 - return PaymentDetails.lightning(swapId: _swapId, description: _description, preimage: _preimage, bolt11: _bolt11, paymentHash: _paymentHash, refundTxId: _refundTxId, refundTxAmountSat: _refundTxAmountSat) + return PaymentDetails.lightning(swapId: _swapId, description: _description, preimage: _preimage, bolt11: _bolt11, bolt12Offer: _bolt12Offer, paymentHash: _paymentHash, refundTxId: _refundTxId, refundTxAmountSat: _refundTxAmountSat) } if type == "liquid" { guard let _destination = paymentDetails["destination"] as? String else { @@ -3421,7 +3608,7 @@ enum BreezSDKLiquidMapper { static func dictionaryOf(paymentDetails: PaymentDetails) -> [String: Any?] { switch paymentDetails { case let .lightning( - swapId, description, preimage, bolt11, paymentHash, refundTxId, refundTxAmountSat + swapId, description, preimage, bolt11, bolt12Offer, paymentHash, refundTxId, refundTxAmountSat ): return [ "type": "lightning", @@ -3429,6 +3616,7 @@ enum BreezSDKLiquidMapper { "description": description, "preimage": preimage == nil ? nil : preimage, "bolt11": bolt11 == nil ? nil : bolt11, + "bolt12Offer": bolt12Offer == nil ? nil : bolt12Offer, "paymentHash": paymentHash == nil ? nil : paymentHash, "refundTxId": refundTxId == nil ? nil : refundTxId, "refundTxAmountSat": refundTxAmountSat == nil ? nil : refundTxAmountSat, @@ -3775,6 +3963,17 @@ enum BreezSDKLiquidMapper { return SendDestination.bolt11(invoice: _invoice) } + if type == "bolt12" { + 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")) + } + return SendDestination.bolt12(offer: _offer, receiverAmountSat: _receiverAmountSat) + } throw SdkError.Generic(message: "Unexpected type \(type) for enum SendDestination") } @@ -3796,6 +3995,15 @@ enum BreezSDKLiquidMapper { "type": "bolt11", "invoice": dictionaryOf(lnInvoice: invoice), ] + + case let .bolt12( + offer, receiverAmountSat + ): + return [ + "type": "bolt12", + "offer": dictionaryOf(lnOffer: offer), + "receiverAmountSat": receiverAmountSat, + ] } } diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index ccf0e9f98..fe71196f5 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -117,6 +117,17 @@ export interface LnInvoice { minFinalCltvExpiryDelta: number } +export interface LnOffer { + offer: string + chains: string[] + paths: LnOfferBlindedPath[] + description?: string + signingPubkey?: string + minAmount?: Amount + absoluteExpiry?: number + issuer?: string +} + export interface LightningPaymentLimitsResponse { send: Limits receive: Limits @@ -146,6 +157,10 @@ export interface ListPaymentsRequest { details?: ListPaymentDetails } +export interface LnOfferBlindedPath { + blindedHops: string[] +} + export interface LnUrlAuthRequestData { k1: string domain: string @@ -409,6 +424,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" } @@ -426,6 +455,7 @@ export enum InputTypeVariant { BITCOIN_ADDRESS = "bitcoinAddress", LIQUID_ADDRESS = "liquidAddress", BOLT11 = "bolt11", + BOLT12_OFFER = "bolt12Offer", NODE_ID = "nodeId", URL = "url", LN_URL_PAY = "lnUrlPay", @@ -443,6 +473,9 @@ export type InputType = { } | { type: InputTypeVariant.BOLT11, invoice: LnInvoice +} | { + type: InputTypeVariant.BOLT12_OFFER, + offer: LnOffer } | { type: InputTypeVariant.NODE_ID, nodeId: string @@ -558,6 +591,7 @@ export type PaymentDetails = { description: string preimage?: string bolt11?: string + bolt12Offer?: string paymentHash?: string refundTxId?: string refundTxAmountSat?: number @@ -628,7 +662,8 @@ export type SdkEvent = { export enum SendDestinationVariant { LIQUID_ADDRESS = "liquidAddress", - BOLT11 = "bolt11" + BOLT11 = "bolt11", + BOLT12 = "bolt12" } export type SendDestination = { @@ -637,6 +672,10 @@ export type SendDestination = { } | { type: SendDestinationVariant.BOLT11, invoice: LnInvoice +} | { + type: SendDestinationVariant.BOLT12, + offer: LnOffer + receiverAmountSat: number } export enum SuccessActionVariant {