Allow custom pay_onchain claim fees (#391)

* Allow optional fee rate for pay onchain BTC claim

* Add recommended_fees method

* Fix example Config

* Address review comments
This commit is contained in:
Ross Savage
2024-07-15 17:38:10 +02:00
committed by GitHub
parent 7a1d0be70b
commit 0da35259fe
25 changed files with 771 additions and 73 deletions

View File

@@ -39,6 +39,10 @@ pub(crate) enum Command {
/// Amount that will be received, in satoshi /// Amount that will be received, in satoshi
receiver_amount_sat: u64, receiver_amount_sat: u64,
// The optional fee rate to use, in satoshi/vbyte
#[clap(short = 'f', long = "fee_rate")]
sat_per_vbyte: Option<u32>,
}, },
/// Receive lbtc and send btc through a swap /// Receive lbtc and send btc through a swap
ReceivePayment { ReceivePayment {
@@ -76,7 +80,7 @@ pub(crate) enum Command {
swap_address: String, swap_address: String,
// Btc onchain address to send the refund to // Btc onchain address to send the refund to
refund_address: String, refund_address: String,
// Fee rate to use // Fee rate to use, in satoshi/vbyte
sat_per_vbyte: u32, sat_per_vbyte: u32,
}, },
/// Broadcast a refund transaction for an incomplete swap /// Broadcast a refund transaction for an incomplete swap
@@ -85,7 +89,7 @@ pub(crate) enum Command {
swap_address: String, swap_address: String,
// Btc onchain address to send the refund to // Btc onchain address to send the refund to
refund_address: String, refund_address: String,
// Fee rate to use // Fee rate to use, in satoshi/vbyte
sat_per_vbyte: u32, sat_per_vbyte: u32,
}, },
/// Rescan onchain swaps /// Rescan onchain swaps
@@ -94,6 +98,8 @@ pub(crate) enum Command {
GetInfo, GetInfo,
/// Sync local data with mempool and onchain data /// Sync local data with mempool and onchain data
Sync, Sync,
/// Get the recommended BTC fees based on the configured mempool.space instance
RecommendedFees,
/// Empties the encrypted transaction cache /// Empties the encrypted transaction cache
EmptyCache, EmptyCache,
/// Backs up the current pending swaps /// Backs up the current pending swaps
@@ -237,17 +243,19 @@ pub(crate) async fn handle_command(
Command::SendOnchainPayment { Command::SendOnchainPayment {
address, address,
receiver_amount_sat, receiver_amount_sat,
sat_per_vbyte,
} => { } => {
let prepare_res = sdk let prepare_res = sdk
.prepare_pay_onchain(&PreparePayOnchainRequest { .prepare_pay_onchain(&PreparePayOnchainRequest {
receiver_amount_sat, receiver_amount_sat,
sat_per_vbyte,
}) })
.await?; .await?;
wait_confirmation!( wait_confirmation!(
format!( format!(
"Fees: {} sat. Are the fees acceptable? (y/N) ", "Fees: {} sat (incl claim fee: {} sat). Are the fees acceptable? (y/N) ",
prepare_res.fees_sat prepare_res.total_fees_sat, prepare_res.claim_fees_sat
), ),
"Payment send halted" "Payment send halted"
); );
@@ -341,6 +349,10 @@ pub(crate) async fn handle_command(
sdk.sync().await?; sdk.sync().await?;
command_result!("Synced successfully") command_result!("Synced successfully")
} }
Command::RecommendedFees => {
let res = sdk.recommended_fees().await?;
command_result!(res)
}
Command::EmptyCache => { Command::EmptyCache => {
sdk.empty_wallet_cache()?; sdk.empty_wallet_cache()?;
command_result!("Cache emptied successfully") command_result!("Cache emptied successfully")

View File

@@ -14,6 +14,8 @@ void store_dart_post_cobject(DartPostCObjectFnType ptr);
// EXTRA END // EXTRA END
typedef struct _Dart_Handle* Dart_Handle; typedef struct _Dart_Handle* Dart_Handle;
#define ESTIMATED_BTC_CLAIM_TX_VSIZE 111
#define STANDARD_FEE_RATE_SAT_PER_VBYTE 0.1 #define STANDARD_FEE_RATE_SAT_PER_VBYTE 0.1
#define LOWBALL_FEE_RATE_SAT_PER_VBYTE 0.01 #define LOWBALL_FEE_RATE_SAT_PER_VBYTE 0.01
@@ -99,7 +101,8 @@ typedef struct wire_cst_ln_url_withdraw_request {
typedef struct wire_cst_prepare_pay_onchain_response { typedef struct wire_cst_prepare_pay_onchain_response {
uint64_t receiver_amount_sat; uint64_t receiver_amount_sat;
uint64_t fees_sat; uint64_t claim_fees_sat;
uint64_t total_fees_sat;
} wire_cst_prepare_pay_onchain_response; } wire_cst_prepare_pay_onchain_response;
typedef struct wire_cst_pay_onchain_request { typedef struct wire_cst_pay_onchain_request {
@@ -109,6 +112,7 @@ typedef struct wire_cst_pay_onchain_request {
typedef struct wire_cst_prepare_pay_onchain_request { typedef struct wire_cst_prepare_pay_onchain_request {
uint64_t receiver_amount_sat; uint64_t receiver_amount_sat;
uint32_t *sat_per_vbyte;
} wire_cst_prepare_pay_onchain_request; } wire_cst_prepare_pay_onchain_request;
typedef struct wire_cst_prepare_receive_onchain_request { typedef struct wire_cst_prepare_receive_onchain_request {
@@ -213,6 +217,7 @@ typedef struct wire_cst_sdk_event {
typedef struct wire_cst_config { typedef struct wire_cst_config {
struct wire_cst_list_prim_u_8_strict *liquid_electrum_url; struct wire_cst_list_prim_u_8_strict *liquid_electrum_url;
struct wire_cst_list_prim_u_8_strict *bitcoin_electrum_url; struct wire_cst_list_prim_u_8_strict *bitcoin_electrum_url;
struct wire_cst_list_prim_u_8_strict *mempoolspace_url;
struct wire_cst_list_prim_u_8_strict *working_dir; struct wire_cst_list_prim_u_8_strict *working_dir;
int32_t network; int32_t network;
uint64_t payment_timeout_sec; uint64_t payment_timeout_sec;
@@ -738,6 +743,14 @@ typedef struct wire_cst_receive_payment_response {
struct wire_cst_list_prim_u_8_strict *invoice; struct wire_cst_list_prim_u_8_strict *invoice;
} wire_cst_receive_payment_response; } wire_cst_receive_payment_response;
typedef struct wire_cst_recommended_fees {
uint64_t fastest_fee;
uint64_t half_hour_fee;
uint64_t hour_fee;
uint64_t economy_fee;
uint64_t minimum_fee;
} wire_cst_recommended_fees;
typedef struct wire_cst_refund_response { typedef struct wire_cst_refund_response {
struct wire_cst_list_prim_u_8_strict *refund_tx_id; struct wire_cst_list_prim_u_8_strict *refund_tx_id;
} wire_cst_refund_response; } wire_cst_refund_response;
@@ -842,6 +855,9 @@ void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_payment
uintptr_t that, uintptr_t that,
struct wire_cst_prepare_receive_response *req); struct wire_cst_prepare_receive_response *req);
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_recommended_fees(int64_t port_,
uintptr_t that);
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_refund(int64_t port_, void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_refund(int64_t port_,
uintptr_t that, uintptr_t that,
struct wire_cst_refund_request *req); struct wire_cst_refund_request *req);
@@ -1049,6 +1065,7 @@ static int64_t dummy_method_to_enforce_bundling(void) {
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_send_payment); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_prepare_send_payment);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_onchain); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_onchain);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_payment); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_payment);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_recommended_fees);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_refund); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_refund);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_rescan_onchain_swaps); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_rescan_onchain_swaps);
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_restore); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_restore);

View File

@@ -285,6 +285,7 @@ enum PaymentError {
dictionary Config { dictionary Config {
string liquid_electrum_url; string liquid_electrum_url;
string bitcoin_electrum_url; string bitcoin_electrum_url;
string mempoolspace_url;
string working_dir; string working_dir;
LiquidNetwork network; LiquidNetwork network;
u64 payment_timeout_sec; u64 payment_timeout_sec;
@@ -354,11 +355,13 @@ dictionary OnchainPaymentLimitsResponse {
dictionary PreparePayOnchainRequest { dictionary PreparePayOnchainRequest {
u64 receiver_amount_sat; u64 receiver_amount_sat;
u32? sat_per_vbyte;
}; };
dictionary PreparePayOnchainResponse { dictionary PreparePayOnchainResponse {
u64 receiver_amount_sat; u64 receiver_amount_sat;
u64 fees_sat; u64 claim_fees_sat;
u64 total_fees_sat;
}; };
dictionary PayOnchainRequest { dictionary PayOnchainRequest {
@@ -431,6 +434,14 @@ dictionary RefundableSwap {
u64 amount_sat; u64 amount_sat;
}; };
dictionary RecommendedFees {
u64 fastest_fee;
u64 half_hour_fee;
u64 hour_fee;
u64 economy_fee;
u64 minimum_fee;
};
dictionary PrepareRefundRequest { dictionary PrepareRefundRequest {
string swap_address; string swap_address;
string refund_address; string refund_address;
@@ -551,6 +562,9 @@ interface BindingLiquidSdk {
[Throws=SdkError] [Throws=SdkError]
void sync(); void sync();
[Throws=SdkError]
RecommendedFees recommended_fees();
[Throws=SdkError] [Throws=SdkError]
void backup(BackupRequest req); void backup(BackupRequest req);

View File

@@ -206,6 +206,10 @@ impl BindingLiquidSdk {
rt().block_on(self.sdk.sync()).map_err(Into::into) rt().block_on(self.sdk.sync()).map_err(Into::into)
} }
pub fn recommended_fees(&self) -> SdkResult<RecommendedFees> {
rt().block_on(self.sdk.recommended_fees())
}
pub fn empty_wallet_cache(&self) -> SdkResult<()> { pub fn empty_wallet_cache(&self) -> SdkResult<()> {
self.sdk.empty_wallet_cache().map_err(Into::into) self.sdk.empty_wallet_cache().map_err(Into::into)
} }

View File

@@ -231,6 +231,10 @@ impl BindingLiquidSdk {
self.sdk.sync().await.map_err(Into::into) self.sdk.sync().await.map_err(Into::into)
} }
pub async fn recommended_fees(&self) -> Result<RecommendedFees, SdkError> {
self.sdk.recommended_fees().await.map_err(Into::into)
}
#[frb(sync)] #[frb(sync)]
pub fn empty_wallet_cache(&self) -> Result<(), SdkError> { pub fn empty_wallet_cache(&self) -> Result<(), SdkError> {
self.sdk.empty_wallet_cache().map_err(Into::into) self.sdk.empty_wallet_cache().map_err(Into::into)

View File

@@ -13,6 +13,9 @@ use lwk_wollet::{
hashes::{sha256, Hash}, hashes::{sha256, Hash},
ElectrumOptions, ElectrumUrl, Error, History, ElectrumOptions, ElectrumUrl, Error, History,
}; };
use sdk_common::prelude::get_parse_and_log_response;
use crate::model::{Config, RecommendedFees};
/// Trait implemented by types that can fetch data from a blockchain data source. /// Trait implemented by types that can fetch data from a blockchain data source.
#[allow(dead_code)] #[allow(dead_code)]
@@ -41,24 +44,33 @@ pub trait BitcoinChainService: Send + Sync {
tx_hex: &str, tx_hex: &str,
verify_confirmation: bool, verify_confirmation: bool,
) -> Result<Transaction>; ) -> Result<Transaction>;
/// Get the recommended fees, in sat/vbyte
async fn recommended_fees(&self) -> Result<RecommendedFees>;
} }
pub(crate) struct ElectrumClient { pub(crate) struct HybridBitcoinChainService {
client: Client, client: Client,
tip: HeaderNotification, tip: HeaderNotification,
config: Config,
} }
impl ElectrumClient { impl HybridBitcoinChainService {
pub fn new(url: &ElectrumUrl) -> Result<Self, Error> { pub fn new(config: Config) -> Result<Self, Error> {
Self::with_options(url, ElectrumOptions::default()) Self::with_options(config, ElectrumOptions::default())
} }
/// Creates an Electrum client specifying non default options like timeout /// Creates an Electrum client specifying non default options like timeout
pub fn with_options(url: &ElectrumUrl, options: ElectrumOptions) -> Result<Self, Error> { pub fn with_options(config: Config, options: ElectrumOptions) -> Result<Self, Error> {
let url = ElectrumUrl::new(&config.bitcoin_electrum_url, true, true);
let client = url.build_client(&options)?; let client = url.build_client(&options)?;
let header = client.block_headers_subscribe_raw()?; let header = client.block_headers_subscribe_raw()?;
let tip: HeaderNotification = header.try_into()?; let tip: HeaderNotification = header.try_into()?;
Ok(Self { client, tip }) Ok(Self {
client,
tip,
config,
})
} }
async fn get_script_history_with_retry( async fn get_script_history_with_retry(
@@ -92,7 +104,7 @@ impl ElectrumClient {
} }
#[async_trait] #[async_trait]
impl BitcoinChainService for ElectrumClient { impl BitcoinChainService for HybridBitcoinChainService {
fn tip(&mut self) -> Result<HeaderNotification> { fn tip(&mut self) -> Result<HeaderNotification> {
let mut maybe_popped_header = None; let mut maybe_popped_header = None;
while let Some(header) = self.client.block_headers_pop_raw()? { while let Some(header) = self.client.block_headers_pop_raw()? {
@@ -185,4 +197,13 @@ impl BitcoinChainService for ElectrumClient {
)), )),
} }
} }
async fn recommended_fees(&self) -> Result<RecommendedFees> {
get_parse_and_log_response(
&format!("{}/v1/fees/recommended", self.config.mempoolspace_url),
true,
)
.await
.map_err(Into::into)
}
} }

View File

@@ -23,6 +23,9 @@ use crate::utils;
use crate::wallet::OnchainWallet; use crate::wallet::OnchainWallet;
use crate::{error::PaymentError, model::PaymentState, persist::Persister}; use crate::{error::PaymentError, model::PaymentState, persist::Persister};
// Estimates based on https://github.com/BoltzExchange/boltz-backend/blob/ee4c77be1fcb9bb2b45703c542ad67f7efbf218d/lib/rates/FeeProvider.ts#L78
pub const ESTIMATED_BTC_CLAIM_TX_VSIZE: u64 = 111;
pub(crate) struct ChainSwapStateHandler { pub(crate) struct ChainSwapStateHandler {
config: Config, config: Config,
onchain_wallet: Arc<dyn OnchainWallet>, onchain_wallet: Arc<dyn OnchainWallet>,

View File

@@ -423,6 +423,7 @@ impl CstDecode<crate::model::Config> for wire_cst_config {
crate::model::Config { crate::model::Config {
liquid_electrum_url: self.liquid_electrum_url.cst_decode(), liquid_electrum_url: self.liquid_electrum_url.cst_decode(),
bitcoin_electrum_url: self.bitcoin_electrum_url.cst_decode(), bitcoin_electrum_url: self.bitcoin_electrum_url.cst_decode(),
mempoolspace_url: self.mempoolspace_url.cst_decode(),
working_dir: self.working_dir.cst_decode(), working_dir: self.working_dir.cst_decode(),
network: self.network.cst_decode(), network: self.network.cst_decode(),
payment_timeout_sec: self.payment_timeout_sec.cst_decode(), payment_timeout_sec: self.payment_timeout_sec.cst_decode(),
@@ -1136,6 +1137,7 @@ impl CstDecode<crate::model::PreparePayOnchainRequest> for wire_cst_prepare_pay_
fn cst_decode(self) -> crate::model::PreparePayOnchainRequest { fn cst_decode(self) -> crate::model::PreparePayOnchainRequest {
crate::model::PreparePayOnchainRequest { crate::model::PreparePayOnchainRequest {
receiver_amount_sat: self.receiver_amount_sat.cst_decode(), receiver_amount_sat: self.receiver_amount_sat.cst_decode(),
sat_per_vbyte: self.sat_per_vbyte.cst_decode(),
} }
} }
} }
@@ -1144,7 +1146,8 @@ impl CstDecode<crate::model::PreparePayOnchainResponse> for wire_cst_prepare_pay
fn cst_decode(self) -> crate::model::PreparePayOnchainResponse { fn cst_decode(self) -> crate::model::PreparePayOnchainResponse {
crate::model::PreparePayOnchainResponse { crate::model::PreparePayOnchainResponse {
receiver_amount_sat: self.receiver_amount_sat.cst_decode(), receiver_amount_sat: self.receiver_amount_sat.cst_decode(),
fees_sat: self.fees_sat.cst_decode(), claim_fees_sat: self.claim_fees_sat.cst_decode(),
total_fees_sat: self.total_fees_sat.cst_decode(),
} }
} }
} }
@@ -1250,6 +1253,18 @@ impl CstDecode<crate::model::ReceivePaymentResponse> for wire_cst_receive_paymen
} }
} }
} }
impl CstDecode<crate::model::RecommendedFees> for wire_cst_recommended_fees {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> crate::model::RecommendedFees {
crate::model::RecommendedFees {
fastest_fee: self.fastest_fee.cst_decode(),
half_hour_fee: self.half_hour_fee.cst_decode(),
hour_fee: self.hour_fee.cst_decode(),
economy_fee: self.economy_fee.cst_decode(),
minimum_fee: self.minimum_fee.cst_decode(),
}
}
}
impl CstDecode<crate::model::RefundRequest> for wire_cst_refund_request { impl CstDecode<crate::model::RefundRequest> for wire_cst_refund_request {
// Codec=Cst (C-struct based), see doc to use other codecs // Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> crate::model::RefundRequest { fn cst_decode(self) -> crate::model::RefundRequest {
@@ -1500,6 +1515,7 @@ impl NewWithNullPtr for wire_cst_config {
Self { Self {
liquid_electrum_url: core::ptr::null_mut(), liquid_electrum_url: core::ptr::null_mut(),
bitcoin_electrum_url: core::ptr::null_mut(), bitcoin_electrum_url: core::ptr::null_mut(),
mempoolspace_url: core::ptr::null_mut(),
working_dir: core::ptr::null_mut(), working_dir: core::ptr::null_mut(),
network: Default::default(), network: Default::default(),
payment_timeout_sec: Default::default(), payment_timeout_sec: Default::default(),
@@ -1976,6 +1992,7 @@ impl NewWithNullPtr for wire_cst_prepare_pay_onchain_request {
fn new_with_null_ptr() -> Self { fn new_with_null_ptr() -> Self {
Self { Self {
receiver_amount_sat: Default::default(), receiver_amount_sat: Default::default(),
sat_per_vbyte: core::ptr::null_mut(),
} }
} }
} }
@@ -1988,7 +2005,8 @@ impl NewWithNullPtr for wire_cst_prepare_pay_onchain_response {
fn new_with_null_ptr() -> Self { fn new_with_null_ptr() -> Self {
Self { Self {
receiver_amount_sat: Default::default(), receiver_amount_sat: Default::default(),
fees_sat: Default::default(), claim_fees_sat: Default::default(),
total_fees_sat: Default::default(),
} }
} }
} }
@@ -2139,6 +2157,22 @@ impl Default for wire_cst_receive_payment_response {
Self::new_with_null_ptr() Self::new_with_null_ptr()
} }
} }
impl NewWithNullPtr for wire_cst_recommended_fees {
fn new_with_null_ptr() -> Self {
Self {
fastest_fee: Default::default(),
half_hour_fee: Default::default(),
hour_fee: Default::default(),
economy_fee: Default::default(),
minimum_fee: Default::default(),
}
}
}
impl Default for wire_cst_recommended_fees {
fn default() -> Self {
Self::new_with_null_ptr()
}
}
impl NewWithNullPtr for wire_cst_refund_request { impl NewWithNullPtr for wire_cst_refund_request {
fn new_with_null_ptr() -> Self { fn new_with_null_ptr() -> Self {
Self { Self {
@@ -2489,6 +2523,14 @@ pub extern "C" fn frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_re
wire__crate__bindings__BindingLiquidSdk_receive_payment_impl(port_, that, req) wire__crate__bindings__BindingLiquidSdk_receive_payment_impl(port_, that, req)
} }
#[no_mangle]
pub extern "C" fn frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_recommended_fees(
port_: i64,
that: usize,
) {
wire__crate__bindings__BindingLiquidSdk_recommended_fees_impl(port_, that)
}
#[no_mangle] #[no_mangle]
pub extern "C" fn frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_refund( pub extern "C" fn frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_refund(
port_: i64, port_: i64,
@@ -3059,6 +3101,7 @@ pub struct wire_cst_bitcoin_address_data {
pub struct wire_cst_config { pub struct wire_cst_config {
liquid_electrum_url: *mut wire_cst_list_prim_u_8_strict, liquid_electrum_url: *mut wire_cst_list_prim_u_8_strict,
bitcoin_electrum_url: *mut wire_cst_list_prim_u_8_strict, bitcoin_electrum_url: *mut wire_cst_list_prim_u_8_strict,
mempoolspace_url: *mut wire_cst_list_prim_u_8_strict,
working_dir: *mut wire_cst_list_prim_u_8_strict, working_dir: *mut wire_cst_list_prim_u_8_strict,
network: i32, network: i32,
payment_timeout_sec: u64, payment_timeout_sec: u64,
@@ -3657,12 +3700,14 @@ pub struct wire_cst_PaymentError_SignerError {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct wire_cst_prepare_pay_onchain_request { pub struct wire_cst_prepare_pay_onchain_request {
receiver_amount_sat: u64, receiver_amount_sat: u64,
sat_per_vbyte: *mut u32,
} }
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct wire_cst_prepare_pay_onchain_response { pub struct wire_cst_prepare_pay_onchain_response {
receiver_amount_sat: u64, receiver_amount_sat: u64,
fees_sat: u64, claim_fees_sat: u64,
total_fees_sat: u64,
} }
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@@ -3731,6 +3776,15 @@ pub struct wire_cst_receive_payment_response {
} }
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct wire_cst_recommended_fees {
fastest_fee: u64,
half_hour_fee: u64,
hour_fee: u64,
economy_fee: u64,
minimum_fee: u64,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_refund_request { pub struct wire_cst_refund_request {
swap_address: *mut wire_cst_list_prim_u_8_strict, swap_address: *mut wire_cst_list_prim_u_8_strict,
refund_address: *mut wire_cst_list_prim_u_8_strict, refund_address: *mut wire_cst_list_prim_u_8_strict,

View File

@@ -39,7 +39,7 @@ flutter_rust_bridge::frb_generated_boilerplate!(
default_rust_auto_opaque = RustAutoOpaqueNom, default_rust_auto_opaque = RustAutoOpaqueNom,
); );
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0"; pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0";
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1318462354; pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 749689565;
// Section: executor // Section: executor
@@ -1072,6 +1072,51 @@ fn wire__crate__bindings__BindingLiquidSdk_receive_payment_impl(
}, },
) )
} }
fn wire__crate__bindings__BindingLiquidSdk_recommended_fees_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
that: impl CstDecode<
RustOpaqueNom<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<BindingLiquidSdk>>,
>,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::DcoCodec, _, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "BindingLiquidSdk_recommended_fees",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
let api_that = that.cst_decode();
move |context| async move {
transform_result_dco::<_, _, crate::error::SdkError>(
(move || async move {
let mut api_that_guard = None;
let decode_indices_ =
flutter_rust_bridge::for_generated::lockable_compute_decode_order(
vec![flutter_rust_bridge::for_generated::LockableOrderInfo::new(
&api_that, 0, false,
)],
);
for i in decode_indices_ {
match i {
0 => {
api_that_guard =
Some(api_that.lockable_decode_async_ref().await)
}
_ => unreachable!(),
}
}
let api_that_guard = api_that_guard.unwrap();
let output_ok =
crate::bindings::BindingLiquidSdk::recommended_fees(&*api_that_guard)
.await?;
Ok(output_ok)
})()
.await,
)
}
},
)
}
fn wire__crate__bindings__BindingLiquidSdk_refund_impl( fn wire__crate__bindings__BindingLiquidSdk_refund_impl(
port_: flutter_rust_bridge::for_generated::MessagePort, port_: flutter_rust_bridge::for_generated::MessagePort,
that: impl CstDecode< that: impl CstDecode<
@@ -1863,6 +1908,7 @@ impl SseDecode for crate::model::Config {
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
let mut var_liquidElectrumUrl = <String>::sse_decode(deserializer); let mut var_liquidElectrumUrl = <String>::sse_decode(deserializer);
let mut var_bitcoinElectrumUrl = <String>::sse_decode(deserializer); let mut var_bitcoinElectrumUrl = <String>::sse_decode(deserializer);
let mut var_mempoolspaceUrl = <String>::sse_decode(deserializer);
let mut var_workingDir = <String>::sse_decode(deserializer); let mut var_workingDir = <String>::sse_decode(deserializer);
let mut var_network = <crate::model::LiquidNetwork>::sse_decode(deserializer); let mut var_network = <crate::model::LiquidNetwork>::sse_decode(deserializer);
let mut var_paymentTimeoutSec = <u64>::sse_decode(deserializer); let mut var_paymentTimeoutSec = <u64>::sse_decode(deserializer);
@@ -1871,6 +1917,7 @@ impl SseDecode for crate::model::Config {
return crate::model::Config { return crate::model::Config {
liquid_electrum_url: var_liquidElectrumUrl, liquid_electrum_url: var_liquidElectrumUrl,
bitcoin_electrum_url: var_bitcoinElectrumUrl, bitcoin_electrum_url: var_bitcoinElectrumUrl,
mempoolspace_url: var_mempoolspaceUrl,
working_dir: var_workingDir, working_dir: var_workingDir,
network: var_network, network: var_network,
payment_timeout_sec: var_paymentTimeoutSec, payment_timeout_sec: var_paymentTimeoutSec,
@@ -2889,8 +2936,10 @@ impl SseDecode for crate::model::PreparePayOnchainRequest {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
let mut var_receiverAmountSat = <u64>::sse_decode(deserializer); let mut var_receiverAmountSat = <u64>::sse_decode(deserializer);
let mut var_satPerVbyte = <Option<u32>>::sse_decode(deserializer);
return crate::model::PreparePayOnchainRequest { return crate::model::PreparePayOnchainRequest {
receiver_amount_sat: var_receiverAmountSat, receiver_amount_sat: var_receiverAmountSat,
sat_per_vbyte: var_satPerVbyte,
}; };
} }
} }
@@ -2899,10 +2948,12 @@ impl SseDecode for crate::model::PreparePayOnchainResponse {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
let mut var_receiverAmountSat = <u64>::sse_decode(deserializer); let mut var_receiverAmountSat = <u64>::sse_decode(deserializer);
let mut var_feesSat = <u64>::sse_decode(deserializer); let mut var_claimFeesSat = <u64>::sse_decode(deserializer);
let mut var_totalFeesSat = <u64>::sse_decode(deserializer);
return crate::model::PreparePayOnchainResponse { return crate::model::PreparePayOnchainResponse {
receiver_amount_sat: var_receiverAmountSat, receiver_amount_sat: var_receiverAmountSat,
fees_sat: var_feesSat, claim_fees_sat: var_claimFeesSat,
total_fees_sat: var_totalFeesSat,
}; };
} }
} }
@@ -3037,6 +3088,24 @@ impl SseDecode for crate::model::ReceivePaymentResponse {
} }
} }
impl SseDecode for crate::model::RecommendedFees {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
let mut var_fastestFee = <u64>::sse_decode(deserializer);
let mut var_halfHourFee = <u64>::sse_decode(deserializer);
let mut var_hourFee = <u64>::sse_decode(deserializer);
let mut var_economyFee = <u64>::sse_decode(deserializer);
let mut var_minimumFee = <u64>::sse_decode(deserializer);
return crate::model::RecommendedFees {
fastest_fee: var_fastestFee,
half_hour_fee: var_halfHourFee,
hour_fee: var_hourFee,
economy_fee: var_economyFee,
minimum_fee: var_minimumFee,
};
}
}
impl SseDecode for crate::model::RefundRequest { impl SseDecode for crate::model::RefundRequest {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
@@ -3448,6 +3517,7 @@ impl flutter_rust_bridge::IntoDart for crate::model::Config {
[ [
self.liquid_electrum_url.into_into_dart().into_dart(), self.liquid_electrum_url.into_into_dart().into_dart(),
self.bitcoin_electrum_url.into_into_dart().into_dart(), self.bitcoin_electrum_url.into_into_dart().into_dart(),
self.mempoolspace_url.into_into_dart().into_dart(),
self.working_dir.into_into_dart().into_dart(), self.working_dir.into_into_dart().into_dart(),
self.network.into_into_dart().into_dart(), self.network.into_into_dart().into_dart(),
self.payment_timeout_sec.into_into_dart().into_dart(), self.payment_timeout_sec.into_into_dart().into_dart(),
@@ -4368,7 +4438,11 @@ impl flutter_rust_bridge::IntoIntoDart<crate::model::PaymentType> for crate::mod
// Codec=Dco (DartCObject based), see doc to use other codecs // Codec=Dco (DartCObject based), see doc to use other codecs
impl flutter_rust_bridge::IntoDart for crate::model::PreparePayOnchainRequest { impl flutter_rust_bridge::IntoDart for crate::model::PreparePayOnchainRequest {
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
[self.receiver_amount_sat.into_into_dart().into_dart()].into_dart() [
self.receiver_amount_sat.into_into_dart().into_dart(),
self.sat_per_vbyte.into_into_dart().into_dart(),
]
.into_dart()
} }
} }
impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive
@@ -4387,7 +4461,8 @@ impl flutter_rust_bridge::IntoDart for crate::model::PreparePayOnchainResponse {
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
[ [
self.receiver_amount_sat.into_into_dart().into_dart(), self.receiver_amount_sat.into_into_dart().into_dart(),
self.fees_sat.into_into_dart().into_dart(), self.claim_fees_sat.into_into_dart().into_dart(),
self.total_fees_sat.into_into_dart().into_dart(),
] ]
.into_dart() .into_dart()
} }
@@ -4625,6 +4700,27 @@ impl flutter_rust_bridge::IntoIntoDart<crate::model::ReceivePaymentResponse>
} }
} }
// Codec=Dco (DartCObject based), see doc to use other codecs // Codec=Dco (DartCObject based), see doc to use other codecs
impl flutter_rust_bridge::IntoDart for crate::model::RecommendedFees {
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
[
self.fastest_fee.into_into_dart().into_dart(),
self.half_hour_fee.into_into_dart().into_dart(),
self.hour_fee.into_into_dart().into_dart(),
self.economy_fee.into_into_dart().into_dart(),
self.minimum_fee.into_into_dart().into_dart(),
]
.into_dart()
}
}
impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for crate::model::RecommendedFees {}
impl flutter_rust_bridge::IntoIntoDart<crate::model::RecommendedFees>
for crate::model::RecommendedFees
{
fn into_into_dart(self) -> crate::model::RecommendedFees {
self
}
}
// Codec=Dco (DartCObject based), see doc to use other codecs
impl flutter_rust_bridge::IntoDart for crate::model::RefundRequest { impl flutter_rust_bridge::IntoDart for crate::model::RefundRequest {
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
[ [
@@ -5001,6 +5097,7 @@ impl SseEncode for crate::model::Config {
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<String>::sse_encode(self.liquid_electrum_url, serializer); <String>::sse_encode(self.liquid_electrum_url, serializer);
<String>::sse_encode(self.bitcoin_electrum_url, serializer); <String>::sse_encode(self.bitcoin_electrum_url, serializer);
<String>::sse_encode(self.mempoolspace_url, serializer);
<String>::sse_encode(self.working_dir, serializer); <String>::sse_encode(self.working_dir, serializer);
<crate::model::LiquidNetwork>::sse_encode(self.network, serializer); <crate::model::LiquidNetwork>::sse_encode(self.network, serializer);
<u64>::sse_encode(self.payment_timeout_sec, serializer); <u64>::sse_encode(self.payment_timeout_sec, serializer);
@@ -5823,6 +5920,7 @@ impl SseEncode for crate::model::PreparePayOnchainRequest {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<u64>::sse_encode(self.receiver_amount_sat, serializer); <u64>::sse_encode(self.receiver_amount_sat, serializer);
<Option<u32>>::sse_encode(self.sat_per_vbyte, serializer);
} }
} }
@@ -5830,7 +5928,8 @@ impl SseEncode for crate::model::PreparePayOnchainResponse {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<u64>::sse_encode(self.receiver_amount_sat, serializer); <u64>::sse_encode(self.receiver_amount_sat, serializer);
<u64>::sse_encode(self.fees_sat, serializer); <u64>::sse_encode(self.claim_fees_sat, serializer);
<u64>::sse_encode(self.total_fees_sat, serializer);
} }
} }
@@ -5921,6 +6020,17 @@ impl SseEncode for crate::model::ReceivePaymentResponse {
} }
} }
impl SseEncode for crate::model::RecommendedFees {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<u64>::sse_encode(self.fastest_fee, serializer);
<u64>::sse_encode(self.half_hour_fee, serializer);
<u64>::sse_encode(self.hour_fee, serializer);
<u64>::sse_encode(self.economy_fee, serializer);
<u64>::sse_encode(self.minimum_fee, serializer);
}
}
impl SseEncode for crate::model::RefundRequest { impl SseEncode for crate::model::RefundRequest {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {

View File

@@ -26,6 +26,8 @@ pub const LOWBALL_FEE_RATE_SAT_PER_VBYTE: f32 = 0.01;
pub struct Config { pub struct Config {
pub liquid_electrum_url: String, pub liquid_electrum_url: String,
pub bitcoin_electrum_url: String, pub bitcoin_electrum_url: String,
/// The mempool.space API URL, has to be in the format: `https://mempool.space/api`
pub mempoolspace_url: String,
/// Directory in which all SDK files (DB, log, cache) are stored. /// Directory in which all SDK files (DB, log, cache) are stored.
/// ///
/// Prefix can be a relative or absolute path to this directory. /// Prefix can be a relative or absolute path to this directory.
@@ -45,6 +47,7 @@ impl Config {
Config { Config {
liquid_electrum_url: "blockstream.info:995".to_string(), liquid_electrum_url: "blockstream.info:995".to_string(),
bitcoin_electrum_url: "blockstream.info:700".to_string(), bitcoin_electrum_url: "blockstream.info:700".to_string(),
mempoolspace_url: "https://mempool.space/api".to_string(),
working_dir: ".".to_string(), working_dir: ".".to_string(),
network: LiquidNetwork::Mainnet, network: LiquidNetwork::Mainnet,
payment_timeout_sec: 15, payment_timeout_sec: 15,
@@ -57,6 +60,7 @@ impl Config {
Config { Config {
liquid_electrum_url: "blockstream.info:465".to_string(), liquid_electrum_url: "blockstream.info:465".to_string(),
bitcoin_electrum_url: "blockstream.info:993".to_string(), bitcoin_electrum_url: "blockstream.info:993".to_string(),
mempoolspace_url: "https://mempool.space/testnet/api".to_string(),
working_dir: ".".to_string(), working_dir: ".".to_string(),
network: LiquidNetwork::Testnet, network: LiquidNetwork::Testnet,
payment_timeout_sec: 15, payment_timeout_sec: 15,
@@ -227,12 +231,14 @@ pub struct SendPaymentResponse {
#[derive(Debug, Serialize, Clone)] #[derive(Debug, Serialize, Clone)]
pub struct PreparePayOnchainRequest { pub struct PreparePayOnchainRequest {
pub receiver_amount_sat: u64, pub receiver_amount_sat: u64,
pub sat_per_vbyte: Option<u32>,
} }
#[derive(Debug, Serialize, Clone)] #[derive(Debug, Serialize, Clone)]
pub struct PreparePayOnchainResponse { pub struct PreparePayOnchainResponse {
pub receiver_amount_sat: u64, pub receiver_amount_sat: u64,
pub fees_sat: u64, pub claim_fees_sat: u64,
pub total_fees_sat: u64,
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@@ -1003,6 +1009,16 @@ impl Payment {
} }
} }
#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RecommendedFees {
pub fastest_fee: u64,
pub half_hour_fee: u64,
pub hour_fee: u64,
pub economy_fee: u64,
pub minimum_fee: u64,
}
/// Internal SDK log entry used in the Uniffi and Dart bindings /// Internal SDK log entry used in the Uniffi and Dart bindings
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct LogEntry { pub struct LogEntry {

View File

@@ -6,14 +6,16 @@ use anyhow::Result;
use async_trait::async_trait; use async_trait::async_trait;
use boltz_client::{swaps::boltzv2::*, util::secrets::Preimage, Bolt11Invoice}; use boltz_client::{swaps::boltzv2::*, util::secrets::Preimage, Bolt11Invoice};
use boltz_client::{LockTime, ToHex}; use boltz_client::{LockTime, ToHex};
use chain::bitcoin::HybridBitcoinChainService;
use chain::liquid::{HybridLiquidChainService, LiquidChainService}; use chain::liquid::{HybridLiquidChainService, LiquidChainService};
use chain_swap::ESTIMATED_BTC_CLAIM_TX_VSIZE;
use futures_util::stream::select_all; use futures_util::stream::select_all;
use futures_util::StreamExt; use futures_util::StreamExt;
use log::{debug, error, info}; use log::{debug, error, info};
use lwk_wollet::bitcoin::hex::DisplayHex; use lwk_wollet::bitcoin::hex::DisplayHex;
use lwk_wollet::hashes::{sha256, Hash}; use lwk_wollet::hashes::{sha256, Hash};
use lwk_wollet::secp256k1::ThirtyTwoByteHash; use lwk_wollet::secp256k1::ThirtyTwoByteHash;
use lwk_wollet::{elements, ElectrumUrl, ElementsNetwork}; use lwk_wollet::{elements, ElementsNetwork};
use sdk_common::bitcoin::secp256k1::Secp256k1; use sdk_common::bitcoin::secp256k1::Secp256k1;
use sdk_common::bitcoin::util::bip32::ChildNumber; use sdk_common::bitcoin::util::bip32::ChildNumber;
use sdk_common::prelude::{FiatAPI, FiatCurrency, LnUrlPayError, LnUrlWithdrawError, Rate}; use sdk_common::prelude::{FiatAPI, FiatCurrency, LnUrlPayError, LnUrlWithdrawError, Rate};
@@ -22,7 +24,7 @@ use tokio::time::MissedTickBehavior;
use tokio_stream::wrappers::BroadcastStream; use tokio_stream::wrappers::BroadcastStream;
use url::Url; use url::Url;
use crate::chain::bitcoin::{self, BitcoinChainService}; use crate::chain::bitcoin::BitcoinChainService;
use crate::chain_swap::ChainSwapStateHandler; use crate::chain_swap::ChainSwapStateHandler;
use crate::error::SdkError; use crate::error::SdkError;
use crate::model::PaymentState::*; use crate::model::PaymentState::*;
@@ -100,9 +102,8 @@ impl LiquidSdk {
let liquid_chain_service = let liquid_chain_service =
Arc::new(Mutex::new(HybridLiquidChainService::new(config.clone())?)); Arc::new(Mutex::new(HybridLiquidChainService::new(config.clone())?));
let bitcoin_chain_service = Arc::new(Mutex::new(bitcoin::ElectrumClient::new( let bitcoin_chain_service =
&ElectrumUrl::new(&config.bitcoin_electrum_url, true, true), Arc::new(Mutex::new(HybridBitcoinChainService::new(config.clone())?));
)?));
let onchain_wallet = Arc::new(LiquidOnchainWallet::new(mnemonic, config.clone())?); let onchain_wallet = Arc::new(LiquidOnchainWallet::new(mnemonic, config.clone())?);
@@ -895,7 +896,10 @@ impl LiquidSdk {
let receiver_amount_sat = req.receiver_amount_sat; let receiver_amount_sat = req.receiver_amount_sat;
let pair = self.validate_chain_pairs(Direction::Outgoing, receiver_amount_sat)?; let pair = self.validate_chain_pairs(Direction::Outgoing, receiver_amount_sat)?;
let claim_fees_sat = pair.fees.claim_estimate(); let claim_fees_sat = match req.sat_per_vbyte {
Some(sat_per_vbyte) => ESTIMATED_BTC_CLAIM_TX_VSIZE * sat_per_vbyte as u64,
None => pair.fees.claim_estimate(),
};
let server_fees_sat = pair.fees.server(); let server_fees_sat = pair.fees.server();
let server_lockup_amount_sat = receiver_amount_sat + claim_fees_sat; let server_lockup_amount_sat = receiver_amount_sat + claim_fees_sat;
let lockup_fees_sat = self let lockup_fees_sat = self
@@ -904,13 +908,14 @@ impl LiquidSdk {
let res = PreparePayOnchainResponse { let res = PreparePayOnchainResponse {
receiver_amount_sat, receiver_amount_sat,
fees_sat: pair.fees.boltz(server_lockup_amount_sat) claim_fees_sat,
total_fees_sat: pair.fees.boltz(server_lockup_amount_sat)
+ lockup_fees_sat + lockup_fees_sat
+ claim_fees_sat + claim_fees_sat
+ server_fees_sat, + server_fees_sat,
}; };
let payer_amount_sat = res.receiver_amount_sat + res.fees_sat; let payer_amount_sat = res.receiver_amount_sat + res.total_fees_sat;
ensure_sdk!( ensure_sdk!(
payer_amount_sat <= self.get_info().await?.balance_sat, payer_amount_sat <= self.get_info().await?.balance_sat,
PaymentError::InsufficientFunds PaymentError::InsufficientFunds
@@ -927,7 +932,7 @@ impl LiquidSdk {
let receiver_amount_sat = req.prepare_res.receiver_amount_sat; let receiver_amount_sat = req.prepare_res.receiver_amount_sat;
let pair = self.validate_chain_pairs(Direction::Outgoing, receiver_amount_sat)?; let pair = self.validate_chain_pairs(Direction::Outgoing, receiver_amount_sat)?;
let claim_fees_sat = pair.fees.claim_estimate(); let claim_fees_sat = req.prepare_res.claim_fees_sat;
let server_fees_sat = pair.fees.server(); let server_fees_sat = pair.fees.server();
let server_lockup_amount_sat = receiver_amount_sat + claim_fees_sat; let server_lockup_amount_sat = receiver_amount_sat + claim_fees_sat;
let lockup_fees_sat = self let lockup_fees_sat = self
@@ -935,7 +940,7 @@ impl LiquidSdk {
.await?; .await?;
ensure_sdk!( ensure_sdk!(
req.prepare_res.fees_sat req.prepare_res.total_fees_sat
== pair.fees.boltz(server_lockup_amount_sat) == pair.fees.boltz(server_lockup_amount_sat)
+ lockup_fees_sat + lockup_fees_sat
+ claim_fees_sat + claim_fees_sat
@@ -943,7 +948,7 @@ impl LiquidSdk {
PaymentError::InvalidOrExpiredFees PaymentError::InvalidOrExpiredFees
); );
let payer_amount_sat = req.prepare_res.fees_sat + receiver_amount_sat; let payer_amount_sat = req.prepare_res.total_fees_sat + receiver_amount_sat;
ensure_sdk!( ensure_sdk!(
payer_amount_sat <= self.get_info().await?.balance_sat, payer_amount_sat <= self.get_info().await?.balance_sat,
PaymentError::InsufficientFunds PaymentError::InsufficientFunds
@@ -978,7 +983,7 @@ impl LiquidSdk {
let create_response_json = ChainSwap::from_boltz_struct_to_json(&create_response, swap_id)?; let create_response_json = ChainSwap::from_boltz_struct_to_json(&create_response, swap_id)?;
let accept_zero_conf = server_lockup_amount_sat <= pair.limits.maximal_zero_conf; let accept_zero_conf = server_lockup_amount_sat <= pair.limits.maximal_zero_conf;
let payer_amount_sat = req.prepare_res.fees_sat + receiver_amount_sat; let payer_amount_sat = req.prepare_res.total_fees_sat + receiver_amount_sat;
let claim_address = req.address.clone(); let claim_address = req.address.clone();
let swap = ChainSwap { let swap = ChainSwap {
@@ -1620,6 +1625,16 @@ impl LiquidSdk {
.map_err(Into::into) .map_err(Into::into)
} }
/// Get the recommended BTC fees based on the configured mempool.space instance
pub async fn recommended_fees(&self) -> Result<RecommendedFees, SdkError> {
Ok(self
.bitcoin_chain_service
.lock()
.await
.recommended_fees()
.await?)
}
pub fn default_config(network: LiquidNetwork) -> Config { pub fn default_config(network: LiquidNetwork) -> Config {
match network { match network {
LiquidNetwork::Mainnet => Config::mainnet(), LiquidNetwork::Mainnet => Config::mainnet(),

View File

@@ -5,6 +5,7 @@ use async_trait::async_trait;
use crate::{ use crate::{
chain::{bitcoin::BitcoinChainService, liquid::LiquidChainService}, chain::{bitcoin::BitcoinChainService, liquid::LiquidChainService},
prelude::RecommendedFees,
utils, utils,
}; };
@@ -107,4 +108,8 @@ impl BitcoinChainService for MockBitcoinChainService {
) -> Result<boltz_client::bitcoin::Transaction> { ) -> Result<boltz_client::bitcoin::Transaction> {
unimplemented!() unimplemented!()
} }
async fn recommended_fees(&self) -> Result<RecommendedFees> {
unimplemented!()
}
} }

View File

@@ -3,11 +3,10 @@
use anyhow::Result; use anyhow::Result;
use std::sync::Arc; use std::sync::Arc;
use lwk_wollet::ElectrumUrl;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::{ use crate::{
chain::{bitcoin, liquid::HybridLiquidChainService}, chain::{bitcoin::HybridBitcoinChainService, liquid::HybridLiquidChainService},
chain_swap::ChainSwapStateHandler, chain_swap::ChainSwapStateHandler,
model::{ChainSwap, Config, Direction, PaymentState}, model::{ChainSwap, Config, Direction, PaymentState},
persist::Persister, persist::Persister,
@@ -24,9 +23,8 @@ pub(crate) fn new_chain_swap_state_handler(
let onchain_wallet = Arc::new(MockWallet::new()); let onchain_wallet = Arc::new(MockWallet::new());
let swapper = Arc::new(BoltzSwapper::new(config.clone(), None)); let swapper = Arc::new(BoltzSwapper::new(config.clone(), None));
let liquid_chain_service = Arc::new(Mutex::new(HybridLiquidChainService::new(config.clone())?)); let liquid_chain_service = Arc::new(Mutex::new(HybridLiquidChainService::new(config.clone())?));
let bitcoin_chain_service = Arc::new(Mutex::new(bitcoin::ElectrumClient::new( let bitcoin_chain_service =
&ElectrumUrl::new(&config.bitcoin_electrum_url, true, true), Arc::new(Mutex::new(HybridBitcoinChainService::new(config.clone())?));
)?));
ChainSwapStateHandler::new( ChainSwapStateHandler::new(
config, config,

View File

@@ -75,6 +75,8 @@ abstract class BindingLiquidSdk implements RustOpaqueInterface {
Future<ReceivePaymentResponse> receivePayment({required PrepareReceiveResponse req}); Future<ReceivePaymentResponse> receivePayment({required PrepareReceiveResponse req});
Future<RecommendedFees> recommendedFees();
Future<RefundResponse> refund({required RefundRequest req}); Future<RefundResponse> refund({required RefundRequest req});
Future<void> rescanOnchainSwaps(); Future<void> rescanOnchainSwaps();

View File

@@ -55,7 +55,7 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
String get codegenVersion => '2.0.0'; String get codegenVersion => '2.0.0';
@override @override
int get rustContentHash => -1318462354; int get rustContentHash => 749689565;
static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig(
stem: 'breez_sdk_liquid', stem: 'breez_sdk_liquid',
@@ -124,6 +124,8 @@ abstract class RustLibApi extends BaseApi {
Future<ReceivePaymentResponse> crateBindingsBindingLiquidSdkReceivePayment( Future<ReceivePaymentResponse> crateBindingsBindingLiquidSdkReceivePayment(
{required BindingLiquidSdk that, required PrepareReceiveResponse req}); {required BindingLiquidSdk that, required PrepareReceiveResponse req});
Future<RecommendedFees> crateBindingsBindingLiquidSdkRecommendedFees({required BindingLiquidSdk that});
Future<RefundResponse> crateBindingsBindingLiquidSdkRefund( Future<RefundResponse> crateBindingsBindingLiquidSdkRefund(
{required BindingLiquidSdk that, required RefundRequest req}); {required BindingLiquidSdk that, required RefundRequest req});
@@ -724,6 +726,30 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
argNames: ["that", "req"], argNames: ["that", "req"],
); );
@override
Future<RecommendedFees> crateBindingsBindingLiquidSdkRecommendedFees({required BindingLiquidSdk that}) {
return handler.executeNormal(NormalTask(
callFfi: (port_) {
var arg0 =
cst_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk(
that);
return wire.wire__crate__bindings__BindingLiquidSdk_recommended_fees(port_, arg0);
},
codec: DcoCodec(
decodeSuccessData: dco_decode_recommended_fees,
decodeErrorData: dco_decode_sdk_error,
),
constMeta: kCrateBindingsBindingLiquidSdkRecommendedFeesConstMeta,
argValues: [that],
apiImpl: this,
));
}
TaskConstMeta get kCrateBindingsBindingLiquidSdkRecommendedFeesConstMeta => const TaskConstMeta(
debugName: "BindingLiquidSdk_recommended_fees",
argNames: ["that"],
);
@override @override
Future<RefundResponse> crateBindingsBindingLiquidSdkRefund( Future<RefundResponse> crateBindingsBindingLiquidSdkRefund(
{required BindingLiquidSdk that, required RefundRequest req}) { {required BindingLiquidSdk that, required RefundRequest req}) {
@@ -1339,15 +1365,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
Config dco_decode_config(dynamic raw) { Config dco_decode_config(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs // Codec=Dco (DartCObject based), see doc to use other codecs
final arr = raw as List<dynamic>; final arr = raw as List<dynamic>;
if (arr.length != 7) throw Exception('unexpected arr length: expect 7 but see ${arr.length}'); if (arr.length != 8) throw Exception('unexpected arr length: expect 8 but see ${arr.length}');
return Config( return Config(
liquidElectrumUrl: dco_decode_String(arr[0]), liquidElectrumUrl: dco_decode_String(arr[0]),
bitcoinElectrumUrl: dco_decode_String(arr[1]), bitcoinElectrumUrl: dco_decode_String(arr[1]),
workingDir: dco_decode_String(arr[2]), mempoolspaceUrl: dco_decode_String(arr[2]),
network: dco_decode_liquid_network(arr[3]), workingDir: dco_decode_String(arr[3]),
paymentTimeoutSec: dco_decode_u_64(arr[4]), network: dco_decode_liquid_network(arr[4]),
zeroConfMinFeeRateMsat: dco_decode_u_32(arr[5]), paymentTimeoutSec: dco_decode_u_64(arr[5]),
zeroConfMaxAmountSat: dco_decode_opt_box_autoadd_u_64(arr[6]), zeroConfMinFeeRateMsat: dco_decode_u_32(arr[6]),
zeroConfMaxAmountSat: dco_decode_opt_box_autoadd_u_64(arr[7]),
); );
} }
@@ -2079,9 +2106,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
PreparePayOnchainRequest dco_decode_prepare_pay_onchain_request(dynamic raw) { PreparePayOnchainRequest dco_decode_prepare_pay_onchain_request(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs // Codec=Dco (DartCObject based), see doc to use other codecs
final arr = raw as List<dynamic>; final arr = raw as List<dynamic>;
if (arr.length != 1) throw Exception('unexpected arr length: expect 1 but see ${arr.length}'); if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}');
return PreparePayOnchainRequest( return PreparePayOnchainRequest(
receiverAmountSat: dco_decode_u_64(arr[0]), receiverAmountSat: dco_decode_u_64(arr[0]),
satPerVbyte: dco_decode_opt_box_autoadd_u_32(arr[1]),
); );
} }
@@ -2089,10 +2117,11 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
PreparePayOnchainResponse dco_decode_prepare_pay_onchain_response(dynamic raw) { PreparePayOnchainResponse dco_decode_prepare_pay_onchain_response(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs // Codec=Dco (DartCObject based), see doc to use other codecs
final arr = raw as List<dynamic>; final arr = raw as List<dynamic>;
if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); if (arr.length != 3) throw Exception('unexpected arr length: expect 3 but see ${arr.length}');
return PreparePayOnchainResponse( return PreparePayOnchainResponse(
receiverAmountSat: dco_decode_u_64(arr[0]), receiverAmountSat: dco_decode_u_64(arr[0]),
feesSat: dco_decode_u_64(arr[1]), claimFeesSat: dco_decode_u_64(arr[1]),
totalFeesSat: dco_decode_u_64(arr[2]),
); );
} }
@@ -2216,6 +2245,20 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
); );
} }
@protected
RecommendedFees dco_decode_recommended_fees(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}');
return RecommendedFees(
fastestFee: dco_decode_u_64(arr[0]),
halfHourFee: dco_decode_u_64(arr[1]),
hourFee: dco_decode_u_64(arr[2]),
economyFee: dco_decode_u_64(arr[3]),
minimumFee: dco_decode_u_64(arr[4]),
);
}
@protected @protected
RefundRequest dco_decode_refund_request(dynamic raw) { RefundRequest dco_decode_refund_request(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs // Codec=Dco (DartCObject based), see doc to use other codecs
@@ -2783,6 +2826,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
var var_liquidElectrumUrl = sse_decode_String(deserializer); var var_liquidElectrumUrl = sse_decode_String(deserializer);
var var_bitcoinElectrumUrl = sse_decode_String(deserializer); var var_bitcoinElectrumUrl = sse_decode_String(deserializer);
var var_mempoolspaceUrl = sse_decode_String(deserializer);
var var_workingDir = sse_decode_String(deserializer); var var_workingDir = sse_decode_String(deserializer);
var var_network = sse_decode_liquid_network(deserializer); var var_network = sse_decode_liquid_network(deserializer);
var var_paymentTimeoutSec = sse_decode_u_64(deserializer); var var_paymentTimeoutSec = sse_decode_u_64(deserializer);
@@ -2791,6 +2835,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return Config( return Config(
liquidElectrumUrl: var_liquidElectrumUrl, liquidElectrumUrl: var_liquidElectrumUrl,
bitcoinElectrumUrl: var_bitcoinElectrumUrl, bitcoinElectrumUrl: var_bitcoinElectrumUrl,
mempoolspaceUrl: var_mempoolspaceUrl,
workingDir: var_workingDir, workingDir: var_workingDir,
network: var_network, network: var_network,
paymentTimeoutSec: var_paymentTimeoutSec, paymentTimeoutSec: var_paymentTimeoutSec,
@@ -3581,15 +3626,20 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
PreparePayOnchainRequest sse_decode_prepare_pay_onchain_request(SseDeserializer deserializer) { PreparePayOnchainRequest sse_decode_prepare_pay_onchain_request(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
var var_receiverAmountSat = sse_decode_u_64(deserializer); var var_receiverAmountSat = sse_decode_u_64(deserializer);
return PreparePayOnchainRequest(receiverAmountSat: var_receiverAmountSat); var var_satPerVbyte = sse_decode_opt_box_autoadd_u_32(deserializer);
return PreparePayOnchainRequest(receiverAmountSat: var_receiverAmountSat, satPerVbyte: var_satPerVbyte);
} }
@protected @protected
PreparePayOnchainResponse sse_decode_prepare_pay_onchain_response(SseDeserializer deserializer) { PreparePayOnchainResponse sse_decode_prepare_pay_onchain_response(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
var var_receiverAmountSat = sse_decode_u_64(deserializer); var var_receiverAmountSat = sse_decode_u_64(deserializer);
var var_feesSat = sse_decode_u_64(deserializer); var var_claimFeesSat = sse_decode_u_64(deserializer);
return PreparePayOnchainResponse(receiverAmountSat: var_receiverAmountSat, feesSat: var_feesSat); var var_totalFeesSat = sse_decode_u_64(deserializer);
return PreparePayOnchainResponse(
receiverAmountSat: var_receiverAmountSat,
claimFeesSat: var_claimFeesSat,
totalFeesSat: var_totalFeesSat);
} }
@protected @protected
@@ -3680,6 +3730,22 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return ReceivePaymentResponse(id: var_id, invoice: var_invoice); return ReceivePaymentResponse(id: var_id, invoice: var_invoice);
} }
@protected
RecommendedFees sse_decode_recommended_fees(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
var var_fastestFee = sse_decode_u_64(deserializer);
var var_halfHourFee = sse_decode_u_64(deserializer);
var var_hourFee = sse_decode_u_64(deserializer);
var var_economyFee = sse_decode_u_64(deserializer);
var var_minimumFee = sse_decode_u_64(deserializer);
return RecommendedFees(
fastestFee: var_fastestFee,
halfHourFee: var_halfHourFee,
hourFee: var_hourFee,
economyFee: var_economyFee,
minimumFee: var_minimumFee);
}
@protected @protected
RefundRequest sse_decode_refund_request(SseDeserializer deserializer) { RefundRequest sse_decode_refund_request(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
@@ -4313,6 +4379,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_String(self.liquidElectrumUrl, serializer); sse_encode_String(self.liquidElectrumUrl, serializer);
sse_encode_String(self.bitcoinElectrumUrl, serializer); sse_encode_String(self.bitcoinElectrumUrl, serializer);
sse_encode_String(self.mempoolspaceUrl, serializer);
sse_encode_String(self.workingDir, serializer); sse_encode_String(self.workingDir, serializer);
sse_encode_liquid_network(self.network, serializer); sse_encode_liquid_network(self.network, serializer);
sse_encode_u_64(self.paymentTimeoutSec, serializer); sse_encode_u_64(self.paymentTimeoutSec, serializer);
@@ -4973,13 +5040,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
void sse_encode_prepare_pay_onchain_request(PreparePayOnchainRequest self, SseSerializer serializer) { void sse_encode_prepare_pay_onchain_request(PreparePayOnchainRequest self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_u_64(self.receiverAmountSat, serializer); sse_encode_u_64(self.receiverAmountSat, serializer);
sse_encode_opt_box_autoadd_u_32(self.satPerVbyte, serializer);
} }
@protected @protected
void sse_encode_prepare_pay_onchain_response(PreparePayOnchainResponse self, SseSerializer serializer) { void sse_encode_prepare_pay_onchain_response(PreparePayOnchainResponse self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_u_64(self.receiverAmountSat, serializer); sse_encode_u_64(self.receiverAmountSat, serializer);
sse_encode_u_64(self.feesSat, serializer); sse_encode_u_64(self.claimFeesSat, serializer);
sse_encode_u_64(self.totalFeesSat, serializer);
} }
@protected @protected
@@ -5060,6 +5129,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_String(self.invoice, serializer); sse_encode_String(self.invoice, serializer);
} }
@protected
void sse_encode_recommended_fees(RecommendedFees self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_u_64(self.fastestFee, serializer);
sse_encode_u_64(self.halfHourFee, serializer);
sse_encode_u_64(self.hourFee, serializer);
sse_encode_u_64(self.economyFee, serializer);
sse_encode_u_64(self.minimumFee, serializer);
}
@protected @protected
void sse_encode_refund_request(RefundRequest self, SseSerializer serializer) { void sse_encode_refund_request(RefundRequest self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
@@ -5325,6 +5404,11 @@ class BindingLiquidSdkImpl extends RustOpaque implements BindingLiquidSdk {
Future<ReceivePaymentResponse> receivePayment({required PrepareReceiveResponse req}) => Future<ReceivePaymentResponse> receivePayment({required PrepareReceiveResponse req}) =>
RustLib.instance.api.crateBindingsBindingLiquidSdkReceivePayment(that: this, req: req); RustLib.instance.api.crateBindingsBindingLiquidSdkReceivePayment(that: this, req: req);
Future<RecommendedFees> recommendedFees() =>
RustLib.instance.api.crateBindingsBindingLiquidSdkRecommendedFees(
that: this,
);
Future<RefundResponse> refund({required RefundRequest req}) => Future<RefundResponse> refund({required RefundRequest req}) =>
RustLib.instance.api.crateBindingsBindingLiquidSdkRefund(that: this, req: req); RustLib.instance.api.crateBindingsBindingLiquidSdkRefund(that: this, req: req);

View File

@@ -395,6 +395,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
ReceivePaymentResponse dco_decode_receive_payment_response(dynamic raw); ReceivePaymentResponse dco_decode_receive_payment_response(dynamic raw);
@protected
RecommendedFees dco_decode_recommended_fees(dynamic raw);
@protected @protected
RefundRequest dco_decode_refund_request(dynamic raw); RefundRequest dco_decode_refund_request(dynamic raw);
@@ -824,6 +827,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
ReceivePaymentResponse sse_decode_receive_payment_response(SseDeserializer deserializer); ReceivePaymentResponse sse_decode_receive_payment_response(SseDeserializer deserializer);
@protected
RecommendedFees sse_decode_recommended_fees(SseDeserializer deserializer);
@protected @protected
RefundRequest sse_decode_refund_request(SseDeserializer deserializer); RefundRequest sse_decode_refund_request(SseDeserializer deserializer);
@@ -1647,6 +1653,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
void cst_api_fill_to_wire_config(Config apiObj, wire_cst_config wireObj) { void cst_api_fill_to_wire_config(Config apiObj, wire_cst_config wireObj) {
wireObj.liquid_electrum_url = cst_encode_String(apiObj.liquidElectrumUrl); wireObj.liquid_electrum_url = cst_encode_String(apiObj.liquidElectrumUrl);
wireObj.bitcoin_electrum_url = cst_encode_String(apiObj.bitcoinElectrumUrl); wireObj.bitcoin_electrum_url = cst_encode_String(apiObj.bitcoinElectrumUrl);
wireObj.mempoolspace_url = cst_encode_String(apiObj.mempoolspaceUrl);
wireObj.working_dir = cst_encode_String(apiObj.workingDir); wireObj.working_dir = cst_encode_String(apiObj.workingDir);
wireObj.network = cst_encode_liquid_network(apiObj.network); wireObj.network = cst_encode_liquid_network(apiObj.network);
wireObj.payment_timeout_sec = cst_encode_u_64(apiObj.paymentTimeoutSec); wireObj.payment_timeout_sec = cst_encode_u_64(apiObj.paymentTimeoutSec);
@@ -2198,13 +2205,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
void cst_api_fill_to_wire_prepare_pay_onchain_request( void cst_api_fill_to_wire_prepare_pay_onchain_request(
PreparePayOnchainRequest apiObj, wire_cst_prepare_pay_onchain_request wireObj) { PreparePayOnchainRequest apiObj, wire_cst_prepare_pay_onchain_request wireObj) {
wireObj.receiver_amount_sat = cst_encode_u_64(apiObj.receiverAmountSat); wireObj.receiver_amount_sat = cst_encode_u_64(apiObj.receiverAmountSat);
wireObj.sat_per_vbyte = cst_encode_opt_box_autoadd_u_32(apiObj.satPerVbyte);
} }
@protected @protected
void cst_api_fill_to_wire_prepare_pay_onchain_response( void cst_api_fill_to_wire_prepare_pay_onchain_response(
PreparePayOnchainResponse apiObj, wire_cst_prepare_pay_onchain_response wireObj) { PreparePayOnchainResponse apiObj, wire_cst_prepare_pay_onchain_response wireObj) {
wireObj.receiver_amount_sat = cst_encode_u_64(apiObj.receiverAmountSat); wireObj.receiver_amount_sat = cst_encode_u_64(apiObj.receiverAmountSat);
wireObj.fees_sat = cst_encode_u_64(apiObj.feesSat); wireObj.claim_fees_sat = cst_encode_u_64(apiObj.claimFeesSat);
wireObj.total_fees_sat = cst_encode_u_64(apiObj.totalFeesSat);
} }
@protected @protected
@@ -2282,6 +2291,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
wireObj.invoice = cst_encode_String(apiObj.invoice); wireObj.invoice = cst_encode_String(apiObj.invoice);
} }
@protected
void cst_api_fill_to_wire_recommended_fees(RecommendedFees apiObj, wire_cst_recommended_fees wireObj) {
wireObj.fastest_fee = cst_encode_u_64(apiObj.fastestFee);
wireObj.half_hour_fee = cst_encode_u_64(apiObj.halfHourFee);
wireObj.hour_fee = cst_encode_u_64(apiObj.hourFee);
wireObj.economy_fee = cst_encode_u_64(apiObj.economyFee);
wireObj.minimum_fee = cst_encode_u_64(apiObj.minimumFee);
}
@protected @protected
void cst_api_fill_to_wire_refund_request(RefundRequest apiObj, wire_cst_refund_request wireObj) { void cst_api_fill_to_wire_refund_request(RefundRequest apiObj, wire_cst_refund_request wireObj) {
wireObj.swap_address = cst_encode_String(apiObj.swapAddress); wireObj.swap_address = cst_encode_String(apiObj.swapAddress);
@@ -2862,6 +2880,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
void sse_encode_receive_payment_response(ReceivePaymentResponse self, SseSerializer serializer); void sse_encode_receive_payment_response(ReceivePaymentResponse self, SseSerializer serializer);
@protected
void sse_encode_recommended_fees(RecommendedFees self, SseSerializer serializer);
@protected @protected
void sse_encode_refund_request(RefundRequest self, SseSerializer serializer); void sse_encode_refund_request(RefundRequest self, SseSerializer serializer);
@@ -3360,6 +3381,22 @@ class RustLibWire implements BaseWire {
_wire__crate__bindings__BindingLiquidSdk_receive_paymentPtr _wire__crate__bindings__BindingLiquidSdk_receive_paymentPtr
.asFunction<void Function(int, int, ffi.Pointer<wire_cst_prepare_receive_response>)>(); .asFunction<void Function(int, int, ffi.Pointer<wire_cst_prepare_receive_response>)>();
void wire__crate__bindings__BindingLiquidSdk_recommended_fees(
int port_,
int that,
) {
return _wire__crate__bindings__BindingLiquidSdk_recommended_fees(
port_,
that,
);
}
late final _wire__crate__bindings__BindingLiquidSdk_recommended_feesPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64, ffi.UintPtr)>>(
'frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_recommended_fees');
late final _wire__crate__bindings__BindingLiquidSdk_recommended_fees =
_wire__crate__bindings__BindingLiquidSdk_recommended_feesPtr.asFunction<void Function(int, int)>();
void wire__crate__bindings__BindingLiquidSdk_refund( void wire__crate__bindings__BindingLiquidSdk_refund(
int port_, int port_,
int that, int that,
@@ -4247,7 +4284,10 @@ final class wire_cst_prepare_pay_onchain_response extends ffi.Struct {
external int receiver_amount_sat; external int receiver_amount_sat;
@ffi.Uint64() @ffi.Uint64()
external int fees_sat; external int claim_fees_sat;
@ffi.Uint64()
external int total_fees_sat;
} }
final class wire_cst_pay_onchain_request extends ffi.Struct { final class wire_cst_pay_onchain_request extends ffi.Struct {
@@ -4259,6 +4299,8 @@ final class wire_cst_pay_onchain_request extends ffi.Struct {
final class wire_cst_prepare_pay_onchain_request extends ffi.Struct { final class wire_cst_prepare_pay_onchain_request extends ffi.Struct {
@ffi.Uint64() @ffi.Uint64()
external int receiver_amount_sat; external int receiver_amount_sat;
external ffi.Pointer<ffi.Uint32> sat_per_vbyte;
} }
final class wire_cst_prepare_receive_onchain_request extends ffi.Struct { final class wire_cst_prepare_receive_onchain_request extends ffi.Struct {
@@ -4403,6 +4445,8 @@ final class wire_cst_config extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> bitcoin_electrum_url; external ffi.Pointer<wire_cst_list_prim_u_8_strict> bitcoin_electrum_url;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> mempoolspace_url;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> working_dir; external ffi.Pointer<wire_cst_list_prim_u_8_strict> working_dir;
@ffi.Int32() @ffi.Int32()
@@ -5084,6 +5128,23 @@ final class wire_cst_receive_payment_response extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> invoice; external ffi.Pointer<wire_cst_list_prim_u_8_strict> invoice;
} }
final class wire_cst_recommended_fees extends ffi.Struct {
@ffi.Uint64()
external int fastest_fee;
@ffi.Uint64()
external int half_hour_fee;
@ffi.Uint64()
external int hour_fee;
@ffi.Uint64()
external int economy_fee;
@ffi.Uint64()
external int minimum_fee;
}
final class wire_cst_refund_response extends ffi.Struct { final class wire_cst_refund_response extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> refund_tx_id; external ffi.Pointer<wire_cst_list_prim_u_8_strict> refund_tx_id;
} }
@@ -5113,6 +5174,8 @@ final class wire_cst_send_payment_response extends ffi.Struct {
external wire_cst_payment payment; external wire_cst_payment payment;
} }
const int ESTIMATED_BTC_CLAIM_TX_VSIZE = 111;
const double STANDARD_FEE_RATE_SAT_PER_VBYTE = 0.1; const double STANDARD_FEE_RATE_SAT_PER_VBYTE = 0.1;
const double LOWBALL_FEE_RATE_SAT_PER_VBYTE = 0.01; const double LOWBALL_FEE_RATE_SAT_PER_VBYTE = 0.01;

View File

@@ -34,6 +34,9 @@ class Config {
final String liquidElectrumUrl; final String liquidElectrumUrl;
final String bitcoinElectrumUrl; final String bitcoinElectrumUrl;
/// The mempool.space API URL, has to be in the format: `https://mempool.space/api`
final String mempoolspaceUrl;
/// Directory in which all SDK files (DB, log, cache) are stored. /// Directory in which all SDK files (DB, log, cache) are stored.
/// ///
/// Prefix can be a relative or absolute path to this directory. /// Prefix can be a relative or absolute path to this directory.
@@ -53,6 +56,7 @@ class Config {
const Config({ const Config({
required this.liquidElectrumUrl, required this.liquidElectrumUrl,
required this.bitcoinElectrumUrl, required this.bitcoinElectrumUrl,
required this.mempoolspaceUrl,
required this.workingDir, required this.workingDir,
required this.network, required this.network,
required this.paymentTimeoutSec, required this.paymentTimeoutSec,
@@ -64,6 +68,7 @@ class Config {
int get hashCode => int get hashCode =>
liquidElectrumUrl.hashCode ^ liquidElectrumUrl.hashCode ^
bitcoinElectrumUrl.hashCode ^ bitcoinElectrumUrl.hashCode ^
mempoolspaceUrl.hashCode ^
workingDir.hashCode ^ workingDir.hashCode ^
network.hashCode ^ network.hashCode ^
paymentTimeoutSec.hashCode ^ paymentTimeoutSec.hashCode ^
@@ -77,6 +82,7 @@ class Config {
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&
liquidElectrumUrl == other.liquidElectrumUrl && liquidElectrumUrl == other.liquidElectrumUrl &&
bitcoinElectrumUrl == other.bitcoinElectrumUrl && bitcoinElectrumUrl == other.bitcoinElectrumUrl &&
mempoolspaceUrl == other.mempoolspaceUrl &&
workingDir == other.workingDir && workingDir == other.workingDir &&
network == other.network && network == other.network &&
paymentTimeoutSec == other.paymentTimeoutSec && paymentTimeoutSec == other.paymentTimeoutSec &&
@@ -512,33 +518,38 @@ enum PaymentType {
class PreparePayOnchainRequest { class PreparePayOnchainRequest {
final BigInt receiverAmountSat; final BigInt receiverAmountSat;
final int? satPerVbyte;
const PreparePayOnchainRequest({ const PreparePayOnchainRequest({
required this.receiverAmountSat, required this.receiverAmountSat,
this.satPerVbyte,
}); });
@override @override
int get hashCode => receiverAmountSat.hashCode; int get hashCode => receiverAmountSat.hashCode ^ satPerVbyte.hashCode;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is PreparePayOnchainRequest && other is PreparePayOnchainRequest &&
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&
receiverAmountSat == other.receiverAmountSat; receiverAmountSat == other.receiverAmountSat &&
satPerVbyte == other.satPerVbyte;
} }
class PreparePayOnchainResponse { class PreparePayOnchainResponse {
final BigInt receiverAmountSat; final BigInt receiverAmountSat;
final BigInt feesSat; final BigInt claimFeesSat;
final BigInt totalFeesSat;
const PreparePayOnchainResponse({ const PreparePayOnchainResponse({
required this.receiverAmountSat, required this.receiverAmountSat,
required this.feesSat, required this.claimFeesSat,
required this.totalFeesSat,
}); });
@override @override
int get hashCode => receiverAmountSat.hashCode ^ feesSat.hashCode; int get hashCode => receiverAmountSat.hashCode ^ claimFeesSat.hashCode ^ totalFeesSat.hashCode;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
@@ -546,7 +557,8 @@ class PreparePayOnchainResponse {
other is PreparePayOnchainResponse && other is PreparePayOnchainResponse &&
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&
receiverAmountSat == other.receiverAmountSat && receiverAmountSat == other.receiverAmountSat &&
feesSat == other.feesSat; claimFeesSat == other.claimFeesSat &&
totalFeesSat == other.totalFeesSat;
} }
class PrepareReceiveOnchainRequest { class PrepareReceiveOnchainRequest {
@@ -754,6 +766,41 @@ class ReceivePaymentResponse {
invoice == other.invoice; invoice == other.invoice;
} }
class RecommendedFees {
final BigInt fastestFee;
final BigInt halfHourFee;
final BigInt hourFee;
final BigInt economyFee;
final BigInt minimumFee;
const RecommendedFees({
required this.fastestFee,
required this.halfHourFee,
required this.hourFee,
required this.economyFee,
required this.minimumFee,
});
@override
int get hashCode =>
fastestFee.hashCode ^
halfHourFee.hashCode ^
hourFee.hashCode ^
economyFee.hashCode ^
minimumFee.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is RecommendedFees &&
runtimeType == other.runtimeType &&
fastestFee == other.fastestFee &&
halfHourFee == other.halfHourFee &&
hourFee == other.hourFee &&
economyFee == other.economyFee &&
minimumFee == other.minimumFee;
}
class RefundRequest { class RefundRequest {
final String swapAddress; final String swapAddress;
final String refundAddress; final String refundAddress;

View File

@@ -18,6 +18,7 @@ extension ConfigCopyWith on Config {
Config copyWith({ Config copyWith({
String? liquidElectrumUrl, String? liquidElectrumUrl,
String? bitcoinElectrumUrl, String? bitcoinElectrumUrl,
String? mempoolspaceUrl,
String? workingDir, String? workingDir,
LiquidNetwork? network, LiquidNetwork? network,
BigInt? paymentTimeoutSec, BigInt? paymentTimeoutSec,
@@ -26,6 +27,7 @@ extension ConfigCopyWith on Config {
return Config( return Config(
liquidElectrumUrl: liquidElectrumUrl ?? this.liquidElectrumUrl, liquidElectrumUrl: liquidElectrumUrl ?? this.liquidElectrumUrl,
bitcoinElectrumUrl: bitcoinElectrumUrl ?? this.bitcoinElectrumUrl, bitcoinElectrumUrl: bitcoinElectrumUrl ?? this.bitcoinElectrumUrl,
mempoolspaceUrl: mempoolspaceUrl ?? this.mempoolspaceUrl,
workingDir: workingDir ?? this.workingDir, workingDir: workingDir ?? this.workingDir,
network: network ?? this.network, network: network ?? this.network,
paymentTimeoutSec: paymentTimeoutSec ?? this.paymentTimeoutSec, paymentTimeoutSec: paymentTimeoutSec ?? this.paymentTimeoutSec,

View File

@@ -453,6 +453,23 @@ class FlutterBreezLiquidBindings {
_frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_paymentPtr _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_receive_paymentPtr
.asFunction<void Function(int, int, ffi.Pointer<wire_cst_prepare_receive_response>)>(); .asFunction<void Function(int, int, ffi.Pointer<wire_cst_prepare_receive_response>)>();
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_recommended_fees(
int port_,
int that,
) {
return _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_recommended_fees(
port_,
that,
);
}
late final _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_recommended_feesPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64, ffi.UintPtr)>>(
'frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_recommended_fees');
late final _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_recommended_fees =
_frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_recommended_feesPtr
.asFunction<void Function(int, int)>();
void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_refund( void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_refund(
int port_, int port_,
int that, int that,
@@ -1409,7 +1426,10 @@ final class wire_cst_prepare_pay_onchain_response extends ffi.Struct {
external int receiver_amount_sat; external int receiver_amount_sat;
@ffi.Uint64() @ffi.Uint64()
external int fees_sat; external int claim_fees_sat;
@ffi.Uint64()
external int total_fees_sat;
} }
final class wire_cst_pay_onchain_request extends ffi.Struct { final class wire_cst_pay_onchain_request extends ffi.Struct {
@@ -1421,6 +1441,8 @@ final class wire_cst_pay_onchain_request extends ffi.Struct {
final class wire_cst_prepare_pay_onchain_request extends ffi.Struct { final class wire_cst_prepare_pay_onchain_request extends ffi.Struct {
@ffi.Uint64() @ffi.Uint64()
external int receiver_amount_sat; external int receiver_amount_sat;
external ffi.Pointer<ffi.Uint32> sat_per_vbyte;
} }
final class wire_cst_prepare_receive_onchain_request extends ffi.Struct { final class wire_cst_prepare_receive_onchain_request extends ffi.Struct {
@@ -1565,6 +1587,8 @@ final class wire_cst_config extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> bitcoin_electrum_url; external ffi.Pointer<wire_cst_list_prim_u_8_strict> bitcoin_electrum_url;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> mempoolspace_url;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> working_dir; external ffi.Pointer<wire_cst_list_prim_u_8_strict> working_dir;
@ffi.Int32() @ffi.Int32()
@@ -2246,6 +2270,23 @@ final class wire_cst_receive_payment_response extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> invoice; external ffi.Pointer<wire_cst_list_prim_u_8_strict> invoice;
} }
final class wire_cst_recommended_fees extends ffi.Struct {
@ffi.Uint64()
external int fastest_fee;
@ffi.Uint64()
external int half_hour_fee;
@ffi.Uint64()
external int hour_fee;
@ffi.Uint64()
external int economy_fee;
@ffi.Uint64()
external int minimum_fee;
}
final class wire_cst_refund_response extends ffi.Struct { final class wire_cst_refund_response extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> refund_tx_id; external ffi.Pointer<wire_cst_list_prim_u_8_strict> refund_tx_id;
} }
@@ -2278,6 +2319,8 @@ final class wire_cst_send_payment_response extends ffi.Struct {
/// EXTRA BEGIN /// EXTRA BEGIN
typedef WireSyncRust2DartDco = ffi.Pointer<DartCObject>; typedef WireSyncRust2DartDco = ffi.Pointer<DartCObject>;
const int ESTIMATED_BTC_CLAIM_TX_VSIZE = 111;
const double STANDARD_FEE_RATE_SAT_PER_VBYTE = 0.1; const double STANDARD_FEE_RATE_SAT_PER_VBYTE = 0.1;
const double LOWBALL_FEE_RATE_SAT_PER_VBYTE = 0.01; const double LOWBALL_FEE_RATE_SAT_PER_VBYTE = 0.01;

View File

@@ -120,6 +120,7 @@ fun asConfig(config: ReadableMap): Config? {
arrayOf( arrayOf(
"liquidElectrumUrl", "liquidElectrumUrl",
"bitcoinElectrumUrl", "bitcoinElectrumUrl",
"mempoolspaceUrl",
"workingDir", "workingDir",
"network", "network",
"paymentTimeoutSec", "paymentTimeoutSec",
@@ -131,6 +132,7 @@ fun asConfig(config: ReadableMap): Config? {
} }
val liquidElectrumUrl = config.getString("liquidElectrumUrl")!! val liquidElectrumUrl = config.getString("liquidElectrumUrl")!!
val bitcoinElectrumUrl = config.getString("bitcoinElectrumUrl")!! val bitcoinElectrumUrl = config.getString("bitcoinElectrumUrl")!!
val mempoolspaceUrl = config.getString("mempoolspaceUrl")!!
val workingDir = config.getString("workingDir")!! val workingDir = config.getString("workingDir")!!
val network = config.getString("network")?.let { asLiquidNetwork(it) }!! val network = config.getString("network")?.let { asLiquidNetwork(it) }!!
val paymentTimeoutSec = config.getDouble("paymentTimeoutSec").toULong() val paymentTimeoutSec = config.getDouble("paymentTimeoutSec").toULong()
@@ -148,6 +150,7 @@ fun asConfig(config: ReadableMap): Config? {
return Config( return Config(
liquidElectrumUrl, liquidElectrumUrl,
bitcoinElectrumUrl, bitcoinElectrumUrl,
mempoolspaceUrl,
workingDir, workingDir,
network, network,
paymentTimeoutSec, paymentTimeoutSec,
@@ -160,6 +163,7 @@ fun readableMapOf(config: Config): ReadableMap =
readableMapOf( readableMapOf(
"liquidElectrumUrl" to config.liquidElectrumUrl, "liquidElectrumUrl" to config.liquidElectrumUrl,
"bitcoinElectrumUrl" to config.bitcoinElectrumUrl, "bitcoinElectrumUrl" to config.bitcoinElectrumUrl,
"mempoolspaceUrl" to config.mempoolspaceUrl,
"workingDir" to config.workingDir, "workingDir" to config.workingDir,
"network" to config.network.name.lowercase(), "network" to config.network.name.lowercase(),
"paymentTimeoutSec" to config.paymentTimeoutSec, "paymentTimeoutSec" to config.paymentTimeoutSec,
@@ -1222,14 +1226,26 @@ fun asPreparePayOnchainRequest(preparePayOnchainRequest: ReadableMap): PreparePa
return null return null
} }
val receiverAmountSat = preparePayOnchainRequest.getDouble("receiverAmountSat").toULong() val receiverAmountSat = preparePayOnchainRequest.getDouble("receiverAmountSat").toULong()
val satPerVbyte =
if (hasNonNullKey(
preparePayOnchainRequest,
"satPerVbyte",
)
) {
preparePayOnchainRequest.getInt("satPerVbyte").toUInt()
} else {
null
}
return PreparePayOnchainRequest( return PreparePayOnchainRequest(
receiverAmountSat, receiverAmountSat,
satPerVbyte,
) )
} }
fun readableMapOf(preparePayOnchainRequest: PreparePayOnchainRequest): ReadableMap = fun readableMapOf(preparePayOnchainRequest: PreparePayOnchainRequest): ReadableMap =
readableMapOf( readableMapOf(
"receiverAmountSat" to preparePayOnchainRequest.receiverAmountSat, "receiverAmountSat" to preparePayOnchainRequest.receiverAmountSat,
"satPerVbyte" to preparePayOnchainRequest.satPerVbyte,
) )
fun asPreparePayOnchainRequestList(arr: ReadableArray): List<PreparePayOnchainRequest> { fun asPreparePayOnchainRequestList(arr: ReadableArray): List<PreparePayOnchainRequest> {
@@ -1248,24 +1264,28 @@ fun asPreparePayOnchainResponse(preparePayOnchainResponse: ReadableMap): Prepare
preparePayOnchainResponse, preparePayOnchainResponse,
arrayOf( arrayOf(
"receiverAmountSat", "receiverAmountSat",
"feesSat", "claimFeesSat",
"totalFeesSat",
), ),
) )
) { ) {
return null return null
} }
val receiverAmountSat = preparePayOnchainResponse.getDouble("receiverAmountSat").toULong() val receiverAmountSat = preparePayOnchainResponse.getDouble("receiverAmountSat").toULong()
val feesSat = preparePayOnchainResponse.getDouble("feesSat").toULong() val claimFeesSat = preparePayOnchainResponse.getDouble("claimFeesSat").toULong()
val totalFeesSat = preparePayOnchainResponse.getDouble("totalFeesSat").toULong()
return PreparePayOnchainResponse( return PreparePayOnchainResponse(
receiverAmountSat, receiverAmountSat,
feesSat, claimFeesSat,
totalFeesSat,
) )
} }
fun readableMapOf(preparePayOnchainResponse: PreparePayOnchainResponse): ReadableMap = fun readableMapOf(preparePayOnchainResponse: PreparePayOnchainResponse): ReadableMap =
readableMapOf( readableMapOf(
"receiverAmountSat" to preparePayOnchainResponse.receiverAmountSat, "receiverAmountSat" to preparePayOnchainResponse.receiverAmountSat,
"feesSat" to preparePayOnchainResponse.feesSat, "claimFeesSat" to preparePayOnchainResponse.claimFeesSat,
"totalFeesSat" to preparePayOnchainResponse.totalFeesSat,
) )
fun asPreparePayOnchainResponseList(arr: ReadableArray): List<PreparePayOnchainResponse> { fun asPreparePayOnchainResponseList(arr: ReadableArray): List<PreparePayOnchainResponse> {
@@ -1670,6 +1690,54 @@ fun asReceivePaymentResponseList(arr: ReadableArray): List<ReceivePaymentRespons
return list return list
} }
fun asRecommendedFees(recommendedFees: ReadableMap): RecommendedFees? {
if (!validateMandatoryFields(
recommendedFees,
arrayOf(
"fastestFee",
"halfHourFee",
"hourFee",
"economyFee",
"minimumFee",
),
)
) {
return null
}
val fastestFee = recommendedFees.getDouble("fastestFee").toULong()
val halfHourFee = recommendedFees.getDouble("halfHourFee").toULong()
val hourFee = recommendedFees.getDouble("hourFee").toULong()
val economyFee = recommendedFees.getDouble("economyFee").toULong()
val minimumFee = recommendedFees.getDouble("minimumFee").toULong()
return RecommendedFees(
fastestFee,
halfHourFee,
hourFee,
economyFee,
minimumFee,
)
}
fun readableMapOf(recommendedFees: RecommendedFees): ReadableMap =
readableMapOf(
"fastestFee" to recommendedFees.fastestFee,
"halfHourFee" to recommendedFees.halfHourFee,
"hourFee" to recommendedFees.hourFee,
"economyFee" to recommendedFees.economyFee,
"minimumFee" to recommendedFees.minimumFee,
)
fun asRecommendedFeesList(arr: ReadableArray): List<RecommendedFees> {
val list = ArrayList<RecommendedFees>()
for (value in arr.toArrayList()) {
when (value) {
is ReadableMap -> list.add(asRecommendedFees(value)!!)
else -> throw SdkException.Generic(errUnexpectedType("${value::class.java.name}"))
}
}
return list
}
fun asRefundRequest(refundRequest: ReadableMap): RefundRequest? { fun asRefundRequest(refundRequest: ReadableMap): RefundRequest? {
if (!validateMandatoryFields( if (!validateMandatoryFields(
refundRequest, refundRequest,

View File

@@ -439,6 +439,18 @@ class BreezSDKLiquidModule(
} }
} }
@ReactMethod
fun recommendedFees(promise: Promise) {
executor.execute {
try {
val res = getBindingLiquidSdk().recommendedFees()
promise.resolve(readableMapOf(res))
} catch (e: Exception) {
promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e)
}
}
}
@ReactMethod @ReactMethod
fun backup( fun backup(
req: ReadableMap, req: ReadableMap,

View File

@@ -150,6 +150,9 @@ enum BreezSDKLiquidMapper {
guard let bitcoinElectrumUrl = config["bitcoinElectrumUrl"] as? String else { guard let bitcoinElectrumUrl = config["bitcoinElectrumUrl"] as? String else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "bitcoinElectrumUrl", typeName: "Config")) throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "bitcoinElectrumUrl", typeName: "Config"))
} }
guard let mempoolspaceUrl = config["mempoolspaceUrl"] as? String else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "mempoolspaceUrl", typeName: "Config"))
}
guard let workingDir = config["workingDir"] as? String else { guard let workingDir = config["workingDir"] as? String else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "workingDir", typeName: "Config")) throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "workingDir", typeName: "Config"))
} }
@@ -175,6 +178,7 @@ enum BreezSDKLiquidMapper {
return Config( return Config(
liquidElectrumUrl: liquidElectrumUrl, liquidElectrumUrl: liquidElectrumUrl,
bitcoinElectrumUrl: bitcoinElectrumUrl, bitcoinElectrumUrl: bitcoinElectrumUrl,
mempoolspaceUrl: mempoolspaceUrl,
workingDir: workingDir, workingDir: workingDir,
network: network, network: network,
paymentTimeoutSec: paymentTimeoutSec, paymentTimeoutSec: paymentTimeoutSec,
@@ -187,6 +191,7 @@ enum BreezSDKLiquidMapper {
return [ return [
"liquidElectrumUrl": config.liquidElectrumUrl, "liquidElectrumUrl": config.liquidElectrumUrl,
"bitcoinElectrumUrl": config.bitcoinElectrumUrl, "bitcoinElectrumUrl": config.bitcoinElectrumUrl,
"mempoolspaceUrl": config.mempoolspaceUrl,
"workingDir": config.workingDir, "workingDir": config.workingDir,
"network": valueOf(liquidNetwork: config.network), "network": valueOf(liquidNetwork: config.network),
"paymentTimeoutSec": config.paymentTimeoutSec, "paymentTimeoutSec": config.paymentTimeoutSec,
@@ -1460,14 +1465,24 @@ enum BreezSDKLiquidMapper {
guard let receiverAmountSat = preparePayOnchainRequest["receiverAmountSat"] as? UInt64 else { guard let receiverAmountSat = preparePayOnchainRequest["receiverAmountSat"] as? UInt64 else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "receiverAmountSat", typeName: "PreparePayOnchainRequest")) throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "receiverAmountSat", typeName: "PreparePayOnchainRequest"))
} }
var satPerVbyte: UInt32?
if hasNonNilKey(data: preparePayOnchainRequest, key: "satPerVbyte") {
guard let satPerVbyteTmp = preparePayOnchainRequest["satPerVbyte"] as? UInt32 else {
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "satPerVbyte"))
}
satPerVbyte = satPerVbyteTmp
}
return PreparePayOnchainRequest( return PreparePayOnchainRequest(
receiverAmountSat: receiverAmountSat) receiverAmountSat: receiverAmountSat,
satPerVbyte: satPerVbyte
)
} }
static func dictionaryOf(preparePayOnchainRequest: PreparePayOnchainRequest) -> [String: Any?] { static func dictionaryOf(preparePayOnchainRequest: PreparePayOnchainRequest) -> [String: Any?] {
return [ return [
"receiverAmountSat": preparePayOnchainRequest.receiverAmountSat, "receiverAmountSat": preparePayOnchainRequest.receiverAmountSat,
"satPerVbyte": preparePayOnchainRequest.satPerVbyte == nil ? nil : preparePayOnchainRequest.satPerVbyte,
] ]
} }
@@ -1492,20 +1507,25 @@ enum BreezSDKLiquidMapper {
guard let receiverAmountSat = preparePayOnchainResponse["receiverAmountSat"] as? UInt64 else { guard let receiverAmountSat = preparePayOnchainResponse["receiverAmountSat"] as? UInt64 else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "receiverAmountSat", typeName: "PreparePayOnchainResponse")) throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "receiverAmountSat", typeName: "PreparePayOnchainResponse"))
} }
guard let feesSat = preparePayOnchainResponse["feesSat"] as? UInt64 else { guard let claimFeesSat = preparePayOnchainResponse["claimFeesSat"] as? UInt64 else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "feesSat", typeName: "PreparePayOnchainResponse")) throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "claimFeesSat", typeName: "PreparePayOnchainResponse"))
}
guard let totalFeesSat = preparePayOnchainResponse["totalFeesSat"] as? UInt64 else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "totalFeesSat", typeName: "PreparePayOnchainResponse"))
} }
return PreparePayOnchainResponse( return PreparePayOnchainResponse(
receiverAmountSat: receiverAmountSat, receiverAmountSat: receiverAmountSat,
feesSat: feesSat claimFeesSat: claimFeesSat,
totalFeesSat: totalFeesSat
) )
} }
static func dictionaryOf(preparePayOnchainResponse: PreparePayOnchainResponse) -> [String: Any?] { static func dictionaryOf(preparePayOnchainResponse: PreparePayOnchainResponse) -> [String: Any?] {
return [ return [
"receiverAmountSat": preparePayOnchainResponse.receiverAmountSat, "receiverAmountSat": preparePayOnchainResponse.receiverAmountSat,
"feesSat": preparePayOnchainResponse.feesSat, "claimFeesSat": preparePayOnchainResponse.claimFeesSat,
"totalFeesSat": preparePayOnchainResponse.totalFeesSat,
] ]
} }
@@ -1940,6 +1960,59 @@ enum BreezSDKLiquidMapper {
return receivePaymentResponseList.map { v -> [String: Any?] in return dictionaryOf(receivePaymentResponse: v) } return receivePaymentResponseList.map { v -> [String: Any?] in return dictionaryOf(receivePaymentResponse: v) }
} }
static func asRecommendedFees(recommendedFees: [String: Any?]) throws -> RecommendedFees {
guard let fastestFee = recommendedFees["fastestFee"] as? UInt64 else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "fastestFee", typeName: "RecommendedFees"))
}
guard let halfHourFee = recommendedFees["halfHourFee"] as? UInt64 else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "halfHourFee", typeName: "RecommendedFees"))
}
guard let hourFee = recommendedFees["hourFee"] as? UInt64 else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "hourFee", typeName: "RecommendedFees"))
}
guard let economyFee = recommendedFees["economyFee"] as? UInt64 else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "economyFee", typeName: "RecommendedFees"))
}
guard let minimumFee = recommendedFees["minimumFee"] as? UInt64 else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "minimumFee", typeName: "RecommendedFees"))
}
return RecommendedFees(
fastestFee: fastestFee,
halfHourFee: halfHourFee,
hourFee: hourFee,
economyFee: economyFee,
minimumFee: minimumFee
)
}
static func dictionaryOf(recommendedFees: RecommendedFees) -> [String: Any?] {
return [
"fastestFee": recommendedFees.fastestFee,
"halfHourFee": recommendedFees.halfHourFee,
"hourFee": recommendedFees.hourFee,
"economyFee": recommendedFees.economyFee,
"minimumFee": recommendedFees.minimumFee,
]
}
static func asRecommendedFeesList(arr: [Any]) throws -> [RecommendedFees] {
var list = [RecommendedFees]()
for value in arr {
if let val = value as? [String: Any?] {
var recommendedFees = try asRecommendedFees(recommendedFees: val)
list.append(recommendedFees)
} else {
throw SdkError.Generic(message: errUnexpectedType(typeName: "RecommendedFees"))
}
}
return list
}
static func arrayOf(recommendedFeesList: [RecommendedFees]) -> [Any] {
return recommendedFeesList.map { v -> [String: Any?] in return dictionaryOf(recommendedFees: v) }
}
static func asRefundRequest(refundRequest: [String: Any?]) throws -> RefundRequest { static func asRefundRequest(refundRequest: [String: Any?]) throws -> RefundRequest {
guard let swapAddress = refundRequest["swapAddress"] as? String else { guard let swapAddress = refundRequest["swapAddress"] as? String else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "swapAddress", typeName: "RefundRequest")) throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "swapAddress", typeName: "RefundRequest"))

View File

@@ -139,6 +139,11 @@ RCT_EXTERN_METHOD(
reject: (RCTPromiseRejectBlock)reject reject: (RCTPromiseRejectBlock)reject
) )
RCT_EXTERN_METHOD(
recommendedFees: (RCTPromiseResolveBlock)resolve
reject: (RCTPromiseRejectBlock)reject
)
RCT_EXTERN_METHOD( RCT_EXTERN_METHOD(
backup: (NSDictionary*)req backup: (NSDictionary*)req
resolve: (RCTPromiseResolveBlock)resolve resolve: (RCTPromiseResolveBlock)resolve

View File

@@ -335,6 +335,16 @@ class RNBreezSDKLiquid: RCTEventEmitter {
} }
} }
@objc(recommendedFees:reject:)
func recommendedFees(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
do {
var res = try getBindingLiquidSdk().recommendedFees()
resolve(BreezSDKLiquidMapper.dictionaryOf(recommendedFees: res))
} catch let err {
rejectErr(err: err, reject: reject)
}
}
@objc(backup:resolve:reject:) @objc(backup:resolve:reject:)
func backup(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { func backup(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
do { do {

View File

@@ -39,6 +39,7 @@ export interface BitcoinAddressData {
export interface Config { export interface Config {
liquidElectrumUrl: string liquidElectrumUrl: string
bitcoinElectrumUrl: string bitcoinElectrumUrl: string
mempoolspaceUrl: string
workingDir: string workingDir: string
network: LiquidNetwork network: LiquidNetwork
paymentTimeoutSec: number paymentTimeoutSec: number
@@ -211,11 +212,13 @@ export interface Payment {
export interface PreparePayOnchainRequest { export interface PreparePayOnchainRequest {
receiverAmountSat: number receiverAmountSat: number
satPerVbyte?: number
} }
export interface PreparePayOnchainResponse { export interface PreparePayOnchainResponse {
receiverAmountSat: number receiverAmountSat: number
feesSat: number claimFeesSat: number
totalFeesSat: number
} }
export interface PrepareReceiveOnchainRequest { export interface PrepareReceiveOnchainRequest {
@@ -272,6 +275,14 @@ export interface ReceivePaymentResponse {
invoice: string invoice: string
} }
export interface RecommendedFees {
fastestFee: number
halfHourFee: number
hourFee: number
economyFee: number
minimumFee: number
}
export interface RefundRequest { export interface RefundRequest {
swapAddress: string swapAddress: string
refundAddress: string refundAddress: string
@@ -623,6 +634,11 @@ export const sync = async (): Promise<void> => {
await BreezSDKLiquid.sync() await BreezSDKLiquid.sync()
} }
export const recommendedFees = async (): Promise<RecommendedFees> => {
const response = await BreezSDKLiquid.recommendedFees()
return response
}
export const backup = async (req: BackupRequest): Promise<void> => { export const backup = async (req: BackupRequest): Promise<void> => {
await BreezSDKLiquid.backup(req) await BreezSDKLiquid.backup(req)
} }