feat: add BIP21 support (#414)

Co-authored-by: Erdem Yerebasmaz <erdem@yerebasmaz.com>
Co-authored-by: ok300 <106775972+ok300@users.noreply.github.com>
This commit is contained in:
yse
2024-08-22 12:23:36 +02:00
committed by GitHub
parent 5248dfc235
commit 1a89bcd6c1
44 changed files with 5039 additions and 3089 deletions

View File

@@ -22,7 +22,17 @@ pub(crate) enum Command {
/// Send lbtc and receive btc lightning through a swap /// Send lbtc and receive btc lightning through a swap
SendPayment { SendPayment {
/// Invoice which has to be paid /// Invoice which has to be paid
bolt11: String, #[arg(long)]
bolt11: Option<String>,
/// Either BIP21 URI or Liquid address we intend to pay to
#[arg(long)]
address: Option<String>,
/// The amount in satoshi to pay, in case of a direct Liquid address
/// or amount-less BIP21
#[arg(short, long)]
amount_sat: Option<u64>,
/// Delay for the send, in seconds /// Delay for the send, in seconds
#[arg(short, long)] #[arg(short, long)]
@@ -46,18 +56,19 @@ pub(crate) enum Command {
}, },
/// Receive lbtc and send btc through a swap /// Receive lbtc and send btc through a swap
ReceivePayment { ReceivePayment {
/// The method to use when receiving. Either "lightning", "bitcoin" or "liquid"
#[arg(short = 'm', long = "method")]
payment_method: Option<PaymentMethod>,
/// Amount the payer will send, in satoshi /// Amount the payer will send, in satoshi
payer_amount_sat: u64, /// If not specified, it will generate a BIP21 URI/Liquid address with no amount
#[arg(short, long)]
payer_amount_sat: Option<u64>,
/// Optional description for the invoice /// Optional description for the invoice
#[clap(short = 'd', long = "description")] #[clap(short = 'd', long = "description")]
description: Option<String>, description: Option<String>,
}, },
/// Receive lbtc and send btc onchain through a swap
ReceiveOnchainPayment {
/// Amount the payer will send, in satoshi
payer_amount_sat: u64,
},
/// Generates an URL to buy bitcoin from a 3rd party provider /// Generates an URL to buy bitcoin from a 3rd party provider
BuyBitcoin { BuyBitcoin {
provider: BuyBitcoinProvider, provider: BuyBitcoinProvider,
@@ -200,32 +211,49 @@ pub(crate) async fn handle_command(
) -> Result<String> { ) -> Result<String> {
Ok(match command { Ok(match command {
Command::ReceivePayment { Command::ReceivePayment {
payment_method,
payer_amount_sat, payer_amount_sat,
description, description,
} => { } => {
let prepare_res = sdk let prepare_response = sdk
.prepare_receive_payment(&PrepareReceivePaymentRequest { payer_amount_sat }) .prepare_receive_payment(&PrepareReceiveRequest {
payer_amount_sat,
payment_method: payment_method.unwrap_or(PaymentMethod::Lightning),
})
.await?; .await?;
wait_confirmation!( wait_confirmation!(
format!( format!(
"Fees: {} sat. Are the fees acceptable? (y/N) ", "Fees: {} sat. Are the fees acceptable? (y/N) ",
prepare_res.fees_sat prepare_response.fees_sat
), ),
"Payment receive halted" "Payment receive halted"
); );
let response = sdk let response = sdk
.receive_payment(&ReceivePaymentRequest { .receive_payment(&ReceivePaymentRequest {
prepare_res, prepare_response,
description, description,
}) })
.await?; .await?;
let invoice = response.invoice.clone();
let mut result = command_result!(response); let mut result = command_result!(&response);
result.push('\n'); result.push('\n');
result.push_str(&build_qr_text(&invoice));
match parse(&response.destination).await? {
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:?}")
})?))
}
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:?}")
})?))
}
_ => {}
}
result result
} }
Command::FetchLightningLimits => { Command::FetchLightningLimits => {
@@ -236,9 +264,32 @@ pub(crate) async fn handle_command(
let limits = sdk.fetch_onchain_limits().await?; let limits = sdk.fetch_onchain_limits().await?;
command_result!(limits) command_result!(limits)
} }
Command::SendPayment { bolt11, delay } => { Command::SendPayment {
bolt11,
address,
amount_sat,
delay,
} => {
let destination = match (bolt11, address) {
(None, None) => {
return Err(anyhow::anyhow!(
"Must specify either a `bolt11` invoice or a direct/BIP21 `address`."
))
}
(Some(bolt11), None) => bolt11,
(None, Some(address)) => address,
(Some(_), Some(_)) => {
return Err(anyhow::anyhow!(
"Cannot specify both `bolt11` and `address` at the same time."
))
}
};
let prepare_response = sdk let prepare_response = sdk
.prepare_send_payment(&PrepareSendRequest { invoice: bolt11 }) .prepare_send_payment(&PrepareSendRequest {
destination,
amount_sat,
})
.await?; .await?;
wait_confirmation!( wait_confirmation!(
@@ -249,17 +300,20 @@ pub(crate) async fn handle_command(
"Payment send halted" "Payment send halted"
); );
let send_payment_req = SendPaymentRequest {
prepare_response: prepare_response.clone(),
};
if let Some(delay) = delay { if let Some(delay) = delay {
let sdk_cloned = sdk.clone(); let sdk_cloned = sdk.clone();
let prepare_cloned = prepare_response.clone();
tokio::spawn(async move { tokio::spawn(async move {
thread::sleep(Duration::from_secs(delay)); thread::sleep(Duration::from_secs(delay));
sdk_cloned.send_payment(&prepare_cloned).await.unwrap(); sdk_cloned.send_payment(&send_payment_req).await.unwrap();
}); });
command_result!(prepare_response) command_result!(prepare_response)
} else { } else {
let response = sdk.send_payment(&prepare_response).await?; let response = sdk.send_payment(&send_payment_req).await?;
command_result!(response) command_result!(response)
} }
} }
@@ -268,7 +322,7 @@ pub(crate) async fn handle_command(
receiver_amount_sat, receiver_amount_sat,
sat_per_vbyte, sat_per_vbyte,
} => { } => {
let prepare_res = sdk let prepare_response = sdk
.prepare_pay_onchain(&PreparePayOnchainRequest { .prepare_pay_onchain(&PreparePayOnchainRequest {
receiver_amount_sat, receiver_amount_sat,
sat_per_vbyte, sat_per_vbyte,
@@ -278,7 +332,7 @@ pub(crate) async fn handle_command(
wait_confirmation!( wait_confirmation!(
format!( format!(
"Fees: {} sat (incl claim fee: {} sat). Are the fees acceptable? (y/N) ", "Fees: {} sat (incl claim fee: {} sat). Are the fees acceptable? (y/N) ",
prepare_res.total_fees_sat, prepare_res.claim_fees_sat prepare_response.total_fees_sat, prepare_response.claim_fees_sat
), ),
"Payment send halted" "Payment send halted"
); );
@@ -286,37 +340,16 @@ pub(crate) async fn handle_command(
let response = sdk let response = sdk
.pay_onchain(&PayOnchainRequest { .pay_onchain(&PayOnchainRequest {
address, address,
prepare_res, prepare_response,
}) })
.await?; .await?;
command_result!(response) command_result!(response)
} }
Command::ReceiveOnchainPayment { payer_amount_sat } => {
let prepare_res = sdk
.prepare_receive_onchain(&PrepareReceiveOnchainRequest { payer_amount_sat })
.await?;
wait_confirmation!(
format!(
"Fees: {} sat. Are the fees acceptable? (y/N) ",
prepare_res.fees_sat
),
"Payment receive halted"
);
let response = sdk.receive_onchain(&prepare_res).await?;
let bip21 = response.bip21.clone();
let mut result = command_result!(response);
result.push('\n');
result.push_str(&build_qr_text(&bip21));
result
}
Command::BuyBitcoin { Command::BuyBitcoin {
provider, provider,
amount_sat, amount_sat,
} => { } => {
let prepare_res = sdk let prepare_response = sdk
.prepare_buy_bitcoin(&PrepareBuyBitcoinRequest { .prepare_buy_bitcoin(&PrepareBuyBitcoinRequest {
provider, provider,
amount_sat, amount_sat,
@@ -326,14 +359,14 @@ pub(crate) async fn handle_command(
wait_confirmation!( wait_confirmation!(
format!( format!(
"Fees: {} sat. Are the fees acceptable? (y/N) ", "Fees: {} sat. Are the fees acceptable? (y/N) ",
prepare_res.fees_sat prepare_response.fees_sat
), ),
"Buy Bitcoin halted" "Buy Bitcoin halted"
); );
let url = sdk let url = sdk
.buy_bitcoin(&BuyBitcoinRequest { .buy_bitcoin(&BuyBitcoinRequest {
prepare_res, prepare_response,
redirect_url: None, redirect_url: None,
}) })
.await?; .await?;

View File

@@ -45,4 +45,5 @@ gen-external-apklibs
# End of https://www.toptal.com/developers/gitignore/api/android # End of https://www.toptal.com/developers/gitignore/api/android
lib/src/main/jniLibs/ lib/src/main/jniLibs/
lib/src/main/kotlin/breez_sdk_liquid.kt
lib/src/main/kotlin/breez_sdk_liquid/breez_sdk_liquid.kt lib/src/main/kotlin/breez_sdk_liquid/breez_sdk_liquid.kt

View File

@@ -53,7 +53,7 @@ typedef struct wire_cst_prepare_buy_bitcoin_response {
} wire_cst_prepare_buy_bitcoin_response; } wire_cst_prepare_buy_bitcoin_response;
typedef struct wire_cst_buy_bitcoin_request { typedef struct wire_cst_buy_bitcoin_request {
struct wire_cst_prepare_buy_bitcoin_response prepare_res; struct wire_cst_prepare_buy_bitcoin_response prepare_response;
struct wire_cst_list_prim_u_8_strict *redirect_url; struct wire_cst_list_prim_u_8_strict *redirect_url;
} wire_cst_buy_bitcoin_request; } wire_cst_buy_bitcoin_request;
@@ -119,7 +119,7 @@ typedef struct wire_cst_prepare_pay_onchain_response {
typedef struct wire_cst_pay_onchain_request { typedef struct wire_cst_pay_onchain_request {
struct wire_cst_list_prim_u_8_strict *address; struct wire_cst_list_prim_u_8_strict *address;
struct wire_cst_prepare_pay_onchain_response prepare_res; struct wire_cst_prepare_pay_onchain_response prepare_response;
} wire_cst_pay_onchain_request; } wire_cst_pay_onchain_request;
typedef struct wire_cst_prepare_buy_bitcoin_request { typedef struct wire_cst_prepare_buy_bitcoin_request {
@@ -132,13 +132,10 @@ typedef struct wire_cst_prepare_pay_onchain_request {
uint32_t *sat_per_vbyte; uint32_t *sat_per_vbyte;
} wire_cst_prepare_pay_onchain_request; } wire_cst_prepare_pay_onchain_request;
typedef struct wire_cst_prepare_receive_onchain_request { typedef struct wire_cst_prepare_receive_request {
uint64_t payer_amount_sat; uint64_t *payer_amount_sat;
} wire_cst_prepare_receive_onchain_request; int32_t payment_method;
} wire_cst_prepare_receive_request;
typedef struct wire_cst_prepare_receive_payment_request {
uint64_t payer_amount_sat;
} wire_cst_prepare_receive_payment_request;
typedef struct wire_cst_prepare_refund_request { typedef struct wire_cst_prepare_refund_request {
struct wire_cst_list_prim_u_8_strict *swap_address; struct wire_cst_list_prim_u_8_strict *swap_address;
@@ -147,22 +144,19 @@ typedef struct wire_cst_prepare_refund_request {
} wire_cst_prepare_refund_request; } wire_cst_prepare_refund_request;
typedef struct wire_cst_prepare_send_request { typedef struct wire_cst_prepare_send_request {
struct wire_cst_list_prim_u_8_strict *invoice; struct wire_cst_list_prim_u_8_strict *destination;
uint64_t *amount_sat;
} wire_cst_prepare_send_request; } wire_cst_prepare_send_request;
typedef struct wire_cst_prepare_receive_onchain_response { typedef struct wire_cst_prepare_receive_response {
uint64_t payer_amount_sat; int32_t payment_method;
uint64_t *payer_amount_sat;
uint64_t fees_sat; uint64_t fees_sat;
} wire_cst_prepare_receive_onchain_response; } wire_cst_prepare_receive_response;
typedef struct wire_cst_prepare_receive_payment_response {
uint64_t payer_amount_sat;
uint64_t fees_sat;
} wire_cst_prepare_receive_payment_response;
typedef struct wire_cst_receive_payment_request { typedef struct wire_cst_receive_payment_request {
struct wire_cst_list_prim_u_8_strict *description; struct wire_cst_list_prim_u_8_strict *description;
struct wire_cst_prepare_receive_payment_response prepare_res; struct wire_cst_prepare_receive_response prepare_response;
} wire_cst_receive_payment_request; } wire_cst_receive_payment_request;
typedef struct wire_cst_refund_request { typedef struct wire_cst_refund_request {
@@ -175,28 +169,126 @@ typedef struct wire_cst_restore_request {
struct wire_cst_list_prim_u_8_strict *backup_path; struct wire_cst_list_prim_u_8_strict *backup_path;
} wire_cst_restore_request; } wire_cst_restore_request;
typedef struct wire_cst_liquid_address_data {
struct wire_cst_list_prim_u_8_strict *address;
int32_t network;
struct wire_cst_list_prim_u_8_strict *asset_id;
uint64_t *amount_sat;
struct wire_cst_list_prim_u_8_strict *label;
struct wire_cst_list_prim_u_8_strict *message;
} wire_cst_liquid_address_data;
typedef struct wire_cst_SendDestination_LiquidAddress {
struct wire_cst_liquid_address_data *address_data;
} wire_cst_SendDestination_LiquidAddress;
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_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_SendDestination_Bolt11 {
struct wire_cst_ln_invoice *invoice;
} wire_cst_SendDestination_Bolt11;
typedef union SendDestinationKind {
struct wire_cst_SendDestination_LiquidAddress LiquidAddress;
struct wire_cst_SendDestination_Bolt11 Bolt11;
} SendDestinationKind;
typedef struct wire_cst_send_destination {
int32_t tag;
union SendDestinationKind kind;
} wire_cst_send_destination;
typedef struct wire_cst_prepare_send_response { typedef struct wire_cst_prepare_send_response {
struct wire_cst_list_prim_u_8_strict *invoice; struct wire_cst_send_destination destination;
uint64_t fees_sat; uint64_t fees_sat;
} wire_cst_prepare_send_response; } wire_cst_prepare_send_response;
typedef struct wire_cst_send_payment_request {
struct wire_cst_prepare_send_response prepare_response;
} wire_cst_send_payment_request;
typedef struct wire_cst_binding_event_listener { typedef struct wire_cst_binding_event_listener {
struct wire_cst_list_prim_u_8_strict *stream; struct wire_cst_list_prim_u_8_strict *stream;
} wire_cst_binding_event_listener; } wire_cst_binding_event_listener;
typedef struct wire_cst_payment { typedef struct wire_cst_PaymentDetails_Lightning {
struct wire_cst_list_prim_u_8_strict *tx_id;
struct wire_cst_list_prim_u_8_strict *swap_id; struct wire_cst_list_prim_u_8_strict *swap_id;
uint32_t timestamp; struct wire_cst_list_prim_u_8_strict *description;
uint64_t amount_sat;
uint64_t fees_sat;
struct wire_cst_list_prim_u_8_strict *preimage; 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 *bolt11;
struct wire_cst_list_prim_u_8_strict *refund_tx_id;
uint64_t *refund_tx_amount_sat;
} wire_cst_PaymentDetails_Lightning;
typedef struct wire_cst_PaymentDetails_Liquid {
struct wire_cst_list_prim_u_8_strict *destination;
struct wire_cst_list_prim_u_8_strict *description;
} wire_cst_PaymentDetails_Liquid;
typedef struct wire_cst_PaymentDetails_Bitcoin {
struct wire_cst_list_prim_u_8_strict *swap_id;
struct wire_cst_list_prim_u_8_strict *description; struct wire_cst_list_prim_u_8_strict *description;
struct wire_cst_list_prim_u_8_strict *refund_tx_id; struct wire_cst_list_prim_u_8_strict *refund_tx_id;
uint64_t *refund_tx_amount_sat; uint64_t *refund_tx_amount_sat;
} wire_cst_PaymentDetails_Bitcoin;
typedef union PaymentDetailsKind {
struct wire_cst_PaymentDetails_Lightning Lightning;
struct wire_cst_PaymentDetails_Liquid Liquid;
struct wire_cst_PaymentDetails_Bitcoin Bitcoin;
} PaymentDetailsKind;
typedef struct wire_cst_payment_details {
int32_t tag;
union PaymentDetailsKind kind;
} wire_cst_payment_details;
typedef struct wire_cst_payment {
struct wire_cst_list_prim_u_8_strict *destination;
struct wire_cst_list_prim_u_8_strict *tx_id;
uint32_t timestamp;
uint64_t amount_sat;
uint64_t fees_sat;
int32_t payment_type; int32_t payment_type;
int32_t status; int32_t status;
struct wire_cst_payment_details *details;
} wire_cst_payment; } wire_cst_payment;
typedef struct wire_cst_SdkEvent_PaymentFailed { typedef struct wire_cst_SdkEvent_PaymentFailed {
@@ -284,54 +376,6 @@ typedef struct wire_cst_bitcoin_address_data {
struct wire_cst_list_prim_u_8_strict *message; struct wire_cst_list_prim_u_8_strict *message;
} wire_cst_bitcoin_address_data; } wire_cst_bitcoin_address_data;
typedef struct wire_cst_liquid_address_data {
struct wire_cst_list_prim_u_8_strict *address;
int32_t network;
struct wire_cst_list_prim_u_8_strict *asset_id;
uint64_t *amount_sat;
struct wire_cst_list_prim_u_8_strict *label;
struct wire_cst_list_prim_u_8_strict *message;
} wire_cst_liquid_address_data;
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_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_ln_url_error_data { typedef struct wire_cst_ln_url_error_data {
struct wire_cst_list_prim_u_8_strict *reason; struct wire_cst_list_prim_u_8_strict *reason;
} wire_cst_ln_url_error_data; } wire_cst_ln_url_error_data;
@@ -721,6 +765,14 @@ typedef struct wire_cst_onchain_payment_limits_response {
struct wire_cst_limits receive; struct wire_cst_limits receive;
} wire_cst_onchain_payment_limits_response; } wire_cst_onchain_payment_limits_response;
typedef struct wire_cst_PaymentError_AmountMissing {
struct wire_cst_list_prim_u_8_strict *err;
} wire_cst_PaymentError_AmountMissing;
typedef struct wire_cst_PaymentError_InvalidNetwork {
struct wire_cst_list_prim_u_8_strict *err;
} wire_cst_PaymentError_InvalidNetwork;
typedef struct wire_cst_PaymentError_Generic { typedef struct wire_cst_PaymentError_Generic {
struct wire_cst_list_prim_u_8_strict *err; struct wire_cst_list_prim_u_8_strict *err;
} wire_cst_PaymentError_Generic; } wire_cst_PaymentError_Generic;
@@ -751,6 +803,8 @@ typedef struct wire_cst_PaymentError_SignerError {
} wire_cst_PaymentError_SignerError; } wire_cst_PaymentError_SignerError;
typedef union PaymentErrorKind { typedef union PaymentErrorKind {
struct wire_cst_PaymentError_AmountMissing AmountMissing;
struct wire_cst_PaymentError_InvalidNetwork InvalidNetwork;
struct wire_cst_PaymentError_Generic Generic; struct wire_cst_PaymentError_Generic Generic;
struct wire_cst_PaymentError_InvalidInvoice InvalidInvoice; struct wire_cst_PaymentError_InvalidInvoice InvalidInvoice;
struct wire_cst_PaymentError_LwkError LwkError; struct wire_cst_PaymentError_LwkError LwkError;
@@ -771,14 +825,8 @@ typedef struct wire_cst_prepare_refund_response {
struct wire_cst_list_prim_u_8_strict *refund_tx_id; struct wire_cst_list_prim_u_8_strict *refund_tx_id;
} wire_cst_prepare_refund_response; } wire_cst_prepare_refund_response;
typedef struct wire_cst_receive_onchain_response {
struct wire_cst_list_prim_u_8_strict *address;
struct wire_cst_list_prim_u_8_strict *bip21;
} wire_cst_receive_onchain_response;
typedef struct wire_cst_receive_payment_response { typedef struct wire_cst_receive_payment_response {
struct wire_cst_list_prim_u_8_strict *id; struct wire_cst_list_prim_u_8_strict *destination;
struct wire_cst_list_prim_u_8_strict *invoice;
} wire_cst_receive_payment_response; } wire_cst_receive_payment_response;
typedef struct wire_cst_recommended_fees { typedef struct wire_cst_recommended_fees {
@@ -877,13 +925,9 @@ void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_pay_onc
uintptr_t that, uintptr_t that,
struct wire_cst_prepare_pay_onchain_request *req); struct wire_cst_prepare_pay_onchain_request *req);
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_onchain(int64_t port_,
uintptr_t that,
struct wire_cst_prepare_receive_onchain_request *req);
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_payment(int64_t port_, void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_payment(int64_t port_,
uintptr_t that, uintptr_t that,
struct wire_cst_prepare_receive_payment_request *req); struct wire_cst_prepare_receive_request *req);
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_refund(int64_t port_, void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_refund(int64_t port_,
uintptr_t that, uintptr_t that,
@@ -893,10 +937,6 @@ void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_send_pa
uintptr_t that, uintptr_t that,
struct wire_cst_prepare_send_request *req); struct wire_cst_prepare_send_request *req);
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_onchain(int64_t port_,
uintptr_t that,
struct wire_cst_prepare_receive_onchain_response *req);
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_payment(int64_t port_, void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_payment(int64_t port_,
uintptr_t that, uintptr_t that,
struct wire_cst_receive_payment_request *req); struct wire_cst_receive_payment_request *req);
@@ -916,7 +956,7 @@ WireSyncRust2DartDco frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_send_payment(int64_t port_, void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_send_payment(int64_t port_,
uintptr_t that, uintptr_t that,
struct wire_cst_prepare_send_response *req); struct wire_cst_send_payment_request *req);
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_sync(int64_t port_, void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_sync(int64_t port_,
uintptr_t that); uintptr_t that);
@@ -990,22 +1030,18 @@ struct wire_cst_pay_onchain_request *frbgen_breez_liquid_cst_new_box_autoadd_pay
struct wire_cst_payment *frbgen_breez_liquid_cst_new_box_autoadd_payment(void); struct wire_cst_payment *frbgen_breez_liquid_cst_new_box_autoadd_payment(void);
struct wire_cst_payment_details *frbgen_breez_liquid_cst_new_box_autoadd_payment_details(void);
struct wire_cst_prepare_buy_bitcoin_request *frbgen_breez_liquid_cst_new_box_autoadd_prepare_buy_bitcoin_request(void); struct wire_cst_prepare_buy_bitcoin_request *frbgen_breez_liquid_cst_new_box_autoadd_prepare_buy_bitcoin_request(void);
struct wire_cst_prepare_pay_onchain_request *frbgen_breez_liquid_cst_new_box_autoadd_prepare_pay_onchain_request(void); struct wire_cst_prepare_pay_onchain_request *frbgen_breez_liquid_cst_new_box_autoadd_prepare_pay_onchain_request(void);
struct wire_cst_prepare_receive_onchain_request *frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_onchain_request(void); struct wire_cst_prepare_receive_request *frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_request(void);
struct wire_cst_prepare_receive_onchain_response *frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_onchain_response(void);
struct wire_cst_prepare_receive_payment_request *frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_payment_request(void);
struct wire_cst_prepare_refund_request *frbgen_breez_liquid_cst_new_box_autoadd_prepare_refund_request(void); struct wire_cst_prepare_refund_request *frbgen_breez_liquid_cst_new_box_autoadd_prepare_refund_request(void);
struct wire_cst_prepare_send_request *frbgen_breez_liquid_cst_new_box_autoadd_prepare_send_request(void); struct wire_cst_prepare_send_request *frbgen_breez_liquid_cst_new_box_autoadd_prepare_send_request(void);
struct wire_cst_prepare_send_response *frbgen_breez_liquid_cst_new_box_autoadd_prepare_send_response(void);
struct wire_cst_receive_payment_request *frbgen_breez_liquid_cst_new_box_autoadd_receive_payment_request(void); struct wire_cst_receive_payment_request *frbgen_breez_liquid_cst_new_box_autoadd_receive_payment_request(void);
struct wire_cst_refund_request *frbgen_breez_liquid_cst_new_box_autoadd_refund_request(void); struct wire_cst_refund_request *frbgen_breez_liquid_cst_new_box_autoadd_refund_request(void);
@@ -1014,6 +1050,8 @@ struct wire_cst_restore_request *frbgen_breez_liquid_cst_new_box_autoadd_restore
struct wire_cst_sdk_event *frbgen_breez_liquid_cst_new_box_autoadd_sdk_event(void); struct wire_cst_sdk_event *frbgen_breez_liquid_cst_new_box_autoadd_sdk_event(void);
struct wire_cst_send_payment_request *frbgen_breez_liquid_cst_new_box_autoadd_send_payment_request(void);
struct wire_cst_success_action_processed *frbgen_breez_liquid_cst_new_box_autoadd_success_action_processed(void); struct wire_cst_success_action_processed *frbgen_breez_liquid_cst_new_box_autoadd_success_action_processed(void);
struct wire_cst_symbol *frbgen_breez_liquid_cst_new_box_autoadd_symbol(void); struct wire_cst_symbol *frbgen_breez_liquid_cst_new_box_autoadd_symbol(void);
@@ -1069,18 +1107,17 @@ static int64_t dummy_method_to_enforce_bundling(void) {
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_message_success_action_data); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_message_success_action_data);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_pay_onchain_request); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_pay_onchain_request);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_payment); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_payment);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_payment_details);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_prepare_buy_bitcoin_request); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_prepare_buy_bitcoin_request);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_prepare_pay_onchain_request); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_prepare_pay_onchain_request);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_onchain_request); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_request);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_onchain_response);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_payment_request);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_prepare_refund_request); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_prepare_refund_request);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_prepare_send_request); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_prepare_send_request);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_prepare_send_response);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_receive_payment_request); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_receive_payment_request);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_refund_request); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_refund_request);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_restore_request); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_restore_request);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_sdk_event); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_sdk_event);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_send_payment_request);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_success_action_processed); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_success_action_processed);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_symbol); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_symbol);
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_32);
@@ -1116,11 +1153,9 @@ static int64_t dummy_method_to_enforce_bundling(void) {
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_pay_onchain); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_pay_onchain);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_buy_bitcoin); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_buy_bitcoin);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_pay_onchain); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_pay_onchain);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_onchain);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_payment); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_payment);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_refund); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_refund);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_send_payment); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_send_payment);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_onchain);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_payment); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_payment);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_recommended_fees); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_recommended_fees);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_refund); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_refund);

View File

@@ -278,10 +278,12 @@ enum PaymentError {
"AlreadyPaid", "AlreadyPaid",
"PaymentInProgress", "PaymentInProgress",
"AmountOutOfRange", "AmountOutOfRange",
"AmountMissing",
"Generic", "Generic",
"InvalidOrExpiredFees", "InvalidOrExpiredFees",
"InsufficientFunds", "InsufficientFunds",
"InvalidInvoice", "InvalidInvoice",
"InvalidNetwork",
"InvalidPreimage", "InvalidPreimage",
"LwkError", "LwkError",
"PairsNotFound", "PairsNotFound",
@@ -323,35 +325,53 @@ dictionary GetInfoResponse {
}; };
dictionary PrepareSendRequest { dictionary PrepareSendRequest {
string invoice; string destination;
u64? amount_sat;
};
[Enum]
interface SendDestination {
LiquidAddress(LiquidAddressData address_data);
Bolt11(LNInvoice invoice);
}; };
dictionary PrepareSendResponse { dictionary PrepareSendResponse {
string invoice; SendDestination destination;
u64 fees_sat; u64 fees_sat;
}; };
dictionary SendPaymentRequest {
PrepareSendResponse prepare_response;
};
dictionary SendPaymentResponse { dictionary SendPaymentResponse {
Payment payment; Payment payment;
}; };
dictionary PrepareReceivePaymentRequest { enum PaymentMethod {
u64 payer_amount_sat; "Lightning",
"BitcoinAddress",
"LiquidAddress",
}; };
dictionary PrepareReceivePaymentResponse { dictionary PrepareReceiveRequest {
u64 payer_amount_sat; u64? payer_amount_sat;
PaymentMethod payment_method;
};
dictionary PrepareReceiveResponse {
u64? payer_amount_sat;
PaymentMethod payment_method;
u64 fees_sat; u64 fees_sat;
}; };
dictionary ReceivePaymentRequest { dictionary ReceivePaymentRequest {
PrepareReceivePaymentResponse prepare_res; PrepareReceiveResponse prepare_response;
string? description = null; string? description = null;
}; };
dictionary ReceivePaymentResponse { dictionary ReceivePaymentResponse {
string id; string destination;
string invoice;
}; };
dictionary Limits { dictionary Limits {
@@ -383,21 +403,7 @@ dictionary PreparePayOnchainResponse {
dictionary PayOnchainRequest { dictionary PayOnchainRequest {
string address; string address;
PreparePayOnchainResponse prepare_res; PreparePayOnchainResponse prepare_response;
};
dictionary PrepareReceiveOnchainRequest {
u64 payer_amount_sat;
};
dictionary PrepareReceiveOnchainResponse {
u64 payer_amount_sat;
u64 fees_sat;
};
dictionary ReceiveOnchainResponse {
string address;
string bip21;
}; };
enum BuyBitcoinProvider { enum BuyBitcoinProvider {
@@ -416,7 +422,7 @@ dictionary PrepareBuyBitcoinResponse {
}; };
dictionary BuyBitcoinRequest { dictionary BuyBitcoinRequest {
PrepareBuyBitcoinResponse prepare_res; PrepareBuyBitcoinResponse prepare_response;
string? redirect_url = null; string? redirect_url = null;
}; };
@@ -436,19 +442,22 @@ dictionary ListPaymentsRequest {
u32? limit = null; u32? limit = null;
}; };
[Enum]
interface PaymentDetails {
Lightning(string swap_id, string description, string? preimage, string? bolt11, 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);
};
dictionary Payment { dictionary Payment {
string? destination;
string? tx_id = null;
u32 timestamp; u32 timestamp;
u64 amount_sat; u64 amount_sat;
u64 fees_sat; u64 fees_sat;
PaymentType payment_type; PaymentType payment_type;
PaymentState status; PaymentState status;
string description; PaymentDetails? details;
string? tx_id = null;
string? swap_id = null;
string? preimage = null;
string? bolt11 = null;
string? refund_tx_id = null;
u64? refund_tx_amount_sat = null;
}; };
enum PaymentType { enum PaymentType {
@@ -556,10 +565,10 @@ interface BindingLiquidSdk {
PrepareSendResponse prepare_send_payment(PrepareSendRequest req); PrepareSendResponse prepare_send_payment(PrepareSendRequest req);
[Throws=PaymentError] [Throws=PaymentError]
SendPaymentResponse send_payment(PrepareSendResponse req); SendPaymentResponse send_payment(SendPaymentRequest req);
[Throws=PaymentError] [Throws=PaymentError]
PrepareReceivePaymentResponse prepare_receive_payment(PrepareReceivePaymentRequest req); PrepareReceiveResponse prepare_receive_payment(PrepareReceiveRequest req);
[Throws=PaymentError] [Throws=PaymentError]
ReceivePaymentResponse receive_payment(ReceivePaymentRequest req); ReceivePaymentResponse receive_payment(ReceivePaymentRequest req);
@@ -576,12 +585,6 @@ interface BindingLiquidSdk {
[Throws=PaymentError] [Throws=PaymentError]
SendPaymentResponse pay_onchain(PayOnchainRequest req); SendPaymentResponse pay_onchain(PayOnchainRequest req);
[Throws=PaymentError]
PrepareReceiveOnchainResponse prepare_receive_onchain(PrepareReceiveOnchainRequest req);
[Throws=PaymentError]
ReceiveOnchainResponse receive_onchain(PrepareReceiveOnchainResponse req);
[Throws=PaymentError] [Throws=PaymentError]
PrepareBuyBitcoinResponse prepare_buy_bitcoin(PrepareBuyBitcoinRequest req); PrepareBuyBitcoinResponse prepare_buy_bitcoin(PrepareBuyBitcoinRequest req);

View File

@@ -94,15 +94,15 @@ impl BindingLiquidSdk {
pub fn send_payment( pub fn send_payment(
&self, &self,
req: PrepareSendResponse, req: SendPaymentRequest,
) -> Result<SendPaymentResponse, PaymentError> { ) -> Result<SendPaymentResponse, PaymentError> {
rt().block_on(self.sdk.send_payment(&req)) rt().block_on(self.sdk.send_payment(&req))
} }
pub fn prepare_receive_payment( pub fn prepare_receive_payment(
&self, &self,
req: PrepareReceivePaymentRequest, req: PrepareReceiveRequest,
) -> Result<PrepareReceivePaymentResponse, PaymentError> { ) -> Result<PrepareReceiveResponse, PaymentError> {
rt().block_on(self.sdk.prepare_receive_payment(&req)) rt().block_on(self.sdk.prepare_receive_payment(&req))
} }
@@ -132,20 +132,6 @@ impl BindingLiquidSdk {
rt().block_on(self.sdk.pay_onchain(&req)) rt().block_on(self.sdk.pay_onchain(&req))
} }
pub fn prepare_receive_onchain(
&self,
req: PrepareReceiveOnchainRequest,
) -> Result<PrepareReceiveOnchainResponse, PaymentError> {
rt().block_on(self.sdk.prepare_receive_onchain(&req))
}
pub fn receive_onchain(
&self,
req: PrepareReceiveOnchainResponse,
) -> Result<ReceiveOnchainResponse, PaymentError> {
rt().block_on(self.sdk.receive_onchain(&req))
}
pub fn prepare_buy_bitcoin( pub fn prepare_buy_bitcoin(
&self, &self,
req: PrepareBuyBitcoinRequest, req: PrepareBuyBitcoinRequest,

View File

@@ -17,7 +17,9 @@ bip39 = "2.0.0"
boltz-client = { git = "https://github.com/dangeross/boltz-rust", branch = "savage-breez-latest" } boltz-client = { git = "https://github.com/dangeross/boltz-rust", branch = "savage-breez-latest" }
chrono = "0.4" chrono = "0.4"
env_logger = "0.11" env_logger = "0.11"
flutter_rust_bridge = { version = "=2.2.0", features = ["chrono"], optional = true } flutter_rust_bridge = { version = "=2.2.0", features = [
"chrono",
], optional = true }
log = { workspace = true } log = { workspace = true }
lwk_common = "0.7.0" lwk_common = "0.7.0"
lwk_signer = "0.7.0" lwk_signer = "0.7.0"
@@ -36,7 +38,10 @@ openssl = { version = "0.10", features = ["vendored"] }
tokio = { version = "1", features = ["rt", "macros"] } tokio = { version = "1", features = ["rt", "macros"] }
tokio-stream = { version = "0.1.14", features = ["sync"] } tokio-stream = { version = "0.1.14", features = ["sync"] }
url = "2.5.0" url = "2.5.0"
futures-util = { version = "0.3.28", default-features = false, features = ["sink", "std"] } futures-util = { version = "0.3.28", default-features = false, features = [
"sink",
"std",
] }
async-trait = "0.1.80" async-trait = "0.1.80"
hex = "0.4" hex = "0.4"
reqwest = { version = "=0.11.20", features = ["json"] } reqwest = { version = "=0.11.20", features = ["json"] }

View File

@@ -101,15 +101,15 @@ impl BindingLiquidSdk {
pub async fn send_payment( pub async fn send_payment(
&self, &self,
req: PrepareSendResponse, req: SendPaymentRequest,
) -> Result<SendPaymentResponse, PaymentError> { ) -> Result<SendPaymentResponse, PaymentError> {
self.sdk.send_payment(&req).await self.sdk.send_payment(&req).await
} }
pub async fn prepare_receive_payment( pub async fn prepare_receive_payment(
&self, &self,
req: PrepareReceivePaymentRequest, req: PrepareReceiveRequest,
) -> Result<PrepareReceivePaymentResponse, PaymentError> { ) -> Result<PrepareReceiveResponse, PaymentError> {
self.sdk.prepare_receive_payment(&req).await self.sdk.prepare_receive_payment(&req).await
} }
@@ -144,20 +144,6 @@ impl BindingLiquidSdk {
self.sdk.pay_onchain(&req).await self.sdk.pay_onchain(&req).await
} }
pub async fn prepare_receive_onchain(
&self,
req: PrepareReceiveOnchainRequest,
) -> Result<PrepareReceiveOnchainResponse, PaymentError> {
self.sdk.prepare_receive_onchain(&req).await
}
pub async fn receive_onchain(
&self,
req: PrepareReceiveOnchainResponse,
) -> Result<ReceiveOnchainResponse, PaymentError> {
self.sdk.receive_onchain(&req).await
}
pub async fn prepare_buy_bitcoin( pub async fn prepare_buy_bitcoin(
&self, &self,
req: PrepareBuyBitcoinRequest, req: PrepareBuyBitcoinRequest,

View File

@@ -399,7 +399,7 @@ impl ChainSwapStateHandler {
fees_sat: lockup_tx_fees_sat + swap.claim_fees_sat, fees_sat: lockup_tx_fees_sat + swap.claim_fees_sat,
payment_type: PaymentType::Send, payment_type: PaymentType::Send,
is_confirmed: false, is_confirmed: false,
})?; }, None)?;
self.update_swap_info(id, Pending, None, Some(&lockup_tx_id), None, None) self.update_swap_info(id, Pending, None, Some(&lockup_tx_id), None, None)
.await?; .await?;
@@ -633,14 +633,17 @@ impl ChainSwapStateHandler {
if chain_swap.direction == Direction::Incoming { if chain_swap.direction == Direction::Incoming {
// We insert a pseudo-claim-tx in case LWK fails to pick up the new mempool tx for a while // We insert a pseudo-claim-tx in case LWK fails to pick up the new mempool tx for a while
// This makes the tx known to the SDK (get_info, list_payments) instantly // This makes the tx known to the SDK (get_info, list_payments) instantly
self.persister.insert_or_update_payment(PaymentTxData { self.persister.insert_or_update_payment(
tx_id: claim_tx_id.clone(), PaymentTxData {
timestamp: Some(utils::now()), tx_id: claim_tx_id.clone(),
amount_sat: chain_swap.receiver_amount_sat, timestamp: Some(utils::now()),
fees_sat: 0, amount_sat: chain_swap.receiver_amount_sat,
payment_type: PaymentType::Receive, fees_sat: 0,
is_confirmed: false, payment_type: PaymentType::Receive,
})?; is_confirmed: false,
},
None,
)?;
} }
self.update_swap_info( self.update_swap_info(

View File

@@ -70,6 +70,12 @@ pub enum PaymentError {
#[error("Amount is out of range")] #[error("Amount is out of range")]
AmountOutOfRange, AmountOutOfRange,
#[error("Amount is missing: {err}")]
AmountMissing { err: String },
#[error("Invalid network: {err}")]
InvalidNetwork { err: String },
#[error("Generic error: {err}")] #[error("Generic error: {err}")]
Generic { err: String }, Generic { err: String },

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -45,32 +45,39 @@
//! //!
//! // Set the amount you wish the payer to send, which should be within the above limits //! // Set the amount you wish the payer to send, which should be within the above limits
//! let prepare_receive_response = sdk //! let prepare_receive_response = sdk
//! .prepare_receive_payment(&PrepareReceivePaymentRequest { //! .prepare_receive_payment(&PrepareReceiveRequest {
//! payer_amount_sat: 5_000, //! payment_method: PaymentMethod::Lightning,
//! payer_amount_sat: Some(5_000),
//! }) //! })
//! .await?; //! .await?;
//! //!
//! // If the fees are acceptable, continue to create the Receive Payment //! // If the fees are acceptable, continue to create the Receive Payment
//! let receive_fees_sat = prepare_receive_response.fees_sat; //! let receive_fees_sat = prepare_receive_response.fees_sat;
//! //!
//! let receive_payment_response = sdk.receive_payment(&prepare_receive_response).await?; //! let receive_payment_response = sdk.receive_payment(&ReceivePaymentRequest {
//! description: Some("my description".to_string()),
//! prepare_response: receive_payment_response,
//! }).await?;
//! //!
//! let invoice = receive_payment_response.invoice; //! let destination = receive_payment_response.destination;
//! ``` //! ```
//! //!
//! or make payments //! or make payments
//! ```ignore //! ```ignore
//! // Set the BOLT11 invoice you wish to pay //! // Set the BOLT11 invoice or Liquid BIP21/address you wish to pay
//! let prepare_send_response = sdk //! let prepare_send_response = sdk
//! .prepare_send_payment(&PrepareSendRequest { //! .prepare_send_payment(&PrepareSendRequest {
//! invoice: "...".to_string(), //! destination: "invoice or Liquid BIP21/address".to_string(),
//! amount_sat: Some(3_000),
//! }) //! })
//! .await?; //! .await?;
//! //!
//! // If the fees are acceptable, continue to create the Send Payment //! // If the fees are acceptable, continue to create the Send Payment
//! let send_fees_sat = prepare_send_response.fees_sat; //! let send_fees_sat = prepare_send_response.fees_sat;
//! //!
//! let send_response = sdk.send_payment(&prepare_send_response).await?; //! let send_response = sdk.send_payment(&SendPayentRequest {
//! prepare_response: prepare_send_response,
//! }).await?;
//! let payment = send_response.payment; //! let payment = send_response.payment;
//! ``` //! ```
//! //!
@@ -108,20 +115,18 @@
//! * [sdk::LiquidSdk::prepare_send_payment] to check fees //! * [sdk::LiquidSdk::prepare_send_payment] to check fees
//! * [sdk::LiquidSdk::send_payment] to pay an invoice //! * [sdk::LiquidSdk::send_payment] to pay an invoice
//! //!
//! ### Receiving a Lightning payment //! ### Receiving a Lightning/onchain payment
//! //!
//! * [sdk::LiquidSdk::prepare_receive_payment] to check fees //! * [sdk::LiquidSdk::prepare_receive_payment] to check fees
//! * [sdk::LiquidSdk::receive_payment] to generate an invoice //! * [sdk::LiquidSdk::receive_payment] to generate an invoice/Liquid BIP21/Liquid address
//! //!
//! ### Sending an onchain payment //! ### Sending an onchain payment
//! //!
//! * [sdk::LiquidSdk::prepare_pay_onchain] to check fees //! * [sdk::LiquidSdk::prepare_pay_onchain] to check fees
//! * [sdk::LiquidSdk::pay_onchain] to pay to a Bitcoin address //! * [sdk::LiquidSdk::pay_onchain] to pay to a Bitcoin address
//! //!
//! ### Receiving an onchain payment //! ### Refunding a payment
//! //!
//! * [sdk::LiquidSdk::prepare_receive_onchain] to check fees
//! * [sdk::LiquidSdk::receive_onchain] to generate a Bitcoin address
//! * [sdk::LiquidSdk::list_refundables] to get a list of refundable swaps //! * [sdk::LiquidSdk::list_refundables] to get a list of refundable swaps
//! * [sdk::LiquidSdk::prepare_refund] to check the refund fees //! * [sdk::LiquidSdk::prepare_refund] to check the refund fees
//! * [sdk::LiquidSdk::refund] to broadcast a refund transaction //! * [sdk::LiquidSdk::refund] to broadcast a refund transaction

View File

@@ -25,8 +25,9 @@ use crate::receive_swap::{
}; };
use crate::utils; use crate::utils;
pub const STANDARD_FEE_RATE_SAT_PER_VBYTE: f32 = 0.1; // Both use f64 for the maximum precision when converting between units
pub const LOWBALL_FEE_RATE_SAT_PER_VBYTE: f32 = 0.01; pub const STANDARD_FEE_RATE_SAT_PER_VBYTE: f64 = 0.1;
pub const LOWBALL_FEE_RATE_SAT_PER_VBYTE: f64 = 0.01;
/// Configuration for the Liquid SDK /// Configuration for the Liquid SDK
#[derive(Clone, Debug, Serialize)] #[derive(Clone, Debug, Serialize)]
@@ -95,7 +96,7 @@ impl Config {
.unwrap_or(DEFAULT_ZERO_CONF_MAX_SAT) .unwrap_or(DEFAULT_ZERO_CONF_MAX_SAT)
} }
pub(crate) fn lowball_fee_rate_msat_per_vbyte(&self) -> Option<f32> { pub(crate) fn lowball_fee_rate_msat_per_vbyte(&self) -> Option<f64> {
match self.network { match self.network {
LiquidNetwork::Mainnet => Some(LOWBALL_FEE_RATE_SAT_PER_VBYTE * 1000.0), LiquidNetwork::Mainnet => Some(LOWBALL_FEE_RATE_SAT_PER_VBYTE * 1000.0),
LiquidNetwork::Testnet => None, LiquidNetwork::Testnet => None,
@@ -194,16 +195,29 @@ pub struct ConnectRequest {
pub config: Config, pub config: Config,
} }
/// The send/receive methods supported by the SDK
#[derive(Clone, Debug, EnumString, Serialize, Eq, PartialEq)]
pub enum PaymentMethod {
#[strum(serialize = "lightning")]
Lightning,
#[strum(serialize = "bitcoin")]
BitcoinAddress,
#[strum(serialize = "liquid")]
LiquidAddress,
}
/// An argument when calling [crate::sdk::LiquidSdk::prepare_receive_payment]. /// An argument when calling [crate::sdk::LiquidSdk::prepare_receive_payment].
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct PrepareReceivePaymentRequest { pub struct PrepareReceiveRequest {
pub payer_amount_sat: u64, pub payer_amount_sat: Option<u64>,
pub payment_method: PaymentMethod,
} }
/// Returned when calling [crate::sdk::LiquidSdk::prepare_receive_payment]. /// Returned when calling [crate::sdk::LiquidSdk::prepare_receive_payment].
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct PrepareReceivePaymentResponse { pub struct PrepareReceiveResponse {
pub payer_amount_sat: u64, pub payment_method: PaymentMethod,
pub payer_amount_sat: Option<u64>,
pub fees_sat: u64, pub fees_sat: u64,
} }
@@ -211,14 +225,15 @@ pub struct PrepareReceivePaymentResponse {
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct ReceivePaymentRequest { pub struct ReceivePaymentRequest {
pub description: Option<String>, pub description: Option<String>,
pub prepare_res: PrepareReceivePaymentResponse, pub prepare_response: PrepareReceiveResponse,
} }
/// Returned when calling [crate::sdk::LiquidSdk::receive_payment]. /// Returned when calling [crate::sdk::LiquidSdk::receive_payment].
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct ReceivePaymentResponse { pub struct ReceivePaymentResponse {
pub id: String, /// Either a BIP21 URI (Liquid or Bitcoin), a Liquid address
pub invoice: String, /// or an invoice, depending on the [PrepareReceivePaymentResponse] parameters
pub destination: String,
} }
/// The minimum and maximum in satoshis of a Lightning or onchain payment. /// The minimum and maximum in satoshis of a Lightning or onchain payment.
@@ -250,16 +265,39 @@ pub struct OnchainPaymentLimitsResponse {
/// An argument when calling [crate::sdk::LiquidSdk::prepare_send_payment]. /// An argument when calling [crate::sdk::LiquidSdk::prepare_send_payment].
#[derive(Debug, Serialize, Clone)] #[derive(Debug, Serialize, Clone)]
pub struct PrepareSendRequest { pub struct PrepareSendRequest {
pub invoice: String, /// The destination we intend to pay to.
/// Supports BIP21 URIs, BOLT11 invoices and Liquid addresses
pub destination: String,
/// Should only be set when paying directly onchain or to a BIP21 URI
/// where no amount is specified
pub amount_sat: Option<u64>,
}
/// Specifies the supported destinations which can be payed by the SDK
#[derive(Clone, Debug, Serialize)]
pub enum SendDestination {
LiquidAddress {
address_data: liquid::LiquidAddressData,
},
Bolt11 {
invoice: LNInvoice,
},
} }
/// Returned when calling [crate::sdk::LiquidSdk::prepare_send_payment]. /// Returned when calling [crate::sdk::LiquidSdk::prepare_send_payment].
#[derive(Debug, Serialize, Clone)] #[derive(Debug, Serialize, Clone)]
pub struct PrepareSendResponse { pub struct PrepareSendResponse {
pub invoice: String, pub destination: SendDestination,
pub fees_sat: u64, pub fees_sat: u64,
} }
/// An argument when calling [crate::sdk::LiquidSdk::send_payment].
#[derive(Debug, Serialize)]
pub struct SendPaymentRequest {
pub prepare_response: PrepareSendResponse,
}
/// Returned when calling [crate::sdk::LiquidSdk::send_payment]. /// Returned when calling [crate::sdk::LiquidSdk::send_payment].
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct SendPaymentResponse { pub struct SendPaymentResponse {
@@ -285,27 +323,7 @@ pub struct PreparePayOnchainResponse {
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct PayOnchainRequest { pub struct PayOnchainRequest {
pub address: String, pub address: String,
pub prepare_res: PreparePayOnchainResponse, pub prepare_response: PreparePayOnchainResponse,
}
/// An argument when calling [crate::sdk::LiquidSdk::prepare_receive_onchain].
#[derive(Debug, Serialize, Clone)]
pub struct PrepareReceiveOnchainRequest {
pub payer_amount_sat: u64,
}
/// Returned when calling [crate::sdk::LiquidSdk::prepare_receive_onchain].
#[derive(Debug, Serialize, Clone)]
pub struct PrepareReceiveOnchainResponse {
pub payer_amount_sat: u64,
pub fees_sat: u64,
}
/// Returned when calling [crate::sdk::LiquidSdk::receive_onchain].
#[derive(Debug, Serialize)]
pub struct ReceiveOnchainResponse {
pub address: String,
pub bip21: String,
} }
/// An argument when calling [crate::sdk::LiquidSdk::prepare_refund]. /// An argument when calling [crate::sdk::LiquidSdk::prepare_refund].
@@ -929,10 +947,19 @@ pub struct PaymentTxData {
pub is_confirmed: bool, pub is_confirmed: bool,
} }
#[derive(Debug, Clone, Serialize)]
pub enum PaymentSwapType {
Receive,
Send,
Chain,
}
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
pub struct PaymentSwapData { pub struct PaymentSwapData {
pub swap_id: String, pub swap_id: String,
pub swap_type: PaymentSwapType,
/// Swap creation timestamp /// Swap creation timestamp
pub created_at: u32, pub created_at: u32,
@@ -951,19 +978,97 @@ pub struct PaymentSwapData {
pub refund_tx_id: Option<String>, pub refund_tx_id: Option<String>,
pub refund_tx_amount_sat: Option<u64>, pub refund_tx_amount_sat: Option<u64>,
/// Present only for chain swaps.
/// In case of an outgoing chain swap, it's the Bitcoin address which will receive the funds
/// In case of an incoming chain swap, it's the Liquid address which will receive the funds
pub claim_address: Option<String>,
/// Payment status derived from the swap status /// Payment status derived from the swap status
pub status: PaymentState, pub status: PaymentState,
} }
/// The specific details of a payment, depending on its type
#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum PaymentDetails {
/// Swapping to or from Lightning
Lightning {
swap_id: String,
/// Represents the invoice description
description: String,
/// In case of a Send swap, this is the preimage of the paid invoice (proof of payment).
preimage: Option<String>,
/// Represents the 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<String>,
/// For a Send swap which was refunded, this is the refund tx id
refund_tx_id: Option<String>,
/// For a Send swap which was refunded, this is the refund amount
refund_tx_amount_sat: Option<u64>,
},
/// Direct onchain payment to a Liquid address
Liquid {
/// Represents either a Liquid BIP21 URI or pure address
destination: String,
/// Represents the BIP21 `message` field
description: String,
},
/// Swapping to or from the Bitcoin chain
Bitcoin {
swap_id: String,
/// Represents the invoice description
description: String,
/// For a Send swap which was refunded, this is the refund tx id
refund_tx_id: Option<String>,
/// For a Send swap which was refunded, this is the refund amount
refund_tx_amount_sat: Option<u64>,
},
}
impl PaymentDetails {
pub(crate) fn get_swap_id(&self) -> Option<String> {
match self {
Self::Lightning { swap_id, .. } | Self::Bitcoin { swap_id, .. } => {
Some(swap_id.clone())
}
Self::Liquid { .. } => None,
}
}
pub(crate) fn get_refund_tx_amount_sat(&self) -> Option<u64> {
match self {
Self::Lightning {
refund_tx_amount_sat,
..
}
| Self::Bitcoin {
refund_tx_amount_sat,
..
} => *refund_tx_amount_sat,
Self::Liquid { .. } => None,
}
}
}
/// Represents an SDK payment. /// Represents an SDK payment.
/// ///
/// By default, this is an onchain tx. It may represent a swap, if swap metadata is available. /// By default, this is an onchain tx. It may represent a swap, if swap metadata is available.
#[derive(Debug, Clone, PartialEq, Serialize)] #[derive(Debug, Clone, PartialEq, Serialize)]
pub struct Payment { pub struct Payment {
pub tx_id: Option<String>, /// The destination associated with the payment, if it was created via our SDK.
/// Can be either a Liquid/Bitcoin address, a Liquid BIP21 URI or an invoice
pub destination: Option<String>,
/// The swap ID, if any swap is associated with this payment pub tx_id: Option<String>,
pub swap_id: Option<String>,
/// Composite timestamp that can be used for sorting or displaying the payment. /// Composite timestamp that can be used for sorting or displaying the payment.
/// ///
@@ -992,23 +1097,6 @@ pub struct Payment {
/// - for Receive payments, this is zero /// - for Receive payments, this is zero
pub fees_sat: u64, pub fees_sat: u64,
/// In case of a Send swap, this is the preimage of the paid invoice (proof of payment).
pub preimage: Option<String>,
/// Represents the 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
pub bolt11: Option<String>,
/// Represents the invoice description
pub description: String,
/// For a Send swap which was refunded, this is the refund tx id
pub refund_tx_id: Option<String>,
/// For a Send swap which was refunded, this is the refund amount
pub refund_tx_amount_sat: Option<u64>,
/// If it is a `Send` or `Receive` payment /// If it is a `Send` or `Receive` payment
pub payment_type: PaymentType, pub payment_type: PaymentType,
@@ -1018,6 +1106,10 @@ pub struct Payment {
/// ///
/// If the tx has an associated swap, this is determined by the swap status (pending or complete). /// If the tx has an associated swap, this is determined by the swap status (pending or complete).
pub status: PaymentState, pub status: PaymentState,
/// The details of a payment, depending on its [destination](Payment::destination) and
/// [type](Payment::payment_type)
pub details: Option<PaymentDetails>,
} }
impl Payment { impl Payment {
pub(crate) fn from_pending_swap(swap: PaymentSwapData, payment_type: PaymentType) -> Payment { pub(crate) fn from_pending_swap(swap: PaymentSwapData, payment_type: PaymentType) -> Payment {
@@ -1027,25 +1119,58 @@ impl Payment {
}; };
Payment { Payment {
destination: swap.bolt11.clone(),
tx_id: None, tx_id: None,
swap_id: Some(swap.swap_id),
timestamp: swap.created_at, timestamp: swap.created_at,
amount_sat, amount_sat,
fees_sat: swap.payer_amount_sat - swap.receiver_amount_sat, fees_sat: swap.payer_amount_sat - swap.receiver_amount_sat,
preimage: swap.preimage,
bolt11: swap.bolt11,
description: swap.description,
refund_tx_id: swap.refund_tx_id,
refund_tx_amount_sat: swap.refund_tx_amount_sat,
payment_type, payment_type,
status: swap.status, status: swap.status,
details: Some(PaymentDetails::Lightning {
swap_id: swap.swap_id,
preimage: swap.preimage,
bolt11: swap.bolt11,
description: swap.description,
refund_tx_id: swap.refund_tx_id,
refund_tx_amount_sat: swap.refund_tx_amount_sat,
}),
} }
} }
pub(crate) fn from_tx_data(tx: PaymentTxData, swap: Option<PaymentSwapData>) -> Payment { pub(crate) fn from_tx_data(
tx: PaymentTxData,
swap: Option<PaymentSwapData>,
payment_details: Option<PaymentDetails>,
) -> Payment {
let description = swap.as_ref().map(|s| s.description.clone());
Payment { Payment {
tx_id: Some(tx.tx_id), tx_id: Some(tx.tx_id),
swap_id: swap.as_ref().map(|s| s.swap_id.clone()), // When the swap is present and of type send and receive, we retrieve the destination from the invoice.
// 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::Chain,
claim_address,
..
}) => claim_address.clone(),
_ => match &payment_details {
Some(PaymentDetails::Liquid { destination, .. }) => Some(destination.clone()),
_ => None,
},
},
timestamp: match swap { timestamp: match swap {
Some(ref swap) => swap.created_at, Some(ref swap) => swap.created_at,
None => tx.timestamp.unwrap_or(utils::now()), None => tx.timestamp.unwrap_or(utils::now()),
@@ -1058,22 +1183,56 @@ impl Payment {
PaymentType::Send => tx.fees_sat, PaymentType::Send => tx.fees_sat,
}, },
}, },
preimage: swap.as_ref().and_then(|s| s.preimage.clone()),
bolt11: swap.as_ref().and_then(|s| s.bolt11.clone()),
description: swap
.as_ref()
.map(|s| s.description.clone())
.unwrap_or("Liquid transfer".to_string()),
refund_tx_id: swap.as_ref().and_then(|s| s.refund_tx_id.clone()),
refund_tx_amount_sat: swap.as_ref().and_then(|s| s.refund_tx_amount_sat),
payment_type: tx.payment_type, payment_type: tx.payment_type,
status: match swap { status: match &swap {
Some(swap) => swap.status, Some(swap) => swap.status,
None => match tx.is_confirmed { None => match tx.is_confirmed {
true => PaymentState::Complete, true => PaymentState::Complete,
false => PaymentState::Pending, false => PaymentState::Pending,
}, },
}, },
details: match swap {
Some(
PaymentSwapData {
swap_type: PaymentSwapType::Receive,
swap_id,
bolt11,
refund_tx_id,
preimage,
refund_tx_amount_sat,
..
}
| PaymentSwapData {
swap_type: PaymentSwapType::Send,
swap_id,
bolt11,
preimage,
refund_tx_id,
refund_tx_amount_sat,
..
},
) => Some(PaymentDetails::Lightning {
swap_id,
preimage,
bolt11,
refund_tx_id,
refund_tx_amount_sat,
description: description.unwrap_or("Liquid transfer".to_string()),
}),
Some(PaymentSwapData {
swap_type: PaymentSwapType::Chain,
swap_id,
refund_tx_id,
refund_tx_amount_sat,
..
}) => Some(PaymentDetails::Bitcoin {
swap_id,
refund_tx_id,
refund_tx_amount_sat,
description: description.unwrap_or("Bitcoin transfer".to_string()),
}),
_ => payment_details,
},
} }
} }
} }
@@ -1114,7 +1273,7 @@ pub struct PrepareBuyBitcoinResponse {
/// An argument when calling [crate::sdk::LiquidSdk::buy_bitcoin]. /// An argument when calling [crate::sdk::LiquidSdk::buy_bitcoin].
#[derive(Clone, Debug, Serialize)] #[derive(Clone, Debug, Serialize)]
pub struct BuyBitcoinRequest { pub struct BuyBitcoinRequest {
pub prepare_res: PrepareBuyBitcoinResponse, pub prepare_response: PrepareBuyBitcoinResponse,
/// The optional URL to redirect to after completing the buy. /// The optional URL to redirect to after completing the buy.
/// ///
@@ -1185,6 +1344,7 @@ impl From<SwapTree> for InternalSwapTree {
/// * `PayError` indicates that an error occurred while trying to pay the invoice from the LNURL endpoint. /// * `PayError` indicates that an error occurred while trying to pay the invoice from the LNURL endpoint.
/// This includes the payment hash of the failed invoice and the failure reason. /// This includes the payment hash of the failed invoice and the failure reason.
#[derive(Serialize)] #[derive(Serialize)]
#[allow(clippy::large_enum_variant)]
pub enum LnUrlPayResult { pub enum LnUrlPayResult {
EndpointSuccess { data: LnUrlPaySuccessData }, EndpointSuccess { data: LnUrlPaySuccessData },
EndpointError { data: LnUrlErrorData }, EndpointError { data: LnUrlErrorData },

View File

@@ -65,5 +65,10 @@ pub(crate) fn current_migrations() -> Vec<&'static str> {
ALTER TABLE send_swaps ADD COLUMN description TEXT; ALTER TABLE send_swaps ADD COLUMN description TEXT;
ALTER TABLE chain_swaps ADD COLUMN description TEXT; ALTER TABLE chain_swaps ADD COLUMN description TEXT;
", ",
"CREATE TABLE IF NOT EXISTS payment_details (
tx_id TEXT NOT NULL PRIMARY KEY,
destination TEXT NOT NULL,
description TEXT NOT NULL
);",
] ]
} }

View File

@@ -83,7 +83,11 @@ impl Persister {
} }
} }
pub(crate) fn insert_or_update_payment(&self, ptx: PaymentTxData) -> Result<()> { pub(crate) fn insert_or_update_payment(
&self,
ptx: PaymentTxData,
details: Option<PaymentDetails>,
) -> Result<()> {
let mut con = self.get_connection()?; let mut con = self.get_connection()?;
let tx = con.transaction()?; let tx = con.transaction()?;
@@ -99,7 +103,7 @@ impl Persister {
VALUES (?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?)
", ",
( (
ptx.tx_id, &ptx.tx_id,
ptx.timestamp, ptx.timestamp,
ptx.amount_sat, ptx.amount_sat,
ptx.fees_sat, ptx.fees_sat,
@@ -107,6 +111,22 @@ impl Persister {
ptx.is_confirmed, ptx.is_confirmed,
), ),
)?; )?;
if let Some(PaymentDetails::Liquid {
destination,
description,
}) = details
{
tx.execute(
"INSERT OR REPLACE INTO payment_details (
tx_id,
destination,
description
)
VALUES (?, ?, ?)
",
(ptx.tx_id, destination, description),
)?;
}
tx.commit()?; tx.commit()?;
Ok(()) Ok(())
@@ -176,8 +196,11 @@ impl Persister {
cs.refund_tx_id, cs.refund_tx_id,
cs.payer_amount_sat, cs.payer_amount_sat,
cs.receiver_amount_sat, cs.receiver_amount_sat,
cs.claim_address,
cs.state, cs.state,
rtx.amount_sat rtx.amount_sat,
pd.destination,
pd.description
FROM payment_tx_data AS ptx -- Payment tx (each tx results in a Payment) FROM payment_tx_data AS ptx -- Payment tx (each tx results in a Payment)
FULL JOIN ( FULL JOIN (
SELECT * FROM receive_swaps SELECT * FROM receive_swaps
@@ -190,6 +213,8 @@ impl Persister {
ON ptx.tx_id in (cs.user_lockup_tx_id, cs.claim_tx_id) ON ptx.tx_id in (cs.user_lockup_tx_id, cs.claim_tx_id)
LEFT JOIN payment_tx_data AS rtx -- Refund tx data LEFT JOIN payment_tx_data AS rtx -- Refund tx data
ON rtx.tx_id in (ss.refund_tx_id, cs.refund_tx_id) ON rtx.tx_id in (ss.refund_tx_id, cs.refund_tx_id)
LEFT JOIN payment_details AS pd -- Payment details
ON pd.tx_id = ptx.tx_id
WHERE -- Filter out refund txs from Send Swaps WHERE -- Filter out refund txs from Send Swaps
ptx.tx_id NOT IN (SELECT refund_tx_id FROM send_swaps WHERE refund_tx_id NOT NULL) ptx.tx_id NOT IN (SELECT refund_tx_id FROM send_swaps WHERE refund_tx_id NOT NULL)
AND -- Filter out refund txs from Chain Swaps AND -- Filter out refund txs from Chain Swaps
@@ -246,14 +271,19 @@ impl Persister {
let maybe_chain_swap_refund_tx_id: Option<String> = row.get(27)?; let maybe_chain_swap_refund_tx_id: Option<String> = row.get(27)?;
let maybe_chain_swap_payer_amount_sat: Option<u64> = row.get(28)?; let maybe_chain_swap_payer_amount_sat: Option<u64> = row.get(28)?;
let maybe_chain_swap_receiver_amount_sat: Option<u64> = row.get(29)?; let maybe_chain_swap_receiver_amount_sat: Option<u64> = row.get(29)?;
let maybe_chain_swap_state: Option<PaymentState> = row.get(30)?; let maybe_chain_swap_claim_address: Option<String> = row.get(30)?;
let maybe_chain_swap_state: Option<PaymentState> = row.get(31)?;
let maybe_swap_refund_tx_amount_sat: Option<u64> = row.get(31)?; let maybe_swap_refund_tx_amount_sat: Option<u64> = row.get(32)?;
let maybe_payment_details_destination: Option<String> = row.get(33)?;
let maybe_payment_details_description: Option<String> = row.get(34)?;
let (swap, payment_type) = match maybe_receive_swap_id { let (swap, payment_type) = match maybe_receive_swap_id {
Some(receive_swap_id) => ( Some(receive_swap_id) => (
Some(PaymentSwapData { Some(PaymentSwapData {
swap_id: receive_swap_id, swap_id: receive_swap_id,
swap_type: PaymentSwapType::Receive,
created_at: maybe_receive_swap_created_at.unwrap_or(utils::now()), created_at: maybe_receive_swap_created_at.unwrap_or(utils::now()),
preimage: None, preimage: None,
bolt11: maybe_receive_swap_invoice.clone(), bolt11: maybe_receive_swap_invoice.clone(),
@@ -266,6 +296,7 @@ impl Persister {
receiver_amount_sat: maybe_receive_swap_receiver_amount_sat.unwrap_or(0), receiver_amount_sat: maybe_receive_swap_receiver_amount_sat.unwrap_or(0),
refund_tx_id: None, refund_tx_id: None,
refund_tx_amount_sat: None, refund_tx_amount_sat: None,
claim_address: None,
status: maybe_receive_swap_receiver_state.unwrap_or(PaymentState::Created), status: maybe_receive_swap_receiver_state.unwrap_or(PaymentState::Created),
}), }),
PaymentType::Receive, PaymentType::Receive,
@@ -274,6 +305,7 @@ impl Persister {
Some(send_swap_id) => ( Some(send_swap_id) => (
Some(PaymentSwapData { Some(PaymentSwapData {
swap_id: send_swap_id, swap_id: send_swap_id,
swap_type: PaymentSwapType::Send,
created_at: maybe_send_swap_created_at.unwrap_or(utils::now()), created_at: maybe_send_swap_created_at.unwrap_or(utils::now()),
preimage: maybe_send_swap_preimage, preimage: maybe_send_swap_preimage,
bolt11: maybe_send_swap_invoice.clone(), bolt11: maybe_send_swap_invoice.clone(),
@@ -286,6 +318,7 @@ impl Persister {
receiver_amount_sat: maybe_send_swap_receiver_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, refund_tx_id: maybe_send_swap_refund_tx_id,
refund_tx_amount_sat: maybe_swap_refund_tx_amount_sat, refund_tx_amount_sat: maybe_swap_refund_tx_amount_sat,
claim_address: None,
status: maybe_send_swap_state.unwrap_or(PaymentState::Created), status: maybe_send_swap_state.unwrap_or(PaymentState::Created),
}), }),
PaymentType::Send, PaymentType::Send,
@@ -294,6 +327,7 @@ impl Persister {
Some(chain_swap_id) => ( Some(chain_swap_id) => (
Some(PaymentSwapData { Some(PaymentSwapData {
swap_id: chain_swap_id, swap_id: chain_swap_id,
swap_type: PaymentSwapType::Chain,
created_at: maybe_chain_swap_created_at.unwrap_or(utils::now()), created_at: maybe_chain_swap_created_at.unwrap_or(utils::now()),
preimage: maybe_chain_swap_preimage, preimage: maybe_chain_swap_preimage,
bolt11: None, bolt11: None,
@@ -303,6 +337,7 @@ impl Persister {
receiver_amount_sat: maybe_chain_swap_receiver_amount_sat.unwrap_or(0), receiver_amount_sat: maybe_chain_swap_receiver_amount_sat.unwrap_or(0),
refund_tx_id: maybe_chain_swap_refund_tx_id, refund_tx_id: maybe_chain_swap_refund_tx_id,
refund_tx_amount_sat: maybe_swap_refund_tx_amount_sat, refund_tx_amount_sat: maybe_swap_refund_tx_amount_sat,
claim_address: maybe_chain_swap_claim_address,
status: maybe_chain_swap_state.unwrap_or(PaymentState::Created), status: maybe_chain_swap_state.unwrap_or(PaymentState::Created),
}), }),
maybe_chain_swap_direction maybe_chain_swap_direction
@@ -314,11 +349,18 @@ impl Persister {
}, },
}; };
let payment_details =
maybe_payment_details_destination.map(|destination| PaymentDetails::Liquid {
destination,
description: maybe_payment_details_description
.unwrap_or("Liquid transfer".to_string()),
});
match (tx, swap.clone()) { match (tx, swap.clone()) {
(None, None) => Err(maybe_tx_tx_id.err().unwrap()), (None, None) => Err(maybe_tx_tx_id.err().unwrap()),
(None, Some(swap)) => Ok(Payment::from_pending_swap(swap, payment_type)), (None, Some(swap)) => Ok(Payment::from_pending_swap(swap, payment_type)),
(Some(tx), None) => Ok(Payment::from_tx_data(tx, None)), (Some(tx), None) => Ok(Payment::from_tx_data(tx, None, payment_details)),
(Some(tx), Some(swap)) => Ok(Payment::from_tx_data(tx, Some(swap))), (Some(tx), Some(swap)) => Ok(Payment::from_tx_data(tx, Some(swap), payment_details)),
} }
} }
@@ -413,7 +455,7 @@ mod tests {
let (_temp_dir, storage) = new_persister()?; let (_temp_dir, storage) = new_persister()?;
let payment_tx_data = new_payment_tx_data(PaymentType::Send); let payment_tx_data = new_payment_tx_data(PaymentType::Send);
storage.insert_or_update_payment(payment_tx_data.clone())?; storage.insert_or_update_payment(payment_tx_data.clone(), None)?;
assert!(storage assert!(storage
.get_payments(&ListPaymentsRequest { .get_payments(&ListPaymentsRequest {

View File

@@ -256,14 +256,17 @@ impl ReceiveSwapStateHandler {
// We insert a pseudo-claim-tx in case LWK fails to pick up the new mempool tx for a while // We insert a pseudo-claim-tx in case LWK fails to pick up the new mempool tx for a while
// This makes the tx known to the SDK (get_info, list_payments) instantly // This makes the tx known to the SDK (get_info, list_payments) instantly
self.persister.insert_or_update_payment(PaymentTxData { self.persister.insert_or_update_payment(
tx_id: claim_tx_id.clone(), PaymentTxData {
timestamp: Some(utils::now()), tx_id: claim_tx_id.clone(),
amount_sat: ongoing_receive_swap.receiver_amount_sat, timestamp: Some(utils::now()),
fees_sat: 0, amount_sat: ongoing_receive_swap.receiver_amount_sat,
payment_type: PaymentType::Receive, fees_sat: 0,
is_confirmed: false, payment_type: PaymentType::Receive,
})?; is_confirmed: false,
},
None,
)?;
self.update_swap_info(swap_id, Pending, Some(&claim_tx_id), None) self.update_swap_info(swap_id, Pending, Some(&claim_tx_id), None)
.await?; .await?;

View File

@@ -14,12 +14,13 @@ use futures_util::stream::select_all;
use futures_util::StreamExt; use futures_util::StreamExt;
use log::{debug, error, info}; use log::{debug, error, info};
use lwk_wollet::bitcoin::hex::DisplayHex; use lwk_wollet::bitcoin::hex::DisplayHex;
use lwk_wollet::elements::AssetId;
use lwk_wollet::hashes::{sha256, Hash}; use lwk_wollet::hashes::{sha256, Hash};
use lwk_wollet::secp256k1::ThirtyTwoByteHash; use lwk_wollet::secp256k1::ThirtyTwoByteHash;
use lwk_wollet::{elements, ElementsNetwork}; use lwk_wollet::{elements, ElementsNetwork};
use sdk_common::bitcoin::secp256k1::Secp256k1; use sdk_common::bitcoin::secp256k1::Secp256k1;
use sdk_common::bitcoin::util::bip32::ChildNumber; use sdk_common::bitcoin::util::bip32::ChildNumber;
use sdk_common::ensure_sdk; use sdk_common::liquid::LiquidAddressData;
use sdk_common::prelude::{FiatAPI, FiatCurrency, LnUrlPayError, LnUrlWithdrawError, Rate}; use sdk_common::prelude::{FiatAPI, FiatCurrency, LnUrlPayError, LnUrlWithdrawError, Rate};
use tokio::sync::{watch, Mutex, RwLock}; use tokio::sync::{watch, Mutex, RwLock};
use tokio::time::MissedTickBehavior; use tokio::time::MissedTickBehavior;
@@ -28,6 +29,7 @@ use url::Url;
use crate::chain::bitcoin::BitcoinChainService; use crate::chain::bitcoin::BitcoinChainService;
use crate::chain_swap::ChainSwapStateHandler; use crate::chain_swap::ChainSwapStateHandler;
use crate::ensure_sdk;
use crate::error::SdkError; use crate::error::SdkError;
use crate::lightning_invoice::{Bolt11Invoice, Bolt11InvoiceDescription}; use crate::lightning_invoice::{Bolt11Invoice, Bolt11InvoiceDescription};
use crate::model::PaymentState::*; use crate::model::PaymentState::*;
@@ -447,8 +449,12 @@ impl LiquidSdk {
.await? .await?
} }
Pending => { Pending => {
let Some(details) = &payment.details else {
return Err(anyhow::anyhow!("No payment details found"));
};
// The swap state has changed to Pending // The swap state has changed to Pending
match payment.swap_id.clone() { match details.get_swap_id() {
Some(swap_id) => match self.persister.fetch_swap_by_id(&swap_id)? { Some(swap_id) => match self.persister.fetch_swap_by_id(&swap_id)? {
Swap::Chain(ChainSwap { claim_tx_id, .. }) Swap::Chain(ChainSwap { claim_tx_id, .. })
| Swap::Receive(ReceiveSwap { claim_tx_id, .. }) => { | Swap::Receive(ReceiveSwap { claim_tx_id, .. }) => {
@@ -537,9 +543,12 @@ impl LiquidSdk {
Complete => confirmed_sent_sat += p.amount_sat, Complete => confirmed_sent_sat += p.amount_sat,
Failed => { Failed => {
confirmed_sent_sat += p.amount_sat; confirmed_sent_sat += p.amount_sat;
confirmed_received_sat += p.refund_tx_amount_sat.unwrap_or_default(); if let Some(details) = p.details {
confirmed_received_sat +=
details.get_refund_tx_amount_sat().unwrap_or_default();
}
} }
Pending => match p.refund_tx_amount_sat { Pending => match p.details.and_then(|d| d.get_refund_tx_amount_sat()) {
Some(refund_tx_amount_sat) => { Some(refund_tx_amount_sat) => {
confirmed_sent_sat += p.amount_sat; confirmed_sent_sat += p.amount_sat;
pending_receive_sat += refund_tx_amount_sat; pending_receive_sat += refund_tx_amount_sat;
@@ -576,7 +585,7 @@ impl LiquidSdk {
("bitcoin", LiquidNetwork::Mainnet) => {} ("bitcoin", LiquidNetwork::Mainnet) => {}
("testnet", LiquidNetwork::Testnet) => {} ("testnet", LiquidNetwork::Testnet) => {}
_ => { _ => {
return Err(PaymentError::InvalidInvoice { return Err(PaymentError::InvalidNetwork {
err: "Invoice cannot be paid on the current network".to_string(), err: "Invoice cannot be paid on the current network".to_string(),
}) })
} }
@@ -659,7 +668,9 @@ impl LiquidSdk {
self.estimate_onchain_tx_fee( self.estimate_onchain_tx_fee(
amount_sat, amount_sat,
temp_p2tr_addr, temp_p2tr_addr,
self.config.lowball_fee_rate_msat_per_vbyte(), self.config
.lowball_fee_rate_msat_per_vbyte()
.map(|v| v as f32),
) )
.await .await
} }
@@ -669,30 +680,92 @@ impl LiquidSdk {
/// # Arguments /// # Arguments
/// ///
/// * `req` - the [PrepareSendRequest] containing: /// * `req` - the [PrepareSendRequest] containing:
/// * `invoice` - the bolt11 Lightning invoice to pay /// * `destination` - Either a Liquid BIP21 URI/address or a BOLT11 invoice
/// * `amount_sat` - Should only be specified when paying directly onchain or via amount-less BIP21
///
/// # Returns
/// Returns a [PrepareSendResponse] containing:
/// * `destination` - the parsed destination, of type [SendDestination]
/// * `fees_sat` - the additional fees which will be paid by the sender
pub async fn prepare_send_payment( pub async fn prepare_send_payment(
&self, &self,
req: &PrepareSendRequest, req: &PrepareSendRequest,
) -> Result<PrepareSendResponse, PaymentError> { ) -> Result<PrepareSendResponse, PaymentError> {
self.ensure_is_started().await?; self.ensure_is_started().await?;
self.ensure_send_is_not_self_transfer(&req.invoice)?; let fees_sat;
let invoice = self.validate_invoice(&req.invoice)?; let receiver_amount_sat;
let payment_destination;
let receiver_amount_sat = invoice match sdk_common::input_parser::parse(&req.destination).await? {
.amount_milli_satoshis() InputType::LiquidAddress {
.ok_or(PaymentError::AmountOutOfRange)? address: mut liquid_address_data,
/ 1000; } => {
let lbtc_pair = self.validate_submarine_pairs(receiver_amount_sat)?; let Some(amount_sat) = liquid_address_data.amount_sat else {
return Err(PaymentError::AmountMissing { err: "`amount_sat` must be present when paying to a `SendDestination::LiquidAddress`".to_string() });
};
let fees_sat = match self.swapper.check_for_mrh(&req.invoice)? { ensure_sdk!(
Some((lbtc_address, _)) => { liquid_address_data.network == self.config.network.into(),
self.estimate_onchain_tx_fee(receiver_amount_sat, &lbtc_address, None) PaymentError::InvalidNetwork {
.await? err: format!(
"Cannot send payment from {} to {}",
Into::<sdk_common::bitcoin::Network>::into(self.config.network),
liquid_address_data.network
)
}
);
receiver_amount_sat = amount_sat;
// TODO Ensure that `None` provides the lowest fees possible (0.01 sat/vbyte)
// once Esplora broadcast is enabled
fees_sat = self
.estimate_onchain_tx_fee(
receiver_amount_sat,
&liquid_address_data.address,
None,
)
.await?;
liquid_address_data.amount_sat = Some(receiver_amount_sat);
payment_destination = SendDestination::LiquidAddress {
address_data: liquid_address_data,
};
} }
None => { InputType::Bolt11 { invoice } => {
let lockup_fees_sat = self.estimate_lockup_tx_fee(receiver_amount_sat).await?; self.ensure_send_is_not_self_transfer(&invoice.bolt11)?;
lbtc_pair.fees.total(receiver_amount_sat) + lockup_fees_sat self.validate_invoice(&invoice.bolt11)?;
receiver_amount_sat = invoice.amount_msat.ok_or(PaymentError::AmountMissing {
err: "Expected invoice with an amount".to_string(),
})? / 1000;
if let Some(amount_sat) = req.amount_sat {
ensure_sdk!(
receiver_amount_sat == amount_sat,
PaymentError::Generic { err: "Amount in the payment request is not the same as the one in the invoice".to_string() }
);
}
let lbtc_pair = self.validate_submarine_pairs(receiver_amount_sat)?;
fees_sat = match self.swapper.check_for_mrh(&invoice.bolt11)? {
Some((lbtc_address, _)) => {
self.estimate_onchain_tx_fee(receiver_amount_sat, &lbtc_address, None)
.await?
}
None => {
let lockup_fees_sat =
self.estimate_lockup_tx_fee(receiver_amount_sat).await?;
lbtc_pair.fees.total(receiver_amount_sat) + lockup_fees_sat
}
};
payment_destination = SendDestination::Bolt11 { invoice };
}
_ => {
return Err(PaymentError::Generic {
err: "Destination is not valid".to_string(),
});
} }
}; };
@@ -703,7 +776,7 @@ impl LiquidSdk {
); );
Ok(PrepareSendResponse { Ok(PrepareSendResponse {
invoice: req.invoice.clone(), destination: payment_destination,
fees_sat, fees_sat,
}) })
} }
@@ -715,7 +788,7 @@ impl LiquidSdk {
} }
} }
/// Pays a Lightning invoice via a submarine swap. /// Either pays a Lightning invoice via a submarine swap or sends funds directly to an address.
/// ///
/// Depending on [Config]'s `payment_timeout_sec`, this function will return: /// Depending on [Config]'s `payment_timeout_sec`, this function will return:
/// * [PaymentState::Pending] payment - if the payment could be initiated but didn't yet /// * [PaymentState::Pending] payment - if the payment could be initiated but didn't yet
@@ -724,59 +797,128 @@ impl LiquidSdk {
/// ///
/// # Arguments /// # Arguments
/// ///
/// * `req` - The [PrepareSendResponse] from calling [LiquidSdk::prepare_send_payment] /// * `req` - A [SendPaymentRequest], containing:
/// * `prepare_response` - the [PrepareSendResponse] returned by [LiquidSdk::prepare_send_payment]
/// ///
/// # Errors /// # Errors
/// ///
/// * [PaymentError::PaymentTimeout] - if the payment could not be initiated in this time /// * [PaymentError::PaymentTimeout] - if the payment could not be initiated in this time
pub async fn send_payment( pub async fn send_payment(
&self, &self,
req: &PrepareSendResponse, req: &SendPaymentRequest,
) -> Result<SendPaymentResponse, PaymentError> { ) -> Result<SendPaymentResponse, PaymentError> {
self.ensure_is_started().await?; self.ensure_is_started().await?;
self.ensure_send_is_not_self_transfer(&req.invoice)?; let PrepareSendResponse {
self.validate_invoice(&req.invoice)?; fees_sat,
destination: payment_destination,
} = &req.prepare_response;
let amount_sat = get_invoice_amount!(&req.invoice); match payment_destination {
let payer_amount_sat = amount_sat + req.fees_sat; SendDestination::LiquidAddress {
address_data: liquid_address_data,
} => {
let Some(amount_sat) = liquid_address_data.amount_sat else {
return Err(PaymentError::AmountMissing { err: "`amount_sat` must be present when paying to a `SendDestination::LiquidAddress`".to_string() });
};
ensure_sdk!(
liquid_address_data.network == self.config.network.into(),
PaymentError::InvalidNetwork {
err: format!(
"Cannot send payment from {} to {}",
Into::<sdk_common::bitcoin::Network>::into(self.config.network),
liquid_address_data.network
)
}
);
let payer_amount_sat = amount_sat + fees_sat;
ensure_sdk!(
payer_amount_sat <= self.get_info().await?.balance_sat,
PaymentError::InsufficientFunds
);
self.pay_liquid(liquid_address_data.clone(), amount_sat, *fees_sat)
.await
}
SendDestination::Bolt11 { invoice } => {
self.pay_invoice(&invoice.bolt11, *fees_sat).await
}
}
}
async fn pay_invoice(
&self,
invoice: &str,
fees_sat: u64,
) -> Result<SendPaymentResponse, PaymentError> {
self.ensure_send_is_not_self_transfer(invoice)?;
self.validate_invoice(invoice)?;
let amount_sat = get_invoice_amount!(invoice);
let payer_amount_sat = amount_sat + fees_sat;
ensure_sdk!( ensure_sdk!(
payer_amount_sat <= self.get_info().await?.balance_sat, payer_amount_sat <= self.get_info().await?.balance_sat,
PaymentError::InsufficientFunds PaymentError::InsufficientFunds
); );
match self.swapper.check_for_mrh(&req.invoice)? { match self.swapper.check_for_mrh(invoice)? {
// If we find a valid MRH, extract the BIP21 amount and address, then pay via onchain tx // If we find a valid MRH, extract the BIP21 amount and address, then pay via onchain tx
Some((address, amount_btc)) => { Some((address, _amount_sat)) => {
self.send_payment_via_mrh(req, &address, amount_btc).await info!("Found MRH for L-BTC address {address} and amount_sat {amount_sat}");
self.pay_liquid(
LiquidAddressData {
address,
network: self.config.network.into(),
asset_id: None,
amount_sat: None,
label: None,
message: None,
},
amount_sat,
fees_sat,
)
.await
} }
// If no MRH found, perform usual swap // If no MRH found, perform usual swap
None => self.send_payment_via_swap(req).await, None => self.send_payment_via_swap(invoice, fees_sat).await,
} }
} }
/// Performs a Send Payment by doing an onchain tx to the L-BTC address in the MRH. /// Performs a Send Payment by doing an onchain tx to a L-BTC address
async fn send_payment_via_mrh( async fn pay_liquid(
&self, &self,
req: &PrepareSendResponse, address_data: LiquidAddressData,
lbtc_address: &str, receiver_amount_sat: u64,
amount_btc: f64, fees_sat: u64,
) -> Result<SendPaymentResponse, PaymentError> { ) -> Result<SendPaymentResponse, PaymentError> {
let amount_sat: u64 = (amount_btc * 100_000_000.0) as u64; // TODO Ensure that `None` provides the lowest fees possible (0.01 sat/vbyte)
info!("Found MRH for L-BTC address {lbtc_address} and amount_sat {amount_sat}"); // once Esplora broadcast is enabled
// Ensure we use the same fee-rate from the `PrepareSendResponse`
let receiver_amount_sat = get_invoice_amount!(req.invoice); let fee_rate_sats_per_kvb = utils::derive_fee_rate_sats_per_kvb(
self.onchain_wallet.clone(),
receiver_amount_sat,
&address_data.address,
fees_sat,
)
.await?;
let tx = self let tx = self
.onchain_wallet .onchain_wallet
.build_tx(None, lbtc_address, receiver_amount_sat) .build_tx(
Some(fee_rate_sats_per_kvb),
&address_data.address,
receiver_amount_sat,
)
.await?; .await?;
let onchain_fees_sat: u64 = tx.all_fees().values().sum();
let payer_amount_sat = receiver_amount_sat + onchain_fees_sat;
info!("Built onchain L-BTC tx with receiver_amount_sat = {receiver_amount_sat}, fees_sat = {onchain_fees_sat}");
info!("Built onchain L-BTC tx with ID {}", tx.txid());
let tx_id = tx.txid().to_string(); let tx_id = tx.txid().to_string();
let payer_amount_sat = receiver_amount_sat + fees_sat;
info!(
"Built onchain L-BTC tx with receiver_amount_sat = {receiver_amount_sat}, fees_sat = {fees_sat} and txid = {tx_id}"
);
let tx_hex = lwk_wollet::elements::encode::serialize(&tx).to_lower_hex_string(); let tx_hex = lwk_wollet::elements::encode::serialize(&tx).to_lower_hex_string();
self.swapper self.swapper
.broadcast_tx(self.config.network.into(), &tx_hex)?; .broadcast_tx(self.config.network.into(), &tx_hex)?;
@@ -787,32 +929,40 @@ impl LiquidSdk {
tx_id: tx_id.clone(), tx_id: tx_id.clone(),
timestamp: Some(utils::now()), timestamp: Some(utils::now()),
amount_sat: payer_amount_sat, amount_sat: payer_amount_sat,
fees_sat: onchain_fees_sat, fees_sat,
payment_type: PaymentType::Send, payment_type: PaymentType::Send,
is_confirmed: false, is_confirmed: false,
}; };
self.persister.insert_or_update_payment(tx_data.clone())?; let payment_details = Some(PaymentDetails::Liquid {
destination: address_data.to_uri().unwrap_or(address_data.address),
description: address_data
.message
.unwrap_or("Liquid transfer".to_string()),
});
self.persister
.insert_or_update_payment(tx_data.clone(), payment_details.clone())?;
self.emit_payment_updated(Some(tx_id)).await?; // Emit Pending event self.emit_payment_updated(Some(tx_id)).await?; // Emit Pending event
Ok(SendPaymentResponse { Ok(SendPaymentResponse {
payment: Payment::from_tx_data(tx_data, None), payment: Payment::from_tx_data(tx_data, None, payment_details),
}) })
} }
/// Performs a Send Payment by doing a swap (create it, fund it, track it, etc). /// Performs a Send Payment by doing a swap (create it, fund it, track it, etc).
async fn send_payment_via_swap( async fn send_payment_via_swap(
&self, &self,
req: &PrepareSendResponse, invoice: &str,
fees_sat: u64,
) -> Result<SendPaymentResponse, PaymentError> { ) -> Result<SendPaymentResponse, PaymentError> {
let receiver_amount_sat = get_invoice_amount!(req.invoice); let receiver_amount_sat = get_invoice_amount!(invoice);
let lbtc_pair = self.validate_submarine_pairs(receiver_amount_sat)?; let lbtc_pair = self.validate_submarine_pairs(receiver_amount_sat)?;
let lockup_tx_fees_sat = self.estimate_lockup_tx_fee(receiver_amount_sat).await?; let lockup_tx_fees_sat = self.estimate_lockup_tx_fee(receiver_amount_sat).await?;
ensure_sdk!( ensure_sdk!(
req.fees_sat == lbtc_pair.fees.total(receiver_amount_sat) + lockup_tx_fees_sat, fees_sat == lbtc_pair.fees.total(receiver_amount_sat) + lockup_tx_fees_sat,
PaymentError::InvalidOrExpiredFees PaymentError::InvalidOrExpiredFees
); );
let swap = match self.persister.fetch_send_swap_by_invoice(&req.invoice)? { let swap = match self.persister.fetch_send_swap_by_invoice(invoice)? {
Some(swap) => match swap.state { Some(swap) => match swap.state {
Pending => return Err(PaymentError::PaymentInProgress), Pending => return Err(PaymentError::PaymentInProgress),
Complete => return Err(PaymentError::AlreadyPaid), Complete => return Err(PaymentError::AlreadyPaid),
@@ -833,7 +983,7 @@ impl LiquidSdk {
let create_response = self.swapper.create_send_swap(CreateSubmarineRequest { let create_response = self.swapper.create_send_swap(CreateSubmarineRequest {
from: "L-BTC".to_string(), from: "L-BTC".to_string(),
to: "BTC".to_string(), to: "BTC".to_string(),
invoice: req.invoice.to_string(), invoice: invoice.to_string(),
refund_public_key, refund_public_key,
pair_hash: Some(lbtc_pair.hash), pair_hash: Some(lbtc_pair.hash),
referral_id: None, referral_id: None,
@@ -842,12 +992,12 @@ impl LiquidSdk {
let swap_id = &create_response.id; let swap_id = &create_response.id;
let create_response_json = let create_response_json =
SendSwap::from_boltz_struct_to_json(&create_response, swap_id)?; SendSwap::from_boltz_struct_to_json(&create_response, swap_id)?;
let description = get_invoice_description!(req.invoice); let description = get_invoice_description!(invoice);
let payer_amount_sat = req.fees_sat + receiver_amount_sat; let payer_amount_sat = fees_sat + receiver_amount_sat;
let swap = SendSwap { let swap = SendSwap {
id: swap_id.clone(), id: swap_id.clone(),
invoice: req.invoice.clone(), invoice: invoice.to_string(),
description, description,
preimage: None, preimage: None,
payer_amount_sat, payer_amount_sat,
@@ -983,7 +1133,7 @@ impl LiquidSdk {
/// ///
/// * `req` - the [PayOnchainRequest] containing: /// * `req` - the [PayOnchainRequest] containing:
/// * `address` - the Bitcoin address to pay to /// * `address` - the Bitcoin address to pay to
/// * `prepare_res` - the [PreparePayOnchainResponse] from calling [LiquidSdk::prepare_pay_onchain] /// * `prepare_response` - the [PreparePayOnchainResponse] from calling [LiquidSdk::prepare_pay_onchain]
/// ///
/// # Errors /// # Errors
/// ///
@@ -994,9 +1144,9 @@ impl LiquidSdk {
) -> Result<SendPaymentResponse, PaymentError> { ) -> Result<SendPaymentResponse, PaymentError> {
self.ensure_is_started().await?; self.ensure_is_started().await?;
let receiver_amount_sat = req.prepare_res.receiver_amount_sat; let receiver_amount_sat = req.prepare_response.receiver_amount_sat;
let pair = self.validate_chain_pairs(Direction::Outgoing, receiver_amount_sat)?; let pair = self.validate_chain_pairs(Direction::Outgoing, receiver_amount_sat)?;
let claim_fees_sat = req.prepare_res.claim_fees_sat; let claim_fees_sat = req.prepare_response.claim_fees_sat;
let server_fees_sat = pair.fees.server(); let server_fees_sat = pair.fees.server();
let server_lockup_amount_sat = receiver_amount_sat + claim_fees_sat; let server_lockup_amount_sat = receiver_amount_sat + claim_fees_sat;
let lockup_fees_sat = self let lockup_fees_sat = self
@@ -1004,7 +1154,7 @@ impl LiquidSdk {
.await?; .await?;
ensure_sdk!( ensure_sdk!(
req.prepare_res.total_fees_sat req.prepare_response.total_fees_sat
== pair.fees.boltz(server_lockup_amount_sat) == pair.fees.boltz(server_lockup_amount_sat)
+ lockup_fees_sat + lockup_fees_sat
+ claim_fees_sat + claim_fees_sat
@@ -1012,7 +1162,7 @@ impl LiquidSdk {
PaymentError::InvalidOrExpiredFees PaymentError::InvalidOrExpiredFees
); );
let payer_amount_sat = req.prepare_res.total_fees_sat + receiver_amount_sat; let payer_amount_sat = req.prepare_response.total_fees_sat + receiver_amount_sat;
ensure_sdk!( ensure_sdk!(
payer_amount_sat <= self.get_info().await?.balance_sat, payer_amount_sat <= self.get_info().await?.balance_sat,
PaymentError::InsufficientFunds PaymentError::InsufficientFunds
@@ -1047,7 +1197,7 @@ impl LiquidSdk {
let create_response_json = ChainSwap::from_boltz_struct_to_json(&create_response, swap_id)?; let create_response_json = ChainSwap::from_boltz_struct_to_json(&create_response, swap_id)?;
let accept_zero_conf = server_lockup_amount_sat <= pair.limits.maximal_zero_conf; let accept_zero_conf = server_lockup_amount_sat <= pair.limits.maximal_zero_conf;
let payer_amount_sat = req.prepare_res.total_fees_sat + receiver_amount_sat; let payer_amount_sat = req.prepare_response.total_fees_sat + receiver_amount_sat;
let claim_address = req.address.clone(); let claim_address = req.address.clone();
let swap = ChainSwap { let swap = ChainSwap {
@@ -1088,7 +1238,7 @@ impl LiquidSdk {
let timeout_fut = tokio::time::sleep(Duration::from_secs(self.config.payment_timeout_sec)); let timeout_fut = tokio::time::sleep(Duration::from_secs(self.config.payment_timeout_sec));
tokio::pin!(timeout_fut); tokio::pin!(timeout_fut);
let swap_id = swap.id(); let expected_swap_id = swap.id();
let mut events_stream = self.event_manager.subscribe(); let mut events_stream = self.event_manager.subscribe();
let mut maybe_payment: Option<Payment> = None; let mut maybe_payment: Option<Payment> = None;
@@ -1099,34 +1249,35 @@ impl LiquidSdk {
None => { None => {
debug!("Timeout occurred without payment, set swap to timed out"); debug!("Timeout occurred without payment, set swap to timed out");
match swap { match swap {
Swap::Send(_) => self.send_swap_state_handler.update_swap_info(&swap_id, TimedOut, None, None, None).await?, Swap::Send(_) => self.send_swap_state_handler.update_swap_info(&expected_swap_id, TimedOut, None, None, None).await?,
Swap::Chain(_) => self.chain_swap_state_handler.update_swap_info(&swap_id, TimedOut, None, None, None, None).await?, Swap::Chain(_) => self.chain_swap_state_handler.update_swap_info(&expected_swap_id, TimedOut, None, None, None, None).await?,
_ => () _ => ()
} }
return Err(PaymentError::PaymentTimeout) return Err(PaymentError::PaymentTimeout)
}, },
}, },
event = events_stream.recv() => match event { event = events_stream.recv() => match event {
Ok(SdkEvent::PaymentPending { details }) => match details.swap_id.clone() { Ok(SdkEvent::PaymentPending { details: payment }) => {
Some(id) if id == swap_id => match accept_zero_conf { let maybe_payment_swap_id = payment.details.as_ref().and_then(|d|d.get_swap_id());
true => { if matches!(maybe_payment_swap_id, Some(swap_id) if swap_id == expected_swap_id) {
debug!("Received Send Payment pending event with zero-conf accepted"); match accept_zero_conf {
return Ok(details) true => {
debug!("Received Send Payment pending event with zero-conf accepted");
return Ok(payment)
}
false => {
debug!("Received Send Payment pending event, waiting for confirmation");
maybe_payment = Some(payment);
}
} }
false => { };
debug!("Received Send Payment pending event, waiting for confirmation");
maybe_payment = Some(details);
}
},
_ => error!("Received Send Payment pending event for payment without swap ID"),
}, },
Ok(SdkEvent::PaymentSucceeded { details }) => match details.swap_id.clone() Ok(SdkEvent::PaymentSucceeded { details: payment }) => {
{ let maybe_payment_swap_id = payment.details.as_ref().and_then(|d| d.get_swap_id());
Some(id) if id == swap_id => { if matches!(maybe_payment_swap_id, Some(swap_id) if swap_id == expected_swap_id) {
debug!("Received Send Payment succeed event"); debug!("Received Send Payment succeed event");
return Ok(details); return Ok(payment);
} }
_ => error!("Received Send Payment succeed event for payment without swap ID"),
}, },
Ok(event) => debug!("Unhandled event: {event:?}"), Ok(event) => debug!("Unhandled event: {event:?}"),
Err(e) => debug!("Received error waiting for event: {e:?}"), Err(e) => debug!("Received error waiting for event: {e:?}"),
@@ -1139,57 +1290,138 @@ impl LiquidSdk {
/// ///
/// # Arguments /// # Arguments
/// ///
/// * `req` - the [PrepareReceivePaymentRequest] containing: /// * `req` - the [PrepareReceiveRequest] containing:
/// * `payer_amount_sat` - the amount in satoshis to be paid by the payer /// * `payer_amount_sat` - the amount in satoshis to be paid by the payer
/// * `payment_method` - the supported payment methods; either an invoice, a Liquid address or a Bitcoin address
pub async fn prepare_receive_payment( pub async fn prepare_receive_payment(
&self, &self,
req: &PrepareReceivePaymentRequest, req: &PrepareReceiveRequest,
) -> Result<PrepareReceivePaymentResponse, PaymentError> { ) -> Result<PrepareReceiveResponse, PaymentError> {
self.ensure_is_started().await?; self.ensure_is_started().await?;
let reverse_pair = self
.swapper
.get_reverse_swap_pairs()?
.ok_or(PaymentError::PairsNotFound)?;
let payer_amount_sat = req.payer_amount_sat; let fees_sat;
let fees_sat = reverse_pair.fees.total(req.payer_amount_sat); match req.payment_method {
PaymentMethod::Lightning => {
let Some(amount_sat) = req.payer_amount_sat else {
return Err(PaymentError::AmountMissing { err: "`amount_sat` must be specified when `PaymentMethod::Lightning` is used.".to_string() });
};
let reverse_pair = self
.swapper
.get_reverse_swap_pairs()?
.ok_or(PaymentError::PairsNotFound)?;
ensure_sdk!(payer_amount_sat > fees_sat, PaymentError::AmountOutOfRange); fees_sat = reverse_pair.fees.total(amount_sat);
reverse_pair ensure_sdk!(amount_sat > fees_sat, PaymentError::AmountOutOfRange);
.limits
.within(payer_amount_sat)
.map_err(|_| PaymentError::AmountOutOfRange)?;
debug!("Preparing Receive Swap with: payer_amount_sat {payer_amount_sat} sat, fees_sat {fees_sat} sat"); reverse_pair
.limits
.within(amount_sat)
.map_err(|_| PaymentError::AmountOutOfRange)?;
Ok(PrepareReceivePaymentResponse { debug!(
payer_amount_sat, "Preparing Lightning Receive Swap with: amount_sat {amount_sat} sat, fees_sat {fees_sat} sat"
);
}
PaymentMethod::BitcoinAddress => {
let Some(amount_sat) = req.payer_amount_sat else {
return Err(PaymentError::AmountMissing { err: "`amount_sat` must be specified when `PaymentMethod::BitcoinAddress` is used.".to_string() });
};
let pair = self.validate_chain_pairs(Direction::Incoming, amount_sat)?;
let claim_fees_sat = pair.fees.claim_estimate();
let server_fees_sat = pair.fees.server();
fees_sat = pair.fees.boltz(amount_sat) + claim_fees_sat + server_fees_sat;
debug!(
"Preparing Chain Receive Swap with: amount_sat {amount_sat} sat, fees_sat {fees_sat} sat"
);
}
PaymentMethod::LiquidAddress => {
fees_sat = 0;
debug!(
"Preparing Liquid Receive Swap with: amount_sat {:?} sat, fees_sat {fees_sat} sat",
req.payer_amount_sat
);
}
};
Ok(PrepareReceiveResponse {
payer_amount_sat: req.payer_amount_sat,
fees_sat, fees_sat,
payment_method: req.payment_method.clone(),
}) })
} }
/// Receive a Lightning payment via a reverse submarine swap. /// Receive a Lightning payment via a reverse submarine swap, a chain swap or via direct Liquid
/// payment.
/// ///
/// # Arguments /// # Arguments
/// ///
/// * `req` - the [ReceivePaymentRequest] containing: /// * `req` - the [ReceivePaymentRequest] containing:
/// * `description` - the optional payment description /// * `description` - the optional payment description
/// * `prepare_res` - the [PrepareReceivePaymentResponse] from calling [LiquidSdk::prepare_receive_payment] /// * `prepare_response` - the [PrepareReceiveResponse] from calling [LiquidSdk::prepare_receive_payment]
/// ///
/// # Returns /// # Returns
/// ///
/// * A [ReceivePaymentResponse] containing: /// * A [ReceivePaymentResponse] containing:
/// * `invoice` - the bolt11 Lightning invoice that should be paid /// * `destination` - the final destination to be paid by the payer, either a BIP21 URI (Liquid or Bitcoin), a Liquid address or an invoice
pub async fn receive_payment( pub async fn receive_payment(
&self, &self,
req: &ReceivePaymentRequest, req: &ReceivePaymentRequest,
) -> Result<ReceivePaymentResponse, PaymentError> { ) -> Result<ReceivePaymentResponse, PaymentError> {
self.ensure_is_started().await?; self.ensure_is_started().await?;
let payer_amount_sat = req.prepare_res.payer_amount_sat; let PrepareReceiveResponse {
let fees_sat = req.prepare_res.fees_sat; payment_method,
payer_amount_sat: amount_sat,
fees_sat,
} = &req.prepare_response;
match payment_method {
PaymentMethod::Lightning => {
let Some(amount_sat) = amount_sat else {
return Err(PaymentError::AmountMissing { err: "`amount_sat` must be specified when `PaymentMethod::Lightning` is used.".to_string() });
};
self.create_receive_swap(*amount_sat, *fees_sat, req.description.clone())
.await
}
PaymentMethod::BitcoinAddress => {
let Some(amount_sat) = amount_sat else {
return Err(PaymentError::AmountMissing { err: "`amount_sat` must be specified when `PaymentMethod::BitcoinAddress` is used.".to_string() });
};
self.receive_onchain(*amount_sat, *fees_sat).await
}
PaymentMethod::LiquidAddress => {
let address = self.onchain_wallet.next_unused_address().await?.to_string();
let receive_destination = match amount_sat {
Some(amount_sat) => LiquidAddressData {
address: address.to_string(),
network: self.config.network.into(),
amount_sat: Some(*amount_sat),
asset_id: Some(AssetId::LIQUID_BTC.to_hex()),
label: None,
message: req.description.clone(),
}
.to_uri()
.map_err(|e| PaymentError::Generic {
err: format!("Could not build BIP21 URI: {e:?}"),
})?,
None => address,
};
Ok(ReceivePaymentResponse {
destination: receive_destination,
})
}
}
}
async fn create_receive_swap(
&self,
payer_amount_sat: u64,
fees_sat: u64,
description: Option<String>,
) -> Result<ReceivePaymentResponse, PaymentError> {
let reverse_pair = self let reverse_pair = self
.swapper .swapper
.get_reverse_swap_pairs()? .get_reverse_swap_pairs()?
@@ -1219,7 +1451,7 @@ impl LiquidSdk {
to: "L-BTC".to_string(), to: "L-BTC".to_string(),
preimage_hash: preimage.sha256, preimage_hash: preimage.sha256,
claim_public_key: keypair.public_key().into(), claim_public_key: keypair.public_key().into(),
description: req.description.clone(), description,
address: Some(mrh_addr_str.clone()), address: Some(mrh_addr_str.clone()),
address_signature: Some(mrh_addr_hash_sig.to_hex()), address_signature: Some(mrh_addr_hash_sig.to_hex()),
referral_id: None, referral_id: None,
@@ -1286,31 +1518,7 @@ impl LiquidSdk {
self.status_stream.track_swap_id(&swap_id)?; self.status_stream.track_swap_id(&swap_id)?;
Ok(ReceivePaymentResponse { Ok(ReceivePaymentResponse {
id: swap_id, destination: invoice.to_string(),
invoice: invoice.to_string(),
})
}
/// Prepares to receive from a Bitcoin transaction via a chain swap.
///
/// # Arguments
///
/// * `req` - the [PrepareReceiveOnchainRequest] containing:
/// * `payer_amount_sat` - the amount in satoshi that will be paid by the payer
pub async fn prepare_receive_onchain(
&self,
req: &PrepareReceiveOnchainRequest,
) -> Result<PrepareReceiveOnchainResponse, PaymentError> {
self.ensure_is_started().await?;
let payer_amount_sat = req.payer_amount_sat;
let pair = self.validate_chain_pairs(Direction::Incoming, payer_amount_sat)?;
let claim_fees_sat = pair.fees.claim_estimate();
let server_fees_sat = pair.fees.server();
Ok(PrepareReceiveOnchainResponse {
payer_amount_sat,
fees_sat: pair.fees.boltz(payer_amount_sat) + claim_fees_sat + server_fees_sat,
}) })
} }
@@ -1389,25 +1597,14 @@ impl LiquidSdk {
} }
/// Receive from a Bitcoin transaction via a chain swap. /// Receive from a Bitcoin transaction via a chain swap.
/// async fn receive_onchain(
/// # Arguments
///
/// * `req` - The [PrepareReceiveOnchainResponse] from calling [LiquidSdk::prepare_receive_onchain]
///
/// # Returns
///
/// * A [ReceiveOnchainResponse] containing:
/// * `address` - the Bitcoin address the payer should pay to
/// * `bip21` - the BIP-21 URI scheme to pay to. See <https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki>
pub async fn receive_onchain(
&self, &self,
req: &PrepareReceiveOnchainResponse, payer_amount_sat: u64,
) -> Result<ReceiveOnchainResponse, PaymentError> { fees_sat: u64,
) -> Result<ReceivePaymentResponse, PaymentError> {
self.ensure_is_started().await?; self.ensure_is_started().await?;
let swap = self let swap = self.create_chain_swap(payer_amount_sat, fees_sat).await?;
.create_chain_swap(req.payer_amount_sat, req.fees_sat)
.await?;
let create_response = swap.get_boltz_create_response()?; let create_response = swap.get_boltz_create_response()?;
let address = create_response.lockup_details.lockup_address; let address = create_response.lockup_details.lockup_address;
@@ -1416,7 +1613,7 @@ impl LiquidSdk {
"bitcoin:{address}?amount={amount}&label=Send%20to%20L-BTC%20address" "bitcoin:{address}?amount={amount}&label=Send%20to%20L-BTC%20address"
)); ));
Ok(ReceiveOnchainResponse { address, bip21 }) Ok(ReceivePaymentResponse { destination: bip21 })
} }
/// List all failed chain swaps that need to be refunded. /// List all failed chain swaps that need to be refunded.
@@ -1491,19 +1688,30 @@ impl LiquidSdk {
req: &PrepareBuyBitcoinRequest, req: &PrepareBuyBitcoinRequest,
) -> Result<PrepareBuyBitcoinResponse, PaymentError> { ) -> Result<PrepareBuyBitcoinResponse, PaymentError> {
if self.config.network != LiquidNetwork::Mainnet { if self.config.network != LiquidNetwork::Mainnet {
return Err(PaymentError::Generic { return Err(PaymentError::InvalidNetwork {
err: "Can only buy bitcoin on Mainnet".to_string(), err: "Can only buy bitcoin on Mainnet".to_string(),
}); });
} }
let res = self let res = self
.prepare_receive_onchain(&PrepareReceiveOnchainRequest { .prepare_receive_payment(&PrepareReceiveRequest {
payer_amount_sat: req.amount_sat, payment_method: PaymentMethod::BitcoinAddress,
payer_amount_sat: Some(req.amount_sat),
}) })
.await?; .await?;
let Some(amount_sat) = res.payer_amount_sat else {
return Err(PaymentError::Generic {
err: format!(
"Expected field `amount_sat` from response, got {:?}",
res.payer_amount_sat
),
});
};
Ok(PrepareBuyBitcoinResponse { Ok(PrepareBuyBitcoinResponse {
provider: req.provider, provider: req.provider,
amount_sat: res.payer_amount_sat, amount_sat,
fees_sat: res.fees_sat, fees_sat: res.fees_sat,
}) })
} }
@@ -1513,16 +1721,23 @@ impl LiquidSdk {
/// # Arguments /// # Arguments
/// ///
/// * `req` - the [BuyBitcoinRequest] containing: /// * `req` - the [BuyBitcoinRequest] containing:
/// * `prepare_res` - the [PrepareBuyBitcoinResponse] from calling [LiquidSdk::prepare_buy_bitcoin] /// * `prepare_response` - the [PrepareBuyBitcoinResponse] from calling [LiquidSdk::prepare_buy_bitcoin]
/// * `redirect_url` - the optional redirect URL the provider should redirect to after purchase /// * `redirect_url` - the optional redirect URL the provider should redirect to after purchase
pub async fn buy_bitcoin(&self, req: &BuyBitcoinRequest) -> Result<String, PaymentError> { pub async fn buy_bitcoin(&self, req: &BuyBitcoinRequest) -> Result<String, PaymentError> {
let swap = self let swap = self
.create_chain_swap(req.prepare_res.amount_sat, req.prepare_res.fees_sat) .create_chain_swap(
req.prepare_response.amount_sat,
req.prepare_response.fees_sat,
)
.await?; .await?;
Ok(self Ok(self
.buy_bitcoin_service .buy_bitcoin_service
.buy_bitcoin(req.prepare_res.provider, &swap, req.redirect_url.clone()) .buy_bitcoin(
req.prepare_response.provider,
&swap,
req.redirect_url.clone(),
)
.await?) .await?)
} }
@@ -1556,17 +1771,20 @@ impl LiquidSdk {
let is_tx_confirmed = tx.height.is_some(); let is_tx_confirmed = tx.height.is_some();
let amount_sat = tx.balance.values().sum::<i64>(); let amount_sat = tx.balance.values().sum::<i64>();
self.persister.insert_or_update_payment(PaymentTxData { self.persister.insert_or_update_payment(
tx_id: tx_id.clone(), PaymentTxData {
timestamp: tx.timestamp, tx_id: tx_id.clone(),
amount_sat: amount_sat.unsigned_abs(), timestamp: tx.timestamp,
fees_sat: tx.fee, amount_sat: amount_sat.unsigned_abs(),
payment_type: match amount_sat >= 0 { fees_sat: tx.fee,
true => PaymentType::Receive, payment_type: match amount_sat >= 0 {
false => PaymentType::Send, true => PaymentType::Receive,
false => PaymentType::Send,
},
is_confirmed: is_tx_confirmed,
}, },
is_confirmed: is_tx_confirmed, None,
})?; )?;
if let Some(swap) = pending_receive_swaps_by_claim_tx_id.get(&tx_id) { if let Some(swap) = pending_receive_swaps_by_claim_tx_id.get(&tx_id) {
if is_tx_confirmed { if is_tx_confirmed {
@@ -1722,21 +1940,32 @@ impl LiquidSdk {
Ok(LnUrlPayResult::EndpointError { data: e }) Ok(LnUrlPayResult::EndpointError { data: e })
} }
ValidatedCallbackResponse::EndpointSuccess { data: cb } => { ValidatedCallbackResponse::EndpointSuccess { data: cb } => {
let pay_req = self let prepare_response = self
.prepare_send_payment(&PrepareSendRequest { .prepare_send_payment(&PrepareSendRequest {
invoice: cb.pr.clone(), destination: cb.pr.clone(),
amount_sat: None,
}) })
.await?; .await?;
let payment = self.send_payment(&pay_req).await?.payment; let payment = self
.send_payment(&SendPaymentRequest { prepare_response })
.await?
.payment;
let maybe_sa_processed: Option<SuccessActionProcessed> = match cb.success_action { let maybe_sa_processed: Option<SuccessActionProcessed> = match cb.success_action {
Some(sa) => { Some(sa) => {
let processed_sa = match sa { let processed_sa = match sa {
// For AES, we decrypt the contents on the fly // For AES, we decrypt the contents on the fly
SuccessAction::Aes(data) => { SuccessAction::Aes(data) => {
let preimage_str = payment let Some(PaymentDetails::Lightning { preimage, .. }) =
.preimage &payment.details
else {
return Err(LnUrlPayError::Generic {
err: format!("Invalid payment type: expected type `PaymentDetails::Lightning`, got payment details {:?}.", payment.details),
});
};
let preimage_str = preimage
.clone() .clone()
.ok_or(SdkError::Generic { .ok_or(SdkError::Generic {
err: "Payment successful but no preimage found".to_string(), err: "Payment successful but no preimage found".to_string(),
@@ -1787,23 +2016,29 @@ impl LiquidSdk {
&self, &self,
req: LnUrlWithdrawRequest, req: LnUrlWithdrawRequest,
) -> Result<LnUrlWithdrawResult, LnUrlWithdrawError> { ) -> Result<LnUrlWithdrawResult, LnUrlWithdrawError> {
let prepare_res = self let prepare_response = self
.prepare_receive_payment(&{ .prepare_receive_payment(&{
PrepareReceivePaymentRequest { PrepareReceiveRequest {
payer_amount_sat: req.amount_msat / 1_000, payment_method: PaymentMethod::Lightning,
payer_amount_sat: Some(req.amount_msat / 1_000),
} }
}) })
.await?; .await?;
let receive_res = self let receive_res = self
.receive_payment(&ReceivePaymentRequest { .receive_payment(&ReceivePaymentRequest {
prepare_res, prepare_response,
description: None, description: None,
}) })
.await?; .await?;
let invoice = parse_invoice(&receive_res.invoice)?;
let res = validate_lnurl_withdraw(req.data, invoice).await?; if let Ok(invoice) = parse_invoice(&receive_res.destination) {
Ok(res) let res = validate_lnurl_withdraw(req.data, invoice).await?;
Ok(res)
} else {
Err(LnUrlWithdrawError::Generic {
err: "Received unexpected output from receive request".to_string(),
})
}
} }
/// Third and last step of LNURL-auth. The first step is [parse], which also validates the LNURL destination /// Third and last step of LNURL-auth. The first step is [parse], which also validates the LNURL destination
@@ -2288,7 +2523,7 @@ mod tests {
Some(true) // sets `update.zero_conf_rejected` Some(true) // sets `update.zero_conf_rejected`
); );
assert_eq!(persisted_swap.user_lockup_tx_id, Some(mock_tx_id.clone())); assert_eq!(persisted_swap.user_lockup_tx_id, Some(mock_tx_id.clone()));
assert_eq!(persisted_swap.accept_zero_conf, false); assert!(!persisted_swap.accept_zero_conf);
} }
// Verify that `TransactionServerMempool` correctly: // Verify that `TransactionServerMempool` correctly:

View File

@@ -86,14 +86,17 @@ impl SendSwapStateHandler {
// We insert a pseudo-lockup-tx in case LWK fails to pick up the new mempool tx for a while // We insert a pseudo-lockup-tx in case LWK fails to pick up the new mempool tx for a while
// This makes the tx known to the SDK (get_info, list_payments) instantly // This makes the tx known to the SDK (get_info, list_payments) instantly
self.persister.insert_or_update_payment(PaymentTxData { self.persister.insert_or_update_payment(
tx_id: lockup_tx_id.clone(), PaymentTxData {
timestamp: Some(utils::now()), tx_id: lockup_tx_id.clone(),
amount_sat: swap.payer_amount_sat, timestamp: Some(utils::now()),
fees_sat: lockup_tx_fees_sat, amount_sat: swap.payer_amount_sat,
payment_type: PaymentType::Send, fees_sat: lockup_tx_fees_sat,
is_confirmed: false, payment_type: PaymentType::Send,
})?; is_confirmed: false,
},
None,
)?;
self.update_swap_info(id, Pending, None, Some(&lockup_tx_id), None) self.update_swap_info(id, Pending, None, Some(&lockup_tx_id), None)
.await?; .await?;
@@ -229,7 +232,9 @@ impl SendSwapStateHandler {
let lockup_tx = self let lockup_tx = self
.onchain_wallet .onchain_wallet
.build_tx( .build_tx(
self.config.lowball_fee_rate_msat_per_vbyte(), self.config
.lowball_fee_rate_msat_per_vbyte()
.map(|v| v as f32),
&create_response.address, &create_response.address,
create_response.expected_amount, create_response.expected_amount,
) )

View File

@@ -109,7 +109,7 @@ impl Swapper for MockSwapper {
hash: generate_random_string(10), hash: generate_random_string(10),
rate: 0.0, rate: 0.0,
limits: PairLimits { limits: PairLimits {
maximal: std::u64::MAX, maximal: u64::MAX,
minimal: 0, minimal: 0,
maximal_zero_conf: 100_000, maximal_zero_conf: 100_000,
}, },
@@ -154,7 +154,7 @@ impl Swapper for MockSwapper {
hash: generate_random_string(10), hash: generate_random_string(10),
rate: 0.0, rate: 0.0,
limits: PairLimits { limits: PairLimits {
maximal: std::u64::MAX, maximal: u64::MAX,
minimal: 0, minimal: 0,
maximal_zero_conf: 100_000, maximal_zero_conf: 100_000,
}, },
@@ -260,7 +260,7 @@ impl Swapper for MockSwapper {
hash: "".to_string(), hash: "".to_string(),
rate: 0.0, rate: 0.0,
limits: ReverseLimits { limits: ReverseLimits {
maximal: std::u64::MAX, maximal: u64::MAX,
minimal: 0, minimal: 0,
}, },
fees: ReverseFees { fees: ReverseFees {

View File

@@ -1,4 +1,5 @@
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use crate::error::{PaymentError, SdkResult}; use crate::error::{PaymentError, SdkResult};
@@ -6,12 +7,14 @@ use crate::prelude::{
Config, LiquidNetwork, SendSwap, LOWBALL_FEE_RATE_SAT_PER_VBYTE, Config, LiquidNetwork, SendSwap, LOWBALL_FEE_RATE_SAT_PER_VBYTE,
STANDARD_FEE_RATE_SAT_PER_VBYTE, STANDARD_FEE_RATE_SAT_PER_VBYTE,
}; };
use crate::wallet::OnchainWallet;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use boltz_client::boltz::{ use boltz_client::boltz::{
BoltzApiClientV2, Cooperative, BOLTZ_MAINNET_URL_V2, BOLTZ_TESTNET_URL_V2, BoltzApiClientV2, Cooperative, BOLTZ_MAINNET_URL_V2, BOLTZ_TESTNET_URL_V2,
}; };
use boltz_client::network::electrum::ElectrumConfig; use boltz_client::network::electrum::ElectrumConfig;
use boltz_client::Amount; use boltz_client::Amount;
use log::debug;
use lwk_wollet::elements::encode::deserialize; use lwk_wollet::elements::encode::deserialize;
use lwk_wollet::elements::hex::FromHex; use lwk_wollet::elements::hex::FromHex;
use lwk_wollet::elements::{ use lwk_wollet::elements::{
@@ -59,6 +62,30 @@ pub(crate) fn deserialize_tx_hex(tx_hex: &str) -> Result<Transaction> {
)?)?) )?)?)
} }
pub(crate) async fn derive_fee_rate_sats_per_kvb(
wallet: Arc<dyn OnchainWallet>,
amount_sat: u64,
recipient_address: &str,
absolute_fees_sat: u64,
) -> Result<f32> {
let standard_fees_sat = wallet
.build_tx(None, recipient_address, amount_sat)
.await?
.all_fees()
.values()
.sum::<u64>() as f64;
// Multiply sats/vb value by 1000 i.e. 1.0 sat/byte = 1000.0 sat/kvb
// We calculate using f64 and convert to f32 in the last step, so we keep the maximum precision possible
let result_sat_per_vb =
STANDARD_FEE_RATE_SAT_PER_VBYTE * absolute_fees_sat as f64 / standard_fees_sat;
let result_sat_per_kvb = result_sat_per_vb * 1000.0;
let result_sat_per_kvb_f32 = result_sat_per_kvb as f32;
debug!("derive_fee_rate_sats_per_kvb: result_sat_per_kvb_f32 {} from inputs: absolute_fees_sat {}, result_sat_per_kvb: {}",
result_sat_per_kvb_f32, absolute_fees_sat, result_sat_per_kvb);
Ok(result_sat_per_kvb_f32)
}
pub(crate) fn estimate_refund_fees( pub(crate) fn estimate_refund_fees(
swap: &SendSwap, swap: &SendSwap,
config: &Config, config: &Config,
@@ -110,5 +137,5 @@ pub(crate) fn estimate_refund_fees(
}; };
let dummy_tx = swap_tx.sign_refund(&swap.get_refund_keypair()?, dummy_fees, cooperative)?; let dummy_tx = swap_tx.sign_refund(&swap.get_refund_keypair()?, dummy_fees, cooperative)?;
Ok((dummy_tx.vsize() as f32 * fee_rate).ceil() as u64) Ok((dummy_tx.vsize() as f64 * fee_rate).ceil() as u64)
} }

View File

@@ -28,7 +28,7 @@ pub trait OnchainWallet: Send + Sync {
/// Build a transaction to send funds to a recipient /// Build a transaction to send funds to a recipient
async fn build_tx( async fn build_tx(
&self, &self,
fee_rate: Option<f32>, fee_rate_sats_per_kvb: Option<f32>,
recipient_address: &str, recipient_address: &str,
amount_sat: u64, amount_sat: u64,
) -> Result<Transaction, PaymentError>; ) -> Result<Transaction, PaymentError>;
@@ -103,7 +103,7 @@ impl OnchainWallet for LiquidOnchainWallet {
/// Build a transaction to send funds to a recipient /// Build a transaction to send funds to a recipient
async fn build_tx( async fn build_tx(
&self, &self,
fee_rate: Option<f32>, fee_rate_sats_per_kvb: Option<f32>,
recipient_address: &str, recipient_address: &str,
amount_sat: u64, amount_sat: u64,
) -> Result<Transaction, PaymentError> { ) -> Result<Transaction, PaymentError> {
@@ -119,7 +119,7 @@ impl OnchainWallet for LiquidOnchainWallet {
})?, })?,
amount_sat, amount_sat,
)? )?
.fee_rate(fee_rate) .fee_rate(fee_rate_sats_per_kvb)
.finish(&lwk_wollet)?; .finish(&lwk_wollet)?;
let signer = AnySigner::Software(self.lwk_signer.clone()); let signer = AnySigner::Software(self.lwk_signer.clone());
signer.sign(&mut pset)?; signer.sign(&mut pset)?;

View File

@@ -67,16 +67,12 @@ abstract class BindingLiquidSdk implements RustOpaqueInterface {
Future<PreparePayOnchainResponse> preparePayOnchain({required PreparePayOnchainRequest req}); Future<PreparePayOnchainResponse> preparePayOnchain({required PreparePayOnchainRequest req});
Future<PrepareReceiveOnchainResponse> prepareReceiveOnchain({required PrepareReceiveOnchainRequest req}); Future<PrepareReceiveResponse> prepareReceivePayment({required PrepareReceiveRequest req});
Future<PrepareReceivePaymentResponse> prepareReceivePayment({required PrepareReceivePaymentRequest req});
Future<PrepareRefundResponse> prepareRefund({required PrepareRefundRequest req}); Future<PrepareRefundResponse> prepareRefund({required PrepareRefundRequest req});
Future<PrepareSendResponse> prepareSendPayment({required PrepareSendRequest req}); Future<PrepareSendResponse> prepareSendPayment({required PrepareSendRequest req});
Future<ReceiveOnchainResponse> receiveOnchain({required PrepareReceiveOnchainResponse req});
Future<ReceivePaymentResponse> receivePayment({required ReceivePaymentRequest req}); Future<ReceivePaymentResponse> receivePayment({required ReceivePaymentRequest req});
Future<RecommendedFees> recommendedFees(); Future<RecommendedFees> recommendedFees();
@@ -87,7 +83,7 @@ abstract class BindingLiquidSdk implements RustOpaqueInterface {
void restore({required RestoreRequest req}); void restore({required RestoreRequest req});
Future<SendPaymentResponse> sendPayment({required PrepareSendResponse req}); Future<SendPaymentResponse> sendPayment({required SendPaymentRequest req});
Future<void> sync(); Future<void> sync();
} }

View File

@@ -16,6 +16,12 @@ sealed class PaymentError with _$PaymentError implements FrbException {
const factory PaymentError.alreadyPaid() = PaymentError_AlreadyPaid; const factory PaymentError.alreadyPaid() = PaymentError_AlreadyPaid;
const factory PaymentError.paymentInProgress() = PaymentError_PaymentInProgress; const factory PaymentError.paymentInProgress() = PaymentError_PaymentInProgress;
const factory PaymentError.amountOutOfRange() = PaymentError_AmountOutOfRange; const factory PaymentError.amountOutOfRange() = PaymentError_AmountOutOfRange;
const factory PaymentError.amountMissing({
required String err,
}) = PaymentError_AmountMissing;
const factory PaymentError.invalidNetwork({
required String err,
}) = PaymentError_InvalidNetwork;
const factory PaymentError.generic({ const factory PaymentError.generic({
required String err, required String err,
}) = PaymentError_Generic; }) = PaymentError_Generic;

View File

@@ -212,6 +212,164 @@ abstract class PaymentError_AmountOutOfRange extends PaymentError {
const PaymentError_AmountOutOfRange._() : super._(); const PaymentError_AmountOutOfRange._() : super._();
} }
/// @nodoc
abstract class _$$PaymentError_AmountMissingImplCopyWith<$Res> {
factory _$$PaymentError_AmountMissingImplCopyWith(
_$PaymentError_AmountMissingImpl value, $Res Function(_$PaymentError_AmountMissingImpl) then) =
__$$PaymentError_AmountMissingImplCopyWithImpl<$Res>;
@useResult
$Res call({String err});
}
/// @nodoc
class __$$PaymentError_AmountMissingImplCopyWithImpl<$Res>
extends _$PaymentErrorCopyWithImpl<$Res, _$PaymentError_AmountMissingImpl>
implements _$$PaymentError_AmountMissingImplCopyWith<$Res> {
__$$PaymentError_AmountMissingImplCopyWithImpl(
_$PaymentError_AmountMissingImpl _value, $Res Function(_$PaymentError_AmountMissingImpl) _then)
: super(_value, _then);
/// Create a copy of PaymentError
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? err = null,
}) {
return _then(_$PaymentError_AmountMissingImpl(
err: null == err
? _value.err
: err // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class _$PaymentError_AmountMissingImpl extends PaymentError_AmountMissing {
const _$PaymentError_AmountMissingImpl({required this.err}) : super._();
@override
final String err;
@override
String toString() {
return 'PaymentError.amountMissing(err: $err)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$PaymentError_AmountMissingImpl &&
(identical(other.err, err) || other.err == err));
}
@override
int get hashCode => Object.hash(runtimeType, err);
/// Create a copy of PaymentError
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$PaymentError_AmountMissingImplCopyWith<_$PaymentError_AmountMissingImpl> get copyWith =>
__$$PaymentError_AmountMissingImplCopyWithImpl<_$PaymentError_AmountMissingImpl>(this, _$identity);
}
abstract class PaymentError_AmountMissing extends PaymentError {
const factory PaymentError_AmountMissing({required final String err}) = _$PaymentError_AmountMissingImpl;
const PaymentError_AmountMissing._() : super._();
String get err;
/// Create a copy of PaymentError
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$PaymentError_AmountMissingImplCopyWith<_$PaymentError_AmountMissingImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$PaymentError_InvalidNetworkImplCopyWith<$Res> {
factory _$$PaymentError_InvalidNetworkImplCopyWith(
_$PaymentError_InvalidNetworkImpl value, $Res Function(_$PaymentError_InvalidNetworkImpl) then) =
__$$PaymentError_InvalidNetworkImplCopyWithImpl<$Res>;
@useResult
$Res call({String err});
}
/// @nodoc
class __$$PaymentError_InvalidNetworkImplCopyWithImpl<$Res>
extends _$PaymentErrorCopyWithImpl<$Res, _$PaymentError_InvalidNetworkImpl>
implements _$$PaymentError_InvalidNetworkImplCopyWith<$Res> {
__$$PaymentError_InvalidNetworkImplCopyWithImpl(
_$PaymentError_InvalidNetworkImpl _value, $Res Function(_$PaymentError_InvalidNetworkImpl) _then)
: super(_value, _then);
/// Create a copy of PaymentError
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? err = null,
}) {
return _then(_$PaymentError_InvalidNetworkImpl(
err: null == err
? _value.err
: err // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class _$PaymentError_InvalidNetworkImpl extends PaymentError_InvalidNetwork {
const _$PaymentError_InvalidNetworkImpl({required this.err}) : super._();
@override
final String err;
@override
String toString() {
return 'PaymentError.invalidNetwork(err: $err)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$PaymentError_InvalidNetworkImpl &&
(identical(other.err, err) || other.err == err));
}
@override
int get hashCode => Object.hash(runtimeType, err);
/// Create a copy of PaymentError
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$PaymentError_InvalidNetworkImplCopyWith<_$PaymentError_InvalidNetworkImpl> get copyWith =>
__$$PaymentError_InvalidNetworkImplCopyWithImpl<_$PaymentError_InvalidNetworkImpl>(this, _$identity);
}
abstract class PaymentError_InvalidNetwork extends PaymentError {
const factory PaymentError_InvalidNetwork({required final String err}) = _$PaymentError_InvalidNetworkImpl;
const PaymentError_InvalidNetwork._() : super._();
String get err;
/// Create a copy of PaymentError
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$PaymentError_InvalidNetworkImplCopyWith<_$PaymentError_InvalidNetworkImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc /// @nodoc
abstract class _$$PaymentError_GenericImplCopyWith<$Res> { abstract class _$$PaymentError_GenericImplCopyWith<$Res> {
factory _$$PaymentError_GenericImplCopyWith( factory _$$PaymentError_GenericImplCopyWith(

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -38,7 +38,7 @@ enum BuyBitcoinProvider {
/// An argument when calling [crate::sdk::LiquidSdk::buy_bitcoin]. /// An argument when calling [crate::sdk::LiquidSdk::buy_bitcoin].
class BuyBitcoinRequest { class BuyBitcoinRequest {
final PrepareBuyBitcoinResponse prepareRes; final PrepareBuyBitcoinResponse prepareResponse;
/// The optional URL to redirect to after completing the buy. /// The optional URL to redirect to after completing the buy.
/// ///
@@ -46,19 +46,19 @@ class BuyBitcoinRequest {
final String? redirectUrl; final String? redirectUrl;
const BuyBitcoinRequest({ const BuyBitcoinRequest({
required this.prepareRes, required this.prepareResponse,
this.redirectUrl, this.redirectUrl,
}); });
@override @override
int get hashCode => prepareRes.hashCode ^ redirectUrl.hashCode; int get hashCode => prepareResponse.hashCode ^ redirectUrl.hashCode;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is BuyBitcoinRequest && other is BuyBitcoinRequest &&
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&
prepareRes == other.prepareRes && prepareResponse == other.prepareResponse &&
redirectUrl == other.redirectUrl; redirectUrl == other.redirectUrl;
} }
@@ -359,15 +359,15 @@ class OnchainPaymentLimitsResponse {
/// An argument when calling [crate::sdk::LiquidSdk::pay_onchain]. /// An argument when calling [crate::sdk::LiquidSdk::pay_onchain].
class PayOnchainRequest { class PayOnchainRequest {
final String address; final String address;
final PreparePayOnchainResponse prepareRes; final PreparePayOnchainResponse prepareResponse;
const PayOnchainRequest({ const PayOnchainRequest({
required this.address, required this.address,
required this.prepareRes, required this.prepareResponse,
}); });
@override @override
int get hashCode => address.hashCode ^ prepareRes.hashCode; int get hashCode => address.hashCode ^ prepareResponse.hashCode;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
@@ -375,18 +375,18 @@ class PayOnchainRequest {
other is PayOnchainRequest && other is PayOnchainRequest &&
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&
address == other.address && address == other.address &&
prepareRes == other.prepareRes; prepareResponse == other.prepareResponse;
} }
/// Represents an SDK payment. /// Represents an SDK payment.
/// ///
/// By default, this is an onchain tx. It may represent a swap, if swap metadata is available. /// By default, this is an onchain tx. It may represent a swap, if swap metadata is available.
class Payment { class Payment {
/// The destination associated with the payment, if it was created via our SDK.
/// Can be either a Liquid/Bitcoin address, a Liquid BIP21 URI or an invoice
final String? destination;
final String? txId; final String? txId;
/// The swap ID, if any swap is associated with this payment
final String? swapId;
/// Composite timestamp that can be used for sorting or displaying the payment. /// Composite timestamp that can be used for sorting or displaying the payment.
/// ///
/// If this payment has an associated swap, it is the swap creation time. Otherwise, the point /// If this payment has an associated swap, it is the swap creation time. Otherwise, the point
@@ -414,23 +414,6 @@ class Payment {
/// - for Receive payments, this is zero /// - for Receive payments, this is zero
final BigInt feesSat; final BigInt feesSat;
/// In case of a Send swap, this is the preimage of the paid invoice (proof of payment).
final String? preimage;
/// Represents the 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
final String? bolt11;
/// Represents the invoice description
final String description;
/// For a Send swap which was refunded, this is the refund tx id
final String? refundTxId;
/// For a Send swap which was refunded, this is the refund amount
final BigInt? refundTxAmountSat;
/// If it is a `Send` or `Receive` payment /// If it is a `Send` or `Receive` payment
final PaymentType paymentType; final PaymentType paymentType;
@@ -441,53 +424,103 @@ class Payment {
/// If the tx has an associated swap, this is determined by the swap status (pending or complete). /// If the tx has an associated swap, this is determined by the swap status (pending or complete).
final PaymentState status; final PaymentState status;
/// The details of a payment, depending on its [destination](Payment::destination) and
/// [type](Payment::payment_type)
final PaymentDetails? details;
const Payment({ const Payment({
this.destination,
this.txId, this.txId,
this.swapId,
required this.timestamp, required this.timestamp,
required this.amountSat, required this.amountSat,
required this.feesSat, required this.feesSat,
this.preimage,
this.bolt11,
required this.description,
this.refundTxId,
this.refundTxAmountSat,
required this.paymentType, required this.paymentType,
required this.status, required this.status,
this.details,
}); });
@override @override
int get hashCode => int get hashCode =>
destination.hashCode ^
txId.hashCode ^ txId.hashCode ^
swapId.hashCode ^
timestamp.hashCode ^ timestamp.hashCode ^
amountSat.hashCode ^ amountSat.hashCode ^
feesSat.hashCode ^ feesSat.hashCode ^
preimage.hashCode ^
bolt11.hashCode ^
description.hashCode ^
refundTxId.hashCode ^
refundTxAmountSat.hashCode ^
paymentType.hashCode ^ paymentType.hashCode ^
status.hashCode; status.hashCode ^
details.hashCode;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is Payment && other is Payment &&
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&
destination == other.destination &&
txId == other.txId && txId == other.txId &&
swapId == other.swapId &&
timestamp == other.timestamp && timestamp == other.timestamp &&
amountSat == other.amountSat && amountSat == other.amountSat &&
feesSat == other.feesSat && feesSat == other.feesSat &&
preimage == other.preimage &&
bolt11 == other.bolt11 &&
description == other.description &&
refundTxId == other.refundTxId &&
refundTxAmountSat == other.refundTxAmountSat &&
paymentType == other.paymentType && paymentType == other.paymentType &&
status == other.status; status == other.status &&
details == other.details;
}
@freezed
sealed class PaymentDetails with _$PaymentDetails {
const PaymentDetails._();
/// Swapping to or from Lightning
const factory PaymentDetails.lightning({
required String swapId,
/// Represents the invoice description
required String description,
/// 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
/// 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,
/// For a Send swap which was refunded, this is the refund tx id
String? refundTxId,
/// For a Send swap which was refunded, this is the refund amount
BigInt? refundTxAmountSat,
}) = PaymentDetails_Lightning;
/// Direct onchain payment to a Liquid address
const factory PaymentDetails.liquid({
/// Represents either a Liquid BIP21 URI or pure address
required String destination,
/// Represents the BIP21 `message` field
required String description,
}) = PaymentDetails_Liquid;
/// Swapping to or from the Bitcoin chain
const factory PaymentDetails.bitcoin({
required String swapId,
/// Represents the invoice description
required String description,
/// For a Send swap which was refunded, this is the refund tx id
String? refundTxId,
/// For a Send swap which was refunded, this is the refund amount
BigInt? refundTxAmountSat,
}) = PaymentDetails_Bitcoin;
}
/// The send/receive methods supported by the SDK
enum PaymentMethod {
lightning,
bitcoinAddress,
liquidAddress,
;
} }
/// The payment state of an individual payment. /// The payment state of an individual payment.
@@ -658,84 +691,49 @@ class PreparePayOnchainResponse {
totalFeesSat == other.totalFeesSat; totalFeesSat == other.totalFeesSat;
} }
/// An argument when calling [crate::sdk::LiquidSdk::prepare_receive_onchain]. /// An argument when calling [crate::sdk::LiquidSdk::prepare_receive_payment].
class PrepareReceiveOnchainRequest { class PrepareReceiveRequest {
final BigInt payerAmountSat; final BigInt? payerAmountSat;
final PaymentMethod paymentMethod;
const PrepareReceiveOnchainRequest({ const PrepareReceiveRequest({
required this.payerAmountSat, this.payerAmountSat,
required this.paymentMethod,
}); });
@override @override
int get hashCode => payerAmountSat.hashCode; int get hashCode => payerAmountSat.hashCode ^ paymentMethod.hashCode;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is PrepareReceiveOnchainRequest && other is PrepareReceiveRequest &&
runtimeType == other.runtimeType &&
payerAmountSat == other.payerAmountSat;
}
/// Returned when calling [crate::sdk::LiquidSdk::prepare_receive_onchain].
class PrepareReceiveOnchainResponse {
final BigInt payerAmountSat;
final BigInt feesSat;
const PrepareReceiveOnchainResponse({
required this.payerAmountSat,
required this.feesSat,
});
@override
int get hashCode => payerAmountSat.hashCode ^ feesSat.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PrepareReceiveOnchainResponse &&
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&
payerAmountSat == other.payerAmountSat && payerAmountSat == other.payerAmountSat &&
feesSat == other.feesSat; paymentMethod == other.paymentMethod;
}
/// An argument when calling [crate::sdk::LiquidSdk::prepare_receive_payment].
class PrepareReceivePaymentRequest {
final BigInt payerAmountSat;
const PrepareReceivePaymentRequest({
required this.payerAmountSat,
});
@override
int get hashCode => payerAmountSat.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PrepareReceivePaymentRequest &&
runtimeType == other.runtimeType &&
payerAmountSat == other.payerAmountSat;
} }
/// Returned when calling [crate::sdk::LiquidSdk::prepare_receive_payment]. /// Returned when calling [crate::sdk::LiquidSdk::prepare_receive_payment].
class PrepareReceivePaymentResponse { class PrepareReceiveResponse {
final BigInt payerAmountSat; final PaymentMethod paymentMethod;
final BigInt? payerAmountSat;
final BigInt feesSat; final BigInt feesSat;
const PrepareReceivePaymentResponse({ const PrepareReceiveResponse({
required this.payerAmountSat, required this.paymentMethod,
this.payerAmountSat,
required this.feesSat, required this.feesSat,
}); });
@override @override
int get hashCode => payerAmountSat.hashCode ^ feesSat.hashCode; int get hashCode => paymentMethod.hashCode ^ payerAmountSat.hashCode ^ feesSat.hashCode;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is PrepareReceivePaymentResponse && other is PrepareReceiveResponse &&
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&
paymentMethod == other.paymentMethod &&
payerAmountSat == other.payerAmountSat && payerAmountSat == other.payerAmountSat &&
feesSat == other.feesSat; feesSat == other.feesSat;
} }
@@ -797,77 +795,65 @@ class PrepareRefundResponse {
/// An argument when calling [crate::sdk::LiquidSdk::prepare_send_payment]. /// An argument when calling [crate::sdk::LiquidSdk::prepare_send_payment].
class PrepareSendRequest { class PrepareSendRequest {
final String invoice; /// The destination we intend to pay to.
/// Supports BIP21 URIs, BOLT11 invoices and Liquid addresses
final String destination;
/// Should only be set when paying directly onchain or to a BIP21 URI
/// where no amount is specified
final BigInt? amountSat;
const PrepareSendRequest({ const PrepareSendRequest({
required this.invoice, required this.destination,
this.amountSat,
}); });
@override @override
int get hashCode => invoice.hashCode; int get hashCode => destination.hashCode ^ amountSat.hashCode;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is PrepareSendRequest && runtimeType == other.runtimeType && invoice == other.invoice; other is PrepareSendRequest &&
runtimeType == other.runtimeType &&
destination == other.destination &&
amountSat == other.amountSat;
} }
/// Returned when calling [crate::sdk::LiquidSdk::prepare_send_payment]. /// Returned when calling [crate::sdk::LiquidSdk::prepare_send_payment].
class PrepareSendResponse { class PrepareSendResponse {
final String invoice; final SendDestination destination;
final BigInt feesSat; final BigInt feesSat;
const PrepareSendResponse({ const PrepareSendResponse({
required this.invoice, required this.destination,
required this.feesSat, required this.feesSat,
}); });
@override @override
int get hashCode => invoice.hashCode ^ feesSat.hashCode; int get hashCode => destination.hashCode ^ feesSat.hashCode;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is PrepareSendResponse && other is PrepareSendResponse &&
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&
invoice == other.invoice && destination == other.destination &&
feesSat == other.feesSat; feesSat == other.feesSat;
} }
/// Returned when calling [crate::sdk::LiquidSdk::receive_onchain].
class ReceiveOnchainResponse {
final String address;
final String bip21;
const ReceiveOnchainResponse({
required this.address,
required this.bip21,
});
@override
int get hashCode => address.hashCode ^ bip21.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ReceiveOnchainResponse &&
runtimeType == other.runtimeType &&
address == other.address &&
bip21 == other.bip21;
}
/// An argument when calling [crate::sdk::LiquidSdk::receive_payment]. /// An argument when calling [crate::sdk::LiquidSdk::receive_payment].
class ReceivePaymentRequest { class ReceivePaymentRequest {
final String? description; final String? description;
final PrepareReceivePaymentResponse prepareRes; final PrepareReceiveResponse prepareResponse;
const ReceivePaymentRequest({ const ReceivePaymentRequest({
this.description, this.description,
required this.prepareRes, required this.prepareResponse,
}); });
@override @override
int get hashCode => description.hashCode ^ prepareRes.hashCode; int get hashCode => description.hashCode ^ prepareResponse.hashCode;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
@@ -875,29 +861,26 @@ class ReceivePaymentRequest {
other is ReceivePaymentRequest && other is ReceivePaymentRequest &&
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&
description == other.description && description == other.description &&
prepareRes == other.prepareRes; prepareResponse == other.prepareResponse;
} }
/// Returned when calling [crate::sdk::LiquidSdk::receive_payment]. /// Returned when calling [crate::sdk::LiquidSdk::receive_payment].
class ReceivePaymentResponse { class ReceivePaymentResponse {
final String id; /// Either a BIP21 URI (Liquid or Bitcoin), a Liquid address
final String invoice; /// or an invoice, depending on the [PrepareReceivePaymentResponse] parameters
final String destination;
const ReceivePaymentResponse({ const ReceivePaymentResponse({
required this.id, required this.destination,
required this.invoice,
}); });
@override @override
int get hashCode => id.hashCode ^ invoice.hashCode; int get hashCode => destination.hashCode;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is ReceivePaymentResponse && other is ReceivePaymentResponse && runtimeType == other.runtimeType && destination == other.destination;
runtimeType == other.runtimeType &&
id == other.id &&
invoice == other.invoice;
} }
/// Returned when calling [crate::sdk::LiquidSdk::recommended_fees]. /// Returned when calling [crate::sdk::LiquidSdk::recommended_fees].
@@ -1050,6 +1033,37 @@ sealed class SdkEvent with _$SdkEvent {
const factory SdkEvent.synced() = SdkEvent_Synced; const factory SdkEvent.synced() = SdkEvent_Synced;
} }
@freezed
sealed class SendDestination with _$SendDestination {
const SendDestination._();
const factory SendDestination.liquidAddress({
required LiquidAddressData addressData,
}) = SendDestination_LiquidAddress;
const factory SendDestination.bolt11({
required LNInvoice invoice,
}) = SendDestination_Bolt11;
}
/// An argument when calling [crate::sdk::LiquidSdk::send_payment].
class SendPaymentRequest {
final PrepareSendResponse prepareResponse;
const SendPaymentRequest({
required this.prepareResponse,
});
@override
int get hashCode => prepareResponse.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is SendPaymentRequest &&
runtimeType == other.runtimeType &&
prepareResponse == other.prepareResponse;
}
/// Returned when calling [crate::sdk::LiquidSdk::send_payment]. /// Returned when calling [crate::sdk::LiquidSdk::send_payment].
class SendPaymentResponse { class SendPaymentResponse {
final Payment payment; final Payment payment;

View File

@@ -283,6 +283,449 @@ abstract class LnUrlPayResult_PayError extends LnUrlPayResult {
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
/// @nodoc
mixin _$PaymentDetails {
/// Represents the invoice description
String get description => throw _privateConstructorUsedError;
/// Create a copy of PaymentDetails
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$PaymentDetailsCopyWith<PaymentDetails> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $PaymentDetailsCopyWith<$Res> {
factory $PaymentDetailsCopyWith(PaymentDetails value, $Res Function(PaymentDetails) then) =
_$PaymentDetailsCopyWithImpl<$Res, PaymentDetails>;
@useResult
$Res call({String description});
}
/// @nodoc
class _$PaymentDetailsCopyWithImpl<$Res, $Val extends PaymentDetails>
implements $PaymentDetailsCopyWith<$Res> {
_$PaymentDetailsCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of PaymentDetails
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? description = null,
}) {
return _then(_value.copyWith(
description: null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$PaymentDetails_LightningImplCopyWith<$Res> implements $PaymentDetailsCopyWith<$Res> {
factory _$$PaymentDetails_LightningImplCopyWith(
_$PaymentDetails_LightningImpl value, $Res Function(_$PaymentDetails_LightningImpl) then) =
__$$PaymentDetails_LightningImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{String swapId,
String description,
String? preimage,
String? bolt11,
String? refundTxId,
BigInt? refundTxAmountSat});
}
/// @nodoc
class __$$PaymentDetails_LightningImplCopyWithImpl<$Res>
extends _$PaymentDetailsCopyWithImpl<$Res, _$PaymentDetails_LightningImpl>
implements _$$PaymentDetails_LightningImplCopyWith<$Res> {
__$$PaymentDetails_LightningImplCopyWithImpl(
_$PaymentDetails_LightningImpl _value, $Res Function(_$PaymentDetails_LightningImpl) _then)
: super(_value, _then);
/// Create a copy of PaymentDetails
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? swapId = null,
Object? description = null,
Object? preimage = freezed,
Object? bolt11 = freezed,
Object? refundTxId = freezed,
Object? refundTxAmountSat = freezed,
}) {
return _then(_$PaymentDetails_LightningImpl(
swapId: null == swapId
? _value.swapId
: swapId // ignore: cast_nullable_to_non_nullable
as String,
description: null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String,
preimage: freezed == preimage
? _value.preimage
: preimage // ignore: cast_nullable_to_non_nullable
as String?,
bolt11: freezed == bolt11
? _value.bolt11
: bolt11 // ignore: cast_nullable_to_non_nullable
as String?,
refundTxId: freezed == refundTxId
? _value.refundTxId
: refundTxId // ignore: cast_nullable_to_non_nullable
as String?,
refundTxAmountSat: freezed == refundTxAmountSat
? _value.refundTxAmountSat
: refundTxAmountSat // ignore: cast_nullable_to_non_nullable
as BigInt?,
));
}
}
/// @nodoc
class _$PaymentDetails_LightningImpl extends PaymentDetails_Lightning {
const _$PaymentDetails_LightningImpl(
{required this.swapId,
required this.description,
this.preimage,
this.bolt11,
this.refundTxId,
this.refundTxAmountSat})
: super._();
@override
final String swapId;
/// Represents the invoice description
@override
final String description;
/// In case of a Send swap, this is the preimage of the paid invoice (proof of payment).
@override
final String? preimage;
/// Represents the 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;
/// For a Send swap which was refunded, this is the refund tx id
@override
final String? refundTxId;
/// For a Send swap which was refunded, this is the refund amount
@override
final BigInt? refundTxAmountSat;
@override
String toString() {
return 'PaymentDetails.lightning(swapId: $swapId, description: $description, preimage: $preimage, bolt11: $bolt11, refundTxId: $refundTxId, refundTxAmountSat: $refundTxAmountSat)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$PaymentDetails_LightningImpl &&
(identical(other.swapId, swapId) || other.swapId == swapId) &&
(identical(other.description, description) || other.description == description) &&
(identical(other.preimage, preimage) || other.preimage == preimage) &&
(identical(other.bolt11, bolt11) || other.bolt11 == bolt11) &&
(identical(other.refundTxId, refundTxId) || other.refundTxId == refundTxId) &&
(identical(other.refundTxAmountSat, refundTxAmountSat) ||
other.refundTxAmountSat == refundTxAmountSat));
}
@override
int get hashCode =>
Object.hash(runtimeType, swapId, description, preimage, bolt11, refundTxId, refundTxAmountSat);
/// Create a copy of PaymentDetails
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$PaymentDetails_LightningImplCopyWith<_$PaymentDetails_LightningImpl> get copyWith =>
__$$PaymentDetails_LightningImplCopyWithImpl<_$PaymentDetails_LightningImpl>(this, _$identity);
}
abstract class PaymentDetails_Lightning extends PaymentDetails {
const factory PaymentDetails_Lightning(
{required final String swapId,
required final String description,
final String? preimage,
final String? bolt11,
final String? refundTxId,
final BigInt? refundTxAmountSat}) = _$PaymentDetails_LightningImpl;
const PaymentDetails_Lightning._() : super._();
String get swapId;
/// Represents the invoice description
@override
String get description;
/// 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
/// 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;
/// For a Send swap which was refunded, this is the refund tx id
String? get refundTxId;
/// For a Send swap which was refunded, this is the refund amount
BigInt? get refundTxAmountSat;
/// Create a copy of PaymentDetails
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$PaymentDetails_LightningImplCopyWith<_$PaymentDetails_LightningImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$PaymentDetails_LiquidImplCopyWith<$Res> implements $PaymentDetailsCopyWith<$Res> {
factory _$$PaymentDetails_LiquidImplCopyWith(
_$PaymentDetails_LiquidImpl value, $Res Function(_$PaymentDetails_LiquidImpl) then) =
__$$PaymentDetails_LiquidImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String destination, String description});
}
/// @nodoc
class __$$PaymentDetails_LiquidImplCopyWithImpl<$Res>
extends _$PaymentDetailsCopyWithImpl<$Res, _$PaymentDetails_LiquidImpl>
implements _$$PaymentDetails_LiquidImplCopyWith<$Res> {
__$$PaymentDetails_LiquidImplCopyWithImpl(
_$PaymentDetails_LiquidImpl _value, $Res Function(_$PaymentDetails_LiquidImpl) _then)
: super(_value, _then);
/// Create a copy of PaymentDetails
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? destination = null,
Object? description = null,
}) {
return _then(_$PaymentDetails_LiquidImpl(
destination: null == destination
? _value.destination
: destination // ignore: cast_nullable_to_non_nullable
as String,
description: null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class _$PaymentDetails_LiquidImpl extends PaymentDetails_Liquid {
const _$PaymentDetails_LiquidImpl({required this.destination, required this.description}) : super._();
/// Represents either a Liquid BIP21 URI or pure address
@override
final String destination;
/// Represents the BIP21 `message` field
@override
final String description;
@override
String toString() {
return 'PaymentDetails.liquid(destination: $destination, description: $description)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$PaymentDetails_LiquidImpl &&
(identical(other.destination, destination) || other.destination == destination) &&
(identical(other.description, description) || other.description == description));
}
@override
int get hashCode => Object.hash(runtimeType, destination, description);
/// Create a copy of PaymentDetails
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$PaymentDetails_LiquidImplCopyWith<_$PaymentDetails_LiquidImpl> get copyWith =>
__$$PaymentDetails_LiquidImplCopyWithImpl<_$PaymentDetails_LiquidImpl>(this, _$identity);
}
abstract class PaymentDetails_Liquid extends PaymentDetails {
const factory PaymentDetails_Liquid(
{required final String destination, required final String description}) = _$PaymentDetails_LiquidImpl;
const PaymentDetails_Liquid._() : super._();
/// Represents either a Liquid BIP21 URI or pure address
String get destination;
/// Represents the BIP21 `message` field
@override
String get description;
/// Create a copy of PaymentDetails
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$PaymentDetails_LiquidImplCopyWith<_$PaymentDetails_LiquidImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$PaymentDetails_BitcoinImplCopyWith<$Res> implements $PaymentDetailsCopyWith<$Res> {
factory _$$PaymentDetails_BitcoinImplCopyWith(
_$PaymentDetails_BitcoinImpl value, $Res Function(_$PaymentDetails_BitcoinImpl) then) =
__$$PaymentDetails_BitcoinImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String swapId, String description, String? refundTxId, BigInt? refundTxAmountSat});
}
/// @nodoc
class __$$PaymentDetails_BitcoinImplCopyWithImpl<$Res>
extends _$PaymentDetailsCopyWithImpl<$Res, _$PaymentDetails_BitcoinImpl>
implements _$$PaymentDetails_BitcoinImplCopyWith<$Res> {
__$$PaymentDetails_BitcoinImplCopyWithImpl(
_$PaymentDetails_BitcoinImpl _value, $Res Function(_$PaymentDetails_BitcoinImpl) _then)
: super(_value, _then);
/// Create a copy of PaymentDetails
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? swapId = null,
Object? description = null,
Object? refundTxId = freezed,
Object? refundTxAmountSat = freezed,
}) {
return _then(_$PaymentDetails_BitcoinImpl(
swapId: null == swapId
? _value.swapId
: swapId // ignore: cast_nullable_to_non_nullable
as String,
description: null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String,
refundTxId: freezed == refundTxId
? _value.refundTxId
: refundTxId // ignore: cast_nullable_to_non_nullable
as String?,
refundTxAmountSat: freezed == refundTxAmountSat
? _value.refundTxAmountSat
: refundTxAmountSat // ignore: cast_nullable_to_non_nullable
as BigInt?,
));
}
}
/// @nodoc
class _$PaymentDetails_BitcoinImpl extends PaymentDetails_Bitcoin {
const _$PaymentDetails_BitcoinImpl(
{required this.swapId, required this.description, this.refundTxId, this.refundTxAmountSat})
: super._();
@override
final String swapId;
/// Represents the invoice description
@override
final String description;
/// For a Send swap which was refunded, this is the refund tx id
@override
final String? refundTxId;
/// For a Send swap which was refunded, this is the refund amount
@override
final BigInt? refundTxAmountSat;
@override
String toString() {
return 'PaymentDetails.bitcoin(swapId: $swapId, description: $description, refundTxId: $refundTxId, refundTxAmountSat: $refundTxAmountSat)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$PaymentDetails_BitcoinImpl &&
(identical(other.swapId, swapId) || other.swapId == swapId) &&
(identical(other.description, description) || other.description == description) &&
(identical(other.refundTxId, refundTxId) || other.refundTxId == refundTxId) &&
(identical(other.refundTxAmountSat, refundTxAmountSat) ||
other.refundTxAmountSat == refundTxAmountSat));
}
@override
int get hashCode => Object.hash(runtimeType, swapId, description, refundTxId, refundTxAmountSat);
/// Create a copy of PaymentDetails
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$PaymentDetails_BitcoinImplCopyWith<_$PaymentDetails_BitcoinImpl> get copyWith =>
__$$PaymentDetails_BitcoinImplCopyWithImpl<_$PaymentDetails_BitcoinImpl>(this, _$identity);
}
abstract class PaymentDetails_Bitcoin extends PaymentDetails {
const factory PaymentDetails_Bitcoin(
{required final String swapId,
required final String description,
final String? refundTxId,
final BigInt? refundTxAmountSat}) = _$PaymentDetails_BitcoinImpl;
const PaymentDetails_Bitcoin._() : super._();
String get swapId;
/// Represents the invoice description
@override
String get description;
/// For a Send swap which was refunded, this is the refund tx id
String? get refundTxId;
/// For a Send swap which was refunded, this is the refund amount
BigInt? get refundTxAmountSat;
/// Create a copy of PaymentDetails
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$PaymentDetails_BitcoinImplCopyWith<_$PaymentDetails_BitcoinImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc /// @nodoc
mixin _$SdkEvent {} mixin _$SdkEvent {}
@@ -825,3 +1268,186 @@ abstract class SdkEvent_Synced extends SdkEvent {
const factory SdkEvent_Synced() = _$SdkEvent_SyncedImpl; const factory SdkEvent_Synced() = _$SdkEvent_SyncedImpl;
const SdkEvent_Synced._() : super._(); const SdkEvent_Synced._() : super._();
} }
/// @nodoc
mixin _$SendDestination {}
/// @nodoc
abstract class $SendDestinationCopyWith<$Res> {
factory $SendDestinationCopyWith(SendDestination value, $Res Function(SendDestination) then) =
_$SendDestinationCopyWithImpl<$Res, SendDestination>;
}
/// @nodoc
class _$SendDestinationCopyWithImpl<$Res, $Val extends SendDestination>
implements $SendDestinationCopyWith<$Res> {
_$SendDestinationCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of SendDestination
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$SendDestination_LiquidAddressImplCopyWith<$Res> {
factory _$$SendDestination_LiquidAddressImplCopyWith(_$SendDestination_LiquidAddressImpl value,
$Res Function(_$SendDestination_LiquidAddressImpl) then) =
__$$SendDestination_LiquidAddressImplCopyWithImpl<$Res>;
@useResult
$Res call({LiquidAddressData addressData});
}
/// @nodoc
class __$$SendDestination_LiquidAddressImplCopyWithImpl<$Res>
extends _$SendDestinationCopyWithImpl<$Res, _$SendDestination_LiquidAddressImpl>
implements _$$SendDestination_LiquidAddressImplCopyWith<$Res> {
__$$SendDestination_LiquidAddressImplCopyWithImpl(
_$SendDestination_LiquidAddressImpl _value, $Res Function(_$SendDestination_LiquidAddressImpl) _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? addressData = null,
}) {
return _then(_$SendDestination_LiquidAddressImpl(
addressData: null == addressData
? _value.addressData
: addressData // ignore: cast_nullable_to_non_nullable
as LiquidAddressData,
));
}
}
/// @nodoc
class _$SendDestination_LiquidAddressImpl extends SendDestination_LiquidAddress {
const _$SendDestination_LiquidAddressImpl({required this.addressData}) : super._();
@override
final LiquidAddressData addressData;
@override
String toString() {
return 'SendDestination.liquidAddress(addressData: $addressData)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SendDestination_LiquidAddressImpl &&
(identical(other.addressData, addressData) || other.addressData == addressData));
}
@override
int get hashCode => Object.hash(runtimeType, addressData);
/// 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_LiquidAddressImplCopyWith<_$SendDestination_LiquidAddressImpl> get copyWith =>
__$$SendDestination_LiquidAddressImplCopyWithImpl<_$SendDestination_LiquidAddressImpl>(
this, _$identity);
}
abstract class SendDestination_LiquidAddress extends SendDestination {
const factory SendDestination_LiquidAddress({required final LiquidAddressData addressData}) =
_$SendDestination_LiquidAddressImpl;
const SendDestination_LiquidAddress._() : super._();
LiquidAddressData get addressData;
/// Create a copy of SendDestination
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SendDestination_LiquidAddressImplCopyWith<_$SendDestination_LiquidAddressImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$SendDestination_Bolt11ImplCopyWith<$Res> {
factory _$$SendDestination_Bolt11ImplCopyWith(
_$SendDestination_Bolt11Impl value, $Res Function(_$SendDestination_Bolt11Impl) then) =
__$$SendDestination_Bolt11ImplCopyWithImpl<$Res>;
@useResult
$Res call({LNInvoice invoice});
}
/// @nodoc
class __$$SendDestination_Bolt11ImplCopyWithImpl<$Res>
extends _$SendDestinationCopyWithImpl<$Res, _$SendDestination_Bolt11Impl>
implements _$$SendDestination_Bolt11ImplCopyWith<$Res> {
__$$SendDestination_Bolt11ImplCopyWithImpl(
_$SendDestination_Bolt11Impl _value, $Res Function(_$SendDestination_Bolt11Impl) _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? invoice = null,
}) {
return _then(_$SendDestination_Bolt11Impl(
invoice: null == invoice
? _value.invoice
: invoice // ignore: cast_nullable_to_non_nullable
as LNInvoice,
));
}
}
/// @nodoc
class _$SendDestination_Bolt11Impl extends SendDestination_Bolt11 {
const _$SendDestination_Bolt11Impl({required this.invoice}) : super._();
@override
final LNInvoice invoice;
@override
String toString() {
return 'SendDestination.bolt11(invoice: $invoice)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SendDestination_Bolt11Impl &&
(identical(other.invoice, invoice) || other.invoice == invoice));
}
@override
int get hashCode => Object.hash(runtimeType, invoice);
/// 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_Bolt11ImplCopyWith<_$SendDestination_Bolt11Impl> get copyWith =>
__$$SendDestination_Bolt11ImplCopyWithImpl<_$SendDestination_Bolt11Impl>(this, _$identity);
}
abstract class SendDestination_Bolt11 extends SendDestination {
const factory SendDestination_Bolt11({required final LNInvoice invoice}) = _$SendDestination_Bolt11Impl;
const SendDestination_Bolt11._() : super._();
LNInvoice get invoice;
/// Create a copy of SendDestination
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SendDestination_Bolt11ImplCopyWith<_$SendDestination_Bolt11Impl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -80,17 +80,21 @@ class _ConnectPageState extends State<ConnectPage> {
debugPrint("${mnemonic.isEmpty ? "Creating" : "Restoring"} wallet with $walletMnemonic"); debugPrint("${mnemonic.isEmpty ? "Creating" : "Restoring"} wallet with $walletMnemonic");
return await initializeWallet(mnemonic: walletMnemonic).then( return await initializeWallet(mnemonic: walletMnemonic).then(
(liquidSDK) async { (liquidSDK) async {
await widget.credentialsManager.storeMnemonic(mnemonic: walletMnemonic).then((_) { await widget.credentialsManager.storeMnemonic(mnemonic: walletMnemonic).then(
Navigator.pushReplacement( (_) {
context, if (mounted) {
MaterialPageRoute( Navigator.pushReplacement(
builder: (BuildContext context) => HomePage( context,
liquidSDK: widget.liquidSDK, MaterialPageRoute(
credentialsManager: widget.credentialsManager, builder: (BuildContext context) => HomePage(
), liquidSDK: widget.liquidSDK,
), credentialsManager: widget.credentialsManager,
); ),
}); ),
);
}
},
);
}, },
); );
} }

View File

@@ -7,7 +7,11 @@ class HomePageDrawer extends StatefulWidget {
final BindingLiquidSdk liquidSDK; final BindingLiquidSdk liquidSDK;
final CredentialsManager credentialsManager; final CredentialsManager credentialsManager;
const HomePageDrawer({super.key, required this.liquidSDK, required this.credentialsManager}); const HomePageDrawer({
super.key,
required this.liquidSDK,
required this.credentialsManager,
});
@override @override
State<HomePageDrawer> createState() => _HomePageDrawerState(); State<HomePageDrawer> createState() => _HomePageDrawerState();
@@ -32,8 +36,11 @@ class _HomePageDrawerState extends State<HomePageDrawer> {
enabled: false, enabled: false,
leading: const Icon(Icons.backup_outlined), leading: const Icon(Icons.backup_outlined),
title: const Text('Backup'), title: const Text('Backup'),
titleTextStyle: titleTextStyle: const TextStyle(
const TextStyle(fontSize: 16.0, color: Colors.white, decoration: TextDecoration.lineThrough), fontSize: 16.0,
color: Colors.white,
decoration: TextDecoration.lineThrough,
),
onTap: () async { onTap: () async {
try { try {
debugPrint("Creating backup."); debugPrint("Creating backup.");
@@ -44,7 +51,10 @@ class _HomePageDrawerState extends State<HomePageDrawer> {
final errMsg = "Failed to create backup. $e"; final errMsg = "Failed to create backup. $e";
debugPrint(errMsg); debugPrint(errMsg);
if (context.mounted) { if (context.mounted) {
final snackBar = SnackBar(behavior: SnackBarBehavior.floating, content: Text(errMsg)); final snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text(errMsg),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar); ScaffoldMessenger.of(context).showSnackBar(snackBar);
} }
} }
@@ -54,8 +64,11 @@ class _HomePageDrawerState extends State<HomePageDrawer> {
enabled: false, enabled: false,
leading: const Icon(Icons.restore), leading: const Icon(Icons.restore),
title: const Text('Restore'), title: const Text('Restore'),
titleTextStyle: titleTextStyle: const TextStyle(
const TextStyle(fontSize: 16.0, color: Colors.white, decoration: TextDecoration.lineThrough), fontSize: 16.0,
color: Colors.white,
decoration: TextDecoration.lineThrough,
),
onTap: () async { onTap: () async {
try { try {
debugPrint("Restoring backup."); debugPrint("Restoring backup.");
@@ -67,7 +80,10 @@ class _HomePageDrawerState extends State<HomePageDrawer> {
final errMsg = "Failed to restore backup. $e"; final errMsg = "Failed to restore backup. $e";
debugPrint(errMsg); debugPrint(errMsg);
if (context.mounted) { if (context.mounted) {
final snackBar = SnackBar(behavior: SnackBarBehavior.floating, content: Text(errMsg)); final snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text(errMsg),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar); ScaffoldMessenger.of(context).showSnackBar(snackBar);
} }
} }
@@ -86,7 +102,10 @@ class _HomePageDrawerState extends State<HomePageDrawer> {
final errMsg = "Failed to empty wallet cache. $e"; final errMsg = "Failed to empty wallet cache. $e";
debugPrint(errMsg); debugPrint(errMsg);
if (context.mounted) { if (context.mounted) {
final snackBar = SnackBar(behavior: SnackBarBehavior.floating, content: Text(errMsg)); final snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text(errMsg),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar); ScaffoldMessenger.of(context).showSnackBar(snackBar);
} }
} }
@@ -98,19 +117,26 @@ class _HomePageDrawerState extends State<HomePageDrawer> {
titleTextStyle: const TextStyle(fontSize: 16.0, color: Colors.white), titleTextStyle: const TextStyle(fontSize: 16.0, color: Colors.white),
onTap: () async { onTap: () async {
try { try {
await widget.credentialsManager.restoreMnemonic().then((mnemonics) { await widget.credentialsManager.restoreMnemonic().then(
showDialog( (mnemonics) {
context: context, if (context.mounted) {
builder: (context) => MnemonicsDialog( showDialog(
mnemonics: mnemonics.split(" "), context: context,
), builder: (context) => MnemonicsDialog(
); mnemonics: mnemonics.split(" "),
}); ),
);
}
},
);
} on Exception catch (e) { } on Exception catch (e) {
final errMsg = "Failed to display mnemonics. $e"; final errMsg = "Failed to display mnemonics. $e";
debugPrint(errMsg); debugPrint(errMsg);
if (context.mounted) { if (context.mounted) {
final snackBar = SnackBar(behavior: SnackBarBehavior.floating, content: Text(errMsg)); final snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text(errMsg),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar); ScaffoldMessenger.of(context).showSnackBar(snackBar);
} }
} }

View File

@@ -10,43 +10,7 @@ class PaymentItem extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListTile( return ListTile(
onLongPress: item.preimage != null onLongPress: () => _onLongPress(context),
? () {
try {
debugPrint("Store payment preimage on clipboard. Preimage: ${item.preimage!}");
Clipboard.setData(ClipboardData(text: item.preimage!));
const snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text('Copied payment preimage to clipboard.'),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
} catch (e) {
final snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text('Failed to copy payment preimage to clipboard. $e'),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
: item.swapId != null
? () {
try {
debugPrint("Store swap ID on clipboard. Swap ID: ${item.swapId!}");
Clipboard.setData(ClipboardData(text: item.swapId!));
const snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text('Copied swap ID to clipboard.'),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
} catch (e) {
final snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text('Failed to copy payment preimage to clipboard. $e'),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
: null,
title: Text(_paymentTitle(item)), title: Text(_paymentTitle(item)),
subtitle: Text( subtitle: Text(
DateFormat('dd/MM/yyyy, HH:mm').format( DateFormat('dd/MM/yyyy, HH:mm').format(
@@ -70,6 +34,67 @@ class PaymentItem extends StatelessWidget {
); );
} }
void _onLongPress(BuildContext context) {
final details = item.details;
if (details == null) return;
if (details is PaymentDetails_Lightning && details.preimage != null) {
try {
debugPrint("Store payment preimage on clipboard. Preimage: ${details.preimage!}");
Clipboard.setData(ClipboardData(text: details.preimage!));
const snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text('Copied payment preimage to clipboard.'),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
} catch (e) {
final snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text('Failed to copy payment preimage to clipboard. $e'),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
if (details is PaymentDetails_Bitcoin) {
try {
debugPrint("Store swap ID on clipboard. Swap ID: ${details.swapId}");
Clipboard.setData(ClipboardData(text: details.swapId));
const snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text('Copied swap ID to clipboard.'),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
} catch (e) {
final snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text('Failed to copy payment swap ID to clipboard. $e'),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
if (details is PaymentDetails_Liquid) {
try {
debugPrint("Store Liquid Address on clipboard. Liquid Address: ${details.destination}");
Clipboard.setData(ClipboardData(text: details.destination));
const snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text('Copied Liquid Address to clipboard.'),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
} catch (e) {
final snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text('Failed to copy payment Liquid Address to clipboard. $e'),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
return;
}
String _paymentTitle(Payment payment) { String _paymentTitle(Payment payment) {
final paymentType = payment.paymentType; final paymentType = payment.paymentType;

View File

@@ -153,7 +153,13 @@ class CancelScanButton extends StatelessWidget {
), ),
), ),
onPressed: () async { onPressed: () async {
controller.stop().then((_) => Navigator.of(context).pop()); controller.stop().then(
(_) {
if (context.mounted) {
Navigator.of(context).pop();
}
},
);
}, },
child: const Text( child: const Text(
"CANCEL", "CANCEL",

View File

@@ -34,10 +34,12 @@ class QrActionButton extends StatelessWidget {
).then((barcode) { ).then((barcode) {
if (barcode == null || barcode.isEmpty) return; if (barcode == null || barcode.isEmpty) return;
debugPrint("Scanned string: '$barcode'"); debugPrint("Scanned string: '$barcode'");
showDialog( if (context.mounted) {
context: context, showDialog(
builder: (context) => SendPaymentDialog(barcodeValue: barcode, liquidSdk: liquidSDK), context: context,
); builder: (context) => SendPaymentDialog(barcodeValue: barcode, liquidSdk: liquidSDK),
);
}
}); });
} }
} }

View File

@@ -22,8 +22,7 @@ class _ReceivePaymentDialogState extends State<ReceivePaymentDialog> {
int? feesSat; int? feesSat;
bool creatingInvoice = false; bool creatingInvoice = false;
String? invoice; String? invoiceDestination;
String? invoiceId;
StreamSubscription<List<Payment>>? streamSubscription; StreamSubscription<List<Payment>>? streamSubscription;
@@ -31,10 +30,19 @@ class _ReceivePaymentDialogState extends State<ReceivePaymentDialog> {
void initState() { void initState() {
super.initState(); super.initState();
streamSubscription = widget.paymentsStream.listen((paymentList) { streamSubscription = widget.paymentsStream.listen((paymentList) {
if (invoiceId != null && invoiceId!.isNotEmpty) { if (invoiceDestination != null && invoiceDestination!.isNotEmpty) {
if (paymentList.any((e) => e.swapId == invoiceId!)) { if (paymentList.any(
debugPrint("Payment Received! Id: $invoiceId"); (e) {
if (context.mounted) { final details = e.details;
if (details == null) return false;
if (details is PaymentDetails_Lightning && details.preimage != null) {
return details.preimage! == invoiceDestination!;
}
return false;
},
)) {
debugPrint("Payment Received! Id: $invoiceDestination");
if (mounted) {
Navigator.of(context).pop(); Navigator.of(context).pop();
} }
} }
@@ -51,13 +59,13 @@ class _ReceivePaymentDialogState extends State<ReceivePaymentDialog> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertDialog( return AlertDialog(
title: creatingInvoice ? null : Text(invoice != null ? "Invoice" : "Receive Payment"), title: creatingInvoice ? null : Text(invoiceDestination != null ? "Invoice" : "Receive Payment"),
content: creatingInvoice || invoice != null content: creatingInvoice || invoiceDestination != null
? Column( ? Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
if (invoice != null) ...[ if (invoiceDestination != null) ...[
AspectRatio( AspectRatio(
aspectRatio: 1, aspectRatio: 1,
child: SizedBox( child: SizedBox(
@@ -65,7 +73,7 @@ class _ReceivePaymentDialogState extends State<ReceivePaymentDialog> {
height: 200.0, height: 200.0,
child: QrImageView( child: QrImageView(
embeddedImage: const AssetImage("assets/icons/app_icon.png"), embeddedImage: const AssetImage("assets/icons/app_icon.png"),
data: invoice!.toUpperCase(), data: invoiceDestination!.toUpperCase(),
size: 200.0, size: 200.0,
), ),
), ),
@@ -128,34 +136,36 @@ class _ReceivePaymentDialogState extends State<ReceivePaymentDialog> {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
), ),
if (invoice == null) ...[ if (invoiceDestination == null) ...[
TextButton( TextButton(
onPressed: () async { onPressed: () async {
try { try {
setState(() => creatingInvoice = true); setState(() => creatingInvoice = true);
int amountSat = int.parse(payerAmountController.text); int amountSat = int.parse(payerAmountController.text);
PrepareReceivePaymentRequest prepareReceiveReq = PrepareReceiveRequest prepareReceiveReq = PrepareReceiveRequest(
PrepareReceivePaymentRequest(payerAmountSat: BigInt.from(amountSat)); paymentMethod: PaymentMethod.lightning,
PrepareReceivePaymentResponse prepareRes = await widget.liquidSDK.prepareReceivePayment( payerAmountSat: BigInt.from(amountSat),
);
PrepareReceiveResponse prepareResponse = await widget.liquidSDK.prepareReceivePayment(
req: prepareReceiveReq, req: prepareReceiveReq,
); );
setState(() { setState(() {
payerAmountSat = prepareRes.payerAmountSat.toInt(); payerAmountSat = prepareResponse.payerAmountSat?.toInt();
feesSat = prepareRes.feesSat.toInt(); feesSat = prepareResponse.feesSat.toInt();
}); });
ReceivePaymentRequest receiveReq = ReceivePaymentRequest(prepareRes: prepareRes); ReceivePaymentRequest receiveReq = ReceivePaymentRequest(
prepareResponse: prepareResponse,
);
ReceivePaymentResponse resp = await widget.liquidSDK.receivePayment(req: receiveReq); ReceivePaymentResponse resp = await widget.liquidSDK.receivePayment(req: receiveReq);
debugPrint( debugPrint(
"Created Invoice for $payerAmountSat sats with $feesSat sats fees.\nInvoice:${resp.invoice}", "Created Invoice for $payerAmountSat sats with $feesSat sats fees.\nDestination:${resp.destination}",
); );
setState(() => invoice = resp.invoice); setState(() => invoiceDestination = resp.destination);
setState(() => invoiceId = resp.id);
} catch (e) { } catch (e) {
setState(() { setState(() {
payerAmountSat = null; payerAmountSat = null;
feesSat = null; feesSat = null;
invoice = null; invoiceDestination = null;
invoiceId = null;
}); });
final errMsg = "Error receiving payment: $e"; final errMsg = "Error receiving payment: $e";
debugPrint(errMsg); debugPrint(errMsg);

View File

@@ -17,7 +17,7 @@ class _SendPaymentDialogState extends State<SendPaymentDialog> {
bool paymentInProgress = false; bool paymentInProgress = false;
PrepareSendResponse? sendPaymentReq; SendPaymentRequest? sendPaymentReq;
@override @override
void initState() { void initState() {
@@ -50,7 +50,7 @@ class _SendPaymentDialogState extends State<SendPaymentDialog> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Text(
'Please confirm that you agree to the payment fee of ${sendPaymentReq!.feesSat} sats.', 'Please confirm that you agree to the payment fee of ${sendPaymentReq!.prepareResponse.feesSat} sats.',
), ),
], ],
), ),
@@ -84,12 +84,16 @@ class _SendPaymentDialogState extends State<SendPaymentDialog> {
onPressed: () async { onPressed: () async {
try { try {
setState(() => paymentInProgress = true); setState(() => paymentInProgress = true);
PrepareSendRequest prepareSendReq = PrepareSendRequest prepareSendReq = PrepareSendRequest(
PrepareSendRequest(invoice: invoiceController.text); destination: invoiceController.text,
PrepareSendResponse req = );
await widget.liquidSdk.prepareSendPayment(req: prepareSendReq); PrepareSendResponse req = await widget.liquidSdk.prepareSendPayment(
debugPrint("PrepareSendResponse for ${req.invoice}, fees: ${req.feesSat}"); req: prepareSendReq,
setState(() => sendPaymentReq = req); );
debugPrint(
"PrepareSendResponse for ${req.destination}, fees: ${req.feesSat}",
);
setState(() => sendPaymentReq = SendPaymentRequest(prepareResponse: req));
} catch (e) { } catch (e) {
final errMsg = "Error preparing payment: $e"; final errMsg = "Error preparing payment: $e";
debugPrint(errMsg); debugPrint(errMsg);

View File

@@ -106,37 +106,37 @@ class BreezSDKLiquid {
(event) async { (event) async {
if (event is liquid_sdk.SdkEvent_PaymentFailed) { if (event is liquid_sdk.SdkEvent_PaymentFailed) {
_logStreamController.add( _logStreamController.add(
liquid_sdk.LogEntry(line: "Payment Failed. ${event.details.swapId}", level: "WARN"), liquid_sdk.LogEntry(line: "Payment Failed. ${event.details}", level: "WARN"),
); );
_paymentResultStream.addError(PaymentException(event.details)); _paymentResultStream.addError(PaymentException(event.details));
} }
if (event is liquid_sdk.SdkEvent_PaymentPending) { if (event is liquid_sdk.SdkEvent_PaymentPending) {
_logStreamController.add( _logStreamController.add(
liquid_sdk.LogEntry(line: "Payment Pending. ${event.details.swapId}", level: "INFO"), liquid_sdk.LogEntry(line: "Payment Pending. ${event.details}", level: "INFO"),
); );
_paymentResultStream.add(event.details); _paymentResultStream.add(event.details);
} }
if (event is liquid_sdk.SdkEvent_PaymentRefunded) { if (event is liquid_sdk.SdkEvent_PaymentRefunded) {
_logStreamController.add( _logStreamController.add(
liquid_sdk.LogEntry(line: "Payment Refunded. ${event.details.swapId}", level: "INFO"), liquid_sdk.LogEntry(line: "Payment Refunded. ${event.details}", level: "INFO"),
); );
_paymentResultStream.add(event.details); _paymentResultStream.add(event.details);
} }
if (event is liquid_sdk.SdkEvent_PaymentRefundPending) { if (event is liquid_sdk.SdkEvent_PaymentRefundPending) {
_logStreamController.add( _logStreamController.add(
liquid_sdk.LogEntry(line: "Pending Payment Refund. ${event.details.swapId}", level: "INFO"), liquid_sdk.LogEntry(line: "Pending Payment Refund. ${event.details}", level: "INFO"),
); );
_paymentResultStream.add(event.details); _paymentResultStream.add(event.details);
} }
if (event is liquid_sdk.SdkEvent_PaymentSucceeded) { if (event is liquid_sdk.SdkEvent_PaymentSucceeded) {
_logStreamController.add( _logStreamController.add(
liquid_sdk.LogEntry(line: "Payment Succeeded. ${event.details.swapId}", level: "INFO"), liquid_sdk.LogEntry(line: "Payment Succeeded. ${event.details}", level: "INFO"),
); );
_paymentResultStream.add(event.details); _paymentResultStream.add(event.details);
} }
if (event is liquid_sdk.SdkEvent_PaymentWaitingConfirmation) { if (event is liquid_sdk.SdkEvent_PaymentWaitingConfirmation) {
_logStreamController.add( _logStreamController.add(
liquid_sdk.LogEntry(line: "Payment Waiting Confirmation. ${event.details.swapId}", level: "INFO"), liquid_sdk.LogEntry(line: "Payment Waiting Confirmation. ${event.details}", level: "INFO"),
); );
_paymentResultStream.add(event.details); _paymentResultStream.add(event.details);
} }

View File

@@ -330,10 +330,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: mobile_scanner name: mobile_scanner
sha256: b8c0e9afcfd52534f85ec666f3d52156f560b5e6c25b1e3d4fe2087763607926 sha256: "6ac2913ad98c83f558d2c8a55bc8f511bdcf28b86639701c04b04c16da1e9ee1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.1.1" version: "5.2.1"
package_config: package_config:
dependency: transitive dependency: transitive
description: description:

View File

@@ -369,32 +369,10 @@ class FlutterBreezLiquidBindings {
_frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_pay_onchainPtr _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_pay_onchainPtr
.asFunction<void Function(int, int, ffi.Pointer<wire_cst_prepare_pay_onchain_request>)>(); .asFunction<void Function(int, int, ffi.Pointer<wire_cst_prepare_pay_onchain_request>)>();
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_onchain(
int port_,
int that,
ffi.Pointer<wire_cst_prepare_receive_onchain_request> req,
) {
return _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_onchain(
port_,
that,
req,
);
}
late final _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_onchainPtr =
_lookup<
ffi.NativeFunction<
ffi.Void Function(
ffi.Int64, ffi.UintPtr, ffi.Pointer<wire_cst_prepare_receive_onchain_request>)>>(
'frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_onchain');
late final _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_onchain =
_frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_onchainPtr
.asFunction<void Function(int, int, ffi.Pointer<wire_cst_prepare_receive_onchain_request>)>();
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_payment( void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_payment(
int port_, int port_,
int that, int that,
ffi.Pointer<wire_cst_prepare_receive_payment_request> req, ffi.Pointer<wire_cst_prepare_receive_request> req,
) { ) {
return _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_payment( return _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_payment(
port_, port_,
@@ -406,12 +384,11 @@ class FlutterBreezLiquidBindings {
late final _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_paymentPtr = late final _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_paymentPtr =
_lookup< _lookup<
ffi.NativeFunction< ffi.NativeFunction<
ffi.Void Function( ffi.Void Function(ffi.Int64, ffi.UintPtr, ffi.Pointer<wire_cst_prepare_receive_request>)>>(
ffi.Int64, ffi.UintPtr, ffi.Pointer<wire_cst_prepare_receive_payment_request>)>>(
'frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_payment'); 'frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_payment');
late final _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_payment = late final _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_payment =
_frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_paymentPtr _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_receive_paymentPtr
.asFunction<void Function(int, int, ffi.Pointer<wire_cst_prepare_receive_payment_request>)>(); .asFunction<void Function(int, int, ffi.Pointer<wire_cst_prepare_receive_request>)>();
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_refund( void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_refund(
int port_, int port_,
@@ -453,27 +430,6 @@ class FlutterBreezLiquidBindings {
_frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_send_paymentPtr _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_send_paymentPtr
.asFunction<void Function(int, int, ffi.Pointer<wire_cst_prepare_send_request>)>(); .asFunction<void Function(int, int, ffi.Pointer<wire_cst_prepare_send_request>)>();
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_onchain(
int port_,
int that,
ffi.Pointer<wire_cst_prepare_receive_onchain_response> req,
) {
return _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_onchain(
port_,
that,
req,
);
}
late final _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_onchainPtr = _lookup<
ffi.NativeFunction<
ffi.Void Function(
ffi.Int64, ffi.UintPtr, ffi.Pointer<wire_cst_prepare_receive_onchain_response>)>>(
'frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_onchain');
late final _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_onchain =
_frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_onchainPtr
.asFunction<void Function(int, int, ffi.Pointer<wire_cst_prepare_receive_onchain_response>)>();
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_payment( void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_payment(
int port_, int port_,
int that, int that,
@@ -569,7 +525,7 @@ class FlutterBreezLiquidBindings {
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_send_payment( void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_send_payment(
int port_, int port_,
int that, int that,
ffi.Pointer<wire_cst_prepare_send_response> req, ffi.Pointer<wire_cst_send_payment_request> req,
) { ) {
return _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_send_payment( return _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_send_payment(
port_, port_,
@@ -580,11 +536,11 @@ class FlutterBreezLiquidBindings {
late final _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_send_paymentPtr = _lookup< late final _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_send_paymentPtr = _lookup<
ffi.NativeFunction< ffi.NativeFunction<
ffi.Void Function(ffi.Int64, ffi.UintPtr, ffi.Pointer<wire_cst_prepare_send_response>)>>( ffi.Void Function(ffi.Int64, ffi.UintPtr, ffi.Pointer<wire_cst_send_payment_request>)>>(
'frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_send_payment'); 'frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_send_payment');
late final _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_send_payment = late final _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_send_payment =
_frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_send_paymentPtr _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_send_paymentPtr
.asFunction<void Function(int, int, ffi.Pointer<wire_cst_prepare_send_response>)>(); .asFunction<void Function(int, int, ffi.Pointer<wire_cst_send_payment_request>)>();
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_sync( void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_sync(
int port_, int port_,
@@ -1020,6 +976,17 @@ class FlutterBreezLiquidBindings {
_frbgen_breez_liquid_cst_new_box_autoadd_paymentPtr _frbgen_breez_liquid_cst_new_box_autoadd_paymentPtr
.asFunction<ffi.Pointer<wire_cst_payment> Function()>(); .asFunction<ffi.Pointer<wire_cst_payment> Function()>();
ffi.Pointer<wire_cst_payment_details> frbgen_breez_liquid_cst_new_box_autoadd_payment_details() {
return _frbgen_breez_liquid_cst_new_box_autoadd_payment_details();
}
late final _frbgen_breez_liquid_cst_new_box_autoadd_payment_detailsPtr =
_lookup<ffi.NativeFunction<ffi.Pointer<wire_cst_payment_details> Function()>>(
'frbgen_breez_liquid_cst_new_box_autoadd_payment_details');
late final _frbgen_breez_liquid_cst_new_box_autoadd_payment_details =
_frbgen_breez_liquid_cst_new_box_autoadd_payment_detailsPtr
.asFunction<ffi.Pointer<wire_cst_payment_details> Function()>();
ffi.Pointer<wire_cst_prepare_buy_bitcoin_request> ffi.Pointer<wire_cst_prepare_buy_bitcoin_request>
frbgen_breez_liquid_cst_new_box_autoadd_prepare_buy_bitcoin_request() { frbgen_breez_liquid_cst_new_box_autoadd_prepare_buy_bitcoin_request() {
return _frbgen_breez_liquid_cst_new_box_autoadd_prepare_buy_bitcoin_request(); return _frbgen_breez_liquid_cst_new_box_autoadd_prepare_buy_bitcoin_request();
@@ -1044,41 +1011,17 @@ class FlutterBreezLiquidBindings {
_frbgen_breez_liquid_cst_new_box_autoadd_prepare_pay_onchain_requestPtr _frbgen_breez_liquid_cst_new_box_autoadd_prepare_pay_onchain_requestPtr
.asFunction<ffi.Pointer<wire_cst_prepare_pay_onchain_request> Function()>(); .asFunction<ffi.Pointer<wire_cst_prepare_pay_onchain_request> Function()>();
ffi.Pointer<wire_cst_prepare_receive_onchain_request> ffi.Pointer<wire_cst_prepare_receive_request>
frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_onchain_request() { frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_request() {
return _frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_onchain_request(); return _frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_request();
} }
late final _frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_onchain_requestPtr = late final _frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_requestPtr =
_lookup<ffi.NativeFunction<ffi.Pointer<wire_cst_prepare_receive_onchain_request> Function()>>( _lookup<ffi.NativeFunction<ffi.Pointer<wire_cst_prepare_receive_request> Function()>>(
'frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_onchain_request'); 'frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_request');
late final _frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_onchain_request = late final _frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_request =
_frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_onchain_requestPtr _frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_requestPtr
.asFunction<ffi.Pointer<wire_cst_prepare_receive_onchain_request> Function()>(); .asFunction<ffi.Pointer<wire_cst_prepare_receive_request> Function()>();
ffi.Pointer<wire_cst_prepare_receive_onchain_response>
frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_onchain_response() {
return _frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_onchain_response();
}
late final _frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_onchain_responsePtr =
_lookup<ffi.NativeFunction<ffi.Pointer<wire_cst_prepare_receive_onchain_response> Function()>>(
'frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_onchain_response');
late final _frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_onchain_response =
_frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_onchain_responsePtr
.asFunction<ffi.Pointer<wire_cst_prepare_receive_onchain_response> Function()>();
ffi.Pointer<wire_cst_prepare_receive_payment_request>
frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_payment_request() {
return _frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_payment_request();
}
late final _frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_payment_requestPtr =
_lookup<ffi.NativeFunction<ffi.Pointer<wire_cst_prepare_receive_payment_request> Function()>>(
'frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_payment_request');
late final _frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_payment_request =
_frbgen_breez_liquid_cst_new_box_autoadd_prepare_receive_payment_requestPtr
.asFunction<ffi.Pointer<wire_cst_prepare_receive_payment_request> Function()>();
ffi.Pointer<wire_cst_prepare_refund_request> ffi.Pointer<wire_cst_prepare_refund_request>
frbgen_breez_liquid_cst_new_box_autoadd_prepare_refund_request() { frbgen_breez_liquid_cst_new_box_autoadd_prepare_refund_request() {
@@ -1103,18 +1046,6 @@ class FlutterBreezLiquidBindings {
_frbgen_breez_liquid_cst_new_box_autoadd_prepare_send_requestPtr _frbgen_breez_liquid_cst_new_box_autoadd_prepare_send_requestPtr
.asFunction<ffi.Pointer<wire_cst_prepare_send_request> Function()>(); .asFunction<ffi.Pointer<wire_cst_prepare_send_request> Function()>();
ffi.Pointer<wire_cst_prepare_send_response>
frbgen_breez_liquid_cst_new_box_autoadd_prepare_send_response() {
return _frbgen_breez_liquid_cst_new_box_autoadd_prepare_send_response();
}
late final _frbgen_breez_liquid_cst_new_box_autoadd_prepare_send_responsePtr =
_lookup<ffi.NativeFunction<ffi.Pointer<wire_cst_prepare_send_response> Function()>>(
'frbgen_breez_liquid_cst_new_box_autoadd_prepare_send_response');
late final _frbgen_breez_liquid_cst_new_box_autoadd_prepare_send_response =
_frbgen_breez_liquid_cst_new_box_autoadd_prepare_send_responsePtr
.asFunction<ffi.Pointer<wire_cst_prepare_send_response> Function()>();
ffi.Pointer<wire_cst_receive_payment_request> ffi.Pointer<wire_cst_receive_payment_request>
frbgen_breez_liquid_cst_new_box_autoadd_receive_payment_request() { frbgen_breez_liquid_cst_new_box_autoadd_receive_payment_request() {
return _frbgen_breez_liquid_cst_new_box_autoadd_receive_payment_request(); return _frbgen_breez_liquid_cst_new_box_autoadd_receive_payment_request();
@@ -1160,6 +1091,17 @@ class FlutterBreezLiquidBindings {
_frbgen_breez_liquid_cst_new_box_autoadd_sdk_eventPtr _frbgen_breez_liquid_cst_new_box_autoadd_sdk_eventPtr
.asFunction<ffi.Pointer<wire_cst_sdk_event> Function()>(); .asFunction<ffi.Pointer<wire_cst_sdk_event> Function()>();
ffi.Pointer<wire_cst_send_payment_request> frbgen_breez_liquid_cst_new_box_autoadd_send_payment_request() {
return _frbgen_breez_liquid_cst_new_box_autoadd_send_payment_request();
}
late final _frbgen_breez_liquid_cst_new_box_autoadd_send_payment_requestPtr =
_lookup<ffi.NativeFunction<ffi.Pointer<wire_cst_send_payment_request> Function()>>(
'frbgen_breez_liquid_cst_new_box_autoadd_send_payment_request');
late final _frbgen_breez_liquid_cst_new_box_autoadd_send_payment_request =
_frbgen_breez_liquid_cst_new_box_autoadd_send_payment_requestPtr
.asFunction<ffi.Pointer<wire_cst_send_payment_request> Function()>();
ffi.Pointer<wire_cst_success_action_processed> ffi.Pointer<wire_cst_success_action_processed>
frbgen_breez_liquid_cst_new_box_autoadd_success_action_processed() { frbgen_breez_liquid_cst_new_box_autoadd_success_action_processed() {
return _frbgen_breez_liquid_cst_new_box_autoadd_success_action_processed(); return _frbgen_breez_liquid_cst_new_box_autoadd_success_action_processed();
@@ -1421,7 +1363,7 @@ final class wire_cst_prepare_buy_bitcoin_response extends ffi.Struct {
} }
final class wire_cst_buy_bitcoin_request extends ffi.Struct { final class wire_cst_buy_bitcoin_request extends ffi.Struct {
external wire_cst_prepare_buy_bitcoin_response prepare_res; external wire_cst_prepare_buy_bitcoin_response prepare_response;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> redirect_url; external ffi.Pointer<wire_cst_list_prim_u_8_strict> redirect_url;
} }
@@ -1529,7 +1471,7 @@ final class wire_cst_prepare_pay_onchain_response extends ffi.Struct {
final class wire_cst_pay_onchain_request extends ffi.Struct { final class wire_cst_pay_onchain_request extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> address; external ffi.Pointer<wire_cst_list_prim_u_8_strict> address;
external wire_cst_prepare_pay_onchain_response prepare_res; external wire_cst_prepare_pay_onchain_response prepare_response;
} }
final class wire_cst_prepare_buy_bitcoin_request extends ffi.Struct { final class wire_cst_prepare_buy_bitcoin_request extends ffi.Struct {
@@ -1547,14 +1489,11 @@ final class wire_cst_prepare_pay_onchain_request extends ffi.Struct {
external ffi.Pointer<ffi.Uint32> sat_per_vbyte; external ffi.Pointer<ffi.Uint32> sat_per_vbyte;
} }
final class wire_cst_prepare_receive_onchain_request extends ffi.Struct { final class wire_cst_prepare_receive_request extends ffi.Struct {
@ffi.Uint64() external ffi.Pointer<ffi.Uint64> payer_amount_sat;
external int payer_amount_sat;
}
final class wire_cst_prepare_receive_payment_request extends ffi.Struct { @ffi.Int32()
@ffi.Uint64() external int payment_method;
external int payer_amount_sat;
} }
final class wire_cst_prepare_refund_request extends ffi.Struct { final class wire_cst_prepare_refund_request extends ffi.Struct {
@@ -1567,20 +1506,16 @@ final class wire_cst_prepare_refund_request extends ffi.Struct {
} }
final class wire_cst_prepare_send_request extends ffi.Struct { final class wire_cst_prepare_send_request extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> invoice; external ffi.Pointer<wire_cst_list_prim_u_8_strict> destination;
external ffi.Pointer<ffi.Uint64> amount_sat;
} }
final class wire_cst_prepare_receive_onchain_response extends ffi.Struct { final class wire_cst_prepare_receive_response extends ffi.Struct {
@ffi.Uint64() @ffi.Int32()
external int payer_amount_sat; external int payment_method;
@ffi.Uint64() external ffi.Pointer<ffi.Uint64> payer_amount_sat;
external int fees_sat;
}
final class wire_cst_prepare_receive_payment_response extends ffi.Struct {
@ffi.Uint64()
external int payer_amount_sat;
@ffi.Uint64() @ffi.Uint64()
external int fees_sat; external int fees_sat;
@@ -1589,7 +1524,7 @@ final class wire_cst_prepare_receive_payment_response extends ffi.Struct {
final class wire_cst_receive_payment_request extends ffi.Struct { final class wire_cst_receive_payment_request extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> description; external ffi.Pointer<wire_cst_list_prim_u_8_strict> description;
external wire_cst_prepare_receive_payment_response prepare_res; external wire_cst_prepare_receive_response prepare_response;
} }
final class wire_cst_refund_request extends ffi.Struct { final class wire_cst_refund_request extends ffi.Struct {
@@ -1605,22 +1540,175 @@ final class wire_cst_restore_request extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> backup_path; external ffi.Pointer<wire_cst_list_prim_u_8_strict> backup_path;
} }
final class wire_cst_liquid_address_data extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> address;
@ffi.Int32()
external int network;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> asset_id;
external ffi.Pointer<ffi.Uint64> amount_sat;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> label;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> message;
}
final class wire_cst_SendDestination_LiquidAddress extends ffi.Struct {
external ffi.Pointer<wire_cst_liquid_address_data> address_data;
}
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_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_SendDestination_Bolt11 extends ffi.Struct {
external ffi.Pointer<wire_cst_ln_invoice> invoice;
}
final class SendDestinationKind extends ffi.Union {
external wire_cst_SendDestination_LiquidAddress LiquidAddress;
external wire_cst_SendDestination_Bolt11 Bolt11;
}
final class wire_cst_send_destination extends ffi.Struct {
@ffi.Int32()
external int tag;
external SendDestinationKind kind;
}
final class wire_cst_prepare_send_response extends ffi.Struct { final class wire_cst_prepare_send_response extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> invoice; external wire_cst_send_destination destination;
@ffi.Uint64() @ffi.Uint64()
external int fees_sat; external int fees_sat;
} }
final class wire_cst_send_payment_request extends ffi.Struct {
external wire_cst_prepare_send_response prepare_response;
}
final class wire_cst_binding_event_listener extends ffi.Struct { final class wire_cst_binding_event_listener extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> stream; external ffi.Pointer<wire_cst_list_prim_u_8_strict> stream;
} }
final class wire_cst_payment extends ffi.Struct { final class wire_cst_PaymentDetails_Lightning extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> tx_id;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> swap_id; external ffi.Pointer<wire_cst_list_prim_u_8_strict> swap_id;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> description;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> preimage;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> bolt11;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> refund_tx_id;
external ffi.Pointer<ffi.Uint64> refund_tx_amount_sat;
}
final class wire_cst_PaymentDetails_Liquid extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> destination;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> description;
}
final class wire_cst_PaymentDetails_Bitcoin extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> swap_id;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> description;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> refund_tx_id;
external ffi.Pointer<ffi.Uint64> refund_tx_amount_sat;
}
final class PaymentDetailsKind extends ffi.Union {
external wire_cst_PaymentDetails_Lightning Lightning;
external wire_cst_PaymentDetails_Liquid Liquid;
external wire_cst_PaymentDetails_Bitcoin Bitcoin;
}
final class wire_cst_payment_details extends ffi.Struct {
@ffi.Int32()
external int tag;
external PaymentDetailsKind kind;
}
final class wire_cst_payment extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> destination;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> tx_id;
@ffi.Uint32() @ffi.Uint32()
external int timestamp; external int timestamp;
@@ -1630,21 +1718,13 @@ final class wire_cst_payment extends ffi.Struct {
@ffi.Uint64() @ffi.Uint64()
external int fees_sat; external int fees_sat;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> preimage;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> bolt11;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> description;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> refund_tx_id;
external ffi.Pointer<ffi.Uint64> refund_tx_amount_sat;
@ffi.Int32() @ffi.Int32()
external int payment_type; external int payment_type;
@ffi.Int32() @ffi.Int32()
external int status; external int status;
external ffi.Pointer<wire_cst_payment_details> details;
} }
final class wire_cst_SdkEvent_PaymentFailed extends ffi.Struct { final class wire_cst_SdkEvent_PaymentFailed extends ffi.Struct {
@@ -1759,89 +1839,6 @@ final class wire_cst_bitcoin_address_data extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> message; external ffi.Pointer<wire_cst_list_prim_u_8_strict> message;
} }
final class wire_cst_liquid_address_data extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> address;
@ffi.Int32()
external int network;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> asset_id;
external ffi.Pointer<ffi.Uint64> amount_sat;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> label;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> message;
}
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_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_ln_url_error_data extends ffi.Struct { final class wire_cst_ln_url_error_data extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> reason; external ffi.Pointer<wire_cst_list_prim_u_8_strict> reason;
} }
@@ -2329,6 +2326,14 @@ final class wire_cst_onchain_payment_limits_response extends ffi.Struct {
external wire_cst_limits receive; external wire_cst_limits receive;
} }
final class wire_cst_PaymentError_AmountMissing extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> err;
}
final class wire_cst_PaymentError_InvalidNetwork extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> err;
}
final class wire_cst_PaymentError_Generic extends ffi.Struct { final class wire_cst_PaymentError_Generic extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> err; external ffi.Pointer<wire_cst_list_prim_u_8_strict> err;
} }
@@ -2360,6 +2365,10 @@ final class wire_cst_PaymentError_SignerError extends ffi.Struct {
} }
final class PaymentErrorKind extends ffi.Union { final class PaymentErrorKind extends ffi.Union {
external wire_cst_PaymentError_AmountMissing AmountMissing;
external wire_cst_PaymentError_InvalidNetwork InvalidNetwork;
external wire_cst_PaymentError_Generic Generic; external wire_cst_PaymentError_Generic Generic;
external wire_cst_PaymentError_InvalidInvoice InvalidInvoice; external wire_cst_PaymentError_InvalidInvoice InvalidInvoice;
@@ -2392,16 +2401,8 @@ final class wire_cst_prepare_refund_response extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> refund_tx_id; external ffi.Pointer<wire_cst_list_prim_u_8_strict> refund_tx_id;
} }
final class wire_cst_receive_onchain_response extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> address;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> bip21;
}
final class wire_cst_receive_payment_response extends ffi.Struct { final class wire_cst_receive_payment_response extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> id; external ffi.Pointer<wire_cst_list_prim_u_8_strict> destination;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> invoice;
} }
final class wire_cst_recommended_fees extends ffi.Struct { final class wire_cst_recommended_fees extends ffi.Struct {

View File

@@ -118,23 +118,23 @@ fun asBuyBitcoinRequest(buyBitcoinRequest: ReadableMap): BuyBitcoinRequest? {
if (!validateMandatoryFields( if (!validateMandatoryFields(
buyBitcoinRequest, buyBitcoinRequest,
arrayOf( arrayOf(
"prepareRes", "prepareResponse",
), ),
) )
) { ) {
return null return null
} }
val prepareRes = buyBitcoinRequest.getMap("prepareRes")?.let { asPrepareBuyBitcoinResponse(it) }!! val prepareResponse = buyBitcoinRequest.getMap("prepareResponse")?.let { asPrepareBuyBitcoinResponse(it) }!!
val redirectUrl = if (hasNonNullKey(buyBitcoinRequest, "redirectUrl")) buyBitcoinRequest.getString("redirectUrl") else null val redirectUrl = if (hasNonNullKey(buyBitcoinRequest, "redirectUrl")) buyBitcoinRequest.getString("redirectUrl") else null
return BuyBitcoinRequest( return BuyBitcoinRequest(
prepareRes, prepareResponse,
redirectUrl, redirectUrl,
) )
} }
fun readableMapOf(buyBitcoinRequest: BuyBitcoinRequest): ReadableMap = fun readableMapOf(buyBitcoinRequest: BuyBitcoinRequest): ReadableMap =
readableMapOf( readableMapOf(
"prepareRes" to readableMapOf(buyBitcoinRequest.prepareRes), "prepareResponse" to readableMapOf(buyBitcoinRequest.prepareResponse),
"redirectUrl" to buyBitcoinRequest.redirectUrl, "redirectUrl" to buyBitcoinRequest.redirectUrl,
) )
@@ -1213,24 +1213,24 @@ fun asPayOnchainRequest(payOnchainRequest: ReadableMap): PayOnchainRequest? {
payOnchainRequest, payOnchainRequest,
arrayOf( arrayOf(
"address", "address",
"prepareRes", "prepareResponse",
), ),
) )
) { ) {
return null return null
} }
val address = payOnchainRequest.getString("address")!! val address = payOnchainRequest.getString("address")!!
val prepareRes = payOnchainRequest.getMap("prepareRes")?.let { asPreparePayOnchainResponse(it) }!! val prepareResponse = payOnchainRequest.getMap("prepareResponse")?.let { asPreparePayOnchainResponse(it) }!!
return PayOnchainRequest( return PayOnchainRequest(
address, address,
prepareRes, prepareResponse,
) )
} }
fun readableMapOf(payOnchainRequest: PayOnchainRequest): ReadableMap = fun readableMapOf(payOnchainRequest: PayOnchainRequest): ReadableMap =
readableMapOf( readableMapOf(
"address" to payOnchainRequest.address, "address" to payOnchainRequest.address,
"prepareRes" to readableMapOf(payOnchainRequest.prepareRes), "prepareResponse" to readableMapOf(payOnchainRequest.prepareResponse),
) )
fun asPayOnchainRequestList(arr: ReadableArray): List<PayOnchainRequest> { fun asPayOnchainRequestList(arr: ReadableArray): List<PayOnchainRequest> {
@@ -1253,54 +1253,41 @@ fun asPayment(payment: ReadableMap): Payment? {
"feesSat", "feesSat",
"paymentType", "paymentType",
"status", "status",
"description",
), ),
) )
) { ) {
return null return null
} }
val destination = if (hasNonNullKey(payment, "destination")) payment.getString("destination") else null
val txId = if (hasNonNullKey(payment, "txId")) payment.getString("txId") else null
val timestamp = payment.getInt("timestamp").toUInt() val timestamp = payment.getInt("timestamp").toUInt()
val amountSat = payment.getDouble("amountSat").toULong() val amountSat = payment.getDouble("amountSat").toULong()
val feesSat = payment.getDouble("feesSat").toULong() val feesSat = payment.getDouble("feesSat").toULong()
val paymentType = payment.getString("paymentType")?.let { asPaymentType(it) }!! val paymentType = payment.getString("paymentType")?.let { asPaymentType(it) }!!
val status = payment.getString("status")?.let { asPaymentState(it) }!! val status = payment.getString("status")?.let { asPaymentState(it) }!!
val description = payment.getString("description")!! val details = if (hasNonNullKey(payment, "details")) payment.getMap("details")?.let { asPaymentDetails(it) } else null
val txId = if (hasNonNullKey(payment, "txId")) payment.getString("txId") else null
val swapId = if (hasNonNullKey(payment, "swapId")) payment.getString("swapId") else null
val preimage = if (hasNonNullKey(payment, "preimage")) payment.getString("preimage") else null
val bolt11 = if (hasNonNullKey(payment, "bolt11")) payment.getString("bolt11") else null
val refundTxId = if (hasNonNullKey(payment, "refundTxId")) payment.getString("refundTxId") else null
val refundTxAmountSat = if (hasNonNullKey(payment, "refundTxAmountSat")) payment.getDouble("refundTxAmountSat").toULong() else null
return Payment( return Payment(
destination,
txId,
timestamp, timestamp,
amountSat, amountSat,
feesSat, feesSat,
paymentType, paymentType,
status, status,
description, details,
txId,
swapId,
preimage,
bolt11,
refundTxId,
refundTxAmountSat,
) )
} }
fun readableMapOf(payment: Payment): ReadableMap = fun readableMapOf(payment: Payment): ReadableMap =
readableMapOf( readableMapOf(
"destination" to payment.destination,
"txId" to payment.txId,
"timestamp" to payment.timestamp, "timestamp" to payment.timestamp,
"amountSat" to payment.amountSat, "amountSat" to payment.amountSat,
"feesSat" to payment.feesSat, "feesSat" to payment.feesSat,
"paymentType" to payment.paymentType.name.lowercase(), "paymentType" to payment.paymentType.name.lowercase(),
"status" to payment.status.name.lowercase(), "status" to payment.status.name.lowercase(),
"description" to payment.description, "details" to payment.details?.let { readableMapOf(it) },
"txId" to payment.txId,
"swapId" to payment.swapId,
"preimage" to payment.preimage,
"bolt11" to payment.bolt11,
"refundTxId" to payment.refundTxId,
"refundTxAmountSat" to payment.refundTxAmountSat,
) )
fun asPaymentList(arr: ReadableArray): List<Payment> { fun asPaymentList(arr: ReadableArray): List<Payment> {
@@ -1474,136 +1461,92 @@ fun asPreparePayOnchainResponseList(arr: ReadableArray): List<PreparePayOnchainR
return list return list
} }
fun asPrepareReceiveOnchainRequest(prepareReceiveOnchainRequest: ReadableMap): PrepareReceiveOnchainRequest? { fun asPrepareReceiveRequest(prepareReceiveRequest: ReadableMap): PrepareReceiveRequest? {
if (!validateMandatoryFields( if (!validateMandatoryFields(
prepareReceiveOnchainRequest, prepareReceiveRequest,
arrayOf( arrayOf(
"payerAmountSat", "paymentMethod",
), ),
) )
) { ) {
return null return null
} }
val payerAmountSat = prepareReceiveOnchainRequest.getDouble("payerAmountSat").toULong() val payerAmountSat =
return PrepareReceiveOnchainRequest( if (hasNonNullKey(
prepareReceiveRequest,
"payerAmountSat",
)
) {
prepareReceiveRequest.getDouble("payerAmountSat").toULong()
} else {
null
}
val paymentMethod = prepareReceiveRequest.getString("paymentMethod")?.let { asPaymentMethod(it) }!!
return PrepareReceiveRequest(
payerAmountSat, payerAmountSat,
paymentMethod,
) )
} }
fun readableMapOf(prepareReceiveOnchainRequest: PrepareReceiveOnchainRequest): ReadableMap = fun readableMapOf(prepareReceiveRequest: PrepareReceiveRequest): ReadableMap =
readableMapOf( readableMapOf(
"payerAmountSat" to prepareReceiveOnchainRequest.payerAmountSat, "payerAmountSat" to prepareReceiveRequest.payerAmountSat,
"paymentMethod" to prepareReceiveRequest.paymentMethod.name.lowercase(),
) )
fun asPrepareReceiveOnchainRequestList(arr: ReadableArray): List<PrepareReceiveOnchainRequest> { fun asPrepareReceiveRequestList(arr: ReadableArray): List<PrepareReceiveRequest> {
val list = ArrayList<PrepareReceiveOnchainRequest>() val list = ArrayList<PrepareReceiveRequest>()
for (value in arr.toArrayList()) { for (value in arr.toArrayList()) {
when (value) { when (value) {
is ReadableMap -> list.add(asPrepareReceiveOnchainRequest(value)!!) is ReadableMap -> list.add(asPrepareReceiveRequest(value)!!)
else -> throw SdkException.Generic(errUnexpectedType("${value::class.java.name}")) else -> throw SdkException.Generic(errUnexpectedType("${value::class.java.name}"))
} }
} }
return list return list
} }
fun asPrepareReceiveOnchainResponse(prepareReceiveOnchainResponse: ReadableMap): PrepareReceiveOnchainResponse? { fun asPrepareReceiveResponse(prepareReceiveResponse: ReadableMap): PrepareReceiveResponse? {
if (!validateMandatoryFields( if (!validateMandatoryFields(
prepareReceiveOnchainResponse, prepareReceiveResponse,
arrayOf( arrayOf(
"payerAmountSat", "paymentMethod",
"feesSat", "feesSat",
), ),
) )
) { ) {
return null return null
} }
val payerAmountSat = prepareReceiveOnchainResponse.getDouble("payerAmountSat").toULong() val payerAmountSat =
val feesSat = prepareReceiveOnchainResponse.getDouble("feesSat").toULong() if (hasNonNullKey(
return PrepareReceiveOnchainResponse( prepareReceiveResponse,
"payerAmountSat",
)
) {
prepareReceiveResponse.getDouble("payerAmountSat").toULong()
} else {
null
}
val paymentMethod = prepareReceiveResponse.getString("paymentMethod")?.let { asPaymentMethod(it) }!!
val feesSat = prepareReceiveResponse.getDouble("feesSat").toULong()
return PrepareReceiveResponse(
payerAmountSat, payerAmountSat,
paymentMethod,
feesSat, feesSat,
) )
} }
fun readableMapOf(prepareReceiveOnchainResponse: PrepareReceiveOnchainResponse): ReadableMap = fun readableMapOf(prepareReceiveResponse: PrepareReceiveResponse): ReadableMap =
readableMapOf( readableMapOf(
"payerAmountSat" to prepareReceiveOnchainResponse.payerAmountSat, "payerAmountSat" to prepareReceiveResponse.payerAmountSat,
"feesSat" to prepareReceiveOnchainResponse.feesSat, "paymentMethod" to prepareReceiveResponse.paymentMethod.name.lowercase(),
"feesSat" to prepareReceiveResponse.feesSat,
) )
fun asPrepareReceiveOnchainResponseList(arr: ReadableArray): List<PrepareReceiveOnchainResponse> { fun asPrepareReceiveResponseList(arr: ReadableArray): List<PrepareReceiveResponse> {
val list = ArrayList<PrepareReceiveOnchainResponse>() val list = ArrayList<PrepareReceiveResponse>()
for (value in arr.toArrayList()) { for (value in arr.toArrayList()) {
when (value) { when (value) {
is ReadableMap -> list.add(asPrepareReceiveOnchainResponse(value)!!) is ReadableMap -> list.add(asPrepareReceiveResponse(value)!!)
else -> throw SdkException.Generic(errUnexpectedType("${value::class.java.name}"))
}
}
return list
}
fun asPrepareReceivePaymentRequest(prepareReceivePaymentRequest: ReadableMap): PrepareReceivePaymentRequest? {
if (!validateMandatoryFields(
prepareReceivePaymentRequest,
arrayOf(
"payerAmountSat",
),
)
) {
return null
}
val payerAmountSat = prepareReceivePaymentRequest.getDouble("payerAmountSat").toULong()
return PrepareReceivePaymentRequest(
payerAmountSat,
)
}
fun readableMapOf(prepareReceivePaymentRequest: PrepareReceivePaymentRequest): ReadableMap =
readableMapOf(
"payerAmountSat" to prepareReceivePaymentRequest.payerAmountSat,
)
fun asPrepareReceivePaymentRequestList(arr: ReadableArray): List<PrepareReceivePaymentRequest> {
val list = ArrayList<PrepareReceivePaymentRequest>()
for (value in arr.toArrayList()) {
when (value) {
is ReadableMap -> list.add(asPrepareReceivePaymentRequest(value)!!)
else -> throw SdkException.Generic(errUnexpectedType("${value::class.java.name}"))
}
}
return list
}
fun asPrepareReceivePaymentResponse(prepareReceivePaymentResponse: ReadableMap): PrepareReceivePaymentResponse? {
if (!validateMandatoryFields(
prepareReceivePaymentResponse,
arrayOf(
"payerAmountSat",
"feesSat",
),
)
) {
return null
}
val payerAmountSat = prepareReceivePaymentResponse.getDouble("payerAmountSat").toULong()
val feesSat = prepareReceivePaymentResponse.getDouble("feesSat").toULong()
return PrepareReceivePaymentResponse(
payerAmountSat,
feesSat,
)
}
fun readableMapOf(prepareReceivePaymentResponse: PrepareReceivePaymentResponse): ReadableMap =
readableMapOf(
"payerAmountSat" to prepareReceivePaymentResponse.payerAmountSat,
"feesSat" to prepareReceivePaymentResponse.feesSat,
)
fun asPrepareReceivePaymentResponseList(arr: ReadableArray): List<PrepareReceivePaymentResponse> {
val list = ArrayList<PrepareReceivePaymentResponse>()
for (value in arr.toArrayList()) {
when (value) {
is ReadableMap -> list.add(asPrepareReceivePaymentResponse(value)!!)
else -> throw SdkException.Generic(errUnexpectedType("${value::class.java.name}")) else -> throw SdkException.Generic(errUnexpectedType("${value::class.java.name}"))
} }
} }
@@ -1693,21 +1636,24 @@ fun asPrepareSendRequest(prepareSendRequest: ReadableMap): PrepareSendRequest? {
if (!validateMandatoryFields( if (!validateMandatoryFields(
prepareSendRequest, prepareSendRequest,
arrayOf( arrayOf(
"invoice", "destination",
), ),
) )
) { ) {
return null return null
} }
val invoice = prepareSendRequest.getString("invoice")!! val destination = prepareSendRequest.getString("destination")!!
val amountSat = if (hasNonNullKey(prepareSendRequest, "amountSat")) prepareSendRequest.getDouble("amountSat").toULong() else null
return PrepareSendRequest( return PrepareSendRequest(
invoice, destination,
amountSat,
) )
} }
fun readableMapOf(prepareSendRequest: PrepareSendRequest): ReadableMap = fun readableMapOf(prepareSendRequest: PrepareSendRequest): ReadableMap =
readableMapOf( readableMapOf(
"invoice" to prepareSendRequest.invoice, "destination" to prepareSendRequest.destination,
"amountSat" to prepareSendRequest.amountSat,
) )
fun asPrepareSendRequestList(arr: ReadableArray): List<PrepareSendRequest> { fun asPrepareSendRequestList(arr: ReadableArray): List<PrepareSendRequest> {
@@ -1725,24 +1671,24 @@ fun asPrepareSendResponse(prepareSendResponse: ReadableMap): PrepareSendResponse
if (!validateMandatoryFields( if (!validateMandatoryFields(
prepareSendResponse, prepareSendResponse,
arrayOf( arrayOf(
"invoice", "destination",
"feesSat", "feesSat",
), ),
) )
) { ) {
return null return null
} }
val invoice = prepareSendResponse.getString("invoice")!! val destination = prepareSendResponse.getMap("destination")?.let { asSendDestination(it) }!!
val feesSat = prepareSendResponse.getDouble("feesSat").toULong() val feesSat = prepareSendResponse.getDouble("feesSat").toULong()
return PrepareSendResponse( return PrepareSendResponse(
invoice, destination,
feesSat, feesSat,
) )
} }
fun readableMapOf(prepareSendResponse: PrepareSendResponse): ReadableMap = fun readableMapOf(prepareSendResponse: PrepareSendResponse): ReadableMap =
readableMapOf( readableMapOf(
"invoice" to prepareSendResponse.invoice, "destination" to readableMapOf(prepareSendResponse.destination),
"feesSat" to prepareSendResponse.feesSat, "feesSat" to prepareSendResponse.feesSat,
) )
@@ -1793,63 +1739,27 @@ fun asRateList(arr: ReadableArray): List<Rate> {
return list return list
} }
fun asReceiveOnchainResponse(receiveOnchainResponse: ReadableMap): ReceiveOnchainResponse? {
if (!validateMandatoryFields(
receiveOnchainResponse,
arrayOf(
"address",
"bip21",
),
)
) {
return null
}
val address = receiveOnchainResponse.getString("address")!!
val bip21 = receiveOnchainResponse.getString("bip21")!!
return ReceiveOnchainResponse(
address,
bip21,
)
}
fun readableMapOf(receiveOnchainResponse: ReceiveOnchainResponse): ReadableMap =
readableMapOf(
"address" to receiveOnchainResponse.address,
"bip21" to receiveOnchainResponse.bip21,
)
fun asReceiveOnchainResponseList(arr: ReadableArray): List<ReceiveOnchainResponse> {
val list = ArrayList<ReceiveOnchainResponse>()
for (value in arr.toArrayList()) {
when (value) {
is ReadableMap -> list.add(asReceiveOnchainResponse(value)!!)
else -> throw SdkException.Generic(errUnexpectedType("${value::class.java.name}"))
}
}
return list
}
fun asReceivePaymentRequest(receivePaymentRequest: ReadableMap): ReceivePaymentRequest? { fun asReceivePaymentRequest(receivePaymentRequest: ReadableMap): ReceivePaymentRequest? {
if (!validateMandatoryFields( if (!validateMandatoryFields(
receivePaymentRequest, receivePaymentRequest,
arrayOf( arrayOf(
"prepareRes", "prepareResponse",
), ),
) )
) { ) {
return null return null
} }
val prepareRes = receivePaymentRequest.getMap("prepareRes")?.let { asPrepareReceivePaymentResponse(it) }!! val prepareResponse = receivePaymentRequest.getMap("prepareResponse")?.let { asPrepareReceiveResponse(it) }!!
val description = if (hasNonNullKey(receivePaymentRequest, "description")) receivePaymentRequest.getString("description") else null val description = if (hasNonNullKey(receivePaymentRequest, "description")) receivePaymentRequest.getString("description") else null
return ReceivePaymentRequest( return ReceivePaymentRequest(
prepareRes, prepareResponse,
description, description,
) )
} }
fun readableMapOf(receivePaymentRequest: ReceivePaymentRequest): ReadableMap = fun readableMapOf(receivePaymentRequest: ReceivePaymentRequest): ReadableMap =
readableMapOf( readableMapOf(
"prepareRes" to readableMapOf(receivePaymentRequest.prepareRes), "prepareResponse" to readableMapOf(receivePaymentRequest.prepareResponse),
"description" to receivePaymentRequest.description, "description" to receivePaymentRequest.description,
) )
@@ -1868,25 +1778,21 @@ fun asReceivePaymentResponse(receivePaymentResponse: ReadableMap): ReceivePaymen
if (!validateMandatoryFields( if (!validateMandatoryFields(
receivePaymentResponse, receivePaymentResponse,
arrayOf( arrayOf(
"id", "destination",
"invoice",
), ),
) )
) { ) {
return null return null
} }
val id = receivePaymentResponse.getString("id")!! val destination = receivePaymentResponse.getString("destination")!!
val invoice = receivePaymentResponse.getString("invoice")!!
return ReceivePaymentResponse( return ReceivePaymentResponse(
id, destination,
invoice,
) )
} }
fun readableMapOf(receivePaymentResponse: ReceivePaymentResponse): ReadableMap = fun readableMapOf(receivePaymentResponse: ReceivePaymentResponse): ReadableMap =
readableMapOf( readableMapOf(
"id" to receivePaymentResponse.id, "destination" to receivePaymentResponse.destination,
"invoice" to receivePaymentResponse.invoice,
) )
fun asReceivePaymentResponseList(arr: ReadableArray): List<ReceivePaymentResponse> { fun asReceivePaymentResponseList(arr: ReadableArray): List<ReceivePaymentResponse> {
@@ -2176,6 +2082,38 @@ fun asRouteHintHopList(arr: ReadableArray): List<RouteHintHop> {
return list return list
} }
fun asSendPaymentRequest(sendPaymentRequest: ReadableMap): SendPaymentRequest? {
if (!validateMandatoryFields(
sendPaymentRequest,
arrayOf(
"prepareResponse",
),
)
) {
return null
}
val prepareResponse = sendPaymentRequest.getMap("prepareResponse")?.let { asPrepareSendResponse(it) }!!
return SendPaymentRequest(
prepareResponse,
)
}
fun readableMapOf(sendPaymentRequest: SendPaymentRequest): ReadableMap =
readableMapOf(
"prepareResponse" to readableMapOf(sendPaymentRequest.prepareResponse),
)
fun asSendPaymentRequestList(arr: ReadableArray): List<SendPaymentRequest> {
val list = ArrayList<SendPaymentRequest>()
for (value in arr.toArrayList()) {
when (value) {
is ReadableMap -> list.add(asSendPaymentRequest(value)!!)
else -> throw SdkException.Generic(errUnexpectedType("${value::class.java.name}"))
}
}
return list
}
fun asSendPaymentResponse(sendPaymentResponse: ReadableMap): SendPaymentResponse? { fun asSendPaymentResponse(sendPaymentResponse: ReadableMap): SendPaymentResponse? {
if (!validateMandatoryFields( if (!validateMandatoryFields(
sendPaymentResponse, sendPaymentResponse,
@@ -2580,6 +2518,73 @@ fun asNetworkList(arr: ReadableArray): List<Network> {
return list return list
} }
fun asPaymentDetails(paymentDetails: ReadableMap): PaymentDetails? {
val type = paymentDetails.getString("type")
if (type == "lightning") {
return PaymentDetails.Lightning(paymentDetails.getString("swapId")!!)
}
if (type == "liquid") {
return PaymentDetails.Liquid(paymentDetails.getString("destination")!!)
}
if (type == "bitcoin") {
return PaymentDetails.Bitcoin(paymentDetails.getString("swapId")!!)
}
return null
}
fun readableMapOf(paymentDetails: PaymentDetails): ReadableMap? {
val map = Arguments.createMap()
when (paymentDetails) {
is PaymentDetails.Lightning -> {
pushToMap(map, "type", "lightning")
pushToMap(map, "swapId", paymentDetails.swapId)
pushToMap(map, "description", paymentDetails.description)
pushToMap(map, "preimage", paymentDetails.preimage)
pushToMap(map, "bolt11", paymentDetails.bolt11)
pushToMap(map, "refundTxId", paymentDetails.refundTxId)
pushToMap(map, "refundTxAmountSat", paymentDetails.refundTxAmountSat)
}
is PaymentDetails.Liquid -> {
pushToMap(map, "type", "liquid")
pushToMap(map, "destination", paymentDetails.destination)
pushToMap(map, "description", paymentDetails.description)
}
is PaymentDetails.Bitcoin -> {
pushToMap(map, "type", "bitcoin")
pushToMap(map, "swapId", paymentDetails.swapId)
pushToMap(map, "description", paymentDetails.description)
pushToMap(map, "refundTxId", paymentDetails.refundTxId)
pushToMap(map, "refundTxAmountSat", paymentDetails.refundTxAmountSat)
}
}
return map
}
fun asPaymentDetailsList(arr: ReadableArray): List<PaymentDetails> {
val list = ArrayList<PaymentDetails>()
for (value in arr.toArrayList()) {
when (value) {
is ReadableMap -> list.add(asPaymentDetails(value)!!)
else -> throw SdkException.Generic(errUnexpectedType("${value::class.java.name}"))
}
}
return list
}
fun asPaymentMethod(type: String): PaymentMethod = PaymentMethod.valueOf(camelToUpperSnakeCase(type))
fun asPaymentMethodList(arr: ReadableArray): List<PaymentMethod> {
val list = ArrayList<PaymentMethod>()
for (value in arr.toArrayList()) {
when (value) {
is String -> list.add(asPaymentMethod(value)!!)
else -> throw SdkException.Generic(errUnexpectedType("${value::class.java.name}"))
}
}
return list
}
fun asPaymentState(type: String): PaymentState = PaymentState.valueOf(camelToUpperSnakeCase(type)) fun asPaymentState(type: String): PaymentState = PaymentState.valueOf(camelToUpperSnakeCase(type))
fun asPaymentStateList(arr: ReadableArray): List<PaymentState> { fun asPaymentStateList(arr: ReadableArray): List<PaymentState> {
@@ -2678,6 +2683,44 @@ fun asSdkEventList(arr: ReadableArray): List<SdkEvent> {
return list return list
} }
fun asSendDestination(sendDestination: ReadableMap): SendDestination? {
val type = sendDestination.getString("type")
if (type == "liquidAddress") {
return SendDestination.LiquidAddress(sendDestination.getMap("addressData")?.let { asLiquidAddressData(it) }!!)
}
if (type == "bolt11") {
return SendDestination.Bolt11(sendDestination.getMap("invoice")?.let { asLnInvoice(it) }!!)
}
return null
}
fun readableMapOf(sendDestination: SendDestination): ReadableMap? {
val map = Arguments.createMap()
when (sendDestination) {
is SendDestination.LiquidAddress -> {
pushToMap(map, "type", "liquidAddress")
pushToMap(map, "addressData", readableMapOf(sendDestination.addressData))
}
is SendDestination.Bolt11 -> {
pushToMap(map, "type", "bolt11")
pushToMap(map, "invoice", readableMapOf(sendDestination.invoice))
}
}
return map
}
fun asSendDestinationList(arr: ReadableArray): List<SendDestination> {
val list = ArrayList<SendDestination>()
for (value in arr.toArrayList()) {
when (value) {
is ReadableMap -> list.add(asSendDestination(value)!!)
else -> throw SdkException.Generic(errUnexpectedType("${value::class.java.name}"))
}
}
return list
}
fun asSuccessActionProcessed(successActionProcessed: ReadableMap): SuccessActionProcessed? { fun asSuccessActionProcessed(successActionProcessed: ReadableMap): SuccessActionProcessed? {
val type = successActionProcessed.getString("type") val type = successActionProcessed.getString("type")

View File

@@ -210,9 +210,9 @@ class BreezSDKLiquidModule(
) { ) {
executor.execute { executor.execute {
try { try {
val prepareSendResponse = val sendPaymentRequest =
asPrepareSendResponse(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "PrepareSendResponse")) } asSendPaymentRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "SendPaymentRequest")) }
val res = getBindingLiquidSdk().sendPayment(prepareSendResponse) val res = getBindingLiquidSdk().sendPayment(sendPaymentRequest)
promise.resolve(readableMapOf(res)) promise.resolve(readableMapOf(res))
} catch (e: Exception) { } catch (e: Exception) {
promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e)
@@ -227,10 +227,10 @@ class BreezSDKLiquidModule(
) { ) {
executor.execute { executor.execute {
try { try {
val prepareReceivePaymentRequest = val prepareReceiveRequest =
asPrepareReceivePaymentRequest(req) asPrepareReceiveRequest(req)
?: run { throw SdkException.Generic(errMissingMandatoryField("req", "PrepareReceivePaymentRequest")) } ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "PrepareReceiveRequest")) }
val res = getBindingLiquidSdk().prepareReceivePayment(prepareReceivePaymentRequest) val res = getBindingLiquidSdk().prepareReceivePayment(prepareReceiveRequest)
promise.resolve(readableMapOf(res)) promise.resolve(readableMapOf(res))
} catch (e: Exception) { } catch (e: Exception) {
promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e)
@@ -315,42 +315,6 @@ class BreezSDKLiquidModule(
} }
} }
@ReactMethod
fun prepareReceiveOnchain(
req: ReadableMap,
promise: Promise,
) {
executor.execute {
try {
val prepareReceiveOnchainRequest =
asPrepareReceiveOnchainRequest(req)
?: run { throw SdkException.Generic(errMissingMandatoryField("req", "PrepareReceiveOnchainRequest")) }
val res = getBindingLiquidSdk().prepareReceiveOnchain(prepareReceiveOnchainRequest)
promise.resolve(readableMapOf(res))
} catch (e: Exception) {
promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e)
}
}
}
@ReactMethod
fun receiveOnchain(
req: ReadableMap,
promise: Promise,
) {
executor.execute {
try {
val prepareReceiveOnchainResponse =
asPrepareReceiveOnchainResponse(req)
?: run { throw SdkException.Generic(errMissingMandatoryField("req", "PrepareReceiveOnchainResponse")) }
val res = getBindingLiquidSdk().receiveOnchain(prepareReceiveOnchainResponse)
promise.resolve(readableMapOf(res))
} catch (e: Exception) {
promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e)
}
}
}
@ReactMethod @ReactMethod
fun prepareBuyBitcoin( fun prepareBuyBitcoin(
req: ReadableMap, req: ReadableMap,

View File

@@ -144,10 +144,10 @@ enum BreezSDKLiquidMapper {
} }
static func asBuyBitcoinRequest(buyBitcoinRequest: [String: Any?]) throws -> BuyBitcoinRequest { static func asBuyBitcoinRequest(buyBitcoinRequest: [String: Any?]) throws -> BuyBitcoinRequest {
guard let prepareResTmp = buyBitcoinRequest["prepareRes"] as? [String: Any?] else { guard let prepareResponseTmp = buyBitcoinRequest["prepareResponse"] as? [String: Any?] else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "prepareRes", typeName: "BuyBitcoinRequest")) throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "prepareResponse", typeName: "BuyBitcoinRequest"))
} }
let prepareRes = try asPrepareBuyBitcoinResponse(prepareBuyBitcoinResponse: prepareResTmp) let prepareResponse = try asPrepareBuyBitcoinResponse(prepareBuyBitcoinResponse: prepareResponseTmp)
var redirectUrl: String? var redirectUrl: String?
if hasNonNilKey(data: buyBitcoinRequest, key: "redirectUrl") { if hasNonNilKey(data: buyBitcoinRequest, key: "redirectUrl") {
@@ -158,14 +158,14 @@ enum BreezSDKLiquidMapper {
} }
return BuyBitcoinRequest( return BuyBitcoinRequest(
prepareRes: prepareRes, prepareResponse: prepareResponse,
redirectUrl: redirectUrl redirectUrl: redirectUrl
) )
} }
static func dictionaryOf(buyBitcoinRequest: BuyBitcoinRequest) -> [String: Any?] { static func dictionaryOf(buyBitcoinRequest: BuyBitcoinRequest) -> [String: Any?] {
return [ return [
"prepareRes": dictionaryOf(prepareBuyBitcoinResponse: buyBitcoinRequest.prepareRes), "prepareResponse": dictionaryOf(prepareBuyBitcoinResponse: buyBitcoinRequest.prepareResponse),
"redirectUrl": buyBitcoinRequest.redirectUrl == nil ? nil : buyBitcoinRequest.redirectUrl, "redirectUrl": buyBitcoinRequest.redirectUrl == nil ? nil : buyBitcoinRequest.redirectUrl,
] ]
} }
@@ -1444,21 +1444,21 @@ enum BreezSDKLiquidMapper {
guard let address = payOnchainRequest["address"] as? String else { guard let address = payOnchainRequest["address"] as? String else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "address", typeName: "PayOnchainRequest")) throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "address", typeName: "PayOnchainRequest"))
} }
guard let prepareResTmp = payOnchainRequest["prepareRes"] as? [String: Any?] else { guard let prepareResponseTmp = payOnchainRequest["prepareResponse"] as? [String: Any?] else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "prepareRes", typeName: "PayOnchainRequest")) throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "prepareResponse", typeName: "PayOnchainRequest"))
} }
let prepareRes = try asPreparePayOnchainResponse(preparePayOnchainResponse: prepareResTmp) let prepareResponse = try asPreparePayOnchainResponse(preparePayOnchainResponse: prepareResponseTmp)
return PayOnchainRequest( return PayOnchainRequest(
address: address, address: address,
prepareRes: prepareRes prepareResponse: prepareResponse
) )
} }
static func dictionaryOf(payOnchainRequest: PayOnchainRequest) -> [String: Any?] { static func dictionaryOf(payOnchainRequest: PayOnchainRequest) -> [String: Any?] {
return [ return [
"address": payOnchainRequest.address, "address": payOnchainRequest.address,
"prepareRes": dictionaryOf(preparePayOnchainResponse: payOnchainRequest.prepareRes), "prepareResponse": dictionaryOf(preparePayOnchainResponse: payOnchainRequest.prepareResponse),
] ]
} }
@@ -1480,6 +1480,20 @@ enum BreezSDKLiquidMapper {
} }
static func asPayment(payment: [String: Any?]) throws -> Payment { static func asPayment(payment: [String: Any?]) throws -> Payment {
var destination: String?
if hasNonNilKey(data: payment, key: "destination") {
guard let destinationTmp = payment["destination"] as? String else {
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "destination"))
}
destination = destinationTmp
}
var txId: String?
if hasNonNilKey(data: payment, key: "txId") {
guard let txIdTmp = payment["txId"] as? String else {
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "txId"))
}
txId = txIdTmp
}
guard let timestamp = payment["timestamp"] as? UInt32 else { guard let timestamp = payment["timestamp"] as? UInt32 else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "timestamp", typeName: "Payment")) throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "timestamp", typeName: "Payment"))
} }
@@ -1499,82 +1513,33 @@ enum BreezSDKLiquidMapper {
} }
let status = try asPaymentState(paymentState: statusTmp) let status = try asPaymentState(paymentState: statusTmp)
guard let description = payment["description"] as? String else { var details: PaymentDetails?
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "description", typeName: "Payment")) if let detailsTmp = payment["details"] as? [String: Any?] {
} details = try asPaymentDetails(paymentDetails: detailsTmp)
var txId: String?
if hasNonNilKey(data: payment, key: "txId") {
guard let txIdTmp = payment["txId"] as? String else {
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "txId"))
}
txId = txIdTmp
}
var swapId: String?
if hasNonNilKey(data: payment, key: "swapId") {
guard let swapIdTmp = payment["swapId"] as? String else {
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "swapId"))
}
swapId = swapIdTmp
}
var preimage: String?
if hasNonNilKey(data: payment, key: "preimage") {
guard let preimageTmp = payment["preimage"] as? String else {
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "preimage"))
}
preimage = preimageTmp
}
var bolt11: String?
if hasNonNilKey(data: payment, key: "bolt11") {
guard let bolt11Tmp = payment["bolt11"] as? String else {
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "bolt11"))
}
bolt11 = bolt11Tmp
}
var refundTxId: String?
if hasNonNilKey(data: payment, key: "refundTxId") {
guard let refundTxIdTmp = payment["refundTxId"] as? String else {
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "refundTxId"))
}
refundTxId = refundTxIdTmp
}
var refundTxAmountSat: UInt64?
if hasNonNilKey(data: payment, key: "refundTxAmountSat") {
guard let refundTxAmountSatTmp = payment["refundTxAmountSat"] as? UInt64 else {
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "refundTxAmountSat"))
}
refundTxAmountSat = refundTxAmountSatTmp
} }
return Payment( return Payment(
destination: destination,
txId: txId,
timestamp: timestamp, timestamp: timestamp,
amountSat: amountSat, amountSat: amountSat,
feesSat: feesSat, feesSat: feesSat,
paymentType: paymentType, paymentType: paymentType,
status: status, status: status,
description: description, details: details
txId: txId,
swapId: swapId,
preimage: preimage,
bolt11: bolt11,
refundTxId: refundTxId,
refundTxAmountSat: refundTxAmountSat
) )
} }
static func dictionaryOf(payment: Payment) -> [String: Any?] { static func dictionaryOf(payment: Payment) -> [String: Any?] {
return [ return [
"destination": payment.destination == nil ? nil : payment.destination,
"txId": payment.txId == nil ? nil : payment.txId,
"timestamp": payment.timestamp, "timestamp": payment.timestamp,
"amountSat": payment.amountSat, "amountSat": payment.amountSat,
"feesSat": payment.feesSat, "feesSat": payment.feesSat,
"paymentType": valueOf(paymentType: payment.paymentType), "paymentType": valueOf(paymentType: payment.paymentType),
"status": valueOf(paymentState: payment.status), "status": valueOf(paymentState: payment.status),
"description": payment.description, "details": payment.details == nil ? nil : dictionaryOf(paymentDetails: payment.details!),
"txId": payment.txId == nil ? nil : payment.txId,
"swapId": payment.swapId == nil ? nil : payment.swapId,
"preimage": payment.preimage == nil ? nil : payment.preimage,
"bolt11": payment.bolt11 == nil ? nil : payment.bolt11,
"refundTxId": payment.refundTxId == nil ? nil : payment.refundTxId,
"refundTxAmountSat": payment.refundTxAmountSat == nil ? nil : payment.refundTxAmountSat,
] ]
} }
@@ -1765,144 +1730,96 @@ enum BreezSDKLiquidMapper {
return preparePayOnchainResponseList.map { v -> [String: Any?] in return dictionaryOf(preparePayOnchainResponse: v) } return preparePayOnchainResponseList.map { v -> [String: Any?] in return dictionaryOf(preparePayOnchainResponse: v) }
} }
static func asPrepareReceiveOnchainRequest(prepareReceiveOnchainRequest: [String: Any?]) throws -> PrepareReceiveOnchainRequest { static func asPrepareReceiveRequest(prepareReceiveRequest: [String: Any?]) throws -> PrepareReceiveRequest {
guard let payerAmountSat = prepareReceiveOnchainRequest["payerAmountSat"] as? UInt64 else { var payerAmountSat: UInt64?
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "payerAmountSat", typeName: "PrepareReceiveOnchainRequest")) if hasNonNilKey(data: prepareReceiveRequest, key: "payerAmountSat") {
guard let payerAmountSatTmp = prepareReceiveRequest["payerAmountSat"] as? UInt64 else {
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "payerAmountSat"))
}
payerAmountSat = payerAmountSatTmp
} }
guard let paymentMethodTmp = prepareReceiveRequest["paymentMethod"] as? String else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "paymentMethod", typeName: "PrepareReceiveRequest"))
}
let paymentMethod = try asPaymentMethod(paymentMethod: paymentMethodTmp)
return PrepareReceiveOnchainRequest( return PrepareReceiveRequest(
payerAmountSat: payerAmountSat) payerAmountSat: payerAmountSat,
paymentMethod: paymentMethod
)
} }
static func dictionaryOf(prepareReceiveOnchainRequest: PrepareReceiveOnchainRequest) -> [String: Any?] { static func dictionaryOf(prepareReceiveRequest: PrepareReceiveRequest) -> [String: Any?] {
return [ return [
"payerAmountSat": prepareReceiveOnchainRequest.payerAmountSat, "payerAmountSat": prepareReceiveRequest.payerAmountSat == nil ? nil : prepareReceiveRequest.payerAmountSat,
"paymentMethod": valueOf(paymentMethod: prepareReceiveRequest.paymentMethod),
] ]
} }
static func asPrepareReceiveOnchainRequestList(arr: [Any]) throws -> [PrepareReceiveOnchainRequest] { static func asPrepareReceiveRequestList(arr: [Any]) throws -> [PrepareReceiveRequest] {
var list = [PrepareReceiveOnchainRequest]() var list = [PrepareReceiveRequest]()
for value in arr { for value in arr {
if let val = value as? [String: Any?] { if let val = value as? [String: Any?] {
var prepareReceiveOnchainRequest = try asPrepareReceiveOnchainRequest(prepareReceiveOnchainRequest: val) var prepareReceiveRequest = try asPrepareReceiveRequest(prepareReceiveRequest: val)
list.append(prepareReceiveOnchainRequest) list.append(prepareReceiveRequest)
} else { } else {
throw SdkError.Generic(message: errUnexpectedType(typeName: "PrepareReceiveOnchainRequest")) throw SdkError.Generic(message: errUnexpectedType(typeName: "PrepareReceiveRequest"))
} }
} }
return list return list
} }
static func arrayOf(prepareReceiveOnchainRequestList: [PrepareReceiveOnchainRequest]) -> [Any] { static func arrayOf(prepareReceiveRequestList: [PrepareReceiveRequest]) -> [Any] {
return prepareReceiveOnchainRequestList.map { v -> [String: Any?] in return dictionaryOf(prepareReceiveOnchainRequest: v) } return prepareReceiveRequestList.map { v -> [String: Any?] in return dictionaryOf(prepareReceiveRequest: v) }
} }
static func asPrepareReceiveOnchainResponse(prepareReceiveOnchainResponse: [String: Any?]) throws -> PrepareReceiveOnchainResponse { static func asPrepareReceiveResponse(prepareReceiveResponse: [String: Any?]) throws -> PrepareReceiveResponse {
guard let payerAmountSat = prepareReceiveOnchainResponse["payerAmountSat"] as? UInt64 else { var payerAmountSat: UInt64?
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "payerAmountSat", typeName: "PrepareReceiveOnchainResponse")) if hasNonNilKey(data: prepareReceiveResponse, key: "payerAmountSat") {
guard let payerAmountSatTmp = prepareReceiveResponse["payerAmountSat"] as? UInt64 else {
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "payerAmountSat"))
}
payerAmountSat = payerAmountSatTmp
} }
guard let feesSat = prepareReceiveOnchainResponse["feesSat"] as? UInt64 else { guard let paymentMethodTmp = prepareReceiveResponse["paymentMethod"] as? String else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "feesSat", typeName: "PrepareReceiveOnchainResponse")) throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "paymentMethod", typeName: "PrepareReceiveResponse"))
}
let paymentMethod = try asPaymentMethod(paymentMethod: paymentMethodTmp)
guard let feesSat = prepareReceiveResponse["feesSat"] as? UInt64 else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "feesSat", typeName: "PrepareReceiveResponse"))
} }
return PrepareReceiveOnchainResponse( return PrepareReceiveResponse(
payerAmountSat: payerAmountSat, payerAmountSat: payerAmountSat,
paymentMethod: paymentMethod,
feesSat: feesSat feesSat: feesSat
) )
} }
static func dictionaryOf(prepareReceiveOnchainResponse: PrepareReceiveOnchainResponse) -> [String: Any?] { static func dictionaryOf(prepareReceiveResponse: PrepareReceiveResponse) -> [String: Any?] {
return [ return [
"payerAmountSat": prepareReceiveOnchainResponse.payerAmountSat, "payerAmountSat": prepareReceiveResponse.payerAmountSat == nil ? nil : prepareReceiveResponse.payerAmountSat,
"feesSat": prepareReceiveOnchainResponse.feesSat, "paymentMethod": valueOf(paymentMethod: prepareReceiveResponse.paymentMethod),
"feesSat": prepareReceiveResponse.feesSat,
] ]
} }
static func asPrepareReceiveOnchainResponseList(arr: [Any]) throws -> [PrepareReceiveOnchainResponse] { static func asPrepareReceiveResponseList(arr: [Any]) throws -> [PrepareReceiveResponse] {
var list = [PrepareReceiveOnchainResponse]() var list = [PrepareReceiveResponse]()
for value in arr { for value in arr {
if let val = value as? [String: Any?] { if let val = value as? [String: Any?] {
var prepareReceiveOnchainResponse = try asPrepareReceiveOnchainResponse(prepareReceiveOnchainResponse: val) var prepareReceiveResponse = try asPrepareReceiveResponse(prepareReceiveResponse: val)
list.append(prepareReceiveOnchainResponse) list.append(prepareReceiveResponse)
} else { } else {
throw SdkError.Generic(message: errUnexpectedType(typeName: "PrepareReceiveOnchainResponse")) throw SdkError.Generic(message: errUnexpectedType(typeName: "PrepareReceiveResponse"))
} }
} }
return list return list
} }
static func arrayOf(prepareReceiveOnchainResponseList: [PrepareReceiveOnchainResponse]) -> [Any] { static func arrayOf(prepareReceiveResponseList: [PrepareReceiveResponse]) -> [Any] {
return prepareReceiveOnchainResponseList.map { v -> [String: Any?] in return dictionaryOf(prepareReceiveOnchainResponse: v) } return prepareReceiveResponseList.map { v -> [String: Any?] in return dictionaryOf(prepareReceiveResponse: v) }
}
static func asPrepareReceivePaymentRequest(prepareReceivePaymentRequest: [String: Any?]) throws -> PrepareReceivePaymentRequest {
guard let payerAmountSat = prepareReceivePaymentRequest["payerAmountSat"] as? UInt64 else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "payerAmountSat", typeName: "PrepareReceivePaymentRequest"))
}
return PrepareReceivePaymentRequest(
payerAmountSat: payerAmountSat)
}
static func dictionaryOf(prepareReceivePaymentRequest: PrepareReceivePaymentRequest) -> [String: Any?] {
return [
"payerAmountSat": prepareReceivePaymentRequest.payerAmountSat,
]
}
static func asPrepareReceivePaymentRequestList(arr: [Any]) throws -> [PrepareReceivePaymentRequest] {
var list = [PrepareReceivePaymentRequest]()
for value in arr {
if let val = value as? [String: Any?] {
var prepareReceivePaymentRequest = try asPrepareReceivePaymentRequest(prepareReceivePaymentRequest: val)
list.append(prepareReceivePaymentRequest)
} else {
throw SdkError.Generic(message: errUnexpectedType(typeName: "PrepareReceivePaymentRequest"))
}
}
return list
}
static func arrayOf(prepareReceivePaymentRequestList: [PrepareReceivePaymentRequest]) -> [Any] {
return prepareReceivePaymentRequestList.map { v -> [String: Any?] in return dictionaryOf(prepareReceivePaymentRequest: v) }
}
static func asPrepareReceivePaymentResponse(prepareReceivePaymentResponse: [String: Any?]) throws -> PrepareReceivePaymentResponse {
guard let payerAmountSat = prepareReceivePaymentResponse["payerAmountSat"] as? UInt64 else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "payerAmountSat", typeName: "PrepareReceivePaymentResponse"))
}
guard let feesSat = prepareReceivePaymentResponse["feesSat"] as? UInt64 else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "feesSat", typeName: "PrepareReceivePaymentResponse"))
}
return PrepareReceivePaymentResponse(
payerAmountSat: payerAmountSat,
feesSat: feesSat
)
}
static func dictionaryOf(prepareReceivePaymentResponse: PrepareReceivePaymentResponse) -> [String: Any?] {
return [
"payerAmountSat": prepareReceivePaymentResponse.payerAmountSat,
"feesSat": prepareReceivePaymentResponse.feesSat,
]
}
static func asPrepareReceivePaymentResponseList(arr: [Any]) throws -> [PrepareReceivePaymentResponse] {
var list = [PrepareReceivePaymentResponse]()
for value in arr {
if let val = value as? [String: Any?] {
var prepareReceivePaymentResponse = try asPrepareReceivePaymentResponse(prepareReceivePaymentResponse: val)
list.append(prepareReceivePaymentResponse)
} else {
throw SdkError.Generic(message: errUnexpectedType(typeName: "PrepareReceivePaymentResponse"))
}
}
return list
}
static func arrayOf(prepareReceivePaymentResponseList: [PrepareReceivePaymentResponse]) -> [Any] {
return prepareReceivePaymentResponseList.map { v -> [String: Any?] in return dictionaryOf(prepareReceivePaymentResponse: v) }
} }
static func asPrepareRefundRequest(prepareRefundRequest: [String: Any?]) throws -> PrepareRefundRequest { static func asPrepareRefundRequest(prepareRefundRequest: [String: Any?]) throws -> PrepareRefundRequest {
@@ -1996,17 +1913,27 @@ enum BreezSDKLiquidMapper {
} }
static func asPrepareSendRequest(prepareSendRequest: [String: Any?]) throws -> PrepareSendRequest { static func asPrepareSendRequest(prepareSendRequest: [String: Any?]) throws -> PrepareSendRequest {
guard let invoice = prepareSendRequest["invoice"] as? String else { guard let destination = prepareSendRequest["destination"] as? String else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "invoice", typeName: "PrepareSendRequest")) throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "destination", typeName: "PrepareSendRequest"))
}
var amountSat: UInt64?
if hasNonNilKey(data: prepareSendRequest, key: "amountSat") {
guard let amountSatTmp = prepareSendRequest["amountSat"] as? UInt64 else {
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "amountSat"))
}
amountSat = amountSatTmp
} }
return PrepareSendRequest( return PrepareSendRequest(
invoice: invoice) destination: destination,
amountSat: amountSat
)
} }
static func dictionaryOf(prepareSendRequest: PrepareSendRequest) -> [String: Any?] { static func dictionaryOf(prepareSendRequest: PrepareSendRequest) -> [String: Any?] {
return [ return [
"invoice": prepareSendRequest.invoice, "destination": prepareSendRequest.destination,
"amountSat": prepareSendRequest.amountSat == nil ? nil : prepareSendRequest.amountSat,
] ]
} }
@@ -2028,22 +1955,24 @@ enum BreezSDKLiquidMapper {
} }
static func asPrepareSendResponse(prepareSendResponse: [String: Any?]) throws -> PrepareSendResponse { static func asPrepareSendResponse(prepareSendResponse: [String: Any?]) throws -> PrepareSendResponse {
guard let invoice = prepareSendResponse["invoice"] as? String else { guard let destinationTmp = prepareSendResponse["destination"] as? [String: Any?] else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "invoice", typeName: "PrepareSendResponse")) throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "destination", typeName: "PrepareSendResponse"))
} }
let destination = try asSendDestination(sendDestination: destinationTmp)
guard let feesSat = prepareSendResponse["feesSat"] as? UInt64 else { guard let feesSat = prepareSendResponse["feesSat"] as? UInt64 else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "feesSat", typeName: "PrepareSendResponse")) throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "feesSat", typeName: "PrepareSendResponse"))
} }
return PrepareSendResponse( return PrepareSendResponse(
invoice: invoice, destination: destination,
feesSat: feesSat feesSat: feesSat
) )
} }
static func dictionaryOf(prepareSendResponse: PrepareSendResponse) -> [String: Any?] { static func dictionaryOf(prepareSendResponse: PrepareSendResponse) -> [String: Any?] {
return [ return [
"invoice": prepareSendResponse.invoice, "destination": dictionaryOf(sendDestination: prepareSendResponse.destination),
"feesSat": prepareSendResponse.feesSat, "feesSat": prepareSendResponse.feesSat,
] ]
} }
@@ -2103,49 +2032,11 @@ enum BreezSDKLiquidMapper {
return rateList.map { v -> [String: Any?] in return dictionaryOf(rate: v) } return rateList.map { v -> [String: Any?] in return dictionaryOf(rate: v) }
} }
static func asReceiveOnchainResponse(receiveOnchainResponse: [String: Any?]) throws -> ReceiveOnchainResponse {
guard let address = receiveOnchainResponse["address"] as? String else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "address", typeName: "ReceiveOnchainResponse"))
}
guard let bip21 = receiveOnchainResponse["bip21"] as? String else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "bip21", typeName: "ReceiveOnchainResponse"))
}
return ReceiveOnchainResponse(
address: address,
bip21: bip21
)
}
static func dictionaryOf(receiveOnchainResponse: ReceiveOnchainResponse) -> [String: Any?] {
return [
"address": receiveOnchainResponse.address,
"bip21": receiveOnchainResponse.bip21,
]
}
static func asReceiveOnchainResponseList(arr: [Any]) throws -> [ReceiveOnchainResponse] {
var list = [ReceiveOnchainResponse]()
for value in arr {
if let val = value as? [String: Any?] {
var receiveOnchainResponse = try asReceiveOnchainResponse(receiveOnchainResponse: val)
list.append(receiveOnchainResponse)
} else {
throw SdkError.Generic(message: errUnexpectedType(typeName: "ReceiveOnchainResponse"))
}
}
return list
}
static func arrayOf(receiveOnchainResponseList: [ReceiveOnchainResponse]) -> [Any] {
return receiveOnchainResponseList.map { v -> [String: Any?] in return dictionaryOf(receiveOnchainResponse: v) }
}
static func asReceivePaymentRequest(receivePaymentRequest: [String: Any?]) throws -> ReceivePaymentRequest { static func asReceivePaymentRequest(receivePaymentRequest: [String: Any?]) throws -> ReceivePaymentRequest {
guard let prepareResTmp = receivePaymentRequest["prepareRes"] as? [String: Any?] else { guard let prepareResponseTmp = receivePaymentRequest["prepareResponse"] as? [String: Any?] else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "prepareRes", typeName: "ReceivePaymentRequest")) throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "prepareResponse", typeName: "ReceivePaymentRequest"))
} }
let prepareRes = try asPrepareReceivePaymentResponse(prepareReceivePaymentResponse: prepareResTmp) let prepareResponse = try asPrepareReceiveResponse(prepareReceiveResponse: prepareResponseTmp)
var description: String? var description: String?
if hasNonNilKey(data: receivePaymentRequest, key: "description") { if hasNonNilKey(data: receivePaymentRequest, key: "description") {
@@ -2156,14 +2047,14 @@ enum BreezSDKLiquidMapper {
} }
return ReceivePaymentRequest( return ReceivePaymentRequest(
prepareRes: prepareRes, prepareResponse: prepareResponse,
description: description description: description
) )
} }
static func dictionaryOf(receivePaymentRequest: ReceivePaymentRequest) -> [String: Any?] { static func dictionaryOf(receivePaymentRequest: ReceivePaymentRequest) -> [String: Any?] {
return [ return [
"prepareRes": dictionaryOf(prepareReceivePaymentResponse: receivePaymentRequest.prepareRes), "prepareResponse": dictionaryOf(prepareReceiveResponse: receivePaymentRequest.prepareResponse),
"description": receivePaymentRequest.description == nil ? nil : receivePaymentRequest.description, "description": receivePaymentRequest.description == nil ? nil : receivePaymentRequest.description,
] ]
} }
@@ -2186,23 +2077,17 @@ enum BreezSDKLiquidMapper {
} }
static func asReceivePaymentResponse(receivePaymentResponse: [String: Any?]) throws -> ReceivePaymentResponse { static func asReceivePaymentResponse(receivePaymentResponse: [String: Any?]) throws -> ReceivePaymentResponse {
guard let id = receivePaymentResponse["id"] as? String else { guard let destination = receivePaymentResponse["destination"] as? String else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "id", typeName: "ReceivePaymentResponse")) throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "destination", typeName: "ReceivePaymentResponse"))
}
guard let invoice = receivePaymentResponse["invoice"] as? String else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "invoice", typeName: "ReceivePaymentResponse"))
} }
return ReceivePaymentResponse( return ReceivePaymentResponse(
id: id, destination: destination)
invoice: invoice
)
} }
static func dictionaryOf(receivePaymentResponse: ReceivePaymentResponse) -> [String: Any?] { static func dictionaryOf(receivePaymentResponse: ReceivePaymentResponse) -> [String: Any?] {
return [ return [
"id": receivePaymentResponse.id, "destination": receivePaymentResponse.destination,
"invoice": receivePaymentResponse.invoice,
] ]
} }
@@ -2534,6 +2419,39 @@ enum BreezSDKLiquidMapper {
return routeHintHopList.map { v -> [String: Any?] in return dictionaryOf(routeHintHop: v) } return routeHintHopList.map { v -> [String: Any?] in return dictionaryOf(routeHintHop: v) }
} }
static func asSendPaymentRequest(sendPaymentRequest: [String: Any?]) throws -> SendPaymentRequest {
guard let prepareResponseTmp = sendPaymentRequest["prepareResponse"] as? [String: Any?] else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "prepareResponse", typeName: "SendPaymentRequest"))
}
let prepareResponse = try asPrepareSendResponse(prepareSendResponse: prepareResponseTmp)
return SendPaymentRequest(
prepareResponse: prepareResponse)
}
static func dictionaryOf(sendPaymentRequest: SendPaymentRequest) -> [String: Any?] {
return [
"prepareResponse": dictionaryOf(prepareSendResponse: sendPaymentRequest.prepareResponse),
]
}
static func asSendPaymentRequestList(arr: [Any]) throws -> [SendPaymentRequest] {
var list = [SendPaymentRequest]()
for value in arr {
if let val = value as? [String: Any?] {
var sendPaymentRequest = try asSendPaymentRequest(sendPaymentRequest: val)
list.append(sendPaymentRequest)
} else {
throw SdkError.Generic(message: errUnexpectedType(typeName: "SendPaymentRequest"))
}
}
return list
}
static func arrayOf(sendPaymentRequestList: [SendPaymentRequest]) -> [Any] {
return sendPaymentRequestList.map { v -> [String: Any?] in return dictionaryOf(sendPaymentRequest: v) }
}
static func asSendPaymentResponse(sendPaymentResponse: [String: Any?]) throws -> SendPaymentResponse { static func asSendPaymentResponse(sendPaymentResponse: [String: Any?]) throws -> SendPaymentResponse {
guard let paymentTmp = sendPaymentResponse["payment"] as? [String: Any?] else { guard let paymentTmp = sendPaymentResponse["payment"] as? [String: Any?] else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "payment", typeName: "SendPaymentResponse")) throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "payment", typeName: "SendPaymentResponse"))
@@ -3222,6 +3140,129 @@ enum BreezSDKLiquidMapper {
return list return list
} }
static func asPaymentDetails(paymentDetails: [String: Any?]) throws -> PaymentDetails {
let type = paymentDetails["type"] as! String
if type == "lightning" {
guard let _swapId = paymentDetails["swapId"] as? String else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "swapId", typeName: "PaymentDetails"))
}
return PaymentDetails.lightning(swapId: _swapId)
}
if type == "liquid" {
guard let _destination = paymentDetails["destination"] as? String else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "destination", typeName: "PaymentDetails"))
}
return PaymentDetails.liquid(destination: _destination)
}
if type == "bitcoin" {
guard let _swapId = paymentDetails["swapId"] as? String else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "swapId", typeName: "PaymentDetails"))
}
return PaymentDetails.bitcoin(swapId: _swapId)
}
throw SdkError.Generic(message: "Unexpected type \(type) for enum PaymentDetails")
}
static func dictionaryOf(paymentDetails: PaymentDetails) -> [String: Any?] {
switch paymentDetails {
case let .lightning(
swapId, description, preimage, bolt11, refundTxId, refundTxAmountSat
):
return [
"type": "lightning",
"swapId": swapId,
"description": description,
"preimage": preimage == nil ? nil : preimage,
"bolt11": bolt11 == nil ? nil : bolt11,
"refundTxId": refundTxId == nil ? nil : refundTxId,
"refundTxAmountSat": refundTxAmountSat == nil ? nil : refundTxAmountSat,
]
case let .liquid(
destination, description
):
return [
"type": "liquid",
"destination": destination,
"description": description,
]
case let .bitcoin(
swapId, description, refundTxId, refundTxAmountSat
):
return [
"type": "bitcoin",
"swapId": swapId,
"description": description,
"refundTxId": refundTxId == nil ? nil : refundTxId,
"refundTxAmountSat": refundTxAmountSat == nil ? nil : refundTxAmountSat,
]
}
}
static func arrayOf(paymentDetailsList: [PaymentDetails]) -> [Any] {
return paymentDetailsList.map { v -> [String: Any?] in return dictionaryOf(paymentDetails: v) }
}
static func asPaymentDetailsList(arr: [Any]) throws -> [PaymentDetails] {
var list = [PaymentDetails]()
for value in arr {
if let val = value as? [String: Any?] {
var paymentDetails = try asPaymentDetails(paymentDetails: val)
list.append(paymentDetails)
} else {
throw SdkError.Generic(message: errUnexpectedType(typeName: "PaymentDetails"))
}
}
return list
}
static func asPaymentMethod(paymentMethod: String) throws -> PaymentMethod {
switch paymentMethod {
case "lightning":
return PaymentMethod.lightning
case "bitcoinAddress":
return PaymentMethod.bitcoinAddress
case "liquidAddress":
return PaymentMethod.liquidAddress
default: throw SdkError.Generic(message: "Invalid variant \(paymentMethod) for enum PaymentMethod")
}
}
static func valueOf(paymentMethod: PaymentMethod) -> String {
switch paymentMethod {
case .lightning:
return "lightning"
case .bitcoinAddress:
return "bitcoinAddress"
case .liquidAddress:
return "liquidAddress"
}
}
static func arrayOf(paymentMethodList: [PaymentMethod]) -> [String] {
return paymentMethodList.map { v -> String in return valueOf(paymentMethod: v) }
}
static func asPaymentMethodList(arr: [Any]) throws -> [PaymentMethod] {
var list = [PaymentMethod]()
for value in arr {
if let val = value as? String {
var paymentMethod = try asPaymentMethod(paymentMethod: val)
list.append(paymentMethod)
} else {
throw SdkError.Generic(message: errUnexpectedType(typeName: "PaymentMethod"))
}
}
return list
}
static func asPaymentState(paymentState: String) throws -> PaymentState { static func asPaymentState(paymentState: String) throws -> PaymentState {
switch paymentState { switch paymentState {
case "created": case "created":
@@ -3461,6 +3502,65 @@ enum BreezSDKLiquidMapper {
return list return list
} }
static func asSendDestination(sendDestination: [String: Any?]) throws -> SendDestination {
let type = sendDestination["type"] as! String
if type == "liquidAddress" {
guard let addressDataTmp = sendDestination["addressData"] as? [String: Any?] else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "addressData", typeName: "SendDestination"))
}
let _addressData = try asLiquidAddressData(liquidAddressData: addressDataTmp)
return SendDestination.liquidAddress(addressData: _addressData)
}
if type == "bolt11" {
guard let invoiceTmp = sendDestination["invoice"] as? [String: Any?] else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "invoice", typeName: "SendDestination"))
}
let _invoice = try asLnInvoice(lnInvoice: invoiceTmp)
return SendDestination.bolt11(invoice: _invoice)
}
throw SdkError.Generic(message: "Unexpected type \(type) for enum SendDestination")
}
static func dictionaryOf(sendDestination: SendDestination) -> [String: Any?] {
switch sendDestination {
case let .liquidAddress(
addressData
):
return [
"type": "liquidAddress",
"addressData": dictionaryOf(liquidAddressData: addressData),
]
case let .bolt11(
invoice
):
return [
"type": "bolt11",
"invoice": dictionaryOf(lnInvoice: invoice),
]
}
}
static func arrayOf(sendDestinationList: [SendDestination]) -> [Any] {
return sendDestinationList.map { v -> [String: Any?] in return dictionaryOf(sendDestination: v) }
}
static func asSendDestinationList(arr: [Any]) throws -> [SendDestination] {
var list = [SendDestination]()
for value in arr {
if let val = value as? [String: Any?] {
var sendDestination = try asSendDestination(sendDestination: val)
list.append(sendDestination)
} else {
throw SdkError.Generic(message: errUnexpectedType(typeName: "SendDestination"))
}
}
return list
}
static func asSuccessActionProcessed(successActionProcessed: [String: Any?]) throws -> SuccessActionProcessed { static func asSuccessActionProcessed(successActionProcessed: [String: Any?]) throws -> SuccessActionProcessed {
let type = successActionProcessed["type"] as! String let type = successActionProcessed["type"] as! String
if type == "aes" { if type == "aes" {

View File

@@ -94,18 +94,6 @@ RCT_EXTERN_METHOD(
reject: (RCTPromiseRejectBlock)reject reject: (RCTPromiseRejectBlock)reject
) )
RCT_EXTERN_METHOD(
prepareReceiveOnchain: (NSDictionary*)req
resolve: (RCTPromiseResolveBlock)resolve
reject: (RCTPromiseRejectBlock)reject
)
RCT_EXTERN_METHOD(
receiveOnchain: (NSDictionary*)req
resolve: (RCTPromiseResolveBlock)resolve
reject: (RCTPromiseRejectBlock)reject
)
RCT_EXTERN_METHOD( RCT_EXTERN_METHOD(
prepareBuyBitcoin: (NSDictionary*)req prepareBuyBitcoin: (NSDictionary*)req
resolve: (RCTPromiseResolveBlock)resolve resolve: (RCTPromiseResolveBlock)resolve

View File

@@ -178,8 +178,8 @@ class RNBreezSDKLiquid: RCTEventEmitter {
@objc(sendPayment:resolve:reject:) @objc(sendPayment:resolve:reject:)
func sendPayment(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { func sendPayment(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
do { do {
let prepareSendResponse = try BreezSDKLiquidMapper.asPrepareSendResponse(prepareSendResponse: req) let sendPaymentRequest = try BreezSDKLiquidMapper.asSendPaymentRequest(sendPaymentRequest: req)
var res = try getBindingLiquidSdk().sendPayment(req: prepareSendResponse) var res = try getBindingLiquidSdk().sendPayment(req: sendPaymentRequest)
resolve(BreezSDKLiquidMapper.dictionaryOf(sendPaymentResponse: res)) resolve(BreezSDKLiquidMapper.dictionaryOf(sendPaymentResponse: res))
} catch let err { } catch let err {
rejectErr(err: err, reject: reject) rejectErr(err: err, reject: reject)
@@ -189,9 +189,9 @@ class RNBreezSDKLiquid: RCTEventEmitter {
@objc(prepareReceivePayment:resolve:reject:) @objc(prepareReceivePayment:resolve:reject:)
func prepareReceivePayment(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { func prepareReceivePayment(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
do { do {
let prepareReceivePaymentRequest = try BreezSDKLiquidMapper.asPrepareReceivePaymentRequest(prepareReceivePaymentRequest: req) let prepareReceiveRequest = try BreezSDKLiquidMapper.asPrepareReceiveRequest(prepareReceiveRequest: req)
var res = try getBindingLiquidSdk().prepareReceivePayment(req: prepareReceivePaymentRequest) var res = try getBindingLiquidSdk().prepareReceivePayment(req: prepareReceiveRequest)
resolve(BreezSDKLiquidMapper.dictionaryOf(prepareReceivePaymentResponse: res)) resolve(BreezSDKLiquidMapper.dictionaryOf(prepareReceiveResponse: res))
} catch let err { } catch let err {
rejectErr(err: err, reject: reject) rejectErr(err: err, reject: reject)
} }
@@ -250,28 +250,6 @@ class RNBreezSDKLiquid: RCTEventEmitter {
} }
} }
@objc(prepareReceiveOnchain:resolve:reject:)
func prepareReceiveOnchain(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
do {
let prepareReceiveOnchainRequest = try BreezSDKLiquidMapper.asPrepareReceiveOnchainRequest(prepareReceiveOnchainRequest: req)
var res = try getBindingLiquidSdk().prepareReceiveOnchain(req: prepareReceiveOnchainRequest)
resolve(BreezSDKLiquidMapper.dictionaryOf(prepareReceiveOnchainResponse: res))
} catch let err {
rejectErr(err: err, reject: reject)
}
}
@objc(receiveOnchain:resolve:reject:)
func receiveOnchain(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
do {
let prepareReceiveOnchainResponse = try BreezSDKLiquidMapper.asPrepareReceiveOnchainResponse(prepareReceiveOnchainResponse: req)
var res = try getBindingLiquidSdk().receiveOnchain(req: prepareReceiveOnchainResponse)
resolve(BreezSDKLiquidMapper.dictionaryOf(receiveOnchainResponse: res))
} catch let err {
rejectErr(err: err, reject: reject)
}
}
@objc(prepareBuyBitcoin:resolve:reject:) @objc(prepareBuyBitcoin:resolve:reject:)
func prepareBuyBitcoin(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { func prepareBuyBitcoin(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
do { do {

View File

@@ -37,7 +37,7 @@ export interface BitcoinAddressData {
} }
export interface BuyBitcoinRequest { export interface BuyBitcoinRequest {
prepareRes: PrepareBuyBitcoinResponse prepareResponse: PrepareBuyBitcoinResponse
redirectUrl?: string redirectUrl?: string
} }
@@ -208,22 +208,18 @@ export interface OnchainPaymentLimitsResponse {
export interface PayOnchainRequest { export interface PayOnchainRequest {
address: string address: string
prepareRes: PreparePayOnchainResponse prepareResponse: PreparePayOnchainResponse
} }
export interface Payment { export interface Payment {
destination?: string
txId?: string
timestamp: number timestamp: number
amountSat: number amountSat: number
feesSat: number feesSat: number
paymentType: PaymentType paymentType: PaymentType
status: PaymentState status: PaymentState
description: string details?: PaymentDetails
txId?: string
swapId?: string
preimage?: string
bolt11?: string
refundTxId?: string
refundTxAmountSat?: number
} }
export interface PrepareBuyBitcoinRequest { export interface PrepareBuyBitcoinRequest {
@@ -248,21 +244,14 @@ export interface PreparePayOnchainResponse {
totalFeesSat: number totalFeesSat: number
} }
export interface PrepareReceiveOnchainRequest { export interface PrepareReceiveRequest {
payerAmountSat: number payerAmountSat?: number
paymentMethod: PaymentMethod
} }
export interface PrepareReceiveOnchainResponse { export interface PrepareReceiveResponse {
payerAmountSat: number payerAmountSat?: number
feesSat: number paymentMethod: PaymentMethod
}
export interface PrepareReceivePaymentRequest {
payerAmountSat: number
}
export interface PrepareReceivePaymentResponse {
payerAmountSat: number
feesSat: number feesSat: number
} }
@@ -279,11 +268,12 @@ export interface PrepareRefundResponse {
} }
export interface PrepareSendRequest { export interface PrepareSendRequest {
invoice: string destination: string
amountSat?: number
} }
export interface PrepareSendResponse { export interface PrepareSendResponse {
invoice: string destination: SendDestination
feesSat: number feesSat: number
} }
@@ -292,19 +282,13 @@ export interface Rate {
value: number value: number
} }
export interface ReceiveOnchainResponse {
address: string
bip21: string
}
export interface ReceivePaymentRequest { export interface ReceivePaymentRequest {
prepareRes: PrepareReceivePaymentResponse prepareResponse: PrepareReceiveResponse
description?: string description?: string
} }
export interface ReceivePaymentResponse { export interface ReceivePaymentResponse {
id: string destination: string
invoice: string
} }
export interface RecommendedFees { export interface RecommendedFees {
@@ -349,6 +333,10 @@ export interface RouteHintHop {
htlcMaximumMsat?: number htlcMaximumMsat?: number
} }
export interface SendPaymentRequest {
prepareResponse: PrepareSendResponse
}
export interface SendPaymentResponse { export interface SendPaymentResponse {
payment: Payment payment: Payment
} }
@@ -482,6 +470,38 @@ export enum Network {
REGTEST = "regtest" REGTEST = "regtest"
} }
export enum PaymentDetailsVariant {
LIGHTNING = "lightning",
LIQUID = "liquid",
BITCOIN = "bitcoin"
}
export type PaymentDetails = {
type: PaymentDetailsVariant.LIGHTNING,
swapId: string
description: string
preimage?: string
bolt11?: string
refundTxId?: string
refundTxAmountSat?: number
} | {
type: PaymentDetailsVariant.LIQUID,
destination: string
description: string
} | {
type: PaymentDetailsVariant.BITCOIN,
swapId: string
description: string
refundTxId?: string
refundTxAmountSat?: number
}
export enum PaymentMethod {
LIGHTNING = "lightning",
BITCOIN_ADDRESS = "bitcoinAddress",
LIQUID_ADDRESS = "liquidAddress"
}
export enum PaymentState { export enum PaymentState {
CREATED = "created", CREATED = "created",
PENDING = "pending", PENDING = "pending",
@@ -529,6 +549,19 @@ export type SdkEvent = {
type: SdkEventVariant.SYNCED type: SdkEventVariant.SYNCED
} }
export enum SendDestinationVariant {
LIQUID_ADDRESS = "liquidAddress",
BOLT11 = "bolt11"
}
export type SendDestination = {
type: SendDestinationVariant.LIQUID_ADDRESS,
addressData: LiquidAddressData
} | {
type: SendDestinationVariant.BOLT11,
invoice: LnInvoice
}
export enum SuccessActionProcessedVariant { export enum SuccessActionProcessedVariant {
AES = "aes", AES = "aes",
MESSAGE = "message", MESSAGE = "message",
@@ -602,12 +635,12 @@ export const prepareSendPayment = async (req: PrepareSendRequest): Promise<Prepa
return response return response
} }
export const sendPayment = async (req: PrepareSendResponse): Promise<SendPaymentResponse> => { export const sendPayment = async (req: SendPaymentRequest): Promise<SendPaymentResponse> => {
const response = await BreezSDKLiquid.sendPayment(req) const response = await BreezSDKLiquid.sendPayment(req)
return response return response
} }
export const prepareReceivePayment = async (req: PrepareReceivePaymentRequest): Promise<PrepareReceivePaymentResponse> => { export const prepareReceivePayment = async (req: PrepareReceiveRequest): Promise<PrepareReceiveResponse> => {
const response = await BreezSDKLiquid.prepareReceivePayment(req) const response = await BreezSDKLiquid.prepareReceivePayment(req)
return response return response
} }
@@ -637,16 +670,6 @@ export const payOnchain = async (req: PayOnchainRequest): Promise<SendPaymentRes
return response return response
} }
export const prepareReceiveOnchain = async (req: PrepareReceiveOnchainRequest): Promise<PrepareReceiveOnchainResponse> => {
const response = await BreezSDKLiquid.prepareReceiveOnchain(req)
return response
}
export const receiveOnchain = async (req: PrepareReceiveOnchainResponse): Promise<ReceiveOnchainResponse> => {
const response = await BreezSDKLiquid.receiveOnchain(req)
return response
}
export const prepareBuyBitcoin = async (req: PrepareBuyBitcoinRequest): Promise<PrepareBuyBitcoinResponse> => { export const prepareBuyBitcoin = async (req: PrepareBuyBitcoinRequest): Promise<PrepareBuyBitcoinResponse> => {
const response = await BreezSDKLiquid.prepareBuyBitcoin(req) const response = await BreezSDKLiquid.prepareBuyBitcoin(req)
return response return response