mirror of
https://github.com/aljazceru/breez-sdk-liquid.git
synced 2025-12-18 22:44:22 +01:00
Expose fees for review + auto accept
This commit is contained in:
@@ -4,7 +4,7 @@ use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use breez_sdk_liquid::prelude::*;
|
||||
use clap::{arg, Parser};
|
||||
use qrcode_rs::render::unicode;
|
||||
@@ -131,6 +131,14 @@ pub(crate) enum Command {
|
||||
/// Lightning payment hash
|
||||
payment_hash: String,
|
||||
},
|
||||
/// Get proposed fees for WaitingFeeAcceptance Payment
|
||||
FetchPaymentProposedFees { swap_id: String },
|
||||
/// Accept proposed fees for WaitingFeeAcceptance Payment
|
||||
AcceptPaymentProposedFees {
|
||||
swap_id: String,
|
||||
// Fee amount obtained using FetchPaymentProposedFees
|
||||
fees_sat: u64,
|
||||
},
|
||||
/// List refundable chain swaps
|
||||
ListRefundables,
|
||||
/// Prepare a refund transaction for an incomplete swap
|
||||
@@ -519,6 +527,23 @@ pub(crate) async fn handle_command(
|
||||
}
|
||||
}
|
||||
}
|
||||
Command::FetchPaymentProposedFees { swap_id } => {
|
||||
let res = sdk
|
||||
.fetch_payment_proposed_fees(&FetchPaymentProposedFeesRequest { swap_id })
|
||||
.await?;
|
||||
command_result!(res)
|
||||
}
|
||||
Command::AcceptPaymentProposedFees { swap_id, fees_sat } => {
|
||||
let res = sdk
|
||||
.fetch_payment_proposed_fees(&FetchPaymentProposedFeesRequest { swap_id })
|
||||
.await?;
|
||||
if fees_sat != res.fees_sat {
|
||||
bail!("Fees changed since they were fetched")
|
||||
}
|
||||
sdk.accept_payment_proposed_fees(&AcceptPaymentProposedFeesRequest { response: res })
|
||||
.await?;
|
||||
command_result!("Proposed fees accepted successfully")
|
||||
}
|
||||
Command::ListRefundables => {
|
||||
let refundables = sdk.list_refundables().await?;
|
||||
command_result!(refundables)
|
||||
|
||||
@@ -16,6 +16,8 @@ typedef struct _Dart_Handle* Dart_Handle;
|
||||
|
||||
#define ESTIMATED_BTC_CLAIM_TX_VSIZE 111
|
||||
|
||||
#define ESTIMATED_BTC_LOCKUP_TX_VSIZE 154
|
||||
|
||||
#define STANDARD_FEE_RATE_SAT_PER_VBYTE 0.1
|
||||
|
||||
#define LOWBALL_FEE_RATE_SAT_PER_VBYTE 0.01
|
||||
@@ -541,6 +543,10 @@ typedef struct wire_cst_SdkEvent_PaymentWaitingConfirmation {
|
||||
struct wire_cst_payment *details;
|
||||
} wire_cst_SdkEvent_PaymentWaitingConfirmation;
|
||||
|
||||
typedef struct wire_cst_SdkEvent_PaymentWaitingFeeAcceptance {
|
||||
struct wire_cst_payment *details;
|
||||
} wire_cst_SdkEvent_PaymentWaitingFeeAcceptance;
|
||||
|
||||
typedef union SdkEventKind {
|
||||
struct wire_cst_SdkEvent_PaymentFailed PaymentFailed;
|
||||
struct wire_cst_SdkEvent_PaymentPending PaymentPending;
|
||||
@@ -548,6 +554,7 @@ typedef union SdkEventKind {
|
||||
struct wire_cst_SdkEvent_PaymentRefundPending PaymentRefundPending;
|
||||
struct wire_cst_SdkEvent_PaymentSucceeded PaymentSucceeded;
|
||||
struct wire_cst_SdkEvent_PaymentWaitingConfirmation PaymentWaitingConfirmation;
|
||||
struct wire_cst_SdkEvent_PaymentWaitingFeeAcceptance PaymentWaitingFeeAcceptance;
|
||||
} SdkEventKind;
|
||||
|
||||
typedef struct wire_cst_sdk_event {
|
||||
@@ -580,6 +587,7 @@ typedef struct wire_cst_config {
|
||||
struct wire_cst_list_prim_u_8_strict *breez_api_key;
|
||||
struct wire_cst_list_external_input_parser *external_input_parsers;
|
||||
bool use_default_external_input_parsers;
|
||||
uint32_t *onchain_fee_rate_leeway_sat_per_vbyte;
|
||||
} wire_cst_config;
|
||||
|
||||
typedef struct wire_cst_connect_request {
|
||||
|
||||
@@ -338,7 +338,7 @@ dictionary Config {
|
||||
u64? zero_conf_max_amount_sat;
|
||||
boolean use_default_external_input_parsers = true;
|
||||
sequence<ExternalInputParser>? external_input_parsers = null;
|
||||
u32? onchain_fee_rate_leeway_sat_per_vbyte;
|
||||
u32? onchain_fee_rate_leeway_sat_per_vbyte = null;
|
||||
};
|
||||
|
||||
enum LiquidNetwork {
|
||||
@@ -599,7 +599,7 @@ enum PaymentState {
|
||||
"TimedOut",
|
||||
"Refundable",
|
||||
"RefundPending",
|
||||
"WaitingUserAction",
|
||||
"WaitingFeeAcceptance",
|
||||
};
|
||||
|
||||
dictionary RefundableSwap {
|
||||
@@ -775,7 +775,7 @@ interface BindingLiquidSdk {
|
||||
[Throws=SdkError]
|
||||
FetchPaymentProposedFeesResponse fetch_payment_proposed_fees(FetchPaymentProposedFeesRequest req);
|
||||
|
||||
[Throws=SdkError]
|
||||
[Throws=PaymentError]
|
||||
void accept_payment_proposed_fees(AcceptPaymentProposedFeesRequest req);
|
||||
|
||||
[Throws=SdkError]
|
||||
|
||||
@@ -170,6 +170,20 @@ impl BindingLiquidSdk {
|
||||
rt().block_on(self.sdk.get_payment(&req))
|
||||
}
|
||||
|
||||
pub fn fetch_payment_proposed_fees(
|
||||
&self,
|
||||
req: FetchPaymentProposedFeesRequest,
|
||||
) -> SdkResult<FetchPaymentProposedFeesResponse> {
|
||||
rt().block_on(self.sdk.fetch_payment_proposed_fees(&req))
|
||||
}
|
||||
|
||||
pub fn accept_payment_proposed_fees(
|
||||
&self,
|
||||
req: AcceptPaymentProposedFeesRequest,
|
||||
) -> Result<(), PaymentError> {
|
||||
rt().block_on(self.sdk.accept_payment_proposed_fees(&req))
|
||||
}
|
||||
|
||||
pub fn prepare_lnurl_pay(
|
||||
&self,
|
||||
req: PrepareLnUrlPayRequest,
|
||||
|
||||
@@ -32,8 +32,9 @@ use crate::{
|
||||
wallet::OnchainWallet,
|
||||
};
|
||||
|
||||
// Estimates based on https://github.com/BoltzExchange/boltz-backend/blob/ee4c77be1fcb9bb2b45703c542ad67f7efbf218d/lib/rates/FeeProvider.ts#L78
|
||||
// Estimates based on https://github.com/BoltzExchange/boltz-backend/blob/ee4c77be1fcb9bb2b45703c542ad67f7efbf218d/lib/rates/FeeProvider.ts#L68
|
||||
pub const ESTIMATED_BTC_CLAIM_TX_VSIZE: u64 = 111;
|
||||
pub const ESTIMATED_BTC_LOCKUP_TX_VSIZE: u64 = 154;
|
||||
|
||||
pub(crate) struct ChainSwapHandler {
|
||||
config: Config,
|
||||
@@ -327,12 +328,12 @@ impl ChainSwapHandler {
|
||||
| ChainSwapStates::TransactionRefunded
|
||||
| ChainSwapStates::SwapExpired => {
|
||||
// Zero-amount Receive Chain Swaps also get to TransactionLockupFailed when user locks up funds
|
||||
let is_zero_amount = swap.payer_amount_sat == 0;
|
||||
let is_zero_amount = swap.get_boltz_create_response()?.lockup_details.amount == 0;
|
||||
if matches!(swap_state, ChainSwapStates::TransactionLockupFailed) && is_zero_amount
|
||||
{
|
||||
match self.handle_amountless_update(swap).await {
|
||||
Ok(_) => {
|
||||
// We successfully accepted the quote, the swap should continue as normal
|
||||
// Either we accepted the quote, or we will be waiting for user fee acceptance
|
||||
return Ok(()); // Break from TxLockupFailed branch
|
||||
}
|
||||
// In case of error, we continue and mark it as refundable
|
||||
@@ -383,17 +384,43 @@ impl ChainSwapHandler {
|
||||
.map(|quote| quote.to_sat())?;
|
||||
info!("Got quote of {quote} sat for swap {}", &swap.id);
|
||||
|
||||
self.validate_and_update_amountless_swap(swap, quote)
|
||||
.await?;
|
||||
match self.validate_amountless_swap(swap, quote).await? {
|
||||
ValidateAmountlessSwapResult::ReadyForAccepting {
|
||||
user_lockup_amount_sat,
|
||||
receiver_amount_sat,
|
||||
} => {
|
||||
debug!("Zero-amount swap validated. Auto-accepting...");
|
||||
self.persister.update_zero_amount_swap_values(
|
||||
&swap.id,
|
||||
user_lockup_amount_sat,
|
||||
receiver_amount_sat,
|
||||
)?;
|
||||
self.swapper
|
||||
.accept_zero_amount_chain_swap_quote(&swap.id, quote)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
ValidateAmountlessSwapResult::RequiresUserAction {
|
||||
user_lockup_amount_sat,
|
||||
receiver_amount_sat_original_estimate,
|
||||
} => {
|
||||
debug!("Zero-amount swap validated. Fees are too high for automatic accepting. Moving to WaitingFeeAcceptance");
|
||||
// While the user doesn't accept new fees, let's continue to show the original estimate
|
||||
self.persister.update_zero_amount_swap_values(
|
||||
&swap.id,
|
||||
user_lockup_amount_sat,
|
||||
receiver_amount_sat_original_estimate,
|
||||
)?;
|
||||
self.update_swap_info(&swap.id, WaitingFeeAcceptance, None, None, None, None)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn validate_and_update_amountless_swap(
|
||||
async fn validate_amountless_swap(
|
||||
&self,
|
||||
swap: &ChainSwap,
|
||||
quote_server_lockup_amount_sat: u64,
|
||||
) -> Result<(), PaymentError> {
|
||||
) -> Result<ValidateAmountlessSwapResult, PaymentError> {
|
||||
debug!("Validating {swap:?}");
|
||||
|
||||
ensure_sdk!(
|
||||
@@ -425,32 +452,45 @@ impl ChainSwapHandler {
|
||||
);
|
||||
|
||||
let pair = swap.get_boltz_pair()?;
|
||||
let swapper_service_feerate = pair.fees.percentage;
|
||||
let swapper_server_fees_sat = pair.fees.server();
|
||||
let service_fees_sat =
|
||||
((swapper_service_feerate / 100.0) * user_lockup_amount_sat as f64).ceil() as u64;
|
||||
let fees_sat = swapper_server_fees_sat + service_fees_sat;
|
||||
ensure_sdk!(
|
||||
user_lockup_amount_sat > fees_sat,
|
||||
PaymentError::generic(&format!("Invalid quote: fees ({fees_sat} sat) are higher than user lockup ({user_lockup_amount_sat} sat)"))
|
||||
);
|
||||
|
||||
let expected_server_lockup_amount_sat = user_lockup_amount_sat - fees_sat;
|
||||
debug!("user_lockup_amount_sat = {}, service_fees_sat = {}, server_fees_sat = {}, expected_server_lockup_amount_sat = {}, quote_server_lockup_amount_sat = {}",
|
||||
user_lockup_amount_sat, service_fees_sat, swapper_server_fees_sat, expected_server_lockup_amount_sat, quote_server_lockup_amount_sat);
|
||||
ensure_sdk!(
|
||||
expected_server_lockup_amount_sat <= quote_server_lockup_amount_sat,
|
||||
PaymentError::generic(&format!("Invalid quote: expected at least {expected_server_lockup_amount_sat} sat, got {quote_server_lockup_amount_sat} sat"))
|
||||
|
||||
// Original server lockup quote estimate
|
||||
let server_fees_estimate_sat = pair.fees.server();
|
||||
let service_fees_sat = pair.fees.boltz(user_lockup_amount_sat);
|
||||
let server_lockup_amount_estimate_sat =
|
||||
user_lockup_amount_sat - server_fees_estimate_sat - service_fees_sat;
|
||||
|
||||
// Min auto accept server lockup quote
|
||||
let server_fees_leeway_sat = self
|
||||
.config
|
||||
.onchain_fee_rate_leeway_sat_per_vbyte
|
||||
.unwrap_or(0) as u64
|
||||
* ESTIMATED_BTC_LOCKUP_TX_VSIZE;
|
||||
let min_auto_accept_server_lockup_amount_sat =
|
||||
server_lockup_amount_estimate_sat.saturating_sub(server_fees_leeway_sat);
|
||||
|
||||
debug!(
|
||||
"user_lockup_amount_sat = {user_lockup_amount_sat}, \
|
||||
service_fees_sat = {service_fees_sat}, \
|
||||
server_fees_estimate_sat = {server_fees_estimate_sat}, \
|
||||
server_fees_leeway_sat = {server_fees_leeway_sat}, \
|
||||
min_auto_accept_server_lockup_amount_sat = {min_auto_accept_server_lockup_amount_sat}, \
|
||||
quote_server_lockup_amount_sat = {quote_server_lockup_amount_sat}",
|
||||
);
|
||||
|
||||
if min_auto_accept_server_lockup_amount_sat > quote_server_lockup_amount_sat {
|
||||
let receiver_amount_sat_original_estimate =
|
||||
server_lockup_amount_estimate_sat - swap.claim_fees_sat;
|
||||
Ok(ValidateAmountlessSwapResult::RequiresUserAction {
|
||||
user_lockup_amount_sat,
|
||||
receiver_amount_sat_original_estimate,
|
||||
})
|
||||
} else {
|
||||
let receiver_amount_sat = quote_server_lockup_amount_sat - swap.claim_fees_sat;
|
||||
self.persister.update_zero_amount_swap_values(
|
||||
&swap.id,
|
||||
Ok(ValidateAmountlessSwapResult::ReadyForAccepting {
|
||||
user_lockup_amount_sat,
|
||||
receiver_amount_sat,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_new_outgoing_status(&self, swap: &ChainSwap, update: &boltz::Update) -> Result<()> {
|
||||
@@ -1077,12 +1117,17 @@ impl ChainSwapHandler {
|
||||
err: "Cannot transition to Created state".to_string(),
|
||||
}),
|
||||
|
||||
(Created | Pending, Pending) => Ok(()),
|
||||
(Created | Pending | WaitingFeeAcceptance, Pending) => Ok(()),
|
||||
(_, Pending) => Err(PaymentError::Generic {
|
||||
err: format!("Cannot transition from {from_state:?} to Pending state"),
|
||||
}),
|
||||
|
||||
(Created | Pending | RefundPending, Complete) => Ok(()),
|
||||
(Created | Pending | WaitingFeeAcceptance, WaitingFeeAcceptance) => Ok(()),
|
||||
(_, WaitingFeeAcceptance) => Err(PaymentError::Generic {
|
||||
err: format!("Cannot transition from {from_state:?} to WaitingFeeAcceptance state"),
|
||||
}),
|
||||
|
||||
(Created | Pending | WaitingFeeAcceptance | RefundPending, Complete) => Ok(()),
|
||||
(_, Complete) => Err(PaymentError::Generic {
|
||||
err: format!("Cannot transition from {from_state:?} to Complete state"),
|
||||
}),
|
||||
@@ -1092,12 +1137,15 @@ impl ChainSwapHandler {
|
||||
err: format!("Cannot transition from {from_state:?} to TimedOut state"),
|
||||
}),
|
||||
|
||||
(Created | Pending | RefundPending | Failed | Complete, Refundable) => Ok(()),
|
||||
(
|
||||
Created | Pending | WaitingFeeAcceptance | RefundPending | Failed | Complete,
|
||||
Refundable,
|
||||
) => Ok(()),
|
||||
(_, Refundable) => Err(PaymentError::Generic {
|
||||
err: format!("Cannot transition from {from_state:?} to Refundable state"),
|
||||
}),
|
||||
|
||||
(Pending | Refundable, RefundPending) => Ok(()),
|
||||
(Pending | WaitingFeeAcceptance | Refundable, RefundPending) => Ok(()),
|
||||
(_, RefundPending) => Err(PaymentError::Generic {
|
||||
err: format!("Cannot transition from {from_state:?} to RefundPending state"),
|
||||
}),
|
||||
@@ -1299,6 +1347,17 @@ impl ChainSwapHandler {
|
||||
}
|
||||
}
|
||||
|
||||
enum ValidateAmountlessSwapResult {
|
||||
ReadyForAccepting {
|
||||
user_lockup_amount_sat: u64,
|
||||
receiver_amount_sat: u64,
|
||||
},
|
||||
RequiresUserAction {
|
||||
user_lockup_amount_sat: u64,
|
||||
receiver_amount_sat_original_estimate: u64,
|
||||
},
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
@@ -1322,15 +1381,47 @@ mod tests {
|
||||
let chain_swap_handler = new_chain_swap_handler(persister.clone())?;
|
||||
|
||||
// Test valid combinations of states
|
||||
let all_states = HashSet::from([Created, Pending, Complete, TimedOut, Failed]);
|
||||
let all_states = HashSet::from([
|
||||
Created,
|
||||
Pending,
|
||||
WaitingFeeAcceptance,
|
||||
Complete,
|
||||
TimedOut,
|
||||
Failed,
|
||||
]);
|
||||
let valid_combinations = HashMap::from([
|
||||
(
|
||||
Created,
|
||||
HashSet::from([Pending, Complete, TimedOut, Refundable, Failed]),
|
||||
HashSet::from([
|
||||
Pending,
|
||||
WaitingFeeAcceptance,
|
||||
Complete,
|
||||
TimedOut,
|
||||
Refundable,
|
||||
Failed,
|
||||
]),
|
||||
),
|
||||
(
|
||||
Pending,
|
||||
HashSet::from([Pending, Complete, Refundable, RefundPending, Failed]),
|
||||
HashSet::from([
|
||||
Pending,
|
||||
WaitingFeeAcceptance,
|
||||
Complete,
|
||||
Refundable,
|
||||
RefundPending,
|
||||
Failed,
|
||||
]),
|
||||
),
|
||||
(
|
||||
WaitingFeeAcceptance,
|
||||
HashSet::from([
|
||||
Pending,
|
||||
WaitingFeeAcceptance,
|
||||
Complete,
|
||||
Refundable,
|
||||
RefundPending,
|
||||
Failed,
|
||||
]),
|
||||
),
|
||||
(TimedOut, HashSet::from([Failed])),
|
||||
(Complete, HashSet::from([Refundable])),
|
||||
@@ -1342,7 +1433,7 @@ mod tests {
|
||||
for (first_state, allowed_states) in valid_combinations.iter() {
|
||||
for allowed_state in allowed_states {
|
||||
let chain_swap =
|
||||
new_chain_swap(Direction::Incoming, Some(*first_state), false, None);
|
||||
new_chain_swap(Direction::Incoming, Some(*first_state), false, None, false);
|
||||
persister.insert_or_update_chain_swap(&chain_swap)?;
|
||||
|
||||
assert!(chain_swap_handler
|
||||
@@ -1369,7 +1460,7 @@ mod tests {
|
||||
for (first_state, disallowed_states) in invalid_combinations.iter() {
|
||||
for disallowed_state in disallowed_states {
|
||||
let chain_swap =
|
||||
new_chain_swap(Direction::Incoming, Some(*first_state), false, None);
|
||||
new_chain_swap(Direction::Incoming, Some(*first_state), false, None, false);
|
||||
persister.insert_or_update_chain_swap(&chain_swap)?;
|
||||
|
||||
assert!(chain_swap_handler
|
||||
|
||||
@@ -2092,6 +2092,7 @@ impl CstDecode<crate::model::PaymentState> for i32 {
|
||||
4 => crate::model::PaymentState::TimedOut,
|
||||
5 => crate::model::PaymentState::Refundable,
|
||||
6 => crate::model::PaymentState::RefundPending,
|
||||
7 => crate::model::PaymentState::WaitingFeeAcceptance,
|
||||
_ => unreachable!("Invalid variant for PaymentState: {}", self),
|
||||
}
|
||||
}
|
||||
@@ -2378,6 +2379,7 @@ impl SseDecode for crate::model::Config {
|
||||
let mut var_externalInputParsers =
|
||||
<Option<Vec<crate::bindings::ExternalInputParser>>>::sse_decode(deserializer);
|
||||
let mut var_useDefaultExternalInputParsers = <bool>::sse_decode(deserializer);
|
||||
let mut var_onchainFeeRateLeewaySatPerVbyte = <Option<u32>>::sse_decode(deserializer);
|
||||
return crate::model::Config {
|
||||
liquid_electrum_url: var_liquidElectrumUrl,
|
||||
bitcoin_electrum_url: var_bitcoinElectrumUrl,
|
||||
@@ -2392,6 +2394,7 @@ impl SseDecode for crate::model::Config {
|
||||
breez_api_key: var_breezApiKey,
|
||||
external_input_parsers: var_externalInputParsers,
|
||||
use_default_external_input_parsers: var_useDefaultExternalInputParsers,
|
||||
onchain_fee_rate_leeway_sat_per_vbyte: var_onchainFeeRateLeewaySatPerVbyte,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -3781,6 +3784,7 @@ impl SseDecode for crate::model::PaymentState {
|
||||
4 => crate::model::PaymentState::TimedOut,
|
||||
5 => crate::model::PaymentState::Refundable,
|
||||
6 => crate::model::PaymentState::RefundPending,
|
||||
7 => crate::model::PaymentState::WaitingFeeAcceptance,
|
||||
_ => unreachable!("Invalid variant for PaymentState: {}", inner),
|
||||
};
|
||||
}
|
||||
@@ -4170,6 +4174,12 @@ impl SseDecode for crate::model::SdkEvent {
|
||||
};
|
||||
}
|
||||
6 => {
|
||||
let mut var_details = <crate::model::Payment>::sse_decode(deserializer);
|
||||
return crate::model::SdkEvent::PaymentWaitingFeeAcceptance {
|
||||
details: var_details,
|
||||
};
|
||||
}
|
||||
7 => {
|
||||
return crate::model::SdkEvent::Synced;
|
||||
}
|
||||
_ => {
|
||||
@@ -4677,6 +4687,9 @@ impl flutter_rust_bridge::IntoDart for crate::model::Config {
|
||||
self.use_default_external_input_parsers
|
||||
.into_into_dart()
|
||||
.into_dart(),
|
||||
self.onchain_fee_rate_leeway_sat_per_vbyte
|
||||
.into_into_dart()
|
||||
.into_dart(),
|
||||
]
|
||||
.into_dart()
|
||||
}
|
||||
@@ -5836,6 +5849,7 @@ impl flutter_rust_bridge::IntoDart for crate::model::PaymentState {
|
||||
Self::TimedOut => 4.into_dart(),
|
||||
Self::Refundable => 5.into_dart(),
|
||||
Self::RefundPending => 6.into_dart(),
|
||||
Self::WaitingFeeAcceptance => 7.into_dart(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -6368,7 +6382,10 @@ impl flutter_rust_bridge::IntoDart for crate::model::SdkEvent {
|
||||
crate::model::SdkEvent::PaymentWaitingConfirmation { details } => {
|
||||
[5.into_dart(), details.into_into_dart().into_dart()].into_dart()
|
||||
}
|
||||
crate::model::SdkEvent::Synced => [6.into_dart()].into_dart(),
|
||||
crate::model::SdkEvent::PaymentWaitingFeeAcceptance { details } => {
|
||||
[6.into_dart(), details.into_into_dart().into_dart()].into_dart()
|
||||
}
|
||||
crate::model::SdkEvent::Synced => [7.into_dart()].into_dart(),
|
||||
_ => {
|
||||
unimplemented!("");
|
||||
}
|
||||
@@ -6787,6 +6804,7 @@ impl SseEncode for crate::model::Config {
|
||||
serializer,
|
||||
);
|
||||
<bool>::sse_encode(self.use_default_external_input_parsers, serializer);
|
||||
<Option<u32>>::sse_encode(self.onchain_fee_rate_leeway_sat_per_vbyte, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7907,6 +7925,7 @@ impl SseEncode for crate::model::PaymentState {
|
||||
crate::model::PaymentState::TimedOut => 4,
|
||||
crate::model::PaymentState::Refundable => 5,
|
||||
crate::model::PaymentState::RefundPending => 6,
|
||||
crate::model::PaymentState::WaitingFeeAcceptance => 7,
|
||||
_ => {
|
||||
unimplemented!("");
|
||||
}
|
||||
@@ -8181,8 +8200,12 @@ impl SseEncode for crate::model::SdkEvent {
|
||||
<i32>::sse_encode(5, serializer);
|
||||
<crate::model::Payment>::sse_encode(details, serializer);
|
||||
}
|
||||
crate::model::SdkEvent::Synced => {
|
||||
crate::model::SdkEvent::PaymentWaitingFeeAcceptance { details } => {
|
||||
<i32>::sse_encode(6, serializer);
|
||||
<crate::model::Payment>::sse_encode(details, serializer);
|
||||
}
|
||||
crate::model::SdkEvent::Synced => {
|
||||
<i32>::sse_encode(7, serializer);
|
||||
}
|
||||
_ => {
|
||||
unimplemented!("");
|
||||
@@ -8947,6 +8970,9 @@ mod io {
|
||||
use_default_external_input_parsers: self
|
||||
.use_default_external_input_parsers
|
||||
.cst_decode(),
|
||||
onchain_fee_rate_leeway_sat_per_vbyte: self
|
||||
.onchain_fee_rate_leeway_sat_per_vbyte
|
||||
.cst_decode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10152,7 +10178,13 @@ mod io {
|
||||
details: ans.details.cst_decode(),
|
||||
}
|
||||
}
|
||||
6 => crate::model::SdkEvent::Synced,
|
||||
6 => {
|
||||
let ans = unsafe { self.kind.PaymentWaitingFeeAcceptance };
|
||||
crate::model::SdkEvent::PaymentWaitingFeeAcceptance {
|
||||
details: ans.details.cst_decode(),
|
||||
}
|
||||
}
|
||||
7 => crate::model::SdkEvent::Synced,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -10437,6 +10469,7 @@ mod io {
|
||||
breez_api_key: core::ptr::null_mut(),
|
||||
external_input_parsers: core::ptr::null_mut(),
|
||||
use_default_external_input_parsers: Default::default(),
|
||||
onchain_fee_rate_leeway_sat_per_vbyte: core::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12517,6 +12550,7 @@ mod io {
|
||||
breez_api_key: *mut wire_cst_list_prim_u_8_strict,
|
||||
external_input_parsers: *mut wire_cst_list_external_input_parser,
|
||||
use_default_external_input_parsers: bool,
|
||||
onchain_fee_rate_leeway_sat_per_vbyte: *mut u32,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -13492,6 +13526,7 @@ mod io {
|
||||
PaymentRefundPending: wire_cst_SdkEvent_PaymentRefundPending,
|
||||
PaymentSucceeded: wire_cst_SdkEvent_PaymentSucceeded,
|
||||
PaymentWaitingConfirmation: wire_cst_SdkEvent_PaymentWaitingConfirmation,
|
||||
PaymentWaitingFeeAcceptance: wire_cst_SdkEvent_PaymentWaitingFeeAcceptance,
|
||||
nil__: (),
|
||||
}
|
||||
#[repr(C)]
|
||||
@@ -13526,6 +13561,11 @@ mod io {
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct wire_cst_SdkEvent_PaymentWaitingFeeAcceptance {
|
||||
details: *mut wire_cst_payment,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct wire_cst_send_destination {
|
||||
tag: i32,
|
||||
kind: SendDestinationKind,
|
||||
|
||||
@@ -65,6 +65,13 @@ pub struct Config {
|
||||
/// ([DEFAULT_EXTERNAL_INPUT_PARSERS](crate::sdk::DEFAULT_EXTERNAL_INPUT_PARSERS)).
|
||||
/// Set this to false in order to prevent their use.
|
||||
pub use_default_external_input_parsers: bool,
|
||||
/// For payments where the onchain fees can only be estimated on creation, this can be used
|
||||
/// in order to automatically allow slightly more expensive fees. If the actual fee rate ends up
|
||||
/// being above the sum of the initial estimate and this leeway, the payment will require
|
||||
/// user fee acceptance. See [WaitingFeeAcceptance](PaymentState::WaitingFeeAcceptance).
|
||||
///
|
||||
/// Defaults to zero.
|
||||
pub onchain_fee_rate_leeway_sat_per_vbyte: Option<u32>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -83,6 +90,7 @@ impl Config {
|
||||
breez_api_key: Some(breez_api_key),
|
||||
external_input_parsers: None,
|
||||
use_default_external_input_parsers: true,
|
||||
onchain_fee_rate_leeway_sat_per_vbyte: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +109,7 @@ impl Config {
|
||||
breez_api_key,
|
||||
external_input_parsers: None,
|
||||
use_default_external_input_parsers: true,
|
||||
onchain_fee_rate_leeway_sat_per_vbyte: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,6 +252,7 @@ pub enum SdkEvent {
|
||||
PaymentRefundPending { details: Payment },
|
||||
PaymentSucceeded { details: Payment },
|
||||
PaymentWaitingConfirmation { details: Payment },
|
||||
PaymentWaitingFeeAcceptance { details: Payment },
|
||||
Synced,
|
||||
}
|
||||
|
||||
@@ -1121,6 +1131,19 @@ pub enum PaymentState {
|
||||
///
|
||||
/// When the refund tx is broadcast, `refund_tx_id` is set in the swap.
|
||||
RefundPending = 6,
|
||||
|
||||
/// ## Chain Swaps
|
||||
///
|
||||
/// This is the state when the user needs to accept new fees before the payment can proceed.
|
||||
///
|
||||
/// Use [LiquidSdk::fetch_payment_proposed_fees](crate::sdk::LiquidSdk::fetch_payment_proposed_fees)
|
||||
/// to find out the current fees and
|
||||
/// [LiquidSdk::accept_payment_proposed_fees](crate::sdk::LiquidSdk::accept_payment_proposed_fees)
|
||||
/// to accept them, allowing the payment to proceed.
|
||||
///
|
||||
/// Otherwise, this payment can be immediately refunded using
|
||||
/// [prepare_refund](crate::sdk::LiquidSdk::prepare_refund)/[refund](crate::sdk::LiquidSdk::refund).
|
||||
WaitingFeeAcceptance = 7,
|
||||
}
|
||||
impl ToSql for PaymentState {
|
||||
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
|
||||
@@ -1138,6 +1161,7 @@ impl FromSql for PaymentState {
|
||||
4 => Ok(PaymentState::TimedOut),
|
||||
5 => Ok(PaymentState::Refundable),
|
||||
6 => Ok(PaymentState::RefundPending),
|
||||
7 => Ok(PaymentState::WaitingFeeAcceptance),
|
||||
_ => Err(FromSqlError::OutOfRange(i)),
|
||||
},
|
||||
_ => Err(FromSqlError::InvalidType),
|
||||
@@ -1731,6 +1755,27 @@ impl Utxo {
|
||||
}
|
||||
}
|
||||
|
||||
/// An argument when calling [crate::sdk::LiquidSdk::fetch_payment_proposed_fees].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FetchPaymentProposedFeesRequest {
|
||||
pub swap_id: String,
|
||||
}
|
||||
|
||||
/// Returned when calling [crate::sdk::LiquidSdk::fetch_payment_proposed_fees].
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct FetchPaymentProposedFeesResponse {
|
||||
pub swap_id: String,
|
||||
pub fees_sat: u64,
|
||||
/// Amount sent by the swap payer
|
||||
pub payer_amount_sat: u64,
|
||||
}
|
||||
|
||||
/// An argument when calling [crate::sdk::LiquidSdk::accept_payment_proposed_fees].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AcceptPaymentProposedFeesRequest {
|
||||
pub response: FetchPaymentProposedFeesResponse,
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! get_invoice_amount {
|
||||
($invoice:expr) => {
|
||||
|
||||
@@ -233,13 +233,14 @@ impl Persister {
|
||||
let where_clause = vec![get_where_clause_state_in(&[
|
||||
PaymentState::Created,
|
||||
PaymentState::Pending,
|
||||
PaymentState::WaitingFeeAcceptance,
|
||||
])];
|
||||
|
||||
self.list_chain_swaps_where(&con, where_clause)
|
||||
}
|
||||
|
||||
pub(crate) fn list_pending_chain_swaps(&self) -> Result<Vec<ChainSwap>> {
|
||||
self.list_chain_swaps_by_state(vec![PaymentState::Pending, PaymentState::RefundPending])
|
||||
self.list_chain_swaps_by_state(vec![PaymentState::Pending, PaymentState::RefundPending, PaymentState::WaitingFeeAcceptance])
|
||||
}
|
||||
|
||||
pub(crate) fn list_refundable_chain_swaps(&self) -> Result<Vec<ChainSwap>> {
|
||||
|
||||
@@ -405,6 +405,10 @@ impl ReceiveSwapHandler {
|
||||
err: format!("Cannot transition from {from_state:?} to Failed state"),
|
||||
}),
|
||||
(_, Failed) => Ok(()),
|
||||
|
||||
(_, WaitingFeeAcceptance) => Err(PaymentError::Generic {
|
||||
err: format!("Cannot transition from {from_state:?} to WaitingFeeAcceptance state"),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::ops::Not as _;
|
||||
use std::time::Instant;
|
||||
use std::{fs, path::PathBuf, str::FromStr, sync::Arc, time::Duration};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use anyhow::{anyhow, ensure, Result};
|
||||
use boltz_client::{swaps::boltz::*, util::secrets::Preimage};
|
||||
use buy::{BuyBitcoinApi, BuyBitcoinService};
|
||||
use chain::bitcoin::HybridBitcoinChainService;
|
||||
@@ -560,6 +560,25 @@ impl LiquidSdk {
|
||||
}
|
||||
};
|
||||
}
|
||||
WaitingFeeAcceptance => {
|
||||
let swap_id = &payment
|
||||
.details
|
||||
.get_swap_id()
|
||||
.ok_or(anyhow!("Payment WaitingFeeAcceptance must have a swap"))?;
|
||||
|
||||
ensure!(
|
||||
matches!(
|
||||
self.persister.fetch_swap_by_id(swap_id)?,
|
||||
Swap::Chain(ChainSwap { .. })
|
||||
),
|
||||
"Swap in WaitingFeeAcceptance payment must be chain swap"
|
||||
);
|
||||
|
||||
self.notify_event_listeners(SdkEvent::PaymentWaitingFeeAcceptance {
|
||||
details: payment,
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
RefundPending => {
|
||||
// The swap state has changed to RefundPending
|
||||
self.notify_event_listeners(SdkEvent::PaymentRefundPending {
|
||||
@@ -1298,6 +1317,12 @@ impl LiquidSdk {
|
||||
"Payment has already failed. Please try with another invoice",
|
||||
))
|
||||
}
|
||||
WaitingFeeAcceptance => {
|
||||
return Err(PaymentError::Generic {
|
||||
err: "Send swap payment cannot be in state WaitingFeeAcceptance"
|
||||
.to_string(),
|
||||
})
|
||||
}
|
||||
},
|
||||
None => {
|
||||
let keypair = utils::generate_keypair();
|
||||
@@ -2541,6 +2566,74 @@ impl LiquidSdk {
|
||||
Ok(self.persister.get_payment_by_request(req)?)
|
||||
}
|
||||
|
||||
/// Fetches an up-to-date fees proposal for a [Payment] that is [WaitingFeeAcceptance].
|
||||
///
|
||||
/// Use [LiquidSdk::accept_payment_proposed_fees] to accept the proposed fees and proceed
|
||||
/// with the payment.
|
||||
pub async fn fetch_payment_proposed_fees(
|
||||
&self,
|
||||
req: &FetchPaymentProposedFeesRequest,
|
||||
) -> SdkResult<FetchPaymentProposedFeesResponse> {
|
||||
let chain_swap =
|
||||
self.persister
|
||||
.fetch_chain_swap_by_id(&req.swap_id)?
|
||||
.ok_or(SdkError::Generic {
|
||||
err: format!("Could not find Swap {}", req.swap_id),
|
||||
})?;
|
||||
|
||||
let server_lockup_quote = self
|
||||
.swapper
|
||||
.get_zero_amount_chain_swap_quote(&req.swap_id)?;
|
||||
|
||||
let payer_amount_sat = chain_swap.payer_amount_sat;
|
||||
let fees_sat = payer_amount_sat - server_lockup_quote.to_sat() + chain_swap.claim_fees_sat;
|
||||
|
||||
Ok(FetchPaymentProposedFeesResponse {
|
||||
swap_id: req.swap_id.clone(),
|
||||
fees_sat,
|
||||
payer_amount_sat,
|
||||
})
|
||||
}
|
||||
|
||||
/// Accepts proposed fees for a [Payment] that is [WaitingFeeAcceptance].
|
||||
///
|
||||
/// Use [LiquidSdk::fetch_payment_proposed_fees] to get an up-to-date fees proposal.
|
||||
pub async fn accept_payment_proposed_fees(
|
||||
&self,
|
||||
req: &AcceptPaymentProposedFeesRequest,
|
||||
) -> Result<(), PaymentError> {
|
||||
let FetchPaymentProposedFeesResponse {
|
||||
swap_id,
|
||||
fees_sat,
|
||||
payer_amount_sat,
|
||||
} = req.clone().response;
|
||||
|
||||
let chain_swap =
|
||||
self.persister
|
||||
.fetch_chain_swap_by_id(&swap_id)?
|
||||
.ok_or(SdkError::Generic {
|
||||
err: format!("Could not find Swap {}", swap_id),
|
||||
})?;
|
||||
|
||||
let server_lockup_quote = self.swapper.get_zero_amount_chain_swap_quote(&swap_id)?;
|
||||
|
||||
ensure_sdk!(
|
||||
fees_sat == payer_amount_sat - server_lockup_quote.to_sat() + chain_swap.claim_fees_sat,
|
||||
PaymentError::InvalidOrExpiredFees
|
||||
);
|
||||
|
||||
self.persister.update_zero_amount_swap_values(
|
||||
&swap_id,
|
||||
payer_amount_sat,
|
||||
payer_amount_sat - fees_sat,
|
||||
)?;
|
||||
self.swapper
|
||||
.accept_zero_amount_chain_swap_quote(&swap_id, server_lockup_quote.to_sat())?;
|
||||
self.chain_swap_handler
|
||||
.update_swap_info(&swap_id, Pending, None, None, None, None)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Empties the Liquid Wallet cache for the [Config::network].
|
||||
pub fn empty_wallet_cache(&self) -> Result<()> {
|
||||
let mut path = PathBuf::from(self.config.working_dir.clone());
|
||||
@@ -3029,6 +3122,8 @@ mod tests {
|
||||
use lwk_wollet::{elements::Txid, hashes::hex::DisplayHex};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::chain_swap::ESTIMATED_BTC_LOCKUP_TX_VSIZE;
|
||||
use crate::test_utils::swapper::ZeroAmountSwapMockConfig;
|
||||
use crate::{
|
||||
model::{Direction, PaymentState, Swap},
|
||||
sdk::LiquidSdk,
|
||||
@@ -3049,6 +3144,7 @@ mod tests {
|
||||
accepts_zero_conf: bool,
|
||||
initial_payment_state: Option<PaymentState>,
|
||||
user_lockup_tx_id: Option<String>,
|
||||
zero_amount: bool,
|
||||
}
|
||||
|
||||
impl Default for NewSwapArgs {
|
||||
@@ -3058,6 +3154,7 @@ mod tests {
|
||||
initial_payment_state: None,
|
||||
direction: Direction::Outgoing,
|
||||
user_lockup_tx_id: None,
|
||||
zero_amount: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3082,6 +3179,11 @@ mod tests {
|
||||
self.initial_payment_state = Some(payment_state);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_zero_amount(mut self, zero_amount: bool) -> Self {
|
||||
self.zero_amount = zero_amount;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! trigger_swap_update {
|
||||
@@ -3101,6 +3203,7 @@ mod tests {
|
||||
$args.initial_payment_state,
|
||||
$args.accepts_zero_conf,
|
||||
$args.user_lockup_tx_id,
|
||||
$args.zero_amount,
|
||||
);
|
||||
$persister.insert_or_update_chain_swap(&swap).unwrap();
|
||||
Swap::Chain(swap)
|
||||
@@ -3274,6 +3377,7 @@ mod tests {
|
||||
status_stream.clone(),
|
||||
liquid_chain_service.clone(),
|
||||
bitcoin_chain_service.clone(),
|
||||
None,
|
||||
)?);
|
||||
|
||||
LiquidSdk::track_swap_updates(&sdk).await;
|
||||
@@ -3463,4 +3567,139 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_zero_amount_chain_swap_zero_leeway() -> Result<()> {
|
||||
let user_lockup_sat = 50_000;
|
||||
|
||||
let (_tmp_dir, persister) = new_persister()?;
|
||||
let persister = Arc::new(persister);
|
||||
let swapper = Arc::new(MockSwapper::new());
|
||||
let status_stream = Arc::new(MockStatusStream::new());
|
||||
let liquid_chain_service = Arc::new(Mutex::new(MockLiquidChainService::new()));
|
||||
let bitcoin_chain_service = Arc::new(Mutex::new(MockBitcoinChainService::new()));
|
||||
|
||||
let sdk = Arc::new(new_liquid_sdk_with_chain_services(
|
||||
persister.clone(),
|
||||
swapper.clone(),
|
||||
status_stream.clone(),
|
||||
liquid_chain_service.clone(),
|
||||
bitcoin_chain_service.clone(),
|
||||
None,
|
||||
)?);
|
||||
|
||||
LiquidSdk::track_swap_updates(&sdk).await;
|
||||
|
||||
// We spawn a new thread since updates can only be sent when called via async runtimes
|
||||
tokio::spawn(async move {
|
||||
// Verify that `TransactionLockupFailed` correctly:
|
||||
// 1. does not affect state when swapper doesn't increase fees
|
||||
// 2. triggers a change to WaitingFeeAcceptance when there is a fee increase > 0
|
||||
for fee_increase in [0, 1] {
|
||||
swapper.set_zero_amount_swap_mock_config(ZeroAmountSwapMockConfig {
|
||||
user_lockup_sat,
|
||||
onchain_fee_increase_sat: fee_increase,
|
||||
});
|
||||
bitcoin_chain_service
|
||||
.lock()
|
||||
.await
|
||||
.set_script_balance_sat(user_lockup_sat);
|
||||
let persisted_swap = trigger_swap_update!(
|
||||
"chain",
|
||||
NewSwapArgs::default()
|
||||
.set_direction(Direction::Incoming)
|
||||
.set_accepts_zero_conf(false)
|
||||
.set_zero_amount(true),
|
||||
persister,
|
||||
status_stream,
|
||||
ChainSwapStates::TransactionLockupFailed,
|
||||
None,
|
||||
None
|
||||
);
|
||||
match fee_increase {
|
||||
0 => {
|
||||
assert_eq!(persisted_swap.state, PaymentState::Created);
|
||||
}
|
||||
1 => {
|
||||
assert_eq!(persisted_swap.state, PaymentState::WaitingFeeAcceptance);
|
||||
}
|
||||
_ => panic!("Unexpected fee_increase"),
|
||||
}
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_zero_amount_chain_swap_with_leeway() -> Result<()> {
|
||||
let user_lockup_sat = 50_000;
|
||||
let onchain_fee_rate_leeway_sat_per_vbyte = 5;
|
||||
|
||||
let (_tmp_dir, persister) = new_persister()?;
|
||||
let persister = Arc::new(persister);
|
||||
let swapper = Arc::new(MockSwapper::new());
|
||||
let status_stream = Arc::new(MockStatusStream::new());
|
||||
let liquid_chain_service = Arc::new(Mutex::new(MockLiquidChainService::new()));
|
||||
let bitcoin_chain_service = Arc::new(Mutex::new(MockBitcoinChainService::new()));
|
||||
|
||||
let sdk = Arc::new(new_liquid_sdk_with_chain_services(
|
||||
persister.clone(),
|
||||
swapper.clone(),
|
||||
status_stream.clone(),
|
||||
liquid_chain_service.clone(),
|
||||
bitcoin_chain_service.clone(),
|
||||
Some(onchain_fee_rate_leeway_sat_per_vbyte),
|
||||
)?);
|
||||
|
||||
LiquidSdk::track_swap_updates(&sdk).await;
|
||||
|
||||
let max_fee_increase_for_auto_accept_sat =
|
||||
onchain_fee_rate_leeway_sat_per_vbyte as u64 * ESTIMATED_BTC_LOCKUP_TX_VSIZE;
|
||||
|
||||
// We spawn a new thread since updates can only be sent when called via async runtimes
|
||||
tokio::spawn(async move {
|
||||
// Verify that `TransactionLockupFailed` correctly:
|
||||
// 1. does not affect state when swapper increases fee by up to sat/vbyte leeway * tx size
|
||||
// 2. triggers a change to WaitingFeeAcceptance when it is any higher
|
||||
for fee_increase in [
|
||||
max_fee_increase_for_auto_accept_sat,
|
||||
max_fee_increase_for_auto_accept_sat + 1,
|
||||
] {
|
||||
swapper.set_zero_amount_swap_mock_config(ZeroAmountSwapMockConfig {
|
||||
user_lockup_sat,
|
||||
onchain_fee_increase_sat: fee_increase,
|
||||
});
|
||||
bitcoin_chain_service
|
||||
.lock()
|
||||
.await
|
||||
.set_script_balance_sat(user_lockup_sat);
|
||||
let persisted_swap = trigger_swap_update!(
|
||||
"chain",
|
||||
NewSwapArgs::default()
|
||||
.set_direction(Direction::Incoming)
|
||||
.set_accepts_zero_conf(false)
|
||||
.set_zero_amount(true),
|
||||
persister,
|
||||
status_stream,
|
||||
ChainSwapStates::TransactionLockupFailed,
|
||||
None,
|
||||
None
|
||||
);
|
||||
match fee_increase {
|
||||
val if val == max_fee_increase_for_auto_accept_sat => {
|
||||
assert_eq!(persisted_swap.state, PaymentState::Created);
|
||||
}
|
||||
val if val == (max_fee_increase_for_auto_accept_sat + 1) => {
|
||||
assert_eq!(persisted_swap.state, PaymentState::WaitingFeeAcceptance);
|
||||
}
|
||||
_ => panic!("Unexpected fee_increase"),
|
||||
}
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -592,6 +592,10 @@ impl SendSwapHandler {
|
||||
err: format!("Cannot transition from {from_state:?} to Failed state"),
|
||||
}),
|
||||
(_, Failed) => Ok(()),
|
||||
|
||||
(_, WaitingFeeAcceptance) => Err(PaymentError::Generic {
|
||||
err: format!("Cannot transition from {from_state:?} to WaitingFeeAcceptance state"),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ impl Swapper for BoltzSwapper {
|
||||
Ok((pair_outgoing, pair_incoming))
|
||||
}
|
||||
|
||||
fn get_zero_amount_chain_swap_quote(&self, swap_id: &str) -> Result<Amount, PaymentError> {
|
||||
fn get_zero_amount_chain_swap_quote(&self, swap_id: &str) -> Result<Amount, SdkError> {
|
||||
self.client
|
||||
.get_quote(swap_id)
|
||||
.map(|r| Amount::from_sat(r.amount))
|
||||
|
||||
@@ -45,7 +45,7 @@ pub trait Swapper: Send + Sync {
|
||||
///
|
||||
/// If the user locked-up funds in the valid range this will return that amount. In all other
|
||||
/// cases, this will return an error.
|
||||
fn get_zero_amount_chain_swap_quote(&self, swap_id: &str) -> Result<Amount, PaymentError>;
|
||||
fn get_zero_amount_chain_swap_quote(&self, swap_id: &str) -> Result<Amount, SdkError>;
|
||||
|
||||
/// Accept a specific quote for a Zero-Amount Receive Chain Swap
|
||||
fn accept_zero_amount_chain_swap_quote(
|
||||
|
||||
@@ -128,17 +128,26 @@ impl LiquidChainService for MockLiquidChainService {
|
||||
|
||||
pub(crate) struct MockBitcoinChainService {
|
||||
history: Vec<MockHistory>,
|
||||
script_balance_sat: u64,
|
||||
}
|
||||
|
||||
impl MockBitcoinChainService {
|
||||
pub(crate) fn new() -> Self {
|
||||
MockBitcoinChainService { history: vec![] }
|
||||
MockBitcoinChainService {
|
||||
history: vec![],
|
||||
script_balance_sat: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_history(&mut self, history: Vec<MockHistory>) -> &mut Self {
|
||||
self.history = history;
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn set_script_balance_sat(&mut self, script_balance_sat: u64) -> &mut Self {
|
||||
self.script_balance_sat = script_balance_sat;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -208,7 +217,10 @@ impl BitcoinChainService for MockBitcoinChainService {
|
||||
_script: &boltz_client::bitcoin::Script,
|
||||
_retries: u64,
|
||||
) -> Result<electrum_client::GetBalanceRes> {
|
||||
unimplemented!()
|
||||
Ok(GetBalanceRes {
|
||||
confirmed: self.script_balance_sat,
|
||||
unconfirmed: 0,
|
||||
})
|
||||
}
|
||||
|
||||
async fn verify_tx(
|
||||
|
||||
@@ -49,7 +49,94 @@ pub(crate) fn new_chain_swap(
|
||||
payment_state: Option<PaymentState>,
|
||||
accept_zero_conf: bool,
|
||||
user_lockup_tx_id: Option<String>,
|
||||
zero_amount: bool,
|
||||
) -> ChainSwap {
|
||||
if zero_amount {
|
||||
if direction == Direction::Outgoing {
|
||||
panic!("Zero amount swaps must be incoming")
|
||||
}
|
||||
return ChainSwap {
|
||||
id: generate_random_string(4),
|
||||
direction: Direction::Incoming,
|
||||
claim_address: None,
|
||||
lockup_address: "tb1p7cftn5u3ndt8ln0m6hruwyhsz8kc5sxt557ua03qcew0z29u5paqh8f7uu"
|
||||
.to_string(),
|
||||
timeout_block_height: 2868778,
|
||||
preimage: "bbce422d96c0386c3a6c1b1fe11fc7be3fdd871c6855db6ab2e319e96ec19c78"
|
||||
.to_string(),
|
||||
description: Some("Bitcoin transfer".to_string()),
|
||||
create_response_json: r#"{
|
||||
"claim_details": {
|
||||
"swapTree": {
|
||||
"claimLeaf": {
|
||||
"output": "82012088a914e5ec6c5b814b2d8616c1a0da0acc8b3388cf80d78820e5f32fc89e6947ca08a7855a99ac145f7de599446a0cc0ff4c9aa2694baa1138ac",
|
||||
"version": 196
|
||||
},
|
||||
"refundLeaf": {
|
||||
"output": "20692bbff63e48c1c05c5efeb7080f7c2416d2f9ecb79d217410eabc125f4d2ff0ad0312a716b1",
|
||||
"version": 196
|
||||
}
|
||||
},
|
||||
"lockupAddress": "tlq1pq0gfse32q454tmr30t7yl6lx2sv5sswdzh3j0zygz9v5jwwdq6deaec8ntnjq55yrx300u9ts5ykqnfcpuzrypmtda9yuszq0zpl6j8l9tunvqjyrdm3",
|
||||
"serverPublicKey": "02692bbff63e48c1c05c5efeb7080f7c2416d2f9ecb79d217410eabc125f4d2ff0",
|
||||
"timeoutBlockHeight": 1484562,
|
||||
"amount": 0,
|
||||
"blindingKey": "ebdd91bb06b2282e879256ff1c1a976016a582fea5418188799b1598281b0a5b"
|
||||
},
|
||||
"lockup_details": {
|
||||
"swapTree": {
|
||||
"claimLeaf": {
|
||||
"output": "82012088a914e5ec6c5b814b2d8616c1a0da0acc8b3388cf80d7882039688adbf0625672ec56e713e65ce809ee84e96525a13a68fe521588bf41628cac",
|
||||
"version": 192
|
||||
},
|
||||
"refundLeaf": {
|
||||
"output": "20edf1db3da18ad19962c8dfd7566048c7dc2e11f3d6580cbfed8f9a1321ffe4c7ad032ac62bb1",
|
||||
"version": 192
|
||||
}
|
||||
},
|
||||
"lockupAddress": "tb1p7cftn5u3ndt8ln0m6hruwyhsz8kc5sxt557ua03qcew0z29u5paqh8f7uu",
|
||||
"serverPublicKey": "0239688adbf0625672ec56e713e65ce809ee84e96525a13a68fe521588bf41628c",
|
||||
"timeoutBlockHeight": 2868778,
|
||||
"amount": 0,
|
||||
"bip21": "bitcoin:tb1p7cftn5u3ndt8ln0m6hruwyhsz8kc5sxt557ua03qcew0z29u5paqh8f7uu?amount=0.0001836&label=Send%20to%20L-BTC%20address"
|
||||
}
|
||||
}"#.to_string(),
|
||||
claim_private_key: "4b04c3b95570fc48c7f33bc900b801245c2be31b90d41616477574aedc5b9d28"
|
||||
.to_string(),
|
||||
refund_private_key: "9e23d322577cfeb2b5490f3f86db58c806004afcb7c88995927bfdfc1c64cd8c"
|
||||
.to_string(),
|
||||
payer_amount_sat: 0,
|
||||
receiver_amount_sat: 0,
|
||||
claim_fees_sat: 144,
|
||||
server_lockup_tx_id: None,
|
||||
user_lockup_tx_id,
|
||||
claim_tx_id: None,
|
||||
refund_tx_id: None,
|
||||
created_at: utils::now(),
|
||||
state: payment_state.unwrap_or(PaymentState::Created),
|
||||
accept_zero_conf,
|
||||
pair_fees_json: r#"{
|
||||
"hash": "43087e267db95668b9b7c48efcf44d922484870f1bdb8b926e5d6b76bf4d0709",
|
||||
"rate": 1,
|
||||
"limits": {
|
||||
"maximal": 4294967,
|
||||
"minimal": 10000,
|
||||
"maximalZeroConf": 0
|
||||
},
|
||||
"fees": {
|
||||
"percentage": 0.1,
|
||||
"minerFees": {
|
||||
"server": 100,
|
||||
"user": {
|
||||
"claim": 100,
|
||||
"lockup": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
}"#
|
||||
.to_string(),
|
||||
};
|
||||
}
|
||||
match direction {
|
||||
Direction::Incoming => ChainSwap {
|
||||
id: generate_random_string(4),
|
||||
|
||||
@@ -21,7 +21,7 @@ pub(crate) fn new_receive_swap_handler(persister: Arc<Persister>) -> Result<Rece
|
||||
let config = Config::testnet(None);
|
||||
let signer: Arc<Box<dyn Signer>> = Arc::new(Box::new(MockSigner::new()?));
|
||||
let onchain_wallet = Arc::new(MockWallet::new(signer)?);
|
||||
let swapper = Arc::new(MockSwapper::new());
|
||||
let swapper = Arc::new(MockSwapper::default());
|
||||
let liquid_chain_service = Arc::new(Mutex::new(MockLiquidChainService::new()));
|
||||
|
||||
Ok(ReceiveSwapHandler::new(
|
||||
|
||||
@@ -40,6 +40,7 @@ pub(crate) fn new_liquid_sdk(
|
||||
status_stream,
|
||||
liquid_chain_service,
|
||||
bitcoin_chain_service,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -49,6 +50,7 @@ pub(crate) fn new_liquid_sdk_with_chain_services(
|
||||
status_stream: Arc<MockStatusStream>,
|
||||
liquid_chain_service: Arc<Mutex<MockLiquidChainService>>,
|
||||
bitcoin_chain_service: Arc<Mutex<MockBitcoinChainService>>,
|
||||
onchain_fee_rate_leeway_sat_per_vbyte: Option<u32>,
|
||||
) -> Result<LiquidSdk> {
|
||||
let mut config = Config::testnet(None);
|
||||
config.working_dir = persister
|
||||
@@ -56,6 +58,7 @@ pub(crate) fn new_liquid_sdk_with_chain_services(
|
||||
.to_str()
|
||||
.ok_or(anyhow!("An invalid SDK directory was specified"))?
|
||||
.to_string();
|
||||
config.onchain_fee_rate_leeway_sat_per_vbyte = onchain_fee_rate_leeway_sat_per_vbyte;
|
||||
|
||||
let signer: Arc<Box<dyn Signer>> = Arc::new(Box::new(MockSigner::new()?));
|
||||
let onchain_wallet = Arc::new(MockWallet::new(signer.clone())?);
|
||||
|
||||
@@ -20,7 +20,7 @@ pub(crate) fn new_send_swap_handler(persister: Arc<Persister>) -> Result<SendSwa
|
||||
let config = Config::testnet(None);
|
||||
let signer: Arc<Box<dyn Signer>> = Arc::new(Box::new(MockSigner::new()?));
|
||||
let onchain_wallet = Arc::new(MockWallet::new(signer)?);
|
||||
let swapper = Arc::new(MockSwapper::new());
|
||||
let swapper = Arc::new(MockSwapper::default());
|
||||
let chain_service = Arc::new(Mutex::new(MockLiquidChainService::new()));
|
||||
|
||||
Ok(SendSwapHandler::new(
|
||||
|
||||
@@ -11,8 +11,10 @@ use boltz_client::{
|
||||
Amount, PublicKey,
|
||||
};
|
||||
use sdk_common::invoice::parse_invoice;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use crate::{
|
||||
ensure_sdk,
|
||||
error::{PaymentError, SdkError},
|
||||
model::{Direction, SendSwap, Swap, Transaction as SdkTransaction, Utxo},
|
||||
swapper::Swapper,
|
||||
@@ -23,7 +25,15 @@ use crate::{
|
||||
use super::status_stream::MockStatusStream;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MockSwapper {}
|
||||
pub struct ZeroAmountSwapMockConfig {
|
||||
pub user_lockup_sat: u64,
|
||||
pub onchain_fee_increase_sat: u64,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MockSwapper {
|
||||
zero_amount_swap_mock_config: Mutex<ZeroAmountSwapMockConfig>,
|
||||
}
|
||||
|
||||
impl MockSwapper {
|
||||
pub(crate) fn new() -> Self {
|
||||
@@ -60,6 +70,26 @@ impl MockSwapper {
|
||||
bip21: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_zero_amount_swap_mock_config(&self, config: ZeroAmountSwapMockConfig) {
|
||||
*self.zero_amount_swap_mock_config.lock().unwrap() = config;
|
||||
}
|
||||
|
||||
fn get_zero_amount_swap_server_lockup_sat(&self) -> u64 {
|
||||
let zero_amount_swap_mock_config = self.zero_amount_swap_mock_config.lock().unwrap();
|
||||
|
||||
let pair = self
|
||||
.get_chain_pair(Direction::Incoming)
|
||||
.expect("mock get_chain_pair failed")
|
||||
.expect("no chainpair in mock");
|
||||
|
||||
let fees = pair
|
||||
.fees
|
||||
.boltz(zero_amount_swap_mock_config.user_lockup_sat)
|
||||
+ pair.fees.server()
|
||||
+ zero_amount_swap_mock_config.onchain_fee_increase_sat;
|
||||
zero_amount_swap_mock_config.user_lockup_sat - fees
|
||||
}
|
||||
}
|
||||
|
||||
impl Swapper for MockSwapper {
|
||||
@@ -314,15 +344,20 @@ impl Swapper for MockSwapper {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_zero_amount_chain_swap_quote(&self, _swap_id: &str) -> Result<Amount, PaymentError> {
|
||||
unimplemented!()
|
||||
fn get_zero_amount_chain_swap_quote(&self, _swap_id: &str) -> Result<Amount, SdkError> {
|
||||
let server_lockup_amount_sat = self.get_zero_amount_swap_server_lockup_sat();
|
||||
Ok(Amount::from_sat(server_lockup_amount_sat))
|
||||
}
|
||||
|
||||
fn accept_zero_amount_chain_swap_quote(
|
||||
&self,
|
||||
_swap_id: &str,
|
||||
_server_lockup_sat: u64,
|
||||
server_lockup_sat: u64,
|
||||
) -> Result<(), PaymentError> {
|
||||
unimplemented!()
|
||||
ensure_sdk!(
|
||||
server_lockup_sat == self.get_zero_amount_swap_server_lockup_sat(),
|
||||
PaymentError::InvalidOrExpiredFees
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1476,12 +1476,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return dco_decode_ln_url_error_data(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
LnUrlInfo dco_decode_box_autoadd_ln_url_info(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return dco_decode_ln_url_info(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
LnUrlPayErrorData dco_decode_box_autoadd_ln_url_pay_error_data(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@@ -1709,11 +1703,11 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
network: dco_decode_liquid_network(arr[5]),
|
||||
paymentTimeoutSec: dco_decode_u_64(arr[6]),
|
||||
zeroConfMinFeeRateMsat: dco_decode_u_32(arr[7]),
|
||||
syncServiceUrl: dco_decode_String(arr[8]),
|
||||
zeroConfMaxAmountSat: dco_decode_opt_box_autoadd_u_64(arr[9]),
|
||||
breezApiKey: dco_decode_opt_String(arr[10]),
|
||||
externalInputParsers: dco_decode_opt_list_external_input_parser(arr[11]),
|
||||
useDefaultExternalInputParsers: dco_decode_bool(arr[12]),
|
||||
zeroConfMaxAmountSat: dco_decode_opt_box_autoadd_u_64(arr[8]),
|
||||
breezApiKey: dco_decode_opt_String(arr[9]),
|
||||
externalInputParsers: dco_decode_opt_list_external_input_parser(arr[10]),
|
||||
useDefaultExternalInputParsers: dco_decode_bool(arr[11]),
|
||||
onchainFeeRateLeewaySatPerVbyte: dco_decode_opt_box_autoadd_u_32(arr[12]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1964,12 +1958,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
List<PaymentState> dco_decode_list_payment_state(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return (raw as List<dynamic>).map(dco_decode_payment_state).toList();
|
||||
}
|
||||
|
||||
@protected
|
||||
List<PaymentType> dco_decode_list_payment_type(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@@ -1980,15 +1968,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
ListPaymentsRequest dco_decode_list_payments_request(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
final arr = raw as List<dynamic>;
|
||||
if (arr.length != 7) throw Exception('unexpected arr length: expect 7 but see ${arr.length}');
|
||||
if (arr.length != 6) throw Exception('unexpected arr length: expect 6 but see ${arr.length}');
|
||||
return ListPaymentsRequest(
|
||||
filters: dco_decode_opt_list_payment_type(arr[0]),
|
||||
states: dco_decode_opt_list_payment_state(arr[1]),
|
||||
fromTimestamp: dco_decode_opt_box_autoadd_i_64(arr[2]),
|
||||
toTimestamp: dco_decode_opt_box_autoadd_i_64(arr[3]),
|
||||
offset: dco_decode_opt_box_autoadd_u_32(arr[4]),
|
||||
limit: dco_decode_opt_box_autoadd_u_32(arr[5]),
|
||||
details: dco_decode_opt_box_autoadd_list_payment_details(arr[6]),
|
||||
fromTimestamp: dco_decode_opt_box_autoadd_i_64(arr[1]),
|
||||
toTimestamp: dco_decode_opt_box_autoadd_i_64(arr[2]),
|
||||
offset: dco_decode_opt_box_autoadd_u_32(arr[3]),
|
||||
limit: dco_decode_opt_box_autoadd_u_32(arr[4]),
|
||||
details: dco_decode_opt_box_autoadd_list_payment_details(arr[5]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2129,22 +2116,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
LnUrlInfo dco_decode_ln_url_info(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
final arr = raw as List<dynamic>;
|
||||
if (arr.length != 7) throw Exception('unexpected arr length: expect 7 but see ${arr.length}');
|
||||
return LnUrlInfo(
|
||||
lnAddress: dco_decode_opt_String(arr[0]),
|
||||
lnurlPayComment: dco_decode_opt_String(arr[1]),
|
||||
lnurlPayDomain: dco_decode_opt_String(arr[2]),
|
||||
lnurlPayMetadata: dco_decode_opt_String(arr[3]),
|
||||
lnurlPaySuccessAction: dco_decode_opt_box_autoadd_success_action_processed(arr[4]),
|
||||
lnurlPayUnprocessedSuccessAction: dco_decode_opt_box_autoadd_success_action(arr[5]),
|
||||
lnurlWithdrawEndpoint: dco_decode_opt_String(arr[6]),
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
LnUrlPayError dco_decode_ln_url_pay_error(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@@ -2458,12 +2429,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return raw == null ? null : dco_decode_box_autoadd_list_payment_details(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
LnUrlInfo? dco_decode_opt_box_autoadd_ln_url_info(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return raw == null ? null : dco_decode_box_autoadd_ln_url_info(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
PayAmount? dco_decode_opt_box_autoadd_pay_amount(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@@ -2512,12 +2477,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return raw == null ? null : dco_decode_list_external_input_parser(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
List<PaymentState>? dco_decode_opt_list_payment_state(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return raw == null ? null : dco_decode_list_payment_state(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
List<PaymentType>? dco_decode_opt_list_payment_type(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@@ -2581,9 +2540,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
bolt11: dco_decode_opt_String(raw[4]),
|
||||
bolt12Offer: dco_decode_opt_String(raw[5]),
|
||||
paymentHash: dco_decode_opt_String(raw[6]),
|
||||
lnurlInfo: dco_decode_opt_box_autoadd_ln_url_info(raw[7]),
|
||||
refundTxId: dco_decode_opt_String(raw[8]),
|
||||
refundTxAmountSat: dco_decode_opt_box_autoadd_u_64(raw[9]),
|
||||
refundTxId: dco_decode_opt_String(raw[7]),
|
||||
refundTxAmountSat: dco_decode_opt_box_autoadd_u_64(raw[8]),
|
||||
);
|
||||
case 1:
|
||||
return PaymentDetails_Liquid(
|
||||
@@ -2732,13 +2690,11 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
PrepareLnUrlPayResponse dco_decode_prepare_ln_url_pay_response(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
final arr = raw as List<dynamic>;
|
||||
if (arr.length != 5) throw Exception('unexpected arr length: expect 5 but see ${arr.length}');
|
||||
if (arr.length != 3) throw Exception('unexpected arr length: expect 3 but see ${arr.length}');
|
||||
return PrepareLnUrlPayResponse(
|
||||
destination: dco_decode_send_destination(arr[0]),
|
||||
feesSat: dco_decode_u_64(arr[1]),
|
||||
data: dco_decode_ln_url_pay_request_data(arr[2]),
|
||||
comment: dco_decode_opt_String(arr[3]),
|
||||
successAction: dco_decode_opt_box_autoadd_success_action(arr[4]),
|
||||
successAction: dco_decode_opt_box_autoadd_success_action(arr[2]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3004,6 +2960,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
details: dco_decode_box_autoadd_payment(raw[1]),
|
||||
);
|
||||
case 6:
|
||||
return SdkEvent_PaymentWaitingFeeAcceptance(
|
||||
details: dco_decode_box_autoadd_payment(raw[1]),
|
||||
);
|
||||
case 7:
|
||||
return SdkEvent_Synced();
|
||||
default:
|
||||
throw Exception("unreachable");
|
||||
@@ -3443,12 +3403,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return (sse_decode_ln_url_error_data(deserializer));
|
||||
}
|
||||
|
||||
@protected
|
||||
LnUrlInfo sse_decode_box_autoadd_ln_url_info(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
return (sse_decode_ln_url_info(deserializer));
|
||||
}
|
||||
|
||||
@protected
|
||||
LnUrlPayErrorData sse_decode_box_autoadd_ln_url_pay_error_data(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -3665,11 +3619,11 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
var var_network = sse_decode_liquid_network(deserializer);
|
||||
var var_paymentTimeoutSec = sse_decode_u_64(deserializer);
|
||||
var var_zeroConfMinFeeRateMsat = sse_decode_u_32(deserializer);
|
||||
var var_syncServiceUrl = sse_decode_String(deserializer);
|
||||
var var_zeroConfMaxAmountSat = sse_decode_opt_box_autoadd_u_64(deserializer);
|
||||
var var_breezApiKey = sse_decode_opt_String(deserializer);
|
||||
var var_externalInputParsers = sse_decode_opt_list_external_input_parser(deserializer);
|
||||
var var_useDefaultExternalInputParsers = sse_decode_bool(deserializer);
|
||||
var var_onchainFeeRateLeewaySatPerVbyte = sse_decode_opt_box_autoadd_u_32(deserializer);
|
||||
return Config(
|
||||
liquidElectrumUrl: var_liquidElectrumUrl,
|
||||
bitcoinElectrumUrl: var_bitcoinElectrumUrl,
|
||||
@@ -3679,11 +3633,11 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
network: var_network,
|
||||
paymentTimeoutSec: var_paymentTimeoutSec,
|
||||
zeroConfMinFeeRateMsat: var_zeroConfMinFeeRateMsat,
|
||||
syncServiceUrl: var_syncServiceUrl,
|
||||
zeroConfMaxAmountSat: var_zeroConfMaxAmountSat,
|
||||
breezApiKey: var_breezApiKey,
|
||||
externalInputParsers: var_externalInputParsers,
|
||||
useDefaultExternalInputParsers: var_useDefaultExternalInputParsers);
|
||||
useDefaultExternalInputParsers: var_useDefaultExternalInputParsers,
|
||||
onchainFeeRateLeewaySatPerVbyte: var_onchainFeeRateLeewaySatPerVbyte);
|
||||
}
|
||||
|
||||
@protected
|
||||
@@ -3964,18 +3918,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
List<PaymentState> sse_decode_list_payment_state(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
|
||||
var len_ = sse_decode_i_32(deserializer);
|
||||
var ans_ = <PaymentState>[];
|
||||
for (var idx_ = 0; idx_ < len_; ++idx_) {
|
||||
ans_.add(sse_decode_payment_state(deserializer));
|
||||
}
|
||||
return ans_;
|
||||
}
|
||||
|
||||
@protected
|
||||
List<PaymentType> sse_decode_list_payment_type(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -3992,7 +3934,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
ListPaymentsRequest sse_decode_list_payments_request(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
var var_filters = sse_decode_opt_list_payment_type(deserializer);
|
||||
var var_states = sse_decode_opt_list_payment_state(deserializer);
|
||||
var var_fromTimestamp = sse_decode_opt_box_autoadd_i_64(deserializer);
|
||||
var var_toTimestamp = sse_decode_opt_box_autoadd_i_64(deserializer);
|
||||
var var_offset = sse_decode_opt_box_autoadd_u_32(deserializer);
|
||||
@@ -4000,7 +3941,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
var var_details = sse_decode_opt_box_autoadd_list_payment_details(deserializer);
|
||||
return ListPaymentsRequest(
|
||||
filters: var_filters,
|
||||
states: var_states,
|
||||
fromTimestamp: var_fromTimestamp,
|
||||
toTimestamp: var_toTimestamp,
|
||||
offset: var_offset,
|
||||
@@ -4175,26 +4115,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return LnUrlErrorData(reason: var_reason);
|
||||
}
|
||||
|
||||
@protected
|
||||
LnUrlInfo sse_decode_ln_url_info(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
var var_lnAddress = sse_decode_opt_String(deserializer);
|
||||
var var_lnurlPayComment = sse_decode_opt_String(deserializer);
|
||||
var var_lnurlPayDomain = sse_decode_opt_String(deserializer);
|
||||
var var_lnurlPayMetadata = sse_decode_opt_String(deserializer);
|
||||
var var_lnurlPaySuccessAction = sse_decode_opt_box_autoadd_success_action_processed(deserializer);
|
||||
var var_lnurlPayUnprocessedSuccessAction = sse_decode_opt_box_autoadd_success_action(deserializer);
|
||||
var var_lnurlWithdrawEndpoint = sse_decode_opt_String(deserializer);
|
||||
return LnUrlInfo(
|
||||
lnAddress: var_lnAddress,
|
||||
lnurlPayComment: var_lnurlPayComment,
|
||||
lnurlPayDomain: var_lnurlPayDomain,
|
||||
lnurlPayMetadata: var_lnurlPayMetadata,
|
||||
lnurlPaySuccessAction: var_lnurlPaySuccessAction,
|
||||
lnurlPayUnprocessedSuccessAction: var_lnurlPayUnprocessedSuccessAction,
|
||||
lnurlWithdrawEndpoint: var_lnurlWithdrawEndpoint);
|
||||
}
|
||||
|
||||
@protected
|
||||
LnUrlPayError sse_decode_ln_url_pay_error(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -4502,17 +4422,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
LnUrlInfo? sse_decode_opt_box_autoadd_ln_url_info(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
|
||||
if (sse_decode_bool(deserializer)) {
|
||||
return (sse_decode_box_autoadd_ln_url_info(deserializer));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
PayAmount? sse_decode_opt_box_autoadd_pay_amount(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -4601,17 +4510,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
List<PaymentState>? sse_decode_opt_list_payment_state(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
|
||||
if (sse_decode_bool(deserializer)) {
|
||||
return (sse_decode_list_payment_state(deserializer));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
List<PaymentType>? sse_decode_opt_list_payment_type(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -4686,7 +4584,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
var var_bolt11 = sse_decode_opt_String(deserializer);
|
||||
var var_bolt12Offer = sse_decode_opt_String(deserializer);
|
||||
var var_paymentHash = sse_decode_opt_String(deserializer);
|
||||
var var_lnurlInfo = sse_decode_opt_box_autoadd_ln_url_info(deserializer);
|
||||
var var_refundTxId = sse_decode_opt_String(deserializer);
|
||||
var var_refundTxAmountSat = sse_decode_opt_box_autoadd_u_64(deserializer);
|
||||
return PaymentDetails_Lightning(
|
||||
@@ -4696,7 +4593,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
bolt11: var_bolt11,
|
||||
bolt12Offer: var_bolt12Offer,
|
||||
paymentHash: var_paymentHash,
|
||||
lnurlInfo: var_lnurlInfo,
|
||||
refundTxId: var_refundTxId,
|
||||
refundTxAmountSat: var_refundTxAmountSat);
|
||||
case 1:
|
||||
@@ -4839,15 +4735,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
var var_destination = sse_decode_send_destination(deserializer);
|
||||
var var_feesSat = sse_decode_u_64(deserializer);
|
||||
var var_data = sse_decode_ln_url_pay_request_data(deserializer);
|
||||
var var_comment = sse_decode_opt_String(deserializer);
|
||||
var var_successAction = sse_decode_opt_box_autoadd_success_action(deserializer);
|
||||
return PrepareLnUrlPayResponse(
|
||||
destination: var_destination,
|
||||
feesSat: var_feesSat,
|
||||
data: var_data,
|
||||
comment: var_comment,
|
||||
successAction: var_successAction);
|
||||
destination: var_destination, feesSat: var_feesSat, successAction: var_successAction);
|
||||
}
|
||||
|
||||
@protected
|
||||
@@ -5084,6 +4974,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
var var_details = sse_decode_box_autoadd_payment(deserializer);
|
||||
return SdkEvent_PaymentWaitingConfirmation(details: var_details);
|
||||
case 6:
|
||||
var var_details = sse_decode_box_autoadd_payment(deserializer);
|
||||
return SdkEvent_PaymentWaitingFeeAcceptance(details: var_details);
|
||||
case 7:
|
||||
return SdkEvent_Synced();
|
||||
default:
|
||||
throw UnimplementedError('');
|
||||
@@ -5596,12 +5489,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
sse_encode_ln_url_error_data(self, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_ln_url_info(LnUrlInfo self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_ln_url_info(self, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_ln_url_pay_error_data(LnUrlPayErrorData self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -5821,11 +5708,11 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
sse_encode_liquid_network(self.network, serializer);
|
||||
sse_encode_u_64(self.paymentTimeoutSec, serializer);
|
||||
sse_encode_u_32(self.zeroConfMinFeeRateMsat, serializer);
|
||||
sse_encode_String(self.syncServiceUrl, serializer);
|
||||
sse_encode_opt_box_autoadd_u_64(self.zeroConfMaxAmountSat, serializer);
|
||||
sse_encode_opt_String(self.breezApiKey, serializer);
|
||||
sse_encode_opt_list_external_input_parser(self.externalInputParsers, serializer);
|
||||
sse_encode_bool(self.useDefaultExternalInputParsers, serializer);
|
||||
sse_encode_opt_box_autoadd_u_32(self.onchainFeeRateLeewaySatPerVbyte, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
@@ -6052,15 +5939,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_list_payment_state(List<PaymentState> self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_i_32(self.length, serializer);
|
||||
for (final item in self) {
|
||||
sse_encode_payment_state(item, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_list_payment_type(List<PaymentType> self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -6074,7 +5952,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
void sse_encode_list_payments_request(ListPaymentsRequest self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_opt_list_payment_type(self.filters, serializer);
|
||||
sse_encode_opt_list_payment_state(self.states, serializer);
|
||||
sse_encode_opt_box_autoadd_i_64(self.fromTimestamp, serializer);
|
||||
sse_encode_opt_box_autoadd_i_64(self.toTimestamp, serializer);
|
||||
sse_encode_opt_box_autoadd_u_32(self.offset, serializer);
|
||||
@@ -6208,18 +6085,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
sse_encode_String(self.reason, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_ln_url_info(LnUrlInfo self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_opt_String(self.lnAddress, serializer);
|
||||
sse_encode_opt_String(self.lnurlPayComment, serializer);
|
||||
sse_encode_opt_String(self.lnurlPayDomain, serializer);
|
||||
sse_encode_opt_String(self.lnurlPayMetadata, serializer);
|
||||
sse_encode_opt_box_autoadd_success_action_processed(self.lnurlPaySuccessAction, serializer);
|
||||
sse_encode_opt_box_autoadd_success_action(self.lnurlPayUnprocessedSuccessAction, serializer);
|
||||
sse_encode_opt_String(self.lnurlWithdrawEndpoint, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_ln_url_pay_error(LnUrlPayError self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -6487,16 +6352,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_opt_box_autoadd_ln_url_info(LnUrlInfo? self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
|
||||
sse_encode_bool(self != null, serializer);
|
||||
if (self != null) {
|
||||
sse_encode_box_autoadd_ln_url_info(self, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_opt_box_autoadd_pay_amount(PayAmount? self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -6578,16 +6433,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_opt_list_payment_state(List<PaymentState>? self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
|
||||
sse_encode_bool(self != null, serializer);
|
||||
if (self != null) {
|
||||
sse_encode_list_payment_state(self, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_opt_list_payment_type(List<PaymentType>? self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -6645,7 +6490,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
bolt11: final bolt11,
|
||||
bolt12Offer: final bolt12Offer,
|
||||
paymentHash: final paymentHash,
|
||||
lnurlInfo: final lnurlInfo,
|
||||
refundTxId: final refundTxId,
|
||||
refundTxAmountSat: final refundTxAmountSat
|
||||
):
|
||||
@@ -6656,7 +6500,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
sse_encode_opt_String(bolt11, serializer);
|
||||
sse_encode_opt_String(bolt12Offer, serializer);
|
||||
sse_encode_opt_String(paymentHash, serializer);
|
||||
sse_encode_opt_box_autoadd_ln_url_info(lnurlInfo, serializer);
|
||||
sse_encode_opt_String(refundTxId, serializer);
|
||||
sse_encode_opt_box_autoadd_u_64(refundTxAmountSat, serializer);
|
||||
case PaymentDetails_Liquid(destination: final destination, description: final description):
|
||||
@@ -6788,8 +6631,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_send_destination(self.destination, serializer);
|
||||
sse_encode_u_64(self.feesSat, serializer);
|
||||
sse_encode_ln_url_pay_request_data(self.data, serializer);
|
||||
sse_encode_opt_String(self.comment, serializer);
|
||||
sse_encode_opt_box_autoadd_success_action(self.successAction, serializer);
|
||||
}
|
||||
|
||||
@@ -6974,8 +6815,11 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
case SdkEvent_PaymentWaitingConfirmation(details: final details):
|
||||
sse_encode_i_32(5, serializer);
|
||||
sse_encode_box_autoadd_payment(details, serializer);
|
||||
case SdkEvent_Synced():
|
||||
case SdkEvent_PaymentWaitingFeeAcceptance(details: final details):
|
||||
sse_encode_i_32(6, serializer);
|
||||
sse_encode_box_autoadd_payment(details, serializer);
|
||||
case SdkEvent_Synced():
|
||||
sse_encode_i_32(7, serializer);
|
||||
default:
|
||||
throw UnimplementedError('');
|
||||
}
|
||||
|
||||
@@ -2280,6 +2280,8 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
wireObj.breez_api_key = cst_encode_opt_String(apiObj.breezApiKey);
|
||||
wireObj.external_input_parsers = cst_encode_opt_list_external_input_parser(apiObj.externalInputParsers);
|
||||
wireObj.use_default_external_input_parsers = cst_encode_bool(apiObj.useDefaultExternalInputParsers);
|
||||
wireObj.onchain_fee_rate_leeway_sat_per_vbyte =
|
||||
cst_encode_opt_box_autoadd_u_32(apiObj.onchainFeeRateLeewaySatPerVbyte);
|
||||
}
|
||||
|
||||
@protected
|
||||
@@ -3217,8 +3219,14 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
wireObj.kind.PaymentWaitingConfirmation.details = pre_details;
|
||||
return;
|
||||
}
|
||||
if (apiObj is SdkEvent_Synced) {
|
||||
if (apiObj is SdkEvent_PaymentWaitingFeeAcceptance) {
|
||||
var pre_details = cst_encode_box_autoadd_payment(apiObj.details);
|
||||
wireObj.tag = 6;
|
||||
wireObj.kind.PaymentWaitingFeeAcceptance.details = pre_details;
|
||||
return;
|
||||
}
|
||||
if (apiObj is SdkEvent_Synced) {
|
||||
wireObj.tag = 7;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -6222,6 +6230,10 @@ final class wire_cst_SdkEvent_PaymentWaitingConfirmation extends ffi.Struct {
|
||||
external ffi.Pointer<wire_cst_payment> details;
|
||||
}
|
||||
|
||||
final class wire_cst_SdkEvent_PaymentWaitingFeeAcceptance extends ffi.Struct {
|
||||
external ffi.Pointer<wire_cst_payment> details;
|
||||
}
|
||||
|
||||
final class SdkEventKind extends ffi.Union {
|
||||
external wire_cst_SdkEvent_PaymentFailed PaymentFailed;
|
||||
|
||||
@@ -6234,6 +6246,8 @@ final class SdkEventKind extends ffi.Union {
|
||||
external wire_cst_SdkEvent_PaymentSucceeded PaymentSucceeded;
|
||||
|
||||
external wire_cst_SdkEvent_PaymentWaitingConfirmation PaymentWaitingConfirmation;
|
||||
|
||||
external wire_cst_SdkEvent_PaymentWaitingFeeAcceptance PaymentWaitingFeeAcceptance;
|
||||
}
|
||||
|
||||
final class wire_cst_sdk_event extends ffi.Struct {
|
||||
@@ -6288,6 +6302,8 @@ final class wire_cst_config extends ffi.Struct {
|
||||
|
||||
@ffi.Bool()
|
||||
external bool use_default_external_input_parsers;
|
||||
|
||||
external ffi.Pointer<ffi.Uint32> onchain_fee_rate_leeway_sat_per_vbyte;
|
||||
}
|
||||
|
||||
final class wire_cst_connect_request extends ffi.Struct {
|
||||
@@ -6906,6 +6922,8 @@ final class wire_cst_sign_message_response extends ffi.Struct {
|
||||
|
||||
const int ESTIMATED_BTC_CLAIM_TX_VSIZE = 111;
|
||||
|
||||
const int ESTIMATED_BTC_LOCKUP_TX_VSIZE = 154;
|
||||
|
||||
const double STANDARD_FEE_RATE_SAT_PER_VBYTE = 0.1;
|
||||
|
||||
const double LOWBALL_FEE_RATE_SAT_PER_VBYTE = 0.01;
|
||||
|
||||
@@ -154,6 +154,14 @@ class Config {
|
||||
/// Set this to false in order to prevent their use.
|
||||
final bool useDefaultExternalInputParsers;
|
||||
|
||||
/// For payments where the onchain fees can only be estimated on creation, this can be used
|
||||
/// in order to automatically allow slightly more expensive fees. If the actual fee rate ends up
|
||||
/// being above the sum of the initial estimate and this leeway, the payment will require
|
||||
/// user fee acceptance. See [WaitingFeeAcceptance](PaymentState::WaitingFeeAcceptance).
|
||||
///
|
||||
/// Defaults to zero.
|
||||
final int? onchainFeeRateLeewaySatPerVbyte;
|
||||
|
||||
const Config({
|
||||
required this.liquidElectrumUrl,
|
||||
required this.bitcoinElectrumUrl,
|
||||
@@ -168,6 +176,7 @@ class Config {
|
||||
this.breezApiKey,
|
||||
this.externalInputParsers,
|
||||
required this.useDefaultExternalInputParsers,
|
||||
this.onchainFeeRateLeewaySatPerVbyte,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -184,7 +193,8 @@ class Config {
|
||||
zeroConfMaxAmountSat.hashCode ^
|
||||
breezApiKey.hashCode ^
|
||||
externalInputParsers.hashCode ^
|
||||
useDefaultExternalInputParsers.hashCode;
|
||||
useDefaultExternalInputParsers.hashCode ^
|
||||
onchainFeeRateLeewaySatPerVbyte.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
@@ -203,7 +213,8 @@ class Config {
|
||||
zeroConfMaxAmountSat == other.zeroConfMaxAmountSat &&
|
||||
breezApiKey == other.breezApiKey &&
|
||||
externalInputParsers == other.externalInputParsers &&
|
||||
useDefaultExternalInputParsers == other.useDefaultExternalInputParsers;
|
||||
useDefaultExternalInputParsers == other.useDefaultExternalInputParsers &&
|
||||
onchainFeeRateLeewaySatPerVbyte == other.onchainFeeRateLeewaySatPerVbyte;
|
||||
}
|
||||
|
||||
/// An argument when calling [crate::sdk::LiquidSdk::connect].
|
||||
@@ -817,6 +828,19 @@ enum PaymentState {
|
||||
///
|
||||
/// When the refund tx is broadcast, `refund_tx_id` is set in the swap.
|
||||
refundPending,
|
||||
|
||||
/// ## Chain Swaps
|
||||
///
|
||||
/// This is the state when the user needs to accept new fees before the payment can proceed.
|
||||
///
|
||||
/// Use [LiquidSdk::fetch_payment_proposed_fees](crate::sdk::LiquidSdk::fetch_payment_proposed_fees)
|
||||
/// to find out the current fees and
|
||||
/// [LiquidSdk::accept_payment_proposed_fees](crate::sdk::LiquidSdk::accept_payment_proposed_fees)
|
||||
/// to accept them, allowing the payment to proceed.
|
||||
///
|
||||
/// Otherwise, this payment can be immediately refunded using
|
||||
/// [prepare_refund](crate::sdk::LiquidSdk::prepare_refund)/[refund](crate::sdk::LiquidSdk::refund).
|
||||
waitingFeeAcceptance,
|
||||
;
|
||||
}
|
||||
|
||||
@@ -1385,6 +1409,9 @@ sealed class SdkEvent with _$SdkEvent {
|
||||
const factory SdkEvent.paymentWaitingConfirmation({
|
||||
required Payment details,
|
||||
}) = SdkEvent_PaymentWaitingConfirmation;
|
||||
const factory SdkEvent.paymentWaitingFeeAcceptance({
|
||||
required Payment details,
|
||||
}) = SdkEvent_PaymentWaitingFeeAcceptance;
|
||||
const factory SdkEvent.synced() = SdkEvent_Synced;
|
||||
}
|
||||
|
||||
|
||||
@@ -1724,6 +1724,88 @@ abstract class SdkEvent_PaymentWaitingConfirmation extends SdkEvent {
|
||||
get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$SdkEvent_PaymentWaitingFeeAcceptanceImplCopyWith<$Res> {
|
||||
factory _$$SdkEvent_PaymentWaitingFeeAcceptanceImplCopyWith(
|
||||
_$SdkEvent_PaymentWaitingFeeAcceptanceImpl value,
|
||||
$Res Function(_$SdkEvent_PaymentWaitingFeeAcceptanceImpl) then) =
|
||||
__$$SdkEvent_PaymentWaitingFeeAcceptanceImplCopyWithImpl<$Res>;
|
||||
@useResult
|
||||
$Res call({Payment details});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$SdkEvent_PaymentWaitingFeeAcceptanceImplCopyWithImpl<$Res>
|
||||
extends _$SdkEventCopyWithImpl<$Res, _$SdkEvent_PaymentWaitingFeeAcceptanceImpl>
|
||||
implements _$$SdkEvent_PaymentWaitingFeeAcceptanceImplCopyWith<$Res> {
|
||||
__$$SdkEvent_PaymentWaitingFeeAcceptanceImplCopyWithImpl(_$SdkEvent_PaymentWaitingFeeAcceptanceImpl _value,
|
||||
$Res Function(_$SdkEvent_PaymentWaitingFeeAcceptanceImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of SdkEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? details = null,
|
||||
}) {
|
||||
return _then(_$SdkEvent_PaymentWaitingFeeAcceptanceImpl(
|
||||
details: null == details
|
||||
? _value.details
|
||||
: details // ignore: cast_nullable_to_non_nullable
|
||||
as Payment,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$SdkEvent_PaymentWaitingFeeAcceptanceImpl extends SdkEvent_PaymentWaitingFeeAcceptance {
|
||||
const _$SdkEvent_PaymentWaitingFeeAcceptanceImpl({required this.details}) : super._();
|
||||
|
||||
@override
|
||||
final Payment details;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SdkEvent.paymentWaitingFeeAcceptance(details: $details)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$SdkEvent_PaymentWaitingFeeAcceptanceImpl &&
|
||||
(identical(other.details, details) || other.details == details));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, details);
|
||||
|
||||
/// Create a copy of SdkEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$SdkEvent_PaymentWaitingFeeAcceptanceImplCopyWith<_$SdkEvent_PaymentWaitingFeeAcceptanceImpl>
|
||||
get copyWith => __$$SdkEvent_PaymentWaitingFeeAcceptanceImplCopyWithImpl<
|
||||
_$SdkEvent_PaymentWaitingFeeAcceptanceImpl>(this, _$identity);
|
||||
}
|
||||
|
||||
abstract class SdkEvent_PaymentWaitingFeeAcceptance extends SdkEvent {
|
||||
const factory SdkEvent_PaymentWaitingFeeAcceptance({required final Payment details}) =
|
||||
_$SdkEvent_PaymentWaitingFeeAcceptanceImpl;
|
||||
const SdkEvent_PaymentWaitingFeeAcceptance._() : super._();
|
||||
|
||||
Payment get details;
|
||||
|
||||
/// Create a copy of SdkEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$SdkEvent_PaymentWaitingFeeAcceptanceImplCopyWith<_$SdkEvent_PaymentWaitingFeeAcceptanceImpl>
|
||||
get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$SdkEvent_SyncedImplCopyWith<$Res> {
|
||||
factory _$$SdkEvent_SyncedImplCopyWith(
|
||||
|
||||
@@ -4642,6 +4642,10 @@ final class wire_cst_SdkEvent_PaymentWaitingConfirmation extends ffi.Struct {
|
||||
external ffi.Pointer<wire_cst_payment> details;
|
||||
}
|
||||
|
||||
final class wire_cst_SdkEvent_PaymentWaitingFeeAcceptance extends ffi.Struct {
|
||||
external ffi.Pointer<wire_cst_payment> details;
|
||||
}
|
||||
|
||||
final class SdkEventKind extends ffi.Union {
|
||||
external wire_cst_SdkEvent_PaymentFailed PaymentFailed;
|
||||
|
||||
@@ -4654,6 +4658,8 @@ final class SdkEventKind extends ffi.Union {
|
||||
external wire_cst_SdkEvent_PaymentSucceeded PaymentSucceeded;
|
||||
|
||||
external wire_cst_SdkEvent_PaymentWaitingConfirmation PaymentWaitingConfirmation;
|
||||
|
||||
external wire_cst_SdkEvent_PaymentWaitingFeeAcceptance PaymentWaitingFeeAcceptance;
|
||||
}
|
||||
|
||||
final class wire_cst_sdk_event extends ffi.Struct {
|
||||
@@ -4708,6 +4714,8 @@ final class wire_cst_config extends ffi.Struct {
|
||||
|
||||
@ffi.Bool()
|
||||
external bool use_default_external_input_parsers;
|
||||
|
||||
external ffi.Pointer<ffi.Uint32> onchain_fee_rate_leeway_sat_per_vbyte;
|
||||
}
|
||||
|
||||
final class wire_cst_connect_request extends ffi.Struct {
|
||||
@@ -5367,6 +5375,8 @@ typedef DartUniFfiRustFutureContinuationFunction = void Function(ffi.Pointer<ffi
|
||||
|
||||
const int ESTIMATED_BTC_CLAIM_TX_VSIZE = 111;
|
||||
|
||||
const int ESTIMATED_BTC_LOCKUP_TX_VSIZE = 154;
|
||||
|
||||
const double STANDARD_FEE_RATE_SAT_PER_VBYTE = 0.1;
|
||||
|
||||
const double LOWBALL_FEE_RATE_SAT_PER_VBYTE = 0.01;
|
||||
|
||||
@@ -3,6 +3,36 @@ import breez_sdk_liquid.*
|
||||
import com.facebook.react.bridge.*
|
||||
import java.util.*
|
||||
|
||||
fun asAcceptPaymentProposedFeesRequest(acceptPaymentProposedFeesRequest: ReadableMap): AcceptPaymentProposedFeesRequest? {
|
||||
if (!validateMandatoryFields(
|
||||
acceptPaymentProposedFeesRequest,
|
||||
arrayOf(
|
||||
"response",
|
||||
),
|
||||
)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
val response = acceptPaymentProposedFeesRequest.getMap("response")?.let { asFetchPaymentProposedFeesResponse(it) }!!
|
||||
return AcceptPaymentProposedFeesRequest(response)
|
||||
}
|
||||
|
||||
fun readableMapOf(acceptPaymentProposedFeesRequest: AcceptPaymentProposedFeesRequest): ReadableMap =
|
||||
readableMapOf(
|
||||
"response" to readableMapOf(acceptPaymentProposedFeesRequest.response),
|
||||
)
|
||||
|
||||
fun asAcceptPaymentProposedFeesRequestList(arr: ReadableArray): List<AcceptPaymentProposedFeesRequest> {
|
||||
val list = ArrayList<AcceptPaymentProposedFeesRequest>()
|
||||
for (value in arr.toList()) {
|
||||
when (value) {
|
||||
is ReadableMap -> list.add(asAcceptPaymentProposedFeesRequest(value)!!)
|
||||
else -> throw SdkException.Generic(errUnexpectedType(value))
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
fun asAesSuccessActionData(aesSuccessActionData: ReadableMap): AesSuccessActionData? {
|
||||
if (!validateMandatoryFields(
|
||||
aesSuccessActionData,
|
||||
@@ -284,6 +314,16 @@ fun asConfig(config: ReadableMap): Config? {
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val onchainFeeRateLeewaySatPerVbyte =
|
||||
if (hasNonNullKey(
|
||||
config,
|
||||
"onchainFeeRateLeewaySatPerVbyte",
|
||||
)
|
||||
) {
|
||||
config.getInt("onchainFeeRateLeewaySatPerVbyte").toUInt()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
return Config(
|
||||
liquidElectrumUrl,
|
||||
bitcoinElectrumUrl,
|
||||
@@ -298,6 +338,7 @@ fun asConfig(config: ReadableMap): Config? {
|
||||
zeroConfMaxAmountSat,
|
||||
useDefaultExternalInputParsers,
|
||||
externalInputParsers,
|
||||
onchainFeeRateLeewaySatPerVbyte,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -316,6 +357,7 @@ fun readableMapOf(config: Config): ReadableMap =
|
||||
"zeroConfMaxAmountSat" to config.zeroConfMaxAmountSat,
|
||||
"useDefaultExternalInputParsers" to config.useDefaultExternalInputParsers,
|
||||
"externalInputParsers" to config.externalInputParsers?.let { readableArrayOf(it) },
|
||||
"onchainFeeRateLeewaySatPerVbyte" to config.onchainFeeRateLeewaySatPerVbyte,
|
||||
)
|
||||
|
||||
fun asConfigList(arr: ReadableArray): List<Config> {
|
||||
@@ -473,6 +515,72 @@ fun asExternalInputParserList(arr: ReadableArray): List<ExternalInputParser> {
|
||||
return list
|
||||
}
|
||||
|
||||
fun asFetchPaymentProposedFeesRequest(fetchPaymentProposedFeesRequest: ReadableMap): FetchPaymentProposedFeesRequest? {
|
||||
if (!validateMandatoryFields(
|
||||
fetchPaymentProposedFeesRequest,
|
||||
arrayOf(
|
||||
"swapId",
|
||||
),
|
||||
)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
val swapId = fetchPaymentProposedFeesRequest.getString("swapId")!!
|
||||
return FetchPaymentProposedFeesRequest(swapId)
|
||||
}
|
||||
|
||||
fun readableMapOf(fetchPaymentProposedFeesRequest: FetchPaymentProposedFeesRequest): ReadableMap =
|
||||
readableMapOf(
|
||||
"swapId" to fetchPaymentProposedFeesRequest.swapId,
|
||||
)
|
||||
|
||||
fun asFetchPaymentProposedFeesRequestList(arr: ReadableArray): List<FetchPaymentProposedFeesRequest> {
|
||||
val list = ArrayList<FetchPaymentProposedFeesRequest>()
|
||||
for (value in arr.toList()) {
|
||||
when (value) {
|
||||
is ReadableMap -> list.add(asFetchPaymentProposedFeesRequest(value)!!)
|
||||
else -> throw SdkException.Generic(errUnexpectedType(value))
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
fun asFetchPaymentProposedFeesResponse(fetchPaymentProposedFeesResponse: ReadableMap): FetchPaymentProposedFeesResponse? {
|
||||
if (!validateMandatoryFields(
|
||||
fetchPaymentProposedFeesResponse,
|
||||
arrayOf(
|
||||
"swapId",
|
||||
"feesSat",
|
||||
"payerAmountSat",
|
||||
),
|
||||
)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
val swapId = fetchPaymentProposedFeesResponse.getString("swapId")!!
|
||||
val feesSat = fetchPaymentProposedFeesResponse.getDouble("feesSat").toULong()
|
||||
val payerAmountSat = fetchPaymentProposedFeesResponse.getDouble("payerAmountSat").toULong()
|
||||
return FetchPaymentProposedFeesResponse(swapId, feesSat, payerAmountSat)
|
||||
}
|
||||
|
||||
fun readableMapOf(fetchPaymentProposedFeesResponse: FetchPaymentProposedFeesResponse): ReadableMap =
|
||||
readableMapOf(
|
||||
"swapId" to fetchPaymentProposedFeesResponse.swapId,
|
||||
"feesSat" to fetchPaymentProposedFeesResponse.feesSat,
|
||||
"payerAmountSat" to fetchPaymentProposedFeesResponse.payerAmountSat,
|
||||
)
|
||||
|
||||
fun asFetchPaymentProposedFeesResponseList(arr: ReadableArray): List<FetchPaymentProposedFeesResponse> {
|
||||
val list = ArrayList<FetchPaymentProposedFeesResponse>()
|
||||
for (value in arr.toList()) {
|
||||
when (value) {
|
||||
is ReadableMap -> list.add(asFetchPaymentProposedFeesResponse(value)!!)
|
||||
else -> throw SdkException.Generic(errUnexpectedType(value))
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
fun asFiatCurrency(fiatCurrency: ReadableMap): FiatCurrency? {
|
||||
if (!validateMandatoryFields(
|
||||
fiatCurrency,
|
||||
@@ -3230,6 +3338,10 @@ fun asSdkEvent(sdkEvent: ReadableMap): SdkEvent? {
|
||||
val details = sdkEvent.getMap("details")?.let { asPayment(it) }!!
|
||||
return SdkEvent.PaymentWaitingConfirmation(details)
|
||||
}
|
||||
if (type == "paymentWaitingFeeAcceptance") {
|
||||
val details = sdkEvent.getMap("details")?.let { asPayment(it) }!!
|
||||
return SdkEvent.PaymentWaitingFeeAcceptance(details)
|
||||
}
|
||||
if (type == "synced") {
|
||||
return SdkEvent.Synced
|
||||
}
|
||||
@@ -3263,6 +3375,10 @@ fun readableMapOf(sdkEvent: SdkEvent): ReadableMap? {
|
||||
pushToMap(map, "type", "paymentWaitingConfirmation")
|
||||
pushToMap(map, "details", readableMapOf(sdkEvent.details))
|
||||
}
|
||||
is SdkEvent.PaymentWaitingFeeAcceptance -> {
|
||||
pushToMap(map, "type", "paymentWaitingFeeAcceptance")
|
||||
pushToMap(map, "details", readableMapOf(sdkEvent.details))
|
||||
}
|
||||
is SdkEvent.Synced -> {
|
||||
pushToMap(map, "type", "synced")
|
||||
}
|
||||
|
||||
@@ -420,6 +420,42 @@ class BreezSDKLiquidModule(
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun fetchPaymentProposedFees(
|
||||
req: ReadableMap,
|
||||
promise: Promise,
|
||||
) {
|
||||
executor.execute {
|
||||
try {
|
||||
val fetchPaymentProposedFeesRequest =
|
||||
asFetchPaymentProposedFeesRequest(req)
|
||||
?: run { throw SdkException.Generic(errMissingMandatoryField("req", "FetchPaymentProposedFeesRequest")) }
|
||||
val res = getBindingLiquidSdk().fetchPaymentProposedFees(fetchPaymentProposedFeesRequest)
|
||||
promise.resolve(readableMapOf(res))
|
||||
} catch (e: Exception) {
|
||||
promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun acceptPaymentProposedFees(
|
||||
req: ReadableMap,
|
||||
promise: Promise,
|
||||
) {
|
||||
executor.execute {
|
||||
try {
|
||||
val acceptPaymentProposedFeesRequest =
|
||||
asAcceptPaymentProposedFeesRequest(req)
|
||||
?: run { throw SdkException.Generic(errMissingMandatoryField("req", "AcceptPaymentProposedFeesRequest")) }
|
||||
getBindingLiquidSdk().acceptPaymentProposedFees(acceptPaymentProposedFeesRequest)
|
||||
promise.resolve(readableMapOf("status" to "ok"))
|
||||
} catch (e: Exception) {
|
||||
promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun listRefundables(promise: Promise) {
|
||||
executor.execute {
|
||||
|
||||
@@ -2,6 +2,38 @@ import BreezSDKLiquid
|
||||
import Foundation
|
||||
|
||||
enum BreezSDKLiquidMapper {
|
||||
static func asAcceptPaymentProposedFeesRequest(acceptPaymentProposedFeesRequest: [String: Any?]) throws -> AcceptPaymentProposedFeesRequest {
|
||||
guard let responseTmp = acceptPaymentProposedFeesRequest["response"] as? [String: Any?] else {
|
||||
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "response", typeName: "AcceptPaymentProposedFeesRequest"))
|
||||
}
|
||||
let response = try asFetchPaymentProposedFeesResponse(fetchPaymentProposedFeesResponse: responseTmp)
|
||||
|
||||
return AcceptPaymentProposedFeesRequest(response: response)
|
||||
}
|
||||
|
||||
static func dictionaryOf(acceptPaymentProposedFeesRequest: AcceptPaymentProposedFeesRequest) -> [String: Any?] {
|
||||
return [
|
||||
"response": dictionaryOf(fetchPaymentProposedFeesResponse: acceptPaymentProposedFeesRequest.response),
|
||||
]
|
||||
}
|
||||
|
||||
static func asAcceptPaymentProposedFeesRequestList(arr: [Any]) throws -> [AcceptPaymentProposedFeesRequest] {
|
||||
var list = [AcceptPaymentProposedFeesRequest]()
|
||||
for value in arr {
|
||||
if let val = value as? [String: Any?] {
|
||||
var acceptPaymentProposedFeesRequest = try asAcceptPaymentProposedFeesRequest(acceptPaymentProposedFeesRequest: val)
|
||||
list.append(acceptPaymentProposedFeesRequest)
|
||||
} else {
|
||||
throw SdkError.Generic(message: errUnexpectedType(typeName: "AcceptPaymentProposedFeesRequest"))
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
static func arrayOf(acceptPaymentProposedFeesRequestList: [AcceptPaymentProposedFeesRequest]) -> [Any] {
|
||||
return acceptPaymentProposedFeesRequestList.map { v -> [String: Any?] in return dictionaryOf(acceptPaymentProposedFeesRequest: v) }
|
||||
}
|
||||
|
||||
static func asAesSuccessActionData(aesSuccessActionData: [String: Any?]) throws -> AesSuccessActionData {
|
||||
guard let description = aesSuccessActionData["description"] as? String else {
|
||||
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "description", typeName: "AesSuccessActionData"))
|
||||
@@ -307,9 +339,6 @@ enum BreezSDKLiquidMapper {
|
||||
guard let zeroConfMinFeeRateMsat = config["zeroConfMinFeeRateMsat"] as? UInt32 else {
|
||||
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "zeroConfMinFeeRateMsat", typeName: "Config"))
|
||||
}
|
||||
guard let syncServiceUrl = config["syncServiceUrl"] as? String else {
|
||||
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "syncServiceUrl", typeName: "Config"))
|
||||
}
|
||||
var breezApiKey: String?
|
||||
if hasNonNilKey(data: config, key: "breezApiKey") {
|
||||
guard let breezApiKeyTmp = config["breezApiKey"] as? String else {
|
||||
@@ -339,7 +368,15 @@ enum BreezSDKLiquidMapper {
|
||||
externalInputParsers = try asExternalInputParserList(arr: externalInputParsersTmp)
|
||||
}
|
||||
|
||||
return Config(liquidElectrumUrl: liquidElectrumUrl, bitcoinElectrumUrl: bitcoinElectrumUrl, mempoolspaceUrl: mempoolspaceUrl, workingDir: workingDir, network: network, paymentTimeoutSec: paymentTimeoutSec, zeroConfMinFeeRateMsat: zeroConfMinFeeRateMsat, syncServiceUrl: syncServiceUrl, breezApiKey: breezApiKey, cacheDir: cacheDir, zeroConfMaxAmountSat: zeroConfMaxAmountSat, useDefaultExternalInputParsers: useDefaultExternalInputParsers, externalInputParsers: externalInputParsers)
|
||||
var onchainFeeRateLeewaySatPerVbyte: UInt32?
|
||||
if hasNonNilKey(data: config, key: "onchainFeeRateLeewaySatPerVbyte") {
|
||||
guard let onchainFeeRateLeewaySatPerVbyteTmp = config["onchainFeeRateLeewaySatPerVbyte"] as? UInt32 else {
|
||||
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "onchainFeeRateLeewaySatPerVbyte"))
|
||||
}
|
||||
onchainFeeRateLeewaySatPerVbyte = onchainFeeRateLeewaySatPerVbyteTmp
|
||||
}
|
||||
|
||||
return Config(liquidElectrumUrl: liquidElectrumUrl, bitcoinElectrumUrl: bitcoinElectrumUrl, mempoolspaceUrl: mempoolspaceUrl, workingDir: workingDir, network: network, paymentTimeoutSec: paymentTimeoutSec, zeroConfMinFeeRateMsat: zeroConfMinFeeRateMsat, breezApiKey: breezApiKey, cacheDir: cacheDir, zeroConfMaxAmountSat: zeroConfMaxAmountSat, useDefaultExternalInputParsers: useDefaultExternalInputParsers, externalInputParsers: externalInputParsers, onchainFeeRateLeewaySatPerVbyte: onchainFeeRateLeewaySatPerVbyte)
|
||||
}
|
||||
|
||||
static func dictionaryOf(config: Config) -> [String: Any?] {
|
||||
@@ -351,12 +388,12 @@ enum BreezSDKLiquidMapper {
|
||||
"network": valueOf(liquidNetwork: config.network),
|
||||
"paymentTimeoutSec": config.paymentTimeoutSec,
|
||||
"zeroConfMinFeeRateMsat": config.zeroConfMinFeeRateMsat,
|
||||
"syncServiceUrl": config.syncServiceUrl,
|
||||
"breezApiKey": config.breezApiKey == nil ? nil : config.breezApiKey,
|
||||
"cacheDir": config.cacheDir == nil ? nil : config.cacheDir,
|
||||
"zeroConfMaxAmountSat": config.zeroConfMaxAmountSat == nil ? nil : config.zeroConfMaxAmountSat,
|
||||
"useDefaultExternalInputParsers": config.useDefaultExternalInputParsers,
|
||||
"externalInputParsers": config.externalInputParsers == nil ? nil : arrayOf(externalInputParserList: config.externalInputParsers!),
|
||||
"onchainFeeRateLeewaySatPerVbyte": config.onchainFeeRateLeewaySatPerVbyte == nil ? nil : config.onchainFeeRateLeewaySatPerVbyte,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -551,6 +588,76 @@ enum BreezSDKLiquidMapper {
|
||||
return externalInputParserList.map { v -> [String: Any?] in return dictionaryOf(externalInputParser: v) }
|
||||
}
|
||||
|
||||
static func asFetchPaymentProposedFeesRequest(fetchPaymentProposedFeesRequest: [String: Any?]) throws -> FetchPaymentProposedFeesRequest {
|
||||
guard let swapId = fetchPaymentProposedFeesRequest["swapId"] as? String else {
|
||||
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "swapId", typeName: "FetchPaymentProposedFeesRequest"))
|
||||
}
|
||||
|
||||
return FetchPaymentProposedFeesRequest(swapId: swapId)
|
||||
}
|
||||
|
||||
static func dictionaryOf(fetchPaymentProposedFeesRequest: FetchPaymentProposedFeesRequest) -> [String: Any?] {
|
||||
return [
|
||||
"swapId": fetchPaymentProposedFeesRequest.swapId,
|
||||
]
|
||||
}
|
||||
|
||||
static func asFetchPaymentProposedFeesRequestList(arr: [Any]) throws -> [FetchPaymentProposedFeesRequest] {
|
||||
var list = [FetchPaymentProposedFeesRequest]()
|
||||
for value in arr {
|
||||
if let val = value as? [String: Any?] {
|
||||
var fetchPaymentProposedFeesRequest = try asFetchPaymentProposedFeesRequest(fetchPaymentProposedFeesRequest: val)
|
||||
list.append(fetchPaymentProposedFeesRequest)
|
||||
} else {
|
||||
throw SdkError.Generic(message: errUnexpectedType(typeName: "FetchPaymentProposedFeesRequest"))
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
static func arrayOf(fetchPaymentProposedFeesRequestList: [FetchPaymentProposedFeesRequest]) -> [Any] {
|
||||
return fetchPaymentProposedFeesRequestList.map { v -> [String: Any?] in return dictionaryOf(fetchPaymentProposedFeesRequest: v) }
|
||||
}
|
||||
|
||||
static func asFetchPaymentProposedFeesResponse(fetchPaymentProposedFeesResponse: [String: Any?]) throws -> FetchPaymentProposedFeesResponse {
|
||||
guard let swapId = fetchPaymentProposedFeesResponse["swapId"] as? String else {
|
||||
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "swapId", typeName: "FetchPaymentProposedFeesResponse"))
|
||||
}
|
||||
guard let feesSat = fetchPaymentProposedFeesResponse["feesSat"] as? UInt64 else {
|
||||
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "feesSat", typeName: "FetchPaymentProposedFeesResponse"))
|
||||
}
|
||||
guard let payerAmountSat = fetchPaymentProposedFeesResponse["payerAmountSat"] as? UInt64 else {
|
||||
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "payerAmountSat", typeName: "FetchPaymentProposedFeesResponse"))
|
||||
}
|
||||
|
||||
return FetchPaymentProposedFeesResponse(swapId: swapId, feesSat: feesSat, payerAmountSat: payerAmountSat)
|
||||
}
|
||||
|
||||
static func dictionaryOf(fetchPaymentProposedFeesResponse: FetchPaymentProposedFeesResponse) -> [String: Any?] {
|
||||
return [
|
||||
"swapId": fetchPaymentProposedFeesResponse.swapId,
|
||||
"feesSat": fetchPaymentProposedFeesResponse.feesSat,
|
||||
"payerAmountSat": fetchPaymentProposedFeesResponse.payerAmountSat,
|
||||
]
|
||||
}
|
||||
|
||||
static func asFetchPaymentProposedFeesResponseList(arr: [Any]) throws -> [FetchPaymentProposedFeesResponse] {
|
||||
var list = [FetchPaymentProposedFeesResponse]()
|
||||
for value in arr {
|
||||
if let val = value as? [String: Any?] {
|
||||
var fetchPaymentProposedFeesResponse = try asFetchPaymentProposedFeesResponse(fetchPaymentProposedFeesResponse: val)
|
||||
list.append(fetchPaymentProposedFeesResponse)
|
||||
} else {
|
||||
throw SdkError.Generic(message: errUnexpectedType(typeName: "FetchPaymentProposedFeesResponse"))
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
static func arrayOf(fetchPaymentProposedFeesResponseList: [FetchPaymentProposedFeesResponse]) -> [Any] {
|
||||
return fetchPaymentProposedFeesResponseList.map { v -> [String: Any?] in return dictionaryOf(fetchPaymentProposedFeesResponse: v) }
|
||||
}
|
||||
|
||||
static func asFiatCurrency(fiatCurrency: [String: Any?]) throws -> FiatCurrency {
|
||||
guard let id = fiatCurrency["id"] as? String else {
|
||||
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "id", typeName: "FiatCurrency"))
|
||||
@@ -956,11 +1063,6 @@ enum BreezSDKLiquidMapper {
|
||||
filters = try asPaymentTypeList(arr: filtersTmp)
|
||||
}
|
||||
|
||||
var states: [PaymentState]?
|
||||
if let statesTmp = listPaymentsRequest["states"] as? [String] {
|
||||
states = try asPaymentStateList(arr: statesTmp)
|
||||
}
|
||||
|
||||
var fromTimestamp: Int64?
|
||||
if hasNonNilKey(data: listPaymentsRequest, key: "fromTimestamp") {
|
||||
guard let fromTimestampTmp = listPaymentsRequest["fromTimestamp"] as? Int64 else {
|
||||
@@ -994,13 +1096,12 @@ enum BreezSDKLiquidMapper {
|
||||
details = try asListPaymentDetails(listPaymentDetails: detailsTmp)
|
||||
}
|
||||
|
||||
return ListPaymentsRequest(filters: filters, states: states, fromTimestamp: fromTimestamp, toTimestamp: toTimestamp, offset: offset, limit: limit, details: details)
|
||||
return ListPaymentsRequest(filters: filters, fromTimestamp: fromTimestamp, toTimestamp: toTimestamp, offset: offset, limit: limit, details: details)
|
||||
}
|
||||
|
||||
static func dictionaryOf(listPaymentsRequest: ListPaymentsRequest) -> [String: Any?] {
|
||||
return [
|
||||
"filters": listPaymentsRequest.filters == nil ? nil : arrayOf(paymentTypeList: listPaymentsRequest.filters!),
|
||||
"states": listPaymentsRequest.states == nil ? nil : arrayOf(paymentStateList: listPaymentsRequest.states!),
|
||||
"fromTimestamp": listPaymentsRequest.fromTimestamp == nil ? nil : listPaymentsRequest.fromTimestamp,
|
||||
"toTimestamp": listPaymentsRequest.toTimestamp == nil ? nil : listPaymentsRequest.toTimestamp,
|
||||
"offset": listPaymentsRequest.offset == nil ? nil : listPaymentsRequest.offset,
|
||||
@@ -1135,85 +1236,6 @@ enum BreezSDKLiquidMapper {
|
||||
return lnUrlErrorDataList.map { v -> [String: Any?] in return dictionaryOf(lnUrlErrorData: v) }
|
||||
}
|
||||
|
||||
static func asLnUrlInfo(lnUrlInfo: [String: Any?]) throws -> LnUrlInfo {
|
||||
var lnAddress: String?
|
||||
if hasNonNilKey(data: lnUrlInfo, key: "lnAddress") {
|
||||
guard let lnAddressTmp = lnUrlInfo["lnAddress"] as? String else {
|
||||
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "lnAddress"))
|
||||
}
|
||||
lnAddress = lnAddressTmp
|
||||
}
|
||||
var lnurlPayComment: String?
|
||||
if hasNonNilKey(data: lnUrlInfo, key: "lnurlPayComment") {
|
||||
guard let lnurlPayCommentTmp = lnUrlInfo["lnurlPayComment"] as? String else {
|
||||
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "lnurlPayComment"))
|
||||
}
|
||||
lnurlPayComment = lnurlPayCommentTmp
|
||||
}
|
||||
var lnurlPayDomain: String?
|
||||
if hasNonNilKey(data: lnUrlInfo, key: "lnurlPayDomain") {
|
||||
guard let lnurlPayDomainTmp = lnUrlInfo["lnurlPayDomain"] as? String else {
|
||||
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "lnurlPayDomain"))
|
||||
}
|
||||
lnurlPayDomain = lnurlPayDomainTmp
|
||||
}
|
||||
var lnurlPayMetadata: String?
|
||||
if hasNonNilKey(data: lnUrlInfo, key: "lnurlPayMetadata") {
|
||||
guard let lnurlPayMetadataTmp = lnUrlInfo["lnurlPayMetadata"] as? String else {
|
||||
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "lnurlPayMetadata"))
|
||||
}
|
||||
lnurlPayMetadata = lnurlPayMetadataTmp
|
||||
}
|
||||
var lnurlPaySuccessAction: SuccessActionProcessed?
|
||||
if let lnurlPaySuccessActionTmp = lnUrlInfo["lnurlPaySuccessAction"] as? [String: Any?] {
|
||||
lnurlPaySuccessAction = try asSuccessActionProcessed(successActionProcessed: lnurlPaySuccessActionTmp)
|
||||
}
|
||||
|
||||
var lnurlPayUnprocessedSuccessAction: SuccessAction?
|
||||
if let lnurlPayUnprocessedSuccessActionTmp = lnUrlInfo["lnurlPayUnprocessedSuccessAction"] as? [String: Any?] {
|
||||
lnurlPayUnprocessedSuccessAction = try asSuccessAction(successAction: lnurlPayUnprocessedSuccessActionTmp)
|
||||
}
|
||||
|
||||
var lnurlWithdrawEndpoint: String?
|
||||
if hasNonNilKey(data: lnUrlInfo, key: "lnurlWithdrawEndpoint") {
|
||||
guard let lnurlWithdrawEndpointTmp = lnUrlInfo["lnurlWithdrawEndpoint"] as? String else {
|
||||
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "lnurlWithdrawEndpoint"))
|
||||
}
|
||||
lnurlWithdrawEndpoint = lnurlWithdrawEndpointTmp
|
||||
}
|
||||
|
||||
return LnUrlInfo(lnAddress: lnAddress, lnurlPayComment: lnurlPayComment, lnurlPayDomain: lnurlPayDomain, lnurlPayMetadata: lnurlPayMetadata, lnurlPaySuccessAction: lnurlPaySuccessAction, lnurlPayUnprocessedSuccessAction: lnurlPayUnprocessedSuccessAction, lnurlWithdrawEndpoint: lnurlWithdrawEndpoint)
|
||||
}
|
||||
|
||||
static func dictionaryOf(lnUrlInfo: LnUrlInfo) -> [String: Any?] {
|
||||
return [
|
||||
"lnAddress": lnUrlInfo.lnAddress == nil ? nil : lnUrlInfo.lnAddress,
|
||||
"lnurlPayComment": lnUrlInfo.lnurlPayComment == nil ? nil : lnUrlInfo.lnurlPayComment,
|
||||
"lnurlPayDomain": lnUrlInfo.lnurlPayDomain == nil ? nil : lnUrlInfo.lnurlPayDomain,
|
||||
"lnurlPayMetadata": lnUrlInfo.lnurlPayMetadata == nil ? nil : lnUrlInfo.lnurlPayMetadata,
|
||||
"lnurlPaySuccessAction": lnUrlInfo.lnurlPaySuccessAction == nil ? nil : dictionaryOf(successActionProcessed: lnUrlInfo.lnurlPaySuccessAction!),
|
||||
"lnurlPayUnprocessedSuccessAction": lnUrlInfo.lnurlPayUnprocessedSuccessAction == nil ? nil : dictionaryOf(successAction: lnUrlInfo.lnurlPayUnprocessedSuccessAction!),
|
||||
"lnurlWithdrawEndpoint": lnUrlInfo.lnurlWithdrawEndpoint == nil ? nil : lnUrlInfo.lnurlWithdrawEndpoint,
|
||||
]
|
||||
}
|
||||
|
||||
static func asLnUrlInfoList(arr: [Any]) throws -> [LnUrlInfo] {
|
||||
var list = [LnUrlInfo]()
|
||||
for value in arr {
|
||||
if let val = value as? [String: Any?] {
|
||||
var lnUrlInfo = try asLnUrlInfo(lnUrlInfo: val)
|
||||
list.append(lnUrlInfo)
|
||||
} else {
|
||||
throw SdkError.Generic(message: errUnexpectedType(typeName: "LnUrlInfo"))
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
static func arrayOf(lnUrlInfoList: [LnUrlInfo]) -> [Any] {
|
||||
return lnUrlInfoList.map { v -> [String: Any?] in return dictionaryOf(lnUrlInfo: v) }
|
||||
}
|
||||
|
||||
static func asLnUrlPayErrorData(lnUrlPayErrorData: [String: Any?]) throws -> LnUrlPayErrorData {
|
||||
guard let paymentHash = lnUrlPayErrorData["paymentHash"] as? String else {
|
||||
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "paymentHash", typeName: "LnUrlPayErrorData"))
|
||||
@@ -1962,32 +1984,18 @@ enum BreezSDKLiquidMapper {
|
||||
guard let feesSat = prepareLnUrlPayResponse["feesSat"] as? UInt64 else {
|
||||
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "feesSat", typeName: "PrepareLnUrlPayResponse"))
|
||||
}
|
||||
guard let dataTmp = prepareLnUrlPayResponse["data"] as? [String: Any?] else {
|
||||
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "data", typeName: "PrepareLnUrlPayResponse"))
|
||||
}
|
||||
let data = try asLnUrlPayRequestData(lnUrlPayRequestData: dataTmp)
|
||||
|
||||
var comment: String?
|
||||
if hasNonNilKey(data: prepareLnUrlPayResponse, key: "comment") {
|
||||
guard let commentTmp = prepareLnUrlPayResponse["comment"] as? String else {
|
||||
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "comment"))
|
||||
}
|
||||
comment = commentTmp
|
||||
}
|
||||
var successAction: SuccessAction?
|
||||
if let successActionTmp = prepareLnUrlPayResponse["successAction"] as? [String: Any?] {
|
||||
successAction = try asSuccessAction(successAction: successActionTmp)
|
||||
}
|
||||
|
||||
return PrepareLnUrlPayResponse(destination: destination, feesSat: feesSat, data: data, comment: comment, successAction: successAction)
|
||||
return PrepareLnUrlPayResponse(destination: destination, feesSat: feesSat, successAction: successAction)
|
||||
}
|
||||
|
||||
static func dictionaryOf(prepareLnUrlPayResponse: PrepareLnUrlPayResponse) -> [String: Any?] {
|
||||
return [
|
||||
"destination": dictionaryOf(sendDestination: prepareLnUrlPayResponse.destination),
|
||||
"feesSat": prepareLnUrlPayResponse.feesSat,
|
||||
"data": dictionaryOf(lnUrlPayRequestData: prepareLnUrlPayResponse.data),
|
||||
"comment": prepareLnUrlPayResponse.comment == nil ? nil : prepareLnUrlPayResponse.comment,
|
||||
"successAction": prepareLnUrlPayResponse.successAction == nil ? nil : dictionaryOf(successAction: prepareLnUrlPayResponse.successAction!),
|
||||
]
|
||||
}
|
||||
@@ -3764,16 +3772,11 @@ enum BreezSDKLiquidMapper {
|
||||
|
||||
let _paymentHash = paymentDetails["paymentHash"] as? String
|
||||
|
||||
var _lnurlInfo: LnUrlInfo?
|
||||
if let lnurlInfoTmp = paymentDetails["lnurlInfo"] as? [String: Any?] {
|
||||
_lnurlInfo = try asLnUrlInfo(lnUrlInfo: lnurlInfoTmp)
|
||||
}
|
||||
|
||||
let _refundTxId = paymentDetails["refundTxId"] as? String
|
||||
|
||||
let _refundTxAmountSat = paymentDetails["refundTxAmountSat"] as? UInt64
|
||||
|
||||
return PaymentDetails.lightning(swapId: _swapId, description: _description, preimage: _preimage, bolt11: _bolt11, bolt12Offer: _bolt12Offer, paymentHash: _paymentHash, lnurlInfo: _lnurlInfo, refundTxId: _refundTxId, refundTxAmountSat: _refundTxAmountSat)
|
||||
return PaymentDetails.lightning(swapId: _swapId, description: _description, preimage: _preimage, bolt11: _bolt11, bolt12Offer: _bolt12Offer, paymentHash: _paymentHash, refundTxId: _refundTxId, refundTxAmountSat: _refundTxAmountSat)
|
||||
}
|
||||
if type == "liquid" {
|
||||
guard let _destination = paymentDetails["destination"] as? String else {
|
||||
@@ -3804,7 +3807,7 @@ enum BreezSDKLiquidMapper {
|
||||
static func dictionaryOf(paymentDetails: PaymentDetails) -> [String: Any?] {
|
||||
switch paymentDetails {
|
||||
case let .lightning(
|
||||
swapId, description, preimage, bolt11, bolt12Offer, paymentHash, lnurlInfo, refundTxId, refundTxAmountSat
|
||||
swapId, description, preimage, bolt11, bolt12Offer, paymentHash, refundTxId, refundTxAmountSat
|
||||
):
|
||||
return [
|
||||
"type": "lightning",
|
||||
@@ -3814,7 +3817,6 @@ enum BreezSDKLiquidMapper {
|
||||
"bolt11": bolt11 == nil ? nil : bolt11,
|
||||
"bolt12Offer": bolt12Offer == nil ? nil : bolt12Offer,
|
||||
"paymentHash": paymentHash == nil ? nil : paymentHash,
|
||||
"lnurlInfo": lnurlInfo == nil ? nil : dictionaryOf(lnUrlInfo: lnurlInfo!),
|
||||
"refundTxId": refundTxId == nil ? nil : refundTxId,
|
||||
"refundTxAmountSat": refundTxAmountSat == nil ? nil : refundTxAmountSat,
|
||||
]
|
||||
@@ -3926,6 +3928,9 @@ enum BreezSDKLiquidMapper {
|
||||
case "refundPending":
|
||||
return PaymentState.refundPending
|
||||
|
||||
case "waitingFeeAcceptance":
|
||||
return PaymentState.waitingFeeAcceptance
|
||||
|
||||
default: throw SdkError.Generic(message: "Invalid variant \(paymentState) for enum PaymentState")
|
||||
}
|
||||
}
|
||||
@@ -3952,6 +3957,9 @@ enum BreezSDKLiquidMapper {
|
||||
|
||||
case .refundPending:
|
||||
return "refundPending"
|
||||
|
||||
case .waitingFeeAcceptance:
|
||||
return "waitingFeeAcceptance"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4061,6 +4069,14 @@ enum BreezSDKLiquidMapper {
|
||||
|
||||
return SdkEvent.paymentWaitingConfirmation(details: _details)
|
||||
}
|
||||
if type == "paymentWaitingFeeAcceptance" {
|
||||
guard let detailsTmp = sdkEvent["details"] as? [String: Any?] else {
|
||||
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "details", typeName: "SdkEvent"))
|
||||
}
|
||||
let _details = try asPayment(payment: detailsTmp)
|
||||
|
||||
return SdkEvent.paymentWaitingFeeAcceptance(details: _details)
|
||||
}
|
||||
if type == "synced" {
|
||||
return SdkEvent.synced
|
||||
}
|
||||
@@ -4118,6 +4134,14 @@ enum BreezSDKLiquidMapper {
|
||||
"details": dictionaryOf(payment: details),
|
||||
]
|
||||
|
||||
case let .paymentWaitingFeeAcceptance(
|
||||
details
|
||||
):
|
||||
return [
|
||||
"type": "paymentWaitingFeeAcceptance",
|
||||
"details": dictionaryOf(payment: details),
|
||||
]
|
||||
|
||||
case .synced:
|
||||
return [
|
||||
"type": "synced",
|
||||
|
||||
@@ -131,6 +131,18 @@ RCT_EXTERN_METHOD(
|
||||
reject: (RCTPromiseRejectBlock)reject
|
||||
)
|
||||
|
||||
RCT_EXTERN_METHOD(
|
||||
fetchPaymentProposedFees: (NSDictionary*)req
|
||||
resolve: (RCTPromiseResolveBlock)resolve
|
||||
reject: (RCTPromiseRejectBlock)reject
|
||||
)
|
||||
|
||||
RCT_EXTERN_METHOD(
|
||||
acceptPaymentProposedFees: (NSDictionary*)req
|
||||
resolve: (RCTPromiseResolveBlock)resolve
|
||||
reject: (RCTPromiseRejectBlock)reject
|
||||
)
|
||||
|
||||
RCT_EXTERN_METHOD(
|
||||
listRefundables: (RCTPromiseResolveBlock)resolve
|
||||
reject: (RCTPromiseRejectBlock)reject
|
||||
|
||||
@@ -321,6 +321,28 @@ class RNBreezSDKLiquid: RCTEventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
@objc(fetchPaymentProposedFees:resolve:reject:)
|
||||
func fetchPaymentProposedFees(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
||||
do {
|
||||
let fetchPaymentProposedFeesRequest = try BreezSDKLiquidMapper.asFetchPaymentProposedFeesRequest(fetchPaymentProposedFeesRequest: req)
|
||||
var res = try getBindingLiquidSdk().fetchPaymentProposedFees(req: fetchPaymentProposedFeesRequest)
|
||||
resolve(BreezSDKLiquidMapper.dictionaryOf(fetchPaymentProposedFeesResponse: res))
|
||||
} catch let err {
|
||||
rejectErr(err: err, reject: reject)
|
||||
}
|
||||
}
|
||||
|
||||
@objc(acceptPaymentProposedFees:resolve:reject:)
|
||||
func acceptPaymentProposedFees(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
||||
do {
|
||||
let acceptPaymentProposedFeesRequest = try BreezSDKLiquidMapper.asAcceptPaymentProposedFeesRequest(acceptPaymentProposedFeesRequest: req)
|
||||
try getBindingLiquidSdk().acceptPaymentProposedFees(req: acceptPaymentProposedFeesRequest)
|
||||
resolve(["status": "ok"])
|
||||
} catch let err {
|
||||
rejectErr(err: err, reject: reject)
|
||||
}
|
||||
}
|
||||
|
||||
@objc(listRefundables:reject:)
|
||||
func listRefundables(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
||||
do {
|
||||
|
||||
@@ -19,6 +19,10 @@ const BreezSDKLiquid = NativeModules.RNBreezSDKLiquid
|
||||
|
||||
const BreezSDKLiquidEmitter = new NativeEventEmitter(BreezSDKLiquid)
|
||||
|
||||
export interface AcceptPaymentProposedFeesRequest {
|
||||
response: FetchPaymentProposedFeesResponse
|
||||
}
|
||||
|
||||
export interface AesSuccessActionData {
|
||||
description: string
|
||||
ciphertext: string
|
||||
@@ -71,6 +75,7 @@ export interface Config {
|
||||
zeroConfMaxAmountSat?: number
|
||||
useDefaultExternalInputParsers: boolean
|
||||
externalInputParsers?: ExternalInputParser[]
|
||||
onchainFeeRateLeewaySatPerVbyte?: number
|
||||
}
|
||||
|
||||
export interface ConnectRequest {
|
||||
@@ -98,6 +103,16 @@ export interface ExternalInputParser {
|
||||
parserUrl: string
|
||||
}
|
||||
|
||||
export interface FetchPaymentProposedFeesRequest {
|
||||
swapId: string
|
||||
}
|
||||
|
||||
export interface FetchPaymentProposedFeesResponse {
|
||||
swapId: string
|
||||
feesSat: number
|
||||
payerAmountSat: number
|
||||
}
|
||||
|
||||
export interface FiatCurrency {
|
||||
id: string
|
||||
info: CurrencyInfo
|
||||
@@ -648,7 +663,8 @@ export enum PaymentState {
|
||||
FAILED = "failed",
|
||||
TIMED_OUT = "timedOut",
|
||||
REFUNDABLE = "refundable",
|
||||
REFUND_PENDING = "refundPending"
|
||||
REFUND_PENDING = "refundPending",
|
||||
WAITING_FEE_ACCEPTANCE = "waitingFeeAcceptance"
|
||||
}
|
||||
|
||||
export enum PaymentType {
|
||||
@@ -663,6 +679,7 @@ export enum SdkEventVariant {
|
||||
PAYMENT_REFUND_PENDING = "paymentRefundPending",
|
||||
PAYMENT_SUCCEEDED = "paymentSucceeded",
|
||||
PAYMENT_WAITING_CONFIRMATION = "paymentWaitingConfirmation",
|
||||
PAYMENT_WAITING_FEE_ACCEPTANCE = "paymentWaitingFeeAcceptance",
|
||||
SYNCED = "synced"
|
||||
}
|
||||
|
||||
@@ -684,6 +701,9 @@ export type SdkEvent = {
|
||||
} | {
|
||||
type: SdkEventVariant.PAYMENT_WAITING_CONFIRMATION,
|
||||
details: Payment
|
||||
} | {
|
||||
type: SdkEventVariant.PAYMENT_WAITING_FEE_ACCEPTANCE,
|
||||
details: Payment
|
||||
} | {
|
||||
type: SdkEventVariant.SYNCED
|
||||
}
|
||||
@@ -861,6 +881,15 @@ export const getPayment = async (req: GetPaymentRequest): Promise<Payment | null
|
||||
return response
|
||||
}
|
||||
|
||||
export const fetchPaymentProposedFees = async (req: FetchPaymentProposedFeesRequest): Promise<FetchPaymentProposedFeesResponse> => {
|
||||
const response = await BreezSDKLiquid.fetchPaymentProposedFees(req)
|
||||
return response
|
||||
}
|
||||
|
||||
export const acceptPaymentProposedFees = async (req: AcceptPaymentProposedFeesRequest): Promise<void> => {
|
||||
await BreezSDKLiquid.acceptPaymentProposedFees(req)
|
||||
}
|
||||
|
||||
export const listRefundables = async (): Promise<RefundableSwap[]> => {
|
||||
const response = await BreezSDKLiquid.listRefundables()
|
||||
return response
|
||||
|
||||
Reference in New Issue
Block a user