Add ability to parse an invoice (#240)

* Add ability to parse an invoice

* Simplify LNInvoice initialization

* Add missing LNInvoice fields

* Cargo fmt

* Re-generate bindings after merging main branch

* Explicitly check the invoice signature

* Remove redundant signature check

* Strip potential lightning: prefix before parsing bolt11
This commit is contained in:
ok300
2024-05-30 13:26:00 +00:00
committed by GitHub
parent 35561bacc5
commit e5dba1043e
25 changed files with 1849 additions and 64 deletions

2
cli/Cargo.lock generated
View File

@@ -370,7 +370,7 @@ checksum = "829a082bd3761fde7476dc2ed85ca56c11628948460ece621e4f56fef5046567"
[[package]]
name = "boltz-client"
version = "0.1.3"
source = "git+https://github.com/ok300/boltz-rust?branch=ok300-breez-latest-05-27#faecead854e8b0803f744b25bd0c47853cc487da"
source = "git+https://github.com/ok300/boltz-rust?branch=ok300-breez-latest-05-28#92e4166131702dfa17b083796e6d0fc7eacf8f00"
dependencies = [
"bip39",
"bitcoin 0.31.2",

View File

@@ -92,7 +92,7 @@ async fn main() -> Result<()> {
}
let mnemonic = persistence.get_or_create_mnemonic()?;
let network = args.network.unwrap_or(Network::LiquidTestnet);
let network = args.network.unwrap_or(Network::Testnet);
let sdk = LiquidSdk::connect(ConnectRequest {
mnemonic: mnemonic.to_string(),
data_dir: Some(data_dir_str),
@@ -104,8 +104,8 @@ async fn main() -> Result<()> {
.await?;
let cli_prompt = match network {
Network::Liquid => "breez-liquid-cli [mainnet]> ",
Network::LiquidTestnet => "breez-liquid-cli [testnet]> ",
Network::Mainnet => "breez-liquid-cli [mainnet]> ",
Network::Testnet => "breez-liquid-cli [testnet]> ",
};
loop {

2
lib/Cargo.lock generated
View File

@@ -490,7 +490,7 @@ checksum = "829a082bd3761fde7476dc2ed85ca56c11628948460ece621e4f56fef5046567"
[[package]]
name = "boltz-client"
version = "0.1.3"
source = "git+https://github.com/ok300/boltz-rust?branch=ok300-breez-latest-05-27#faecead854e8b0803f744b25bd0c47853cc487da"
source = "git+https://github.com/ok300/boltz-rust?branch=ok300-breez-latest-05-28#92e4166131702dfa17b083796e6d0fc7eacf8f00"
dependencies = [
"bip39",
"bitcoin 0.31.2",

View File

@@ -79,6 +79,30 @@ typedef struct wire_cst_list_payment {
int32_t len;
} wire_cst_list_payment;
typedef struct wire_cst_route_hint_hop {
struct wire_cst_list_prim_u_8_strict *src_node_id;
uint64_t short_channel_id;
uint32_t fees_base_msat;
uint32_t fees_proportional_millionths;
uint64_t cltv_expiry_delta;
uint64_t *htlc_minimum_msat;
uint64_t *htlc_maximum_msat;
} wire_cst_route_hint_hop;
typedef struct wire_cst_list_route_hint_hop {
struct wire_cst_route_hint_hop *ptr;
int32_t len;
} wire_cst_list_route_hint_hop;
typedef struct wire_cst_route_hint {
struct wire_cst_list_route_hint_hop *hops;
} wire_cst_route_hint;
typedef struct wire_cst_list_route_hint {
struct wire_cst_route_hint *ptr;
int32_t len;
} wire_cst_list_route_hint;
typedef struct wire_cst_get_info_response {
uint64_t balance_sat;
uint64_t pending_send_sat;
@@ -137,6 +161,21 @@ typedef struct wire_cst_liquid_sdk_event {
union LiquidSdkEventKind kind;
} wire_cst_liquid_sdk_event;
typedef struct wire_cst_ln_invoice {
struct wire_cst_list_prim_u_8_strict *bolt11;
int32_t network;
struct wire_cst_list_prim_u_8_strict *payee_pubkey;
struct wire_cst_list_prim_u_8_strict *payment_hash;
struct wire_cst_list_prim_u_8_strict *description;
struct wire_cst_list_prim_u_8_strict *description_hash;
uint64_t *amount_msat;
uint64_t timestamp;
uint64_t expiry;
struct wire_cst_list_route_hint *routing_hints;
struct wire_cst_list_prim_u_8_strict *payment_secret;
uint64_t min_final_cltv_expiry_delta;
} wire_cst_ln_invoice;
typedef struct wire_cst_PaymentError_Generic {
struct wire_cst_list_prim_u_8_strict *err;
} wire_cst_PaymentError_Generic;
@@ -227,6 +266,9 @@ void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_sync(int64_t po
void frbgen_breez_liquid_wire__crate__bindings__connect(int64_t port_,
struct wire_cst_connect_request *req);
void frbgen_breez_liquid_wire__crate__bindings__parse_invoice(int64_t port_,
struct wire_cst_list_prim_u_8_strict *input);
void frbgen_breez_liquid_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk(const void *ptr);
void frbgen_breez_liquid_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk(const void *ptr);
@@ -254,6 +296,10 @@ uint64_t *frbgen_breez_liquid_cst_new_box_autoadd_u_64(uint64_t value);
struct wire_cst_list_payment *frbgen_breez_liquid_cst_new_list_payment(int32_t len);
struct wire_cst_list_prim_u_8_strict *frbgen_breez_liquid_cst_new_list_prim_u_8_strict(int32_t len);
struct wire_cst_list_route_hint *frbgen_breez_liquid_cst_new_list_route_hint(int32_t len);
struct wire_cst_list_route_hint_hop *frbgen_breez_liquid_cst_new_list_route_hint_hop(int32_t len);
static int64_t dummy_method_to_enforce_bundling(void) {
int64_t dummy_var = 0;
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_backup_request);
@@ -268,6 +314,8 @@ static int64_t dummy_method_to_enforce_bundling(void) {
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_u_64);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_payment);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_prim_u_8_strict);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_route_hint);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_route_hint_hop);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_add_event_listener);
@@ -283,6 +331,7 @@ static int64_t dummy_method_to_enforce_bundling(void) {
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_send_payment);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_sync);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__connect);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__parse_invoice);
dummy_var ^= ((int64_t) (void*) store_dart_post_cobject);
return dummy_var;
}

View File

@@ -23,8 +23,8 @@ enum PaymentError {
};
enum Network {
"Liquid",
"LiquidTestnet",
"Mainnet",
"Testnet",
};
dictionary ConnectRequest {
@@ -79,6 +79,35 @@ dictionary RestoreRequest {
string? backup_path = null;
};
dictionary RouteHint {
sequence<RouteHintHop> hops;
};
dictionary RouteHintHop {
string src_node_id;
u64 short_channel_id;
u32 fees_base_msat;
u32 fees_proportional_millionths;
u64 cltv_expiry_delta;
u64? htlc_minimum_msat;
u64? htlc_maximum_msat;
};
dictionary LNInvoice {
string bolt11;
Network network;
string payee_pubkey;
string payment_hash;
string? description;
string? description_hash;
u64? amount_msat;
u64 timestamp;
u64 expiry;
sequence<RouteHint> routing_hints;
sequence<u8> payment_secret;
u64 min_final_cltv_expiry_delta;
};
dictionary Payment {
string tx_id;
string? swap_id = null;
@@ -122,6 +151,9 @@ callback interface EventListener {
namespace breez_liquid_sdk {
[Throws=LiquidSdkError]
BindingLiquidSdk connect(ConnectRequest req);
[Throws=PaymentError]
LNInvoice parse_invoice(string invoice);
};
interface BindingLiquidSdk {

View File

@@ -18,6 +18,10 @@ pub fn connect(req: ConnectRequest) -> Result<Arc<BindingLiquidSdk>, LiquidSdkEr
})
}
pub fn parse_invoice(input: String) -> Result<LNInvoice, PaymentError> {
LiquidSdk::parse_invoice(&input)
}
pub struct BindingLiquidSdk {
sdk: Arc<LiquidSdk>,
}

View File

@@ -15,7 +15,7 @@ frb = ["dep:flutter_rust_bridge"]
anyhow = { workspace = true }
bip39 = { version = "2.0.0", features = ["serde"] }
#boltz-client = { git = "https://github.com/SatoshiPortal/boltz-rust", rev = "a05731cc33030ada9ae14afcafe0cded22842ba6" }
boltz-client = { git = "https://github.com/ok300/boltz-rust", branch = "ok300-breez-latest-05-27" }
boltz-client = { git = "https://github.com/ok300/boltz-rust", branch = "ok300-breez-latest-05-28" }
flutter_rust_bridge = { version = "=2.0.0-dev.36", features = ["chrono"], optional = true }
log = "0.4.20"
lwk_common = "0.5.1"

View File

@@ -27,6 +27,10 @@ pub fn connect(req: ConnectRequest) -> Result<BindingLiquidSdk, LiquidSdkError>
})
}
pub fn parse_invoice(input: String) -> Result<LNInvoice, PaymentError> {
LiquidSdk::parse_invoice(&input)
}
pub struct BindingLiquidSdk {
sdk: Arc<LiquidSdk>,
}

View File

@@ -247,6 +247,45 @@ impl CstDecode<Vec<u8>> for *mut wire_cst_list_prim_u_8_strict {
}
}
}
impl CstDecode<Vec<crate::model::RouteHint>> for *mut wire_cst_list_route_hint {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> Vec<crate::model::RouteHint> {
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<Vec<crate::model::RouteHintHop>> for *mut wire_cst_list_route_hint_hop {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> Vec<crate::model::RouteHintHop> {
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<crate::model::LNInvoice> for wire_cst_ln_invoice {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> crate::model::LNInvoice {
crate::model::LNInvoice {
bolt11: self.bolt11.cst_decode(),
network: self.network.cst_decode(),
payee_pubkey: self.payee_pubkey.cst_decode(),
payment_hash: self.payment_hash.cst_decode(),
description: self.description.cst_decode(),
description_hash: self.description_hash.cst_decode(),
amount_msat: self.amount_msat.cst_decode(),
timestamp: self.timestamp.cst_decode(),
expiry: self.expiry.cst_decode(),
routing_hints: self.routing_hints.cst_decode(),
payment_secret: self.payment_secret.cst_decode(),
min_final_cltv_expiry_delta: self.min_final_cltv_expiry_delta.cst_decode(),
}
}
}
impl CstDecode<crate::model::Payment> for wire_cst_payment {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> crate::model::Payment {
@@ -362,6 +401,28 @@ impl CstDecode<crate::model::RestoreRequest> for wire_cst_restore_request {
}
}
}
impl CstDecode<crate::model::RouteHint> for wire_cst_route_hint {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> crate::model::RouteHint {
crate::model::RouteHint {
hops: self.hops.cst_decode(),
}
}
}
impl CstDecode<crate::model::RouteHintHop> for wire_cst_route_hint_hop {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> crate::model::RouteHintHop {
crate::model::RouteHintHop {
src_node_id: self.src_node_id.cst_decode(),
short_channel_id: self.short_channel_id.cst_decode(),
fees_base_msat: self.fees_base_msat.cst_decode(),
fees_proportional_millionths: self.fees_proportional_millionths.cst_decode(),
cltv_expiry_delta: self.cltv_expiry_delta.cst_decode(),
htlc_minimum_msat: self.htlc_minimum_msat.cst_decode(),
htlc_maximum_msat: self.htlc_maximum_msat.cst_decode(),
}
}
}
impl CstDecode<crate::model::SendPaymentResponse> for wire_cst_send_payment_response {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> crate::model::SendPaymentResponse {
@@ -449,6 +510,29 @@ impl Default for wire_cst_liquid_sdk_event {
Self::new_with_null_ptr()
}
}
impl NewWithNullPtr for wire_cst_ln_invoice {
fn new_with_null_ptr() -> Self {
Self {
bolt11: core::ptr::null_mut(),
network: Default::default(),
payee_pubkey: core::ptr::null_mut(),
payment_hash: core::ptr::null_mut(),
description: core::ptr::null_mut(),
description_hash: core::ptr::null_mut(),
amount_msat: core::ptr::null_mut(),
timestamp: Default::default(),
expiry: Default::default(),
routing_hints: core::ptr::null_mut(),
payment_secret: core::ptr::null_mut(),
min_final_cltv_expiry_delta: Default::default(),
}
}
}
impl Default for wire_cst_ln_invoice {
fn default() -> Self {
Self::new_with_null_ptr()
}
}
impl NewWithNullPtr for wire_cst_payment {
fn new_with_null_ptr() -> Self {
Self {
@@ -558,6 +642,36 @@ impl Default for wire_cst_restore_request {
Self::new_with_null_ptr()
}
}
impl NewWithNullPtr for wire_cst_route_hint {
fn new_with_null_ptr() -> Self {
Self {
hops: core::ptr::null_mut(),
}
}
}
impl Default for wire_cst_route_hint {
fn default() -> Self {
Self::new_with_null_ptr()
}
}
impl NewWithNullPtr for wire_cst_route_hint_hop {
fn new_with_null_ptr() -> Self {
Self {
src_node_id: core::ptr::null_mut(),
short_channel_id: Default::default(),
fees_base_msat: Default::default(),
fees_proportional_millionths: Default::default(),
cltv_expiry_delta: Default::default(),
htlc_minimum_msat: core::ptr::null_mut(),
htlc_maximum_msat: core::ptr::null_mut(),
}
}
}
impl Default for wire_cst_route_hint_hop {
fn default() -> Self {
Self::new_with_null_ptr()
}
}
impl NewWithNullPtr for wire_cst_send_payment_response {
fn new_with_null_ptr() -> Self {
Self {
@@ -683,6 +797,14 @@ pub extern "C" fn frbgen_breez_liquid_wire__crate__bindings__connect(
wire__crate__bindings__connect_impl(port_, req)
}
#[no_mangle]
pub extern "C" fn frbgen_breez_liquid_wire__crate__bindings__parse_invoice(
port_: i64,
input: *mut wire_cst_list_prim_u_8_strict,
) {
wire__crate__bindings__parse_invoice_impl(port_, input)
}
#[no_mangle]
pub extern "C" fn frbgen_breez_liquid_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk(
ptr: *const std::ffi::c_void,
@@ -798,6 +920,34 @@ pub extern "C" fn frbgen_breez_liquid_cst_new_list_prim_u_8_strict(
flutter_rust_bridge::for_generated::new_leak_box_ptr(ans)
}
#[no_mangle]
pub extern "C" fn frbgen_breez_liquid_cst_new_list_route_hint(
len: i32,
) -> *mut wire_cst_list_route_hint {
let wrap = wire_cst_list_route_hint {
ptr: flutter_rust_bridge::for_generated::new_leak_vec_ptr(
<wire_cst_route_hint>::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_route_hint_hop(
len: i32,
) -> *mut wire_cst_list_route_hint_hop {
let wrap = wire_cst_list_route_hint_hop {
ptr: flutter_rust_bridge::for_generated::new_leak_vec_ptr(
<wire_cst_route_hint_hop>::new_with_null_ptr(),
len,
),
len,
};
flutter_rust_bridge::for_generated::new_leak_box_ptr(wrap)
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_backup_request {
@@ -901,6 +1051,34 @@ pub struct wire_cst_list_prim_u_8_strict {
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_list_route_hint {
ptr: *mut wire_cst_route_hint,
len: i32,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_list_route_hint_hop {
ptr: *mut wire_cst_route_hint_hop,
len: i32,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_ln_invoice {
bolt11: *mut wire_cst_list_prim_u_8_strict,
network: i32,
payee_pubkey: *mut wire_cst_list_prim_u_8_strict,
payment_hash: *mut wire_cst_list_prim_u_8_strict,
description: *mut wire_cst_list_prim_u_8_strict,
description_hash: *mut wire_cst_list_prim_u_8_strict,
amount_msat: *mut u64,
timestamp: u64,
expiry: u64,
routing_hints: *mut wire_cst_list_route_hint,
payment_secret: *mut wire_cst_list_prim_u_8_strict,
min_final_cltv_expiry_delta: u64,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_payment {
tx_id: *mut wire_cst_list_prim_u_8_strict,
swap_id: *mut wire_cst_list_prim_u_8_strict,
@@ -990,6 +1168,22 @@ pub struct wire_cst_restore_request {
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_route_hint {
hops: *mut wire_cst_list_route_hint_hop,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_route_hint_hop {
src_node_id: *mut wire_cst_list_prim_u_8_strict,
short_channel_id: u64,
fees_base_msat: u32,
fees_proportional_millionths: u32,
cltv_expiry_delta: u64,
htlc_minimum_msat: *mut u64,
htlc_maximum_msat: *mut u64,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_send_payment_response {
payment: wire_cst_payment,
}

View File

@@ -34,7 +34,7 @@ flutter_rust_bridge::frb_generated_boilerplate!(
default_rust_auto_opaque = RustAutoOpaqueNom,
);
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.36";
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1552546000;
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 84136112;
// Section: executor
@@ -320,6 +320,24 @@ fn wire__crate__bindings__connect_impl(
},
)
}
fn wire__crate__bindings__parse_invoice_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
input: impl CstDecode<String>,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::DcoCodec, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "parse_invoice",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
let api_input = input.cst_decode();
move |context| {
transform_result_dco((move || crate::bindings::parse_invoice(api_input))())
}
},
)
}
// Section: dart2rust
@@ -339,8 +357,8 @@ impl CstDecode<crate::model::Network> for i32 {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> crate::model::Network {
match self {
0 => crate::model::Network::Liquid,
1 => crate::model::Network::LiquidTestnet,
0 => crate::model::Network::Mainnet,
1 => crate::model::Network::Testnet,
_ => unreachable!("Invalid variant for Network: {}", self),
}
}
@@ -590,13 +608,69 @@ impl SseDecode for Vec<u8> {
}
}
impl SseDecode for Vec<crate::model::RouteHint> {
// 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_ = <i32>::sse_decode(deserializer);
let mut ans_ = vec![];
for idx_ in 0..len_ {
ans_.push(<crate::model::RouteHint>::sse_decode(deserializer));
}
return ans_;
}
}
impl SseDecode for Vec<crate::model::RouteHintHop> {
// 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_ = <i32>::sse_decode(deserializer);
let mut ans_ = vec![];
for idx_ in 0..len_ {
ans_.push(<crate::model::RouteHintHop>::sse_decode(deserializer));
}
return ans_;
}
}
impl SseDecode for crate::model::LNInvoice {
// 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_bolt11 = <String>::sse_decode(deserializer);
let mut var_network = <crate::model::Network>::sse_decode(deserializer);
let mut var_payeePubkey = <String>::sse_decode(deserializer);
let mut var_paymentHash = <String>::sse_decode(deserializer);
let mut var_description = <Option<String>>::sse_decode(deserializer);
let mut var_descriptionHash = <Option<String>>::sse_decode(deserializer);
let mut var_amountMsat = <Option<u64>>::sse_decode(deserializer);
let mut var_timestamp = <u64>::sse_decode(deserializer);
let mut var_expiry = <u64>::sse_decode(deserializer);
let mut var_routingHints = <Vec<crate::model::RouteHint>>::sse_decode(deserializer);
let mut var_paymentSecret = <Vec<u8>>::sse_decode(deserializer);
let mut var_minFinalCltvExpiryDelta = <u64>::sse_decode(deserializer);
return crate::model::LNInvoice {
bolt11: var_bolt11,
network: var_network,
payee_pubkey: var_payeePubkey,
payment_hash: var_paymentHash,
description: var_description,
description_hash: var_descriptionHash,
amount_msat: var_amountMsat,
timestamp: var_timestamp,
expiry: var_expiry,
routing_hints: var_routingHints,
payment_secret: var_paymentSecret,
min_final_cltv_expiry_delta: var_minFinalCltvExpiryDelta,
};
}
}
impl SseDecode for crate::model::Network {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
let mut inner = <i32>::sse_decode(deserializer);
return match inner {
0 => crate::model::Network::Liquid,
1 => crate::model::Network::LiquidTestnet,
0 => crate::model::Network::Mainnet,
1 => crate::model::Network::Testnet,
_ => unreachable!("Invalid variant for Network: {}", inner),
};
}
@@ -804,6 +878,36 @@ impl SseDecode for crate::model::RestoreRequest {
}
}
impl SseDecode for crate::model::RouteHint {
// 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_hops = <Vec<crate::model::RouteHintHop>>::sse_decode(deserializer);
return crate::model::RouteHint { hops: var_hops };
}
}
impl SseDecode for crate::model::RouteHintHop {
// 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_srcNodeId = <String>::sse_decode(deserializer);
let mut var_shortChannelId = <u64>::sse_decode(deserializer);
let mut var_feesBaseMsat = <u32>::sse_decode(deserializer);
let mut var_feesProportionalMillionths = <u32>::sse_decode(deserializer);
let mut var_cltvExpiryDelta = <u64>::sse_decode(deserializer);
let mut var_htlcMinimumMsat = <Option<u64>>::sse_decode(deserializer);
let mut var_htlcMaximumMsat = <Option<u64>>::sse_decode(deserializer);
return crate::model::RouteHintHop {
src_node_id: var_srcNodeId,
short_channel_id: var_shortChannelId,
fees_base_msat: var_feesBaseMsat,
fees_proportional_millionths: var_feesProportionalMillionths,
cltv_expiry_delta: var_cltvExpiryDelta,
htlc_minimum_msat: var_htlcMinimumMsat,
htlc_maximum_msat: var_htlcMaximumMsat,
};
}
}
impl SseDecode for crate::model::SendPaymentResponse {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
@@ -1011,11 +1115,39 @@ impl flutter_rust_bridge::IntoIntoDart<crate::model::LiquidSdkEvent>
}
}
// Codec=Dco (DartCObject based), see doc to use other codecs
impl flutter_rust_bridge::IntoDart for crate::model::LNInvoice {
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
[
self.bolt11.into_into_dart().into_dart(),
self.network.into_into_dart().into_dart(),
self.payee_pubkey.into_into_dart().into_dart(),
self.payment_hash.into_into_dart().into_dart(),
self.description.into_into_dart().into_dart(),
self.description_hash.into_into_dart().into_dart(),
self.amount_msat.into_into_dart().into_dart(),
self.timestamp.into_into_dart().into_dart(),
self.expiry.into_into_dart().into_dart(),
self.routing_hints.into_into_dart().into_dart(),
self.payment_secret.into_into_dart().into_dart(),
self.min_final_cltv_expiry_delta
.into_into_dart()
.into_dart(),
]
.into_dart()
}
}
impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for crate::model::LNInvoice {}
impl flutter_rust_bridge::IntoIntoDart<crate::model::LNInvoice> for crate::model::LNInvoice {
fn into_into_dart(self) -> crate::model::LNInvoice {
self
}
}
// Codec=Dco (DartCObject based), see doc to use other codecs
impl flutter_rust_bridge::IntoDart for crate::model::Network {
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
match self {
Self::Liquid => 0.into_dart(),
Self::LiquidTestnet => 1.into_dart(),
Self::Mainnet => 0.into_dart(),
Self::Testnet => 1.into_dart(),
}
}
}
@@ -1232,6 +1364,41 @@ impl flutter_rust_bridge::IntoIntoDart<crate::model::RestoreRequest>
}
}
// Codec=Dco (DartCObject based), see doc to use other codecs
impl flutter_rust_bridge::IntoDart for crate::model::RouteHint {
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
[self.hops.into_into_dart().into_dart()].into_dart()
}
}
impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for crate::model::RouteHint {}
impl flutter_rust_bridge::IntoIntoDart<crate::model::RouteHint> for crate::model::RouteHint {
fn into_into_dart(self) -> crate::model::RouteHint {
self
}
}
// Codec=Dco (DartCObject based), see doc to use other codecs
impl flutter_rust_bridge::IntoDart for crate::model::RouteHintHop {
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
[
self.src_node_id.into_into_dart().into_dart(),
self.short_channel_id.into_into_dart().into_dart(),
self.fees_base_msat.into_into_dart().into_dart(),
self.fees_proportional_millionths
.into_into_dart()
.into_dart(),
self.cltv_expiry_delta.into_into_dart().into_dart(),
self.htlc_minimum_msat.into_into_dart().into_dart(),
self.htlc_maximum_msat.into_into_dart().into_dart(),
]
.into_dart()
}
}
impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for crate::model::RouteHintHop {}
impl flutter_rust_bridge::IntoIntoDart<crate::model::RouteHintHop> for crate::model::RouteHintHop {
fn into_into_dart(self) -> crate::model::RouteHintHop {
self
}
}
// Codec=Dco (DartCObject based), see doc to use other codecs
impl flutter_rust_bridge::IntoDart for crate::model::SendPaymentResponse {
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
[self.payment.into_into_dart().into_dart()].into_dart()
@@ -1403,13 +1570,51 @@ impl SseEncode for Vec<u8> {
}
}
impl SseEncode for Vec<crate::model::RouteHint> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<i32>::sse_encode(self.len() as _, serializer);
for item in self {
<crate::model::RouteHint>::sse_encode(item, serializer);
}
}
}
impl SseEncode for Vec<crate::model::RouteHintHop> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<i32>::sse_encode(self.len() as _, serializer);
for item in self {
<crate::model::RouteHintHop>::sse_encode(item, serializer);
}
}
}
impl SseEncode for crate::model::LNInvoice {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<String>::sse_encode(self.bolt11, serializer);
<crate::model::Network>::sse_encode(self.network, serializer);
<String>::sse_encode(self.payee_pubkey, serializer);
<String>::sse_encode(self.payment_hash, serializer);
<Option<String>>::sse_encode(self.description, serializer);
<Option<String>>::sse_encode(self.description_hash, serializer);
<Option<u64>>::sse_encode(self.amount_msat, serializer);
<u64>::sse_encode(self.timestamp, serializer);
<u64>::sse_encode(self.expiry, serializer);
<Vec<crate::model::RouteHint>>::sse_encode(self.routing_hints, serializer);
<Vec<u8>>::sse_encode(self.payment_secret, serializer);
<u64>::sse_encode(self.min_final_cltv_expiry_delta, serializer);
}
}
impl SseEncode for crate::model::Network {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<i32>::sse_encode(
match self {
crate::model::Network::Liquid => 0,
crate::model::Network::LiquidTestnet => 1,
crate::model::Network::Mainnet => 0,
crate::model::Network::Testnet => 1,
_ => {
unimplemented!("");
}
@@ -1587,6 +1792,26 @@ impl SseEncode for crate::model::RestoreRequest {
}
}
impl SseEncode for crate::model::RouteHint {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<Vec<crate::model::RouteHintHop>>::sse_encode(self.hops, serializer);
}
}
impl SseEncode for crate::model::RouteHintHop {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<String>::sse_encode(self.src_node_id, serializer);
<u64>::sse_encode(self.short_channel_id, serializer);
<u32>::sse_encode(self.fees_base_msat, serializer);
<u32>::sse_encode(self.fees_proportional_millionths, serializer);
<u64>::sse_encode(self.cltv_expiry_delta, serializer);
<Option<u64>>::sse_encode(self.htlc_minimum_msat, serializer);
<Option<u64>>::sse_encode(self.htlc_maximum_msat, serializer);
}
}
impl SseEncode for crate::model::SendPaymentResponse {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {

View File

@@ -3,7 +3,7 @@ use boltz_client::network::Chain;
use boltz_client::swaps::boltzv2::{
CreateReverseResponse, CreateSubmarineResponse, Leaf, SwapTree,
};
use boltz_client::Keypair;
use boltz_client::{Keypair, ToHex};
use lwk_signer::SwSigner;
use lwk_wollet::{ElectrumUrl, ElementsNetwork, WolletDescriptor};
use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef};
@@ -15,15 +15,17 @@ use crate::utils;
#[derive(Debug, Copy, Clone, PartialEq, Serialize)]
pub enum Network {
Liquid,
LiquidTestnet,
/// Mainnet Bitcoin and Liquid chains
Mainnet,
/// Testnet Bitcoin and Liquid chains
Testnet,
}
impl From<Network> for ElementsNetwork {
fn from(value: Network) -> Self {
match value {
Network::Liquid => ElementsNetwork::Liquid,
Network::LiquidTestnet => ElementsNetwork::LiquidTestnet,
Network::Mainnet => ElementsNetwork::Liquid,
Network::Testnet => ElementsNetwork::LiquidTestnet,
}
}
}
@@ -31,8 +33,8 @@ impl From<Network> for ElementsNetwork {
impl From<Network> for Chain {
fn from(value: Network) -> Self {
match value {
Network::Liquid => Chain::Liquid,
Network::LiquidTestnet => Chain::LiquidTestnet,
Network::Mainnet => Chain::Liquid,
Network::Testnet => Chain::LiquidTestnet,
}
}
}
@@ -42,8 +44,22 @@ impl TryFrom<&str> for Network {
fn try_from(value: &str) -> Result<Network, anyhow::Error> {
match value.to_lowercase().as_str() {
"mainnet" => Ok(Network::Liquid),
"testnet" => Ok(Network::LiquidTestnet),
"mainnet" => Ok(Network::Mainnet),
"testnet" => Ok(Network::Testnet),
_ => Err(anyhow!("Invalid network")),
}
}
}
impl TryFrom<boltz_client::lightning_invoice::Currency> for Network {
type Error = anyhow::Error;
fn try_from(
value: boltz_client::lightning_invoice::Currency,
) -> Result<Network, anyhow::Error> {
match value {
boltz_client::lightning_invoice::Currency::Bitcoin => Ok(Network::Mainnet),
boltz_client::lightning_invoice::Currency::BitcoinTestnet => Ok(Network::Testnet),
_ => Err(anyhow!("Invalid network")),
}
}
@@ -88,8 +104,8 @@ impl LiquidSdkOptions {
pub(crate) fn get_electrum_url(&self) -> ElectrumUrl {
self.electrum_url.clone().unwrap_or({
let (url, validate_domain, tls) = match &self.network {
Network::Liquid => ("blockstream.info:995", true, true),
Network::LiquidTestnet => ("blockstream.info:465", true, true),
Network::Mainnet => ("blockstream.info:995", true, true),
Network::Testnet => ("blockstream.info:465", true, true),
};
ElectrumUrl::new(url, tls, validate_domain)
})
@@ -539,6 +555,69 @@ impl Payment {
}
}
/// Wrapper for a BOLT11 LN invoice
#[derive(Clone, Debug, PartialEq)]
pub struct LNInvoice {
pub bolt11: String,
pub network: Network,
pub payee_pubkey: String,
pub payment_hash: String,
pub description: Option<String>,
pub description_hash: Option<String>,
pub amount_msat: Option<u64>,
pub timestamp: u64,
pub expiry: u64,
pub routing_hints: Vec<RouteHint>,
pub payment_secret: Vec<u8>,
pub min_final_cltv_expiry_delta: u64,
}
/// A route hint for a LN payment
#[derive(Clone, Debug, PartialEq)]
pub struct RouteHint {
pub hops: Vec<RouteHintHop>,
}
impl RouteHint {
pub fn from_ldk_hint(hint: &boltz_client::lightning_invoice::RouteHint) -> RouteHint {
let mut hops = Vec::new();
for hop in hint.0.iter() {
let pubkey_res = hop.src_node_id.serialize().to_hex();
let router_hop = RouteHintHop {
src_node_id: pubkey_res,
short_channel_id: hop.short_channel_id,
fees_base_msat: hop.fees.base_msat,
fees_proportional_millionths: hop.fees.proportional_millionths,
cltv_expiry_delta: u64::from(hop.cltv_expiry_delta),
htlc_minimum_msat: hop.htlc_minimum_msat,
htlc_maximum_msat: hop.htlc_maximum_msat,
};
hops.push(router_hop);
}
RouteHint { hops }
}
}
/// Details of a specific hop in a larger route hint
#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct RouteHintHop {
/// The node_id of the non-target end of the route
pub src_node_id: String,
/// The short_channel_id of this channel
pub short_channel_id: u64,
/// The fees which must be paid to use this channel
pub fees_base_msat: u32,
pub fees_proportional_millionths: u32,
/// The difference in CLTV values between this node and the next node.
pub cltv_expiry_delta: u64,
/// The minimum value, in msat, which must be relayed to the next hop.
pub htlc_minimum_msat: Option<u64>,
/// The maximum value in msat available for routing with a single HTLC.
pub htlc_maximum_msat: Option<u64>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct InternalLeaf {
pub output: String,

View File

@@ -8,8 +8,8 @@ use crate::model::Network;
impl Persister {
pub(crate) fn get_default_backup_path(&self) -> PathBuf {
self.main_db_dir.join(match self.network {
Network::Liquid => "backup.sql",
Network::LiquidTestnet => "backup-testnet.sql",
Network::Mainnet => "backup.sql",
Network::Testnet => "backup-testnet.sql",
})
}

View File

@@ -32,8 +32,8 @@ impl Persister {
pub(crate) fn get_connection(&self) -> Result<Connection> {
let db_file = match self.network {
Liquid => "storage.sql",
LiquidTestnet => "storage-testnet.sql",
Mainnet => "storage.sql",
Testnet => "storage-testnet.sql",
};
Ok(Connection::open(self.main_db_dir.join(db_file))?)
}

View File

@@ -1,7 +1,14 @@
use std::time::Instant;
use std::{fs, path::PathBuf, str::FromStr, sync::Arc, time::Duration};
use std::{
fs,
path::PathBuf,
str::FromStr,
sync::Arc,
time::{Duration, UNIX_EPOCH},
};
use anyhow::{anyhow, Result};
use boltz_client::lightning_invoice::Bolt11InvoiceDescription;
use boltz_client::network::Chain;
use boltz_client::swaps::boltzv2;
use boltz_client::ToHex;
@@ -65,7 +72,7 @@ pub struct LiquidSdk {
impl LiquidSdk {
pub async fn connect(req: ConnectRequest) -> Result<Arc<LiquidSdk>> {
let is_mainnet = req.network == Network::Liquid;
let is_mainnet = req.network == Network::Mainnet;
let signer = SwSigner::new(&req.mnemonic, is_mainnet)?;
let descriptor = LiquidSdk::get_descriptor(&signer, req.network)?;
@@ -155,9 +162,13 @@ impl LiquidSdk {
}
});
self.status_stream.clone().track_pending_swaps(self.shutdown_receiver.clone()).await;
self.status_stream
.clone()
.track_pending_swaps(self.shutdown_receiver.clone())
.await;
self.track_swap_updates(self.shutdown_receiver.clone()).await;
self.track_swap_updates(self.shutdown_receiver.clone())
.await;
Ok(())
}
@@ -233,7 +244,7 @@ impl LiquidSdk {
}
fn get_descriptor(signer: &SwSigner, network: Network) -> Result<WolletDescriptor> {
let is_mainnet = network == Network::Liquid;
let is_mainnet = network == Network::Mainnet;
let descriptor_str = singlesig_desc(
signer,
Singlesig::Wpkh,
@@ -658,8 +669,8 @@ impl LiquidSdk {
pub(crate) fn boltz_url_v2(network: Network) -> &'static str {
match network {
Network::LiquidTestnet => BOLTZ_TESTNET_URL_V2,
Network::Liquid => BOLTZ_MAINNET_URL_V2,
Network::Testnet => BOLTZ_TESTNET_URL_V2,
Network::Mainnet => BOLTZ_MAINNET_URL_V2,
}
}
@@ -705,8 +716,8 @@ impl LiquidSdk {
.map_err(|_| PaymentError::InvalidInvoice)?;
match (invoice.network().to_string().as_str(), self.network) {
("bitcoin", Network::Liquid) => {}
("testnet", Network::LiquidTestnet) => {}
("bitcoin", Network::Mainnet) => {}
("testnet", Network::Testnet) => {}
_ => return Err(PaymentError::InvalidInvoice),
}
@@ -740,8 +751,8 @@ impl LiquidSdk {
// TODO Replace this with own address when LWK supports taproot
// https://github.com/Blockstream/lwk/issues/31
let temp_p2tr_addr = match self.network {
Network::Liquid => "lq1pqvzxvqhrf54dd4sny4cag7497pe38252qefk46t92frs7us8r80ja9ha8r5me09nn22m4tmdqp5p4wafq3s59cql3v9n45t5trwtxrmxfsyxjnstkctj",
Network::LiquidTestnet => "tlq1pq0wqu32e2xacxeyps22x8gjre4qk3u6r70pj4r62hzczxeyz8x3yxucrpn79zy28plc4x37aaf33kwt6dz2nn6gtkya6h02mwpzy4eh69zzexq7cf5y5"
Network::Mainnet => "lq1pqvzxvqhrf54dd4sny4cag7497pe38252qefk46t92frs7us8r80ja9ha8r5me09nn22m4tmdqp5p4wafq3s59cql3v9n45t5trwtxrmxfsyxjnstkctj",
Network::Testnet => "tlq1pq0wqu32e2xacxeyps22x8gjre4qk3u6r70pj4r62hzczxeyz8x3yxucrpn79zy28plc4x37aaf33kwt6dz2nn6gtkya6h02mwpzy4eh69zzexq7cf5y5"
};
// Create a throw-away tx similar to the lockup tx, in order to estimate fees
@@ -884,8 +895,8 @@ impl LiquidSdk {
Amount::from_sat(self.get_broadcast_fee_estimation(amount_sat).await?);
let client = self.boltz_client_v2();
let is_lowball = match self.network {
Network::Liquid => None,
Network::LiquidTestnet => Some((&client, boltz_client::network::Chain::LiquidTestnet)),
Network::Mainnet => None,
Network::Testnet => Some((&client, boltz_client::network::Chain::LiquidTestnet)),
};
match self
@@ -1399,6 +1410,54 @@ impl LiquidSdk {
err: format!("Could not create LiquidSwapKey: {e:?}"),
})
}
pub fn parse_invoice(input: &str) -> Result<LNInvoice, PaymentError> {
let input = input
.strip_prefix("lightning:")
.or(input.strip_prefix("LIGHTNING:"))
.unwrap_or(input);
let invoice = Bolt11Invoice::from_str(input).map_err(|_| PaymentError::InvalidInvoice)?;
// Try to take payee pubkey from the tagged fields, if doesn't exist recover it from the signature
let payee_pubkey: String = match invoice.payee_pub_key() {
Some(key) => key.serialize().to_hex(),
None => invoice.recover_payee_pub_key().serialize().to_hex(),
};
let description = match invoice.description() {
Bolt11InvoiceDescription::Direct(msg) => Some(msg.to_string()),
Bolt11InvoiceDescription::Hash(_) => None,
};
let description_hash = match invoice.description() {
Bolt11InvoiceDescription::Direct(_) => None,
Bolt11InvoiceDescription::Hash(h) => Some(h.0.to_string()),
};
let timestamp = invoice
.timestamp()
.duration_since(UNIX_EPOCH)
.map_err(|_| PaymentError::InvalidInvoice)?
.as_secs();
let routing_hints = invoice
.route_hints()
.iter()
.map(RouteHint::from_ldk_hint)
.collect();
let res = LNInvoice {
bolt11: input.to_string(),
network: invoice.currency().try_into()?,
payee_pubkey,
payment_hash: invoice.payment_hash().to_hex(),
description,
description_hash,
amount_msat: invoice.amount_milli_satoshis(),
timestamp,
expiry: invoice.expiry_time().as_secs(),
routing_hints,
payment_secret: invoice.payment_secret().0.to_vec(),
min_final_cltv_expiry_delta: invoice.min_final_cltv_expiry_delta(),
};
Ok(res)
}
}
#[cfg(test)]
@@ -1438,7 +1497,7 @@ mod tests {
let sdk = LiquidSdk::connect(ConnectRequest {
mnemonic: TEST_MNEMONIC.to_string(),
data_dir: Some(data_dir_str),
network: Network::LiquidTestnet,
network: Network::Testnet,
})
.await?;
@@ -1456,13 +1515,15 @@ mod tests {
let sdk = LiquidSdk::connect(ConnectRequest {
mnemonic: TEST_MNEMONIC.to_string(),
data_dir: Some(data_dir_str),
network: Network::LiquidTestnet,
network: Network::Testnet,
})
.await?;
let prepare_response = sdk.prepare_receive_payment(&PrepareReceiveRequest {
let prepare_response = sdk
.prepare_receive_payment(&PrepareReceiveRequest {
payer_amount_sat: 1_000,
}).await?;
})
.await?;
sdk.receive_payment(&prepare_response).await?;
assert!(!list_pending(&sdk).await?.is_empty());

View File

@@ -13,6 +13,9 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
Future<BindingLiquidSdk> connect({required ConnectRequest req, dynamic hint}) =>
RustLib.instance.api.crateBindingsConnect(req: req, hint: hint);
Future<LNInvoice> parseInvoice({required String input, dynamic hint}) =>
RustLib.instance.api.crateBindingsParseInvoice(input: input, hint: hint);
// Rust type: RustOpaqueNom<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<BindingLiquidSdk>>
@sealed
class BindingLiquidSdk extends RustOpaque {

View File

@@ -53,7 +53,7 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
String get codegenVersion => '2.0.0-dev.36';
@override
int get rustContentHash => -1552546000;
int get rustContentHash => 84136112;
static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig(
stem: 'breez_liquid_sdk',
@@ -98,6 +98,8 @@ abstract class RustLibApi extends BaseApi {
Future<BindingLiquidSdk> crateBindingsConnect({required ConnectRequest req, dynamic hint});
Future<LNInvoice> crateBindingsParseInvoice({required String input, dynamic hint});
RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_BindingLiquidSdk;
RustArcDecrementStrongCountFnType get rust_arc_decrement_strong_count_BindingLiquidSdk;
@@ -456,6 +458,29 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
argNames: ["req"],
);
@override
Future<LNInvoice> crateBindingsParseInvoice({required String input, dynamic hint}) {
return handler.executeNormal(NormalTask(
callFfi: (port_) {
var arg0 = cst_encode_String(input);
return wire.wire__crate__bindings__parse_invoice(port_, arg0);
},
codec: DcoCodec(
decodeSuccessData: dco_decode_ln_invoice,
decodeErrorData: dco_decode_payment_error,
),
constMeta: kCrateBindingsParseInvoiceConstMeta,
argValues: [input],
apiImpl: this,
hint: hint,
));
}
TaskConstMeta get kCrateBindingsParseInvoiceConstMeta => const TaskConstMeta(
debugName: "parse_invoice",
argNames: ["input"],
);
RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_BindingLiquidSdk => wire
.rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk;
@@ -678,6 +703,39 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return raw as Uint8List;
}
@protected
List<RouteHint> dco_decode_list_route_hint(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return (raw as List<dynamic>).map(dco_decode_route_hint).toList();
}
@protected
List<RouteHintHop> dco_decode_list_route_hint_hop(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return (raw as List<dynamic>).map(dco_decode_route_hint_hop).toList();
}
@protected
LNInvoice dco_decode_ln_invoice(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
final arr = raw as List<dynamic>;
if (arr.length != 12) throw Exception('unexpected arr length: expect 12 but see ${arr.length}');
return LNInvoice(
bolt11: dco_decode_String(arr[0]),
network: dco_decode_network(arr[1]),
payeePubkey: dco_decode_String(arr[2]),
paymentHash: dco_decode_String(arr[3]),
description: dco_decode_opt_String(arr[4]),
descriptionHash: dco_decode_opt_String(arr[5]),
amountMsat: dco_decode_opt_box_autoadd_u_64(arr[6]),
timestamp: dco_decode_u_64(arr[7]),
expiry: dco_decode_u_64(arr[8]),
routingHints: dco_decode_list_route_hint(arr[9]),
paymentSecret: dco_decode_list_prim_u_8_strict(arr[10]),
minFinalCltvExpiryDelta: dco_decode_u_64(arr[11]),
);
}
@protected
Network dco_decode_network(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
@@ -836,6 +894,32 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
);
}
@protected
RouteHint dco_decode_route_hint(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
final arr = raw as List<dynamic>;
if (arr.length != 1) throw Exception('unexpected arr length: expect 1 but see ${arr.length}');
return RouteHint(
hops: dco_decode_list_route_hint_hop(arr[0]),
);
}
@protected
RouteHintHop dco_decode_route_hint_hop(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
final arr = raw as List<dynamic>;
if (arr.length != 7) throw Exception('unexpected arr length: expect 7 but see ${arr.length}');
return RouteHintHop(
srcNodeId: dco_decode_String(arr[0]),
shortChannelId: dco_decode_u_64(arr[1]),
feesBaseMsat: dco_decode_u_32(arr[2]),
feesProportionalMillionths: dco_decode_u_32(arr[3]),
cltvExpiryDelta: dco_decode_u_64(arr[4]),
htlcMinimumMsat: dco_decode_opt_box_autoadd_u_64(arr[5]),
htlcMaximumMsat: dco_decode_opt_box_autoadd_u_64(arr[6]),
);
}
@protected
SendPaymentResponse dco_decode_send_payment_response(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
@@ -1089,6 +1173,60 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return deserializer.buffer.getUint8List(len_);
}
@protected
List<RouteHint> sse_decode_list_route_hint(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
var len_ = sse_decode_i_32(deserializer);
var ans_ = <RouteHint>[];
for (var idx_ = 0; idx_ < len_; ++idx_) {
ans_.add(sse_decode_route_hint(deserializer));
}
return ans_;
}
@protected
List<RouteHintHop> sse_decode_list_route_hint_hop(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
var len_ = sse_decode_i_32(deserializer);
var ans_ = <RouteHintHop>[];
for (var idx_ = 0; idx_ < len_; ++idx_) {
ans_.add(sse_decode_route_hint_hop(deserializer));
}
return ans_;
}
@protected
LNInvoice sse_decode_ln_invoice(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
var var_bolt11 = sse_decode_String(deserializer);
var var_network = sse_decode_network(deserializer);
var var_payeePubkey = sse_decode_String(deserializer);
var var_paymentHash = sse_decode_String(deserializer);
var var_description = sse_decode_opt_String(deserializer);
var var_descriptionHash = sse_decode_opt_String(deserializer);
var var_amountMsat = sse_decode_opt_box_autoadd_u_64(deserializer);
var var_timestamp = sse_decode_u_64(deserializer);
var var_expiry = sse_decode_u_64(deserializer);
var var_routingHints = sse_decode_list_route_hint(deserializer);
var var_paymentSecret = sse_decode_list_prim_u_8_strict(deserializer);
var var_minFinalCltvExpiryDelta = sse_decode_u_64(deserializer);
return LNInvoice(
bolt11: var_bolt11,
network: var_network,
payeePubkey: var_payeePubkey,
paymentHash: var_paymentHash,
description: var_description,
descriptionHash: var_descriptionHash,
amountMsat: var_amountMsat,
timestamp: var_timestamp,
expiry: var_expiry,
routingHints: var_routingHints,
paymentSecret: var_paymentSecret,
minFinalCltvExpiryDelta: var_minFinalCltvExpiryDelta);
}
@protected
Network sse_decode_network(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
@@ -1246,6 +1384,33 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return RestoreRequest(backupPath: var_backupPath);
}
@protected
RouteHint sse_decode_route_hint(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
var var_hops = sse_decode_list_route_hint_hop(deserializer);
return RouteHint(hops: var_hops);
}
@protected
RouteHintHop sse_decode_route_hint_hop(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
var var_srcNodeId = sse_decode_String(deserializer);
var var_shortChannelId = sse_decode_u_64(deserializer);
var var_feesBaseMsat = sse_decode_u_32(deserializer);
var var_feesProportionalMillionths = sse_decode_u_32(deserializer);
var var_cltvExpiryDelta = sse_decode_u_64(deserializer);
var var_htlcMinimumMsat = sse_decode_opt_box_autoadd_u_64(deserializer);
var var_htlcMaximumMsat = sse_decode_opt_box_autoadd_u_64(deserializer);
return RouteHintHop(
srcNodeId: var_srcNodeId,
shortChannelId: var_shortChannelId,
feesBaseMsat: var_feesBaseMsat,
feesProportionalMillionths: var_feesProportionalMillionths,
cltvExpiryDelta: var_cltvExpiryDelta,
htlcMinimumMsat: var_htlcMinimumMsat,
htlcMaximumMsat: var_htlcMaximumMsat);
}
@protected
SendPaymentResponse sse_decode_send_payment_response(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
@@ -1550,6 +1715,41 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
serializer.buffer.putUint8List(self);
}
@protected
void sse_encode_list_route_hint(List<RouteHint> 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_route_hint(item, serializer);
}
}
@protected
void sse_encode_list_route_hint_hop(List<RouteHintHop> 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_route_hint_hop(item, serializer);
}
}
@protected
void sse_encode_ln_invoice(LNInvoice self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_String(self.bolt11, serializer);
sse_encode_network(self.network, serializer);
sse_encode_String(self.payeePubkey, serializer);
sse_encode_String(self.paymentHash, serializer);
sse_encode_opt_String(self.description, serializer);
sse_encode_opt_String(self.descriptionHash, serializer);
sse_encode_opt_box_autoadd_u_64(self.amountMsat, serializer);
sse_encode_u_64(self.timestamp, serializer);
sse_encode_u_64(self.expiry, serializer);
sse_encode_list_route_hint(self.routingHints, serializer);
sse_encode_list_prim_u_8_strict(self.paymentSecret, serializer);
sse_encode_u_64(self.minFinalCltvExpiryDelta, serializer);
}
@protected
void sse_encode_network(Network self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
@@ -1681,6 +1881,24 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_opt_String(self.backupPath, serializer);
}
@protected
void sse_encode_route_hint(RouteHint self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_list_route_hint_hop(self.hops, serializer);
}
@protected
void sse_encode_route_hint_hop(RouteHintHop self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_String(self.srcNodeId, serializer);
sse_encode_u_64(self.shortChannelId, serializer);
sse_encode_u_32(self.feesBaseMsat, serializer);
sse_encode_u_32(self.feesProportionalMillionths, serializer);
sse_encode_u_64(self.cltvExpiryDelta, serializer);
sse_encode_opt_box_autoadd_u_64(self.htlcMinimumMsat, serializer);
sse_encode_opt_box_autoadd_u_64(self.htlcMaximumMsat, serializer);
}
@protected
void sse_encode_send_payment_response(SendPaymentResponse self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs

View File

@@ -103,6 +103,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
@protected
List<RouteHint> dco_decode_list_route_hint(dynamic raw);
@protected
List<RouteHintHop> dco_decode_list_route_hint_hop(dynamic raw);
@protected
LNInvoice dco_decode_ln_invoice(dynamic raw);
@protected
Network dco_decode_network(dynamic raw);
@@ -142,6 +151,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
RestoreRequest dco_decode_restore_request(dynamic raw);
@protected
RouteHint dco_decode_route_hint(dynamic raw);
@protected
RouteHintHop dco_decode_route_hint_hop(dynamic raw);
@protected
SendPaymentResponse dco_decode_send_payment_response(dynamic raw);
@@ -240,6 +255,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
@protected
List<RouteHint> sse_decode_list_route_hint(SseDeserializer deserializer);
@protected
List<RouteHintHop> sse_decode_list_route_hint_hop(SseDeserializer deserializer);
@protected
LNInvoice sse_decode_ln_invoice(SseDeserializer deserializer);
@protected
Network sse_decode_network(SseDeserializer deserializer);
@@ -279,6 +303,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
RestoreRequest sse_decode_restore_request(SseDeserializer deserializer);
@protected
RouteHint sse_decode_route_hint(SseDeserializer deserializer);
@protected
RouteHintHop sse_decode_route_hint_hop(SseDeserializer deserializer);
@protected
SendPaymentResponse sse_decode_send_payment_response(SseDeserializer deserializer);
@@ -411,6 +441,26 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
return ans;
}
@protected
ffi.Pointer<wire_cst_list_route_hint> cst_encode_list_route_hint(List<RouteHint> raw) {
// Codec=Cst (C-struct based), see doc to use other codecs
final ans = wire.cst_new_list_route_hint(raw.length);
for (var i = 0; i < raw.length; ++i) {
cst_api_fill_to_wire_route_hint(raw[i], ans.ref.ptr[i]);
}
return ans;
}
@protected
ffi.Pointer<wire_cst_list_route_hint_hop> cst_encode_list_route_hint_hop(List<RouteHintHop> raw) {
// Codec=Cst (C-struct based), see doc to use other codecs
final ans = wire.cst_new_list_route_hint_hop(raw.length);
for (var i = 0; i < raw.length; ++i) {
cst_api_fill_to_wire_route_hint_hop(raw[i], ans.ref.ptr[i]);
}
return ans;
}
@protected
ffi.Pointer<wire_cst_list_prim_u_8_strict> cst_encode_opt_String(String? raw) {
// Codec=Cst (C-struct based), see doc to use other codecs
@@ -575,6 +625,22 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
}
}
@protected
void cst_api_fill_to_wire_ln_invoice(LNInvoice apiObj, wire_cst_ln_invoice wireObj) {
wireObj.bolt11 = cst_encode_String(apiObj.bolt11);
wireObj.network = cst_encode_network(apiObj.network);
wireObj.payee_pubkey = cst_encode_String(apiObj.payeePubkey);
wireObj.payment_hash = cst_encode_String(apiObj.paymentHash);
wireObj.description = cst_encode_opt_String(apiObj.description);
wireObj.description_hash = cst_encode_opt_String(apiObj.descriptionHash);
wireObj.amount_msat = cst_encode_opt_box_autoadd_u_64(apiObj.amountMsat);
wireObj.timestamp = cst_encode_u_64(apiObj.timestamp);
wireObj.expiry = cst_encode_u_64(apiObj.expiry);
wireObj.routing_hints = cst_encode_list_route_hint(apiObj.routingHints);
wireObj.payment_secret = cst_encode_list_prim_u_8_strict(apiObj.paymentSecret);
wireObj.min_final_cltv_expiry_delta = cst_encode_u_64(apiObj.minFinalCltvExpiryDelta);
}
@protected
void cst_api_fill_to_wire_payment(Payment apiObj, wire_cst_payment wireObj) {
wireObj.tx_id = cst_encode_String(apiObj.txId);
@@ -695,6 +761,22 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
wireObj.backup_path = cst_encode_opt_String(apiObj.backupPath);
}
@protected
void cst_api_fill_to_wire_route_hint(RouteHint apiObj, wire_cst_route_hint wireObj) {
wireObj.hops = cst_encode_list_route_hint_hop(apiObj.hops);
}
@protected
void cst_api_fill_to_wire_route_hint_hop(RouteHintHop apiObj, wire_cst_route_hint_hop wireObj) {
wireObj.src_node_id = cst_encode_String(apiObj.srcNodeId);
wireObj.short_channel_id = cst_encode_u_64(apiObj.shortChannelId);
wireObj.fees_base_msat = cst_encode_u_32(apiObj.feesBaseMsat);
wireObj.fees_proportional_millionths = cst_encode_u_32(apiObj.feesProportionalMillionths);
wireObj.cltv_expiry_delta = cst_encode_u_64(apiObj.cltvExpiryDelta);
wireObj.htlc_minimum_msat = cst_encode_opt_box_autoadd_u_64(apiObj.htlcMinimumMsat);
wireObj.htlc_maximum_msat = cst_encode_opt_box_autoadd_u_64(apiObj.htlcMaximumMsat);
}
@protected
void cst_api_fill_to_wire_send_payment_response(
SendPaymentResponse apiObj, wire_cst_send_payment_response wireObj) {
@@ -816,6 +898,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
void sse_encode_list_prim_u_8_strict(Uint8List self, SseSerializer serializer);
@protected
void sse_encode_list_route_hint(List<RouteHint> self, SseSerializer serializer);
@protected
void sse_encode_list_route_hint_hop(List<RouteHintHop> self, SseSerializer serializer);
@protected
void sse_encode_ln_invoice(LNInvoice self, SseSerializer serializer);
@protected
void sse_encode_network(Network self, SseSerializer serializer);
@@ -855,6 +946,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
void sse_encode_restore_request(RestoreRequest self, SseSerializer serializer);
@protected
void sse_encode_route_hint(RouteHint self, SseSerializer serializer);
@protected
void sse_encode_route_hint_hop(RouteHintHop self, SseSerializer serializer);
@protected
void sse_encode_send_payment_response(SendPaymentResponse self, SseSerializer serializer);
@@ -1149,6 +1246,22 @@ class RustLibWire implements BaseWire {
late final _wire__crate__bindings__connect = _wire__crate__bindings__connectPtr
.asFunction<void Function(int, ffi.Pointer<wire_cst_connect_request>)>();
void wire__crate__bindings__parse_invoice(
int port_,
ffi.Pointer<wire_cst_list_prim_u_8_strict> input,
) {
return _wire__crate__bindings__parse_invoice(
port_,
input,
);
}
late final _wire__crate__bindings__parse_invoicePtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64, ffi.Pointer<wire_cst_list_prim_u_8_strict>)>>(
'frbgen_breez_liquid_wire__crate__bindings__parse_invoice');
late final _wire__crate__bindings__parse_invoice = _wire__crate__bindings__parse_invoicePtr
.asFunction<void Function(int, ffi.Pointer<wire_cst_list_prim_u_8_strict>)>();
void
rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk(
ffi.Pointer<ffi.Void> ptr,
@@ -1313,6 +1426,34 @@ class RustLibWire implements BaseWire {
late final _cst_new_list_prim_u_8_strict =
_cst_new_list_prim_u_8_strictPtr.asFunction<ffi.Pointer<wire_cst_list_prim_u_8_strict> Function(int)>();
ffi.Pointer<wire_cst_list_route_hint> cst_new_list_route_hint(
int len,
) {
return _cst_new_list_route_hint(
len,
);
}
late final _cst_new_list_route_hintPtr =
_lookup<ffi.NativeFunction<ffi.Pointer<wire_cst_list_route_hint> Function(ffi.Int32)>>(
'frbgen_breez_liquid_cst_new_list_route_hint');
late final _cst_new_list_route_hint =
_cst_new_list_route_hintPtr.asFunction<ffi.Pointer<wire_cst_list_route_hint> Function(int)>();
ffi.Pointer<wire_cst_list_route_hint_hop> cst_new_list_route_hint_hop(
int len,
) {
return _cst_new_list_route_hint_hop(
len,
);
}
late final _cst_new_list_route_hint_hopPtr =
_lookup<ffi.NativeFunction<ffi.Pointer<wire_cst_list_route_hint_hop> Function(ffi.Int32)>>(
'frbgen_breez_liquid_cst_new_list_route_hint_hop');
late final _cst_new_list_route_hint_hop =
_cst_new_list_route_hint_hopPtr.asFunction<ffi.Pointer<wire_cst_list_route_hint_hop> Function(int)>();
int dummy_method_to_enforce_bundling() {
return _dummy_method_to_enforce_bundling();
}
@@ -1416,6 +1557,44 @@ final class wire_cst_list_payment extends ffi.Struct {
external int len;
}
final class wire_cst_route_hint_hop extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> src_node_id;
@ffi.Uint64()
external int short_channel_id;
@ffi.Uint32()
external int fees_base_msat;
@ffi.Uint32()
external int fees_proportional_millionths;
@ffi.Uint64()
external int cltv_expiry_delta;
external ffi.Pointer<ffi.Uint64> htlc_minimum_msat;
external ffi.Pointer<ffi.Uint64> htlc_maximum_msat;
}
final class wire_cst_list_route_hint_hop extends ffi.Struct {
external ffi.Pointer<wire_cst_route_hint_hop> ptr;
@ffi.Int32()
external int len;
}
final class wire_cst_route_hint extends ffi.Struct {
external ffi.Pointer<wire_cst_list_route_hint_hop> hops;
}
final class wire_cst_list_route_hint extends ffi.Struct {
external ffi.Pointer<wire_cst_route_hint> ptr;
@ffi.Int32()
external int len;
}
final class wire_cst_get_info_response extends ffi.Struct {
@ffi.Uint64()
external int balance_sat;
@@ -1489,6 +1668,36 @@ final class wire_cst_liquid_sdk_event extends ffi.Struct {
external LiquidSdkEventKind kind;
}
final class wire_cst_ln_invoice extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> bolt11;
@ffi.Int32()
external int network;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> payee_pubkey;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> payment_hash;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> description;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> description_hash;
external ffi.Pointer<ffi.Uint64> amount_msat;
@ffi.Uint64()
external int timestamp;
@ffi.Uint64()
external int expiry;
external ffi.Pointer<wire_cst_list_route_hint> routing_hints;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> payment_secret;
@ffi.Uint64()
external int min_final_cltv_expiry_delta;
}
final class wire_cst_PaymentError_Generic extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> err;
}

View File

@@ -126,9 +126,76 @@ sealed class LiquidSdkEvent with _$LiquidSdkEvent {
const factory LiquidSdkEvent.synced() = LiquidSdkEvent_Synced;
}
/// Wrapper for a BOLT11 LN invoice
class LNInvoice {
final String bolt11;
final Network network;
final String payeePubkey;
final String paymentHash;
final String? description;
final String? descriptionHash;
final BigInt? amountMsat;
final BigInt timestamp;
final BigInt expiry;
final List<RouteHint> routingHints;
final Uint8List paymentSecret;
final BigInt minFinalCltvExpiryDelta;
const LNInvoice({
required this.bolt11,
required this.network,
required this.payeePubkey,
required this.paymentHash,
this.description,
this.descriptionHash,
this.amountMsat,
required this.timestamp,
required this.expiry,
required this.routingHints,
required this.paymentSecret,
required this.minFinalCltvExpiryDelta,
});
@override
int get hashCode =>
bolt11.hashCode ^
network.hashCode ^
payeePubkey.hashCode ^
paymentHash.hashCode ^
description.hashCode ^
descriptionHash.hashCode ^
amountMsat.hashCode ^
timestamp.hashCode ^
expiry.hashCode ^
routingHints.hashCode ^
paymentSecret.hashCode ^
minFinalCltvExpiryDelta.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is LNInvoice &&
runtimeType == other.runtimeType &&
bolt11 == other.bolt11 &&
network == other.network &&
payeePubkey == other.payeePubkey &&
paymentHash == other.paymentHash &&
description == other.description &&
descriptionHash == other.descriptionHash &&
amountMsat == other.amountMsat &&
timestamp == other.timestamp &&
expiry == other.expiry &&
routingHints == other.routingHints &&
paymentSecret == other.paymentSecret &&
minFinalCltvExpiryDelta == other.minFinalCltvExpiryDelta;
}
enum Network {
liquid,
liquidTestnet,
/// Mainnet Bitcoin and Liquid chains
mainnet,
/// Testnet Bitcoin and Liquid chains
testnet,
;
}
@@ -386,6 +453,77 @@ class RestoreRequest {
other is RestoreRequest && runtimeType == other.runtimeType && backupPath == other.backupPath;
}
/// A route hint for a LN payment
class RouteHint {
final List<RouteHintHop> hops;
const RouteHint({
required this.hops,
});
@override
int get hashCode => hops.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) || other is RouteHint && runtimeType == other.runtimeType && hops == other.hops;
}
/// Details of a specific hop in a larger route hint
class RouteHintHop {
/// The node_id of the non-target end of the route
final String srcNodeId;
/// The short_channel_id of this channel
final BigInt shortChannelId;
/// The fees which must be paid to use this channel
final int feesBaseMsat;
final int feesProportionalMillionths;
/// The difference in CLTV values between this node and the next node.
final BigInt cltvExpiryDelta;
/// The minimum value, in msat, which must be relayed to the next hop.
final BigInt? htlcMinimumMsat;
/// The maximum value in msat available for routing with a single HTLC.
final BigInt? htlcMaximumMsat;
const RouteHintHop({
required this.srcNodeId,
required this.shortChannelId,
required this.feesBaseMsat,
required this.feesProportionalMillionths,
required this.cltvExpiryDelta,
this.htlcMinimumMsat,
this.htlcMaximumMsat,
});
@override
int get hashCode =>
srcNodeId.hashCode ^
shortChannelId.hashCode ^
feesBaseMsat.hashCode ^
feesProportionalMillionths.hashCode ^
cltvExpiryDelta.hashCode ^
htlcMinimumMsat.hashCode ^
htlcMaximumMsat.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is RouteHintHop &&
runtimeType == other.runtimeType &&
srcNodeId == other.srcNodeId &&
shortChannelId == other.shortChannelId &&
feesBaseMsat == other.feesBaseMsat &&
feesProportionalMillionths == other.feesProportionalMillionths &&
cltvExpiryDelta == other.cltvExpiryDelta &&
htlcMinimumMsat == other.htlcMinimumMsat &&
htlcMaximumMsat == other.htlcMaximumMsat;
}
class SendPaymentResponse {
final Payment payment;

View File

@@ -77,6 +77,23 @@ class FlutterBreezLiquidBindings {
_frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_backupPtr
.asFunction<void Function(int, int, ffi.Pointer<wire_cst_backup_request>)>();
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_disconnect(
int port_,
int that,
) {
return _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_disconnect(
port_,
that,
);
}
late final _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_disconnectPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64, ffi.UintPtr)>>(
'frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_disconnect');
late final _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_disconnect =
_frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_disconnectPtr
.asFunction<void Function(int, int)>();
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_empty_wallet_cache(
int port_,
int that,
@@ -266,6 +283,23 @@ class FlutterBreezLiquidBindings {
_frbgen_breez_liquid_wire__crate__bindings__connectPtr
.asFunction<void Function(int, ffi.Pointer<wire_cst_connect_request>)>();
void frbgen_breez_liquid_wire__crate__bindings__parse_invoice(
int port_,
ffi.Pointer<wire_cst_list_prim_u_8_strict> input,
) {
return _frbgen_breez_liquid_wire__crate__bindings__parse_invoice(
port_,
input,
);
}
late final _frbgen_breez_liquid_wire__crate__bindings__parse_invoicePtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64, ffi.Pointer<wire_cst_list_prim_u_8_strict>)>>(
'frbgen_breez_liquid_wire__crate__bindings__parse_invoice');
late final _frbgen_breez_liquid_wire__crate__bindings__parse_invoice =
_frbgen_breez_liquid_wire__crate__bindings__parse_invoicePtr
.asFunction<void Function(int, ffi.Pointer<wire_cst_list_prim_u_8_strict>)>();
void
frbgen_breez_liquid_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk(
ffi.Pointer<ffi.Void> ptr,
@@ -443,6 +477,35 @@ class FlutterBreezLiquidBindings {
_frbgen_breez_liquid_cst_new_list_prim_u_8_strictPtr
.asFunction<ffi.Pointer<wire_cst_list_prim_u_8_strict> Function(int)>();
ffi.Pointer<wire_cst_list_route_hint> frbgen_breez_liquid_cst_new_list_route_hint(
int len,
) {
return _frbgen_breez_liquid_cst_new_list_route_hint(
len,
);
}
late final _frbgen_breez_liquid_cst_new_list_route_hintPtr =
_lookup<ffi.NativeFunction<ffi.Pointer<wire_cst_list_route_hint> Function(ffi.Int32)>>(
'frbgen_breez_liquid_cst_new_list_route_hint');
late final _frbgen_breez_liquid_cst_new_list_route_hint = _frbgen_breez_liquid_cst_new_list_route_hintPtr
.asFunction<ffi.Pointer<wire_cst_list_route_hint> Function(int)>();
ffi.Pointer<wire_cst_list_route_hint_hop> frbgen_breez_liquid_cst_new_list_route_hint_hop(
int len,
) {
return _frbgen_breez_liquid_cst_new_list_route_hint_hop(
len,
);
}
late final _frbgen_breez_liquid_cst_new_list_route_hint_hopPtr =
_lookup<ffi.NativeFunction<ffi.Pointer<wire_cst_list_route_hint_hop> Function(ffi.Int32)>>(
'frbgen_breez_liquid_cst_new_list_route_hint_hop');
late final _frbgen_breez_liquid_cst_new_list_route_hint_hop =
_frbgen_breez_liquid_cst_new_list_route_hint_hopPtr
.asFunction<ffi.Pointer<wire_cst_list_route_hint_hop> Function(int)>();
int dummy_method_to_enforce_bundling() {
return _dummy_method_to_enforce_bundling();
}
@@ -557,6 +620,44 @@ final class wire_cst_list_payment extends ffi.Struct {
external int len;
}
final class wire_cst_route_hint_hop extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> src_node_id;
@ffi.Uint64()
external int short_channel_id;
@ffi.Uint32()
external int fees_base_msat;
@ffi.Uint32()
external int fees_proportional_millionths;
@ffi.Uint64()
external int cltv_expiry_delta;
external ffi.Pointer<ffi.Uint64> htlc_minimum_msat;
external ffi.Pointer<ffi.Uint64> htlc_maximum_msat;
}
final class wire_cst_list_route_hint_hop extends ffi.Struct {
external ffi.Pointer<wire_cst_route_hint_hop> ptr;
@ffi.Int32()
external int len;
}
final class wire_cst_route_hint extends ffi.Struct {
external ffi.Pointer<wire_cst_list_route_hint_hop> hops;
}
final class wire_cst_list_route_hint extends ffi.Struct {
external ffi.Pointer<wire_cst_route_hint> ptr;
@ffi.Int32()
external int len;
}
final class wire_cst_get_info_response extends ffi.Struct {
@ffi.Uint64()
external int balance_sat;
@@ -630,6 +731,36 @@ final class wire_cst_liquid_sdk_event extends ffi.Struct {
external LiquidSdkEventKind kind;
}
final class wire_cst_ln_invoice extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> bolt11;
@ffi.Int32()
external int network;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> payee_pubkey;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> payment_hash;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> description;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> description_hash;
external ffi.Pointer<ffi.Uint64> amount_msat;
@ffi.Uint64()
external int timestamp;
@ffi.Uint64()
external int expiry;
external ffi.Pointer<wire_cst_list_route_hint> routing_hints;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> payment_secret;
@ffi.Uint64()
external int min_final_cltv_expiry_delta;
}
final class wire_cst_PaymentError_Generic extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> err;
}

View File

@@ -152,6 +152,80 @@ fun asGetInfoResponseList(arr: ReadableArray): List<GetInfoResponse> {
return list
}
fun asLnInvoice(lnInvoice: ReadableMap): LnInvoice? {
if (!validateMandatoryFields(
lnInvoice,
arrayOf(
"bolt11",
"network",
"payeePubkey",
"paymentHash",
"timestamp",
"expiry",
"routingHints",
"paymentSecret",
"minFinalCltvExpiryDelta",
),
)
) {
return null
}
val bolt11 = lnInvoice.getString("bolt11")!!
val network = lnInvoice.getString("network")?.let { asNetwork(it) }!!
val payeePubkey = lnInvoice.getString("payeePubkey")!!
val paymentHash = lnInvoice.getString("paymentHash")!!
val description = if (hasNonNullKey(lnInvoice, "description")) lnInvoice.getString("description") else null
val descriptionHash = if (hasNonNullKey(lnInvoice, "descriptionHash")) lnInvoice.getString("descriptionHash") else null
val amountMsat = if (hasNonNullKey(lnInvoice, "amountMsat")) lnInvoice.getDouble("amountMsat").toULong() else null
val timestamp = lnInvoice.getDouble("timestamp").toULong()
val expiry = lnInvoice.getDouble("expiry").toULong()
val routingHints = lnInvoice.getArray("routingHints")?.let { asRouteHintList(it) }!!
val paymentSecret = lnInvoice.getArray("paymentSecret")?.let { asUByteList(it) }!!
val minFinalCltvExpiryDelta = lnInvoice.getDouble("minFinalCltvExpiryDelta").toULong()
return LnInvoice(
bolt11,
network,
payeePubkey,
paymentHash,
description,
descriptionHash,
amountMsat,
timestamp,
expiry,
routingHints,
paymentSecret,
minFinalCltvExpiryDelta,
)
}
fun readableMapOf(lnInvoice: LnInvoice): ReadableMap {
return readableMapOf(
"bolt11" to lnInvoice.bolt11,
"network" to lnInvoice.network.name.lowercase(),
"payeePubkey" to lnInvoice.payeePubkey,
"paymentHash" to lnInvoice.paymentHash,
"description" to lnInvoice.description,
"descriptionHash" to lnInvoice.descriptionHash,
"amountMsat" to lnInvoice.amountMsat,
"timestamp" to lnInvoice.timestamp,
"expiry" to lnInvoice.expiry,
"routingHints" to readableArrayOf(lnInvoice.routingHints),
"paymentSecret" to readableArrayOf(lnInvoice.paymentSecret),
"minFinalCltvExpiryDelta" to lnInvoice.minFinalCltvExpiryDelta,
)
}
fun asLnInvoiceList(arr: ReadableArray): List<LnInvoice> {
val list = ArrayList<LnInvoice>()
for (value in arr.toArrayList()) {
when (value) {
is ReadableMap -> list.add(asLnInvoice(value)!!)
else -> throw LiquidSdkException.Generic(errUnexpectedType("${value::class.java.name}"))
}
}
return list
}
fun asPayment(payment: ReadableMap): Payment? {
if (!validateMandatoryFields(
payment,
@@ -424,6 +498,94 @@ fun asRestoreRequestList(arr: ReadableArray): List<RestoreRequest> {
return list
}
fun asRouteHint(routeHint: ReadableMap): RouteHint? {
if (!validateMandatoryFields(
routeHint,
arrayOf(
"hops",
),
)
) {
return null
}
val hops = routeHint.getArray("hops")?.let { asRouteHintHopList(it) }!!
return RouteHint(
hops,
)
}
fun readableMapOf(routeHint: RouteHint): ReadableMap {
return readableMapOf(
"hops" to readableArrayOf(routeHint.hops),
)
}
fun asRouteHintList(arr: ReadableArray): List<RouteHint> {
val list = ArrayList<RouteHint>()
for (value in arr.toArrayList()) {
when (value) {
is ReadableMap -> list.add(asRouteHint(value)!!)
else -> throw LiquidSdkException.Generic(errUnexpectedType("${value::class.java.name}"))
}
}
return list
}
fun asRouteHintHop(routeHintHop: ReadableMap): RouteHintHop? {
if (!validateMandatoryFields(
routeHintHop,
arrayOf(
"srcNodeId",
"shortChannelId",
"feesBaseMsat",
"feesProportionalMillionths",
"cltvExpiryDelta",
),
)
) {
return null
}
val srcNodeId = routeHintHop.getString("srcNodeId")!!
val shortChannelId = routeHintHop.getDouble("shortChannelId").toULong()
val feesBaseMsat = routeHintHop.getInt("feesBaseMsat").toUInt()
val feesProportionalMillionths = routeHintHop.getInt("feesProportionalMillionths").toUInt()
val cltvExpiryDelta = routeHintHop.getDouble("cltvExpiryDelta").toULong()
val htlcMinimumMsat = if (hasNonNullKey(routeHintHop, "htlcMinimumMsat")) routeHintHop.getDouble("htlcMinimumMsat").toULong() else null
val htlcMaximumMsat = if (hasNonNullKey(routeHintHop, "htlcMaximumMsat")) routeHintHop.getDouble("htlcMaximumMsat").toULong() else null
return RouteHintHop(
srcNodeId,
shortChannelId,
feesBaseMsat,
feesProportionalMillionths,
cltvExpiryDelta,
htlcMinimumMsat,
htlcMaximumMsat,
)
}
fun readableMapOf(routeHintHop: RouteHintHop): ReadableMap {
return readableMapOf(
"srcNodeId" to routeHintHop.srcNodeId,
"shortChannelId" to routeHintHop.shortChannelId,
"feesBaseMsat" to routeHintHop.feesBaseMsat,
"feesProportionalMillionths" to routeHintHop.feesProportionalMillionths,
"cltvExpiryDelta" to routeHintHop.cltvExpiryDelta,
"htlcMinimumMsat" to routeHintHop.htlcMinimumMsat,
"htlcMaximumMsat" to routeHintHop.htlcMaximumMsat,
)
}
fun asRouteHintHopList(arr: ReadableArray): List<RouteHintHop> {
val list = ArrayList<RouteHintHop>()
for (value in arr.toArrayList()) {
when (value) {
is ReadableMap -> list.add(asRouteHintHop(value)!!)
else -> throw LiquidSdkException.Generic(errUnexpectedType("${value::class.java.name}"))
}
}
return list
}
fun asSendPaymentResponse(sendPaymentResponse: ReadableMap): SendPaymentResponse? {
if (!validateMandatoryFields(
sendPaymentResponse,
@@ -607,6 +769,9 @@ fun pushToArray(
when (value) {
null -> array.pushNull()
is Payment -> array.pushMap(readableMapOf(value))
is RouteHint -> array.pushMap(readableMapOf(value))
is RouteHintHop -> array.pushMap(readableMapOf(value))
is UByte -> array.pushInt(value.toInt())
is Array<*> -> array.pushArray(readableArrayOf(value.asIterable()))
is List<*> -> array.pushArray(readableArrayOf(value))
else -> throw LiquidSdkException.Generic(errUnexpectedType("${value::class.java.name}"))

View File

@@ -40,6 +40,21 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext
@ReactMethod
fun removeListeners(count: Int) {}
@ReactMethod
fun parseInvoice(
invoice: String,
promise: Promise,
) {
executor.execute {
try {
val res = parseInvoice(invoice)
promise.resolve(readableMapOf(res))
} catch (e: Exception) {
promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e)
}
}
}
@ReactMethod
fun connect(
req: ReadableMap,

View File

@@ -167,6 +167,110 @@ enum BreezLiquidSDKMapper {
return getInfoResponseList.map { v -> [String: Any?] in dictionaryOf(getInfoResponse: v) }
}
static func asLnInvoice(lnInvoice: [String: Any?]) throws -> LnInvoice {
guard let bolt11 = lnInvoice["bolt11"] as? String else {
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "bolt11", typeName: "LnInvoice"))
}
guard let networkTmp = lnInvoice["network"] as? String else {
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "network", typeName: "LnInvoice"))
}
let network = try asNetwork(network: networkTmp)
guard let payeePubkey = lnInvoice["payeePubkey"] as? String else {
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "payeePubkey", typeName: "LnInvoice"))
}
guard let paymentHash = lnInvoice["paymentHash"] as? String else {
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "paymentHash", typeName: "LnInvoice"))
}
var description: String?
if hasNonNilKey(data: lnInvoice, key: "description") {
guard let descriptionTmp = lnInvoice["description"] as? String else {
throw LiquidSdkError.Generic(message: errUnexpectedValue(fieldName: "description"))
}
description = descriptionTmp
}
var descriptionHash: String?
if hasNonNilKey(data: lnInvoice, key: "descriptionHash") {
guard let descriptionHashTmp = lnInvoice["descriptionHash"] as? String else {
throw LiquidSdkError.Generic(message: errUnexpectedValue(fieldName: "descriptionHash"))
}
descriptionHash = descriptionHashTmp
}
var amountMsat: UInt64?
if hasNonNilKey(data: lnInvoice, key: "amountMsat") {
guard let amountMsatTmp = lnInvoice["amountMsat"] as? UInt64 else {
throw LiquidSdkError.Generic(message: errUnexpectedValue(fieldName: "amountMsat"))
}
amountMsat = amountMsatTmp
}
guard let timestamp = lnInvoice["timestamp"] as? UInt64 else {
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "timestamp", typeName: "LnInvoice"))
}
guard let expiry = lnInvoice["expiry"] as? UInt64 else {
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "expiry", typeName: "LnInvoice"))
}
guard let routingHintsTmp = lnInvoice["routingHints"] as? [[String: Any?]] else {
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "routingHints", typeName: "LnInvoice"))
}
let routingHints = try asRouteHintList(arr: routingHintsTmp)
guard let paymentSecret = lnInvoice["paymentSecret"] as? [UInt8] else {
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "paymentSecret", typeName: "LnInvoice"))
}
guard let minFinalCltvExpiryDelta = lnInvoice["minFinalCltvExpiryDelta"] as? UInt64 else {
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "minFinalCltvExpiryDelta", typeName: "LnInvoice"))
}
return LnInvoice(
bolt11: bolt11,
network: network,
payeePubkey: payeePubkey,
paymentHash: paymentHash,
description: description,
descriptionHash: descriptionHash,
amountMsat: amountMsat,
timestamp: timestamp,
expiry: expiry,
routingHints: routingHints,
paymentSecret: paymentSecret,
minFinalCltvExpiryDelta: minFinalCltvExpiryDelta
)
}
static func dictionaryOf(lnInvoice: LnInvoice) -> [String: Any?] {
return [
"bolt11": lnInvoice.bolt11,
"network": valueOf(network: lnInvoice.network),
"payeePubkey": lnInvoice.payeePubkey,
"paymentHash": lnInvoice.paymentHash,
"description": lnInvoice.description == nil ? nil : lnInvoice.description,
"descriptionHash": lnInvoice.descriptionHash == nil ? nil : lnInvoice.descriptionHash,
"amountMsat": lnInvoice.amountMsat == nil ? nil : lnInvoice.amountMsat,
"timestamp": lnInvoice.timestamp,
"expiry": lnInvoice.expiry,
"routingHints": arrayOf(routeHintList: lnInvoice.routingHints),
"paymentSecret": lnInvoice.paymentSecret,
"minFinalCltvExpiryDelta": lnInvoice.minFinalCltvExpiryDelta,
]
}
static func asLnInvoiceList(arr: [Any]) throws -> [LnInvoice] {
var list = [LnInvoice]()
for value in arr {
if let val = value as? [String: Any?] {
var lnInvoice = try asLnInvoice(lnInvoice: val)
list.append(lnInvoice)
} else {
throw LiquidSdkError.Generic(message: errUnexpectedType(typeName: "LnInvoice"))
}
}
return list
}
static func arrayOf(lnInvoiceList: [LnInvoice]) -> [Any] {
return lnInvoiceList.map { v -> [String: Any?] in dictionaryOf(lnInvoice: v) }
}
static func asPayment(payment: [String: Any?]) throws -> Payment {
guard let txId = payment["txId"] as? String else {
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "txId", typeName: "Payment"))
@@ -482,6 +586,110 @@ enum BreezLiquidSDKMapper {
return restoreRequestList.map { v -> [String: Any?] in dictionaryOf(restoreRequest: v) }
}
static func asRouteHint(routeHint: [String: Any?]) throws -> RouteHint {
guard let hopsTmp = routeHint["hops"] as? [[String: Any?]] else {
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "hops", typeName: "RouteHint"))
}
let hops = try asRouteHintHopList(arr: hopsTmp)
return RouteHint(
hops: hops)
}
static func dictionaryOf(routeHint: RouteHint) -> [String: Any?] {
return [
"hops": arrayOf(routeHintHopList: routeHint.hops),
]
}
static func asRouteHintList(arr: [Any]) throws -> [RouteHint] {
var list = [RouteHint]()
for value in arr {
if let val = value as? [String: Any?] {
var routeHint = try asRouteHint(routeHint: val)
list.append(routeHint)
} else {
throw LiquidSdkError.Generic(message: errUnexpectedType(typeName: "RouteHint"))
}
}
return list
}
static func arrayOf(routeHintList: [RouteHint]) -> [Any] {
return routeHintList.map { v -> [String: Any?] in dictionaryOf(routeHint: v) }
}
static func asRouteHintHop(routeHintHop: [String: Any?]) throws -> RouteHintHop {
guard let srcNodeId = routeHintHop["srcNodeId"] as? String else {
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "srcNodeId", typeName: "RouteHintHop"))
}
guard let shortChannelId = routeHintHop["shortChannelId"] as? UInt64 else {
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "shortChannelId", typeName: "RouteHintHop"))
}
guard let feesBaseMsat = routeHintHop["feesBaseMsat"] as? UInt32 else {
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "feesBaseMsat", typeName: "RouteHintHop"))
}
guard let feesProportionalMillionths = routeHintHop["feesProportionalMillionths"] as? UInt32 else {
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "feesProportionalMillionths", typeName: "RouteHintHop"))
}
guard let cltvExpiryDelta = routeHintHop["cltvExpiryDelta"] as? UInt64 else {
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "cltvExpiryDelta", typeName: "RouteHintHop"))
}
var htlcMinimumMsat: UInt64?
if hasNonNilKey(data: routeHintHop, key: "htlcMinimumMsat") {
guard let htlcMinimumMsatTmp = routeHintHop["htlcMinimumMsat"] as? UInt64 else {
throw LiquidSdkError.Generic(message: errUnexpectedValue(fieldName: "htlcMinimumMsat"))
}
htlcMinimumMsat = htlcMinimumMsatTmp
}
var htlcMaximumMsat: UInt64?
if hasNonNilKey(data: routeHintHop, key: "htlcMaximumMsat") {
guard let htlcMaximumMsatTmp = routeHintHop["htlcMaximumMsat"] as? UInt64 else {
throw LiquidSdkError.Generic(message: errUnexpectedValue(fieldName: "htlcMaximumMsat"))
}
htlcMaximumMsat = htlcMaximumMsatTmp
}
return RouteHintHop(
srcNodeId: srcNodeId,
shortChannelId: shortChannelId,
feesBaseMsat: feesBaseMsat,
feesProportionalMillionths: feesProportionalMillionths,
cltvExpiryDelta: cltvExpiryDelta,
htlcMinimumMsat: htlcMinimumMsat,
htlcMaximumMsat: htlcMaximumMsat
)
}
static func dictionaryOf(routeHintHop: RouteHintHop) -> [String: Any?] {
return [
"srcNodeId": routeHintHop.srcNodeId,
"shortChannelId": routeHintHop.shortChannelId,
"feesBaseMsat": routeHintHop.feesBaseMsat,
"feesProportionalMillionths": routeHintHop.feesProportionalMillionths,
"cltvExpiryDelta": routeHintHop.cltvExpiryDelta,
"htlcMinimumMsat": routeHintHop.htlcMinimumMsat == nil ? nil : routeHintHop.htlcMinimumMsat,
"htlcMaximumMsat": routeHintHop.htlcMaximumMsat == nil ? nil : routeHintHop.htlcMaximumMsat,
]
}
static func asRouteHintHopList(arr: [Any]) throws -> [RouteHintHop] {
var list = [RouteHintHop]()
for value in arr {
if let val = value as? [String: Any?] {
var routeHintHop = try asRouteHintHop(routeHintHop: val)
list.append(routeHintHop)
} else {
throw LiquidSdkError.Generic(message: errUnexpectedType(typeName: "RouteHintHop"))
}
}
return list
}
static func arrayOf(routeHintHopList: [RouteHintHop]) -> [Any] {
return routeHintHopList.map { v -> [String: Any?] in dictionaryOf(routeHintHop: v) }
}
static func asSendPaymentResponse(sendPaymentResponse: [String: Any?]) throws -> SendPaymentResponse {
guard let paymentTmp = sendPaymentResponse["payment"] as? [String: Any?] else {
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "payment", typeName: "SendPaymentResponse"))
@@ -648,11 +856,11 @@ enum BreezLiquidSDKMapper {
static func asNetwork(network: String) throws -> Network {
switch network {
case "liquid":
return Network.liquid
case "mainnet":
return Network.mainnet
case "liquidTestnet":
return Network.liquidTestnet
case "testnet":
return Network.testnet
default: throw LiquidSdkError.Generic(message: "Invalid variant \(network) for enum Network")
}
@@ -660,11 +868,11 @@ enum BreezLiquidSDKMapper {
static func valueOf(network: Network) -> String {
switch network {
case .liquid:
return "liquid"
case .mainnet:
return "mainnet"
case .liquidTestnet:
return "liquidTestnet"
case .testnet:
return "testnet"
}
}

View File

@@ -3,6 +3,12 @@
@interface RCT_EXTERN_MODULE(RNBreezLiquidSDK, RCTEventEmitter)
RCT_EXTERN_METHOD(
parseInvoice: (NSString*)invoice
resolve: (RCTPromiseResolveBlock)resolve
reject: (RCTPromiseRejectBlock)reject
)
RCT_EXTERN_METHOD(
connect: (NSDictionary*)req
resolve: (RCTPromiseResolveBlock)resolve

View File

@@ -56,6 +56,16 @@ class RNBreezLiquidSDK: RCTEventEmitter {
throw LiquidSdkError.Generic(message: "Not initialized")
}
@objc(parseInvoice:resolve:reject:)
func parseInvoice(_ invoice: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
do {
var res = try BreezLiquidSDK.parseInvoice(invoice: invoice)
resolve(BreezLiquidSDKMapper.dictionaryOf(lnInvoice: res))
} catch let err {
rejectErr(err: err, reject: reject)
}
}
@objc(connect:resolve:reject:)
func connect(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
if bindingLiquidSdk != nil {

View File

@@ -40,6 +40,21 @@ export interface GetInfoResponse {
pubkey: string
}
export interface LnInvoice {
bolt11: string
network: Network
payeePubkey: string
paymentHash: string
description?: string
descriptionHash?: string
amountMsat?: number
timestamp: number
expiry: number
routingHints: RouteHint[]
paymentSecret: number[]
minFinalCltvExpiryDelta: number
}
export interface Payment {
txId: string
swapId?: string
@@ -80,6 +95,20 @@ export interface RestoreRequest {
backupPath?: string
}
export interface RouteHint {
hops: RouteHintHop[]
}
export interface RouteHintHop {
srcNodeId: string
shortChannelId: number
feesBaseMsat: number
feesProportionalMillionths: number
cltvExpiryDelta: number
htlcMinimumMsat?: number
htlcMaximumMsat?: number
}
export interface SendPaymentResponse {
payment: Payment
}
@@ -117,8 +146,8 @@ export type LiquidSdkEvent = {
}
export enum Network {
LIQUID = "liquid",
LIQUID_TESTNET = "liquidTestnet"
MAINNET = "mainnet",
TESTNET = "testnet"
}
export enum PaymentState {
@@ -146,6 +175,11 @@ export const addEventListener = async (listener: EventListener): Promise<string>
return response
}
export const parseInvoice = async (invoice: string): Promise<LnInvoice> => {
const response = await BreezLiquidSDK.parseInvoice(invoice)
return response
}
export const removeEventListener = async (id: string): Promise<void> => {
await BreezLiquidSDK.removeEventListener(id)