mirror of
https://github.com/aljazceru/breez-sdk-liquid.git
synced 2025-12-24 01:14:22 +01:00
feat: add zero-conf checks when receive lockup is in the mempool (#292)
This commit is contained in:
@@ -14,6 +14,18 @@ void store_dart_post_cobject(DartPostCObjectFnType ptr);
|
|||||||
// EXTRA END
|
// EXTRA END
|
||||||
typedef struct _Dart_Handle* Dart_Handle;
|
typedef struct _Dart_Handle* Dart_Handle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum acceptable fee rate when claiming using zero-conf
|
||||||
|
*/
|
||||||
|
#define DEFAULT_ZERO_CONF_MIN_FEE_RATE_TESTNET 0.1
|
||||||
|
|
||||||
|
#define DEFAULT_ZERO_CONF_MIN_FEE_RATE_MAINNET 0.01
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum acceptable amount in satoshi when claiming using zero-conf
|
||||||
|
*/
|
||||||
|
#define DEFAULT_ZERO_CONF_MAX_SAT 100000
|
||||||
|
|
||||||
typedef struct wire_cst_list_prim_u_8_strict {
|
typedef struct wire_cst_list_prim_u_8_strict {
|
||||||
uint8_t *ptr;
|
uint8_t *ptr;
|
||||||
int32_t len;
|
int32_t len;
|
||||||
@@ -106,6 +118,8 @@ typedef struct wire_cst_config {
|
|||||||
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;
|
||||||
|
float zero_conf_min_fee_rate;
|
||||||
|
uint64_t *zero_conf_max_amount_sat;
|
||||||
} wire_cst_config;
|
} wire_cst_config;
|
||||||
|
|
||||||
typedef struct wire_cst_connect_request {
|
typedef struct wire_cst_connect_request {
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ dictionary Config {
|
|||||||
string working_dir;
|
string working_dir;
|
||||||
Network network;
|
Network network;
|
||||||
u64 payment_timeout_sec;
|
u64 payment_timeout_sec;
|
||||||
|
f32 zero_conf_min_fee_rate;
|
||||||
|
u64? zero_conf_max_amount_sat;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Network {
|
enum Network {
|
||||||
|
|||||||
@@ -182,6 +182,8 @@ impl CstDecode<crate::model::Config> for wire_cst_config {
|
|||||||
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(),
|
||||||
|
zero_conf_min_fee_rate: self.zero_conf_min_fee_rate.cst_decode(),
|
||||||
|
zero_conf_max_amount_sat: self.zero_conf_max_amount_sat.cst_decode(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -525,6 +527,8 @@ impl NewWithNullPtr for wire_cst_config {
|
|||||||
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(),
|
||||||
|
zero_conf_min_fee_rate: Default::default(),
|
||||||
|
zero_conf_max_amount_sat: core::ptr::null_mut(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1083,6 +1087,8 @@ pub struct wire_cst_config {
|
|||||||
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,
|
||||||
|
zero_conf_min_fee_rate: f32,
|
||||||
|
zero_conf_max_amount_sat: *mut u64,
|
||||||
}
|
}
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|||||||
@@ -633,6 +633,12 @@ fn wire__crate__bindings__parse_invoice_impl(
|
|||||||
|
|
||||||
// Section: dart2rust
|
// Section: dart2rust
|
||||||
|
|
||||||
|
impl CstDecode<f32> for f32 {
|
||||||
|
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||||
|
fn cst_decode(self) -> f32 {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
impl CstDecode<i32> for i32 {
|
impl CstDecode<i32> for i32 {
|
||||||
// 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) -> i32 {
|
fn cst_decode(self) -> i32 {
|
||||||
@@ -781,12 +787,16 @@ impl SseDecode for crate::model::Config {
|
|||||||
let mut var_workingDir = <String>::sse_decode(deserializer);
|
let mut var_workingDir = <String>::sse_decode(deserializer);
|
||||||
let mut var_network = <crate::model::Network>::sse_decode(deserializer);
|
let mut var_network = <crate::model::Network>::sse_decode(deserializer);
|
||||||
let mut var_paymentTimeoutSec = <u64>::sse_decode(deserializer);
|
let mut var_paymentTimeoutSec = <u64>::sse_decode(deserializer);
|
||||||
|
let mut var_zeroConfMinFeeRate = <f32>::sse_decode(deserializer);
|
||||||
|
let mut var_zeroConfMaxAmountSat = <Option<u64>>::sse_decode(deserializer);
|
||||||
return crate::model::Config {
|
return crate::model::Config {
|
||||||
boltz_url: var_boltzUrl,
|
boltz_url: var_boltzUrl,
|
||||||
electrum_url: var_electrumUrl,
|
electrum_url: var_electrumUrl,
|
||||||
working_dir: var_workingDir,
|
working_dir: var_workingDir,
|
||||||
network: var_network,
|
network: var_network,
|
||||||
payment_timeout_sec: var_paymentTimeoutSec,
|
payment_timeout_sec: var_paymentTimeoutSec,
|
||||||
|
zero_conf_min_fee_rate: var_zeroConfMinFeeRate,
|
||||||
|
zero_conf_max_amount_sat: var_zeroConfMaxAmountSat,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -803,6 +813,13 @@ impl SseDecode for crate::model::ConnectRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SseDecode for f32 {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||||
|
deserializer.cursor.read_f32::<NativeEndian>().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SseDecode for crate::model::GetInfoResponse {
|
impl SseDecode for crate::model::GetInfoResponse {
|
||||||
// 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 {
|
||||||
@@ -1385,6 +1402,8 @@ impl flutter_rust_bridge::IntoDart for crate::model::Config {
|
|||||||
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(),
|
||||||
|
self.zero_conf_min_fee_rate.into_into_dart().into_dart(),
|
||||||
|
self.zero_conf_max_amount_sat.into_into_dart().into_dart(),
|
||||||
]
|
]
|
||||||
.into_dart()
|
.into_dart()
|
||||||
}
|
}
|
||||||
@@ -1899,6 +1918,8 @@ impl SseEncode for crate::model::Config {
|
|||||||
<String>::sse_encode(self.working_dir, serializer);
|
<String>::sse_encode(self.working_dir, serializer);
|
||||||
<crate::model::Network>::sse_encode(self.network, serializer);
|
<crate::model::Network>::sse_encode(self.network, serializer);
|
||||||
<u64>::sse_encode(self.payment_timeout_sec, serializer);
|
<u64>::sse_encode(self.payment_timeout_sec, serializer);
|
||||||
|
<f32>::sse_encode(self.zero_conf_min_fee_rate, serializer);
|
||||||
|
<Option<u64>>::sse_encode(self.zero_conf_max_amount_sat, serializer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1910,6 +1931,13 @@ impl SseEncode for crate::model::ConnectRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SseEncode for f32 {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||||
|
serializer.cursor.write_f32::<NativeEndian>(self).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SseEncode for crate::model::GetInfoResponse {
|
impl SseEncode for crate::model::GetInfoResponse {
|
||||||
// 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) {
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ use rusqlite::ToSql;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::error::PaymentError;
|
use crate::error::PaymentError;
|
||||||
|
use crate::receive_swap::{
|
||||||
|
DEFAULT_ZERO_CONF_MAX_SAT, DEFAULT_ZERO_CONF_MIN_FEE_RATE_MAINNET,
|
||||||
|
DEFAULT_ZERO_CONF_MIN_FEE_RATE_TESTNET,
|
||||||
|
};
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
|
|
||||||
/// Configuration for the Liquid SDK
|
/// Configuration for the Liquid SDK
|
||||||
@@ -25,7 +29,13 @@ pub struct Config {
|
|||||||
pub network: Network,
|
pub network: Network,
|
||||||
/// Send payment timeout. See [crate::sdk::LiquidSdk::send_payment]
|
/// Send payment timeout. See [crate::sdk::LiquidSdk::send_payment]
|
||||||
pub payment_timeout_sec: u64,
|
pub payment_timeout_sec: u64,
|
||||||
|
/// Zero-conf minimum accepted fee-rate in sat/vbyte
|
||||||
|
pub zero_conf_min_fee_rate: f32,
|
||||||
|
/// Maximum amount in satoshi to accept zero-conf payments with
|
||||||
|
/// Defaults to [crate::receive_swap::DEFAULT_ZERO_CONF_MAX_SAT]
|
||||||
|
pub zero_conf_max_amount_sat: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn mainnet() -> Self {
|
pub fn mainnet() -> Self {
|
||||||
Config {
|
Config {
|
||||||
@@ -34,6 +44,8 @@ impl Config {
|
|||||||
working_dir: ".".to_string(),
|
working_dir: ".".to_string(),
|
||||||
network: Network::Mainnet,
|
network: Network::Mainnet,
|
||||||
payment_timeout_sec: 15,
|
payment_timeout_sec: 15,
|
||||||
|
zero_conf_min_fee_rate: DEFAULT_ZERO_CONF_MIN_FEE_RATE_MAINNET,
|
||||||
|
zero_conf_max_amount_sat: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,8 +56,15 @@ impl Config {
|
|||||||
working_dir: ".".to_string(),
|
working_dir: ".".to_string(),
|
||||||
network: Network::Testnet,
|
network: Network::Testnet,
|
||||||
payment_timeout_sec: 15,
|
payment_timeout_sec: 15,
|
||||||
|
zero_conf_min_fee_rate: DEFAULT_ZERO_CONF_MIN_FEE_RATE_TESTNET,
|
||||||
|
zero_conf_max_amount_sat: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn zero_conf_max_amount_sat(&self) -> u64 {
|
||||||
|
self.zero_conf_max_amount_sat
|
||||||
|
.unwrap_or(DEFAULT_ZERO_CONF_MAX_SAT)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Serialize)]
|
||||||
|
|||||||
@@ -6,15 +6,22 @@ use boltz_client::swaps::boltzv2;
|
|||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
use crate::ensure_sdk;
|
|
||||||
use crate::model::PaymentState::{Complete, Created, Failed, Pending, TimedOut};
|
use crate::model::PaymentState::{Complete, Created, Failed, Pending, TimedOut};
|
||||||
use crate::model::{PaymentTxData, PaymentType, ReceiveSwap};
|
use crate::model::{Config, PaymentTxData, PaymentType, ReceiveSwap};
|
||||||
|
use crate::{ensure_sdk, utils};
|
||||||
use crate::{
|
use crate::{
|
||||||
error::PaymentError, model::PaymentState, persist::Persister, swapper::Swapper,
|
error::PaymentError, model::PaymentState, persist::Persister, swapper::Swapper,
|
||||||
wallet::OnchainWallet,
|
wallet::OnchainWallet,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The minimum acceptable fee rate when claiming using zero-conf
|
||||||
|
pub const DEFAULT_ZERO_CONF_MIN_FEE_RATE_TESTNET: f32 = 0.1;
|
||||||
|
pub const DEFAULT_ZERO_CONF_MIN_FEE_RATE_MAINNET: f32 = 0.01;
|
||||||
|
/// The maximum acceptable amount in satoshi when claiming using zero-conf
|
||||||
|
pub const DEFAULT_ZERO_CONF_MAX_SAT: u64 = 100_000;
|
||||||
|
|
||||||
pub(crate) struct ReceiveSwapStateHandler {
|
pub(crate) struct ReceiveSwapStateHandler {
|
||||||
|
config: Config,
|
||||||
onchain_wallet: Arc<dyn OnchainWallet>,
|
onchain_wallet: Arc<dyn OnchainWallet>,
|
||||||
persister: Arc<Persister>,
|
persister: Arc<Persister>,
|
||||||
swapper: Arc<dyn Swapper>,
|
swapper: Arc<dyn Swapper>,
|
||||||
@@ -23,12 +30,14 @@ pub(crate) struct ReceiveSwapStateHandler {
|
|||||||
|
|
||||||
impl ReceiveSwapStateHandler {
|
impl ReceiveSwapStateHandler {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
|
config: Config,
|
||||||
onchain_wallet: Arc<dyn OnchainWallet>,
|
onchain_wallet: Arc<dyn OnchainWallet>,
|
||||||
persister: Arc<Persister>,
|
persister: Arc<Persister>,
|
||||||
swapper: Arc<dyn Swapper>,
|
swapper: Arc<dyn Swapper>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (subscription_notifier, _) = broadcast::channel::<String>(30);
|
let (subscription_notifier, _) = broadcast::channel::<String>(30);
|
||||||
Self {
|
Self {
|
||||||
|
config,
|
||||||
onchain_wallet,
|
onchain_wallet,
|
||||||
persister,
|
persister,
|
||||||
swapper,
|
swapper,
|
||||||
@@ -63,16 +72,77 @@ impl ReceiveSwapStateHandler {
|
|||||||
self.update_swap_info(id, Failed, None, None).await?;
|
self.update_swap_info(id, Failed, None, None).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
// The lockup tx is in the mempool and we accept 0-conf => try to claim
|
||||||
|
// Execute 0-conf preconditions check
|
||||||
Ok(RevSwapStates::TransactionMempool) => {
|
Ok(RevSwapStates::TransactionMempool) => {
|
||||||
let boltzv2::Update::TransactionMempool { transaction, .. } = update else {
|
let boltzv2::Update::TransactionMempool { transaction, .. } = update else {
|
||||||
return Err(anyhow!("Unexpected payload from Boltz status stream"));
|
return Err(anyhow!("Unexpected payload from Boltz status stream"));
|
||||||
};
|
};
|
||||||
|
|
||||||
let lockup_tx_id = &transaction.id;
|
let lockup_tx_id = &transaction.id;
|
||||||
self.update_swap_info(id, Pending, None, Some(lockup_tx_id))
|
self.update_swap_info(id, Pending, None, Some(lockup_tx_id))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
if let Some(claim_tx_id) = receive_swap.claim_tx_id {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Claim tx for Receive Swap {id} was already broadcast: txid {claim_tx_id}"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let lockup_tx = utils::deserialize_tx_hex(&transaction.hex)?;
|
||||||
|
|
||||||
|
// If the amount is greater than the zero-conf limit
|
||||||
|
let max_amount_sat = self.config.zero_conf_max_amount_sat();
|
||||||
|
let receiver_amount_sat = receive_swap.receiver_amount_sat;
|
||||||
|
if receiver_amount_sat > max_amount_sat {
|
||||||
|
warn!("[Receive Swap {id}] Amount is too high to claim with zero-conf ({receiver_amount_sat} sat > {max_amount_sat} sat). Waiting for confirmation...");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("[Receive Swap {id}] Amount is within valid range for zero-conf ({receiver_amount_sat} < {max_amount_sat} sat)");
|
||||||
|
|
||||||
|
// If the transaction has RBF, see https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki
|
||||||
|
// TODO: Check for inherent RBF by ensuring all tx ancestors are confirmed
|
||||||
|
let rbf_explicit = lockup_tx.input.iter().any(|input| input.sequence.is_rbf());
|
||||||
|
// let rbf_inherent = lockup_tx_history.height < 0;
|
||||||
|
|
||||||
|
if rbf_explicit {
|
||||||
|
warn!("[Receive Swap {id}] Lockup transaction signals RBF. Waiting for confirmation...");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("[Receive Swap {id}] Lockup tx does not signal RBF. Proceeding...");
|
||||||
|
|
||||||
|
// If the fees are higher than our estimated value
|
||||||
|
let tx_fees: u64 = lockup_tx.all_fees().values().sum();
|
||||||
|
let min_fee_rate = self.config.zero_conf_min_fee_rate;
|
||||||
|
let lower_bound_estimated_fees = lockup_tx.vsize() as f32 * min_fee_rate * 0.8;
|
||||||
|
|
||||||
|
if lower_bound_estimated_fees > tx_fees as f32 {
|
||||||
|
warn!("[Receive Swap {id}] Lockup tx fees are too low: Expected at least {lower_bound_estimated_fees} sat, got {tx_fees} sat. Waiting for confirmation...");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("[Receive Swap {id}] Lockup tx fees are within acceptable range ({tx_fees} > {lower_bound_estimated_fees} sat). Proceeding with claim.");
|
||||||
|
|
||||||
|
match self.claim(&receive_swap).await {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => match err {
|
||||||
|
PaymentError::AlreadyClaimed => {
|
||||||
|
warn!("Funds already claimed for Receive Swap {id}")
|
||||||
|
}
|
||||||
|
_ => error!("Claim for Receive Swap {id} failed: {err}"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Ok(RevSwapStates::TransactionConfirmed) => {
|
Ok(RevSwapStates::TransactionConfirmed) => {
|
||||||
|
// TODO: We need to ensure that the lockup tx is actually confirmed
|
||||||
|
// if lockup_tx_history.height <= 0 {
|
||||||
|
// return Err(anyhow!("Tx state mismatch: Lockup transaction was marked as confirmed by the swapper, but isn't."));
|
||||||
|
// }
|
||||||
|
|
||||||
match receive_swap.claim_tx_id {
|
match receive_swap.claim_tx_id {
|
||||||
Some(claim_tx_id) => {
|
Some(claim_tx_id) => {
|
||||||
warn!("Claim tx for Receive Swap {id} was already broadcast: txid {claim_tx_id}")
|
warn!("Claim tx for Receive Swap {id} was already broadcast: txid {claim_tx_id}")
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ impl LiquidSdk {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let receive_swap_state_handler = ReceiveSwapStateHandler::new(
|
let receive_swap_state_handler = ReceiveSwapStateHandler::new(
|
||||||
|
config.clone(),
|
||||||
onchain_wallet.clone(),
|
onchain_wallet.clone(),
|
||||||
persister.clone(),
|
persister.clone(),
|
||||||
swapper.clone(),
|
swapper.clone(),
|
||||||
|
|||||||
@@ -2,8 +2,13 @@ use std::str::FromStr;
|
|||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use crate::error::PaymentError;
|
use crate::error::PaymentError;
|
||||||
use anyhow::Result;
|
use anyhow::{anyhow, Result};
|
||||||
use lwk_wollet::elements::LockTime::{self, *};
|
use lwk_wollet::elements::encode::deserialize;
|
||||||
|
use lwk_wollet::elements::hex::FromHex;
|
||||||
|
use lwk_wollet::elements::{
|
||||||
|
LockTime::{self, *},
|
||||||
|
Transaction,
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) fn now() -> u32 {
|
pub(crate) fn now() -> u32 {
|
||||||
SystemTime::now()
|
SystemTime::now()
|
||||||
@@ -38,3 +43,9 @@ pub(crate) fn is_locktime_expired(current_locktime: LockTime, expiry_locktime: L
|
|||||||
_ => false, // Not using the same units
|
_ => false, // Not using the same units
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn deserialize_tx_hex(tx_hex: &str) -> Result<Transaction> {
|
||||||
|
Ok(deserialize(&Vec::<u8>::from_hex(tx_hex).map_err(
|
||||||
|
|err| anyhow!("Could not deserialize transaction: {err:?}"),
|
||||||
|
)?)?)
|
||||||
|
}
|
||||||
|
|||||||
@@ -677,13 +677,15 @@ 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 != 5) throw Exception('unexpected arr length: expect 5 but see ${arr.length}');
|
if (arr.length != 7) throw Exception('unexpected arr length: expect 7 but see ${arr.length}');
|
||||||
return Config(
|
return Config(
|
||||||
boltzUrl: dco_decode_String(arr[0]),
|
boltzUrl: dco_decode_String(arr[0]),
|
||||||
electrumUrl: dco_decode_String(arr[1]),
|
electrumUrl: dco_decode_String(arr[1]),
|
||||||
workingDir: dco_decode_String(arr[2]),
|
workingDir: dco_decode_String(arr[2]),
|
||||||
network: dco_decode_network(arr[3]),
|
network: dco_decode_network(arr[3]),
|
||||||
paymentTimeoutSec: dco_decode_u_64(arr[4]),
|
paymentTimeoutSec: dco_decode_u_64(arr[4]),
|
||||||
|
zeroConfMinFeeRate: dco_decode_f_32(arr[5]),
|
||||||
|
zeroConfMaxAmountSat: dco_decode_opt_box_autoadd_u_64(arr[6]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -698,6 +700,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
double dco_decode_f_32(dynamic raw) {
|
||||||
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
|
return raw as double;
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
GetInfoResponse dco_decode_get_info_response(dynamic raw) {
|
GetInfoResponse dco_decode_get_info_response(dynamic raw) {
|
||||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
@@ -1203,12 +1211,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
|||||||
var var_workingDir = sse_decode_String(deserializer);
|
var var_workingDir = sse_decode_String(deserializer);
|
||||||
var var_network = sse_decode_network(deserializer);
|
var var_network = sse_decode_network(deserializer);
|
||||||
var var_paymentTimeoutSec = sse_decode_u_64(deserializer);
|
var var_paymentTimeoutSec = sse_decode_u_64(deserializer);
|
||||||
|
var var_zeroConfMinFeeRate = sse_decode_f_32(deserializer);
|
||||||
|
var var_zeroConfMaxAmountSat = sse_decode_opt_box_autoadd_u_64(deserializer);
|
||||||
return Config(
|
return Config(
|
||||||
boltzUrl: var_boltzUrl,
|
boltzUrl: var_boltzUrl,
|
||||||
electrumUrl: var_electrumUrl,
|
electrumUrl: var_electrumUrl,
|
||||||
workingDir: var_workingDir,
|
workingDir: var_workingDir,
|
||||||
network: var_network,
|
network: var_network,
|
||||||
paymentTimeoutSec: var_paymentTimeoutSec);
|
paymentTimeoutSec: var_paymentTimeoutSec,
|
||||||
|
zeroConfMinFeeRate: var_zeroConfMinFeeRate,
|
||||||
|
zeroConfMaxAmountSat: var_zeroConfMaxAmountSat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
@@ -1219,6 +1231,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
|||||||
return ConnectRequest(mnemonic: var_mnemonic, config: var_config);
|
return ConnectRequest(mnemonic: var_mnemonic, config: var_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
double sse_decode_f_32(SseDeserializer deserializer) {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
return deserializer.buffer.getFloat32();
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
GetInfoResponse sse_decode_get_info_response(SseDeserializer deserializer) {
|
GetInfoResponse sse_decode_get_info_response(SseDeserializer deserializer) {
|
||||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
@@ -1631,6 +1649,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
|||||||
return (raw as BindingLiquidSdkImpl).frbInternalCstEncode();
|
return (raw as BindingLiquidSdkImpl).frbInternalCstEncode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
double cst_encode_f_32(double raw) {
|
||||||
|
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
int cst_encode_i_32(int raw) {
|
int cst_encode_i_32(int raw) {
|
||||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||||
@@ -1812,6 +1836,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
|||||||
sse_encode_String(self.workingDir, serializer);
|
sse_encode_String(self.workingDir, serializer);
|
||||||
sse_encode_network(self.network, serializer);
|
sse_encode_network(self.network, serializer);
|
||||||
sse_encode_u_64(self.paymentTimeoutSec, serializer);
|
sse_encode_u_64(self.paymentTimeoutSec, serializer);
|
||||||
|
sse_encode_f_32(self.zeroConfMinFeeRate, serializer);
|
||||||
|
sse_encode_opt_box_autoadd_u_64(self.zeroConfMaxAmountSat, serializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
@@ -1821,6 +1847,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
|||||||
sse_encode_config(self.config, serializer);
|
sse_encode_config(self.config, serializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_f_32(double self, SseSerializer serializer) {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
serializer.buffer.putFloat32(self);
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_get_info_response(GetInfoResponse self, SseSerializer serializer) {
|
void sse_encode_get_info_response(GetInfoResponse self, SseSerializer serializer) {
|
||||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
|||||||
@@ -94,6 +94,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
@protected
|
@protected
|
||||||
ConnectRequest dco_decode_connect_request(dynamic raw);
|
ConnectRequest dco_decode_connect_request(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
double dco_decode_f_32(dynamic raw);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
GetInfoResponse dco_decode_get_info_response(dynamic raw);
|
GetInfoResponse dco_decode_get_info_response(dynamic raw);
|
||||||
|
|
||||||
@@ -258,6 +261,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
@protected
|
@protected
|
||||||
ConnectRequest sse_decode_connect_request(SseDeserializer deserializer);
|
ConnectRequest sse_decode_connect_request(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
double sse_decode_f_32(SseDeserializer deserializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
GetInfoResponse sse_decode_get_info_response(SseDeserializer deserializer);
|
GetInfoResponse sse_decode_get_info_response(SseDeserializer deserializer);
|
||||||
|
|
||||||
@@ -612,6 +618,8 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
wireObj.working_dir = cst_encode_String(apiObj.workingDir);
|
wireObj.working_dir = cst_encode_String(apiObj.workingDir);
|
||||||
wireObj.network = cst_encode_network(apiObj.network);
|
wireObj.network = cst_encode_network(apiObj.network);
|
||||||
wireObj.payment_timeout_sec = cst_encode_u_64(apiObj.paymentTimeoutSec);
|
wireObj.payment_timeout_sec = cst_encode_u_64(apiObj.paymentTimeoutSec);
|
||||||
|
wireObj.zero_conf_min_fee_rate = cst_encode_f_32(apiObj.zeroConfMinFeeRate);
|
||||||
|
wireObj.zero_conf_max_amount_sat = cst_encode_opt_box_autoadd_u_64(apiObj.zeroConfMaxAmountSat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
@@ -890,6 +898,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
int cst_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk(
|
int cst_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk(
|
||||||
BindingLiquidSdk raw);
|
BindingLiquidSdk raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
double cst_encode_f_32(double raw);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
int cst_encode_i_32(int raw);
|
int cst_encode_i_32(int raw);
|
||||||
|
|
||||||
@@ -981,6 +992,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
@protected
|
@protected
|
||||||
void sse_encode_connect_request(ConnectRequest self, SseSerializer serializer);
|
void sse_encode_connect_request(ConnectRequest self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_f_32(double self, SseSerializer serializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_get_info_response(GetInfoResponse self, SseSerializer serializer);
|
void sse_encode_get_info_response(GetInfoResponse self, SseSerializer serializer);
|
||||||
|
|
||||||
@@ -1622,12 +1636,9 @@ class RustLibWire implements BaseWire {
|
|||||||
_dummy_method_to_enforce_bundlingPtr.asFunction<int Function()>();
|
_dummy_method_to_enforce_bundlingPtr.asFunction<int Function()>();
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef DartPostCObjectFnType = ffi.Pointer<ffi.NativeFunction<DartPostCObjectFnTypeFunction>>;
|
typedef DartPostCObjectFnType
|
||||||
typedef DartPostCObjectFnTypeFunction = ffi.Bool Function(DartPort port_id, ffi.Pointer<ffi.Void> message);
|
= ffi.Pointer<ffi.NativeFunction<ffi.Bool Function(DartPort port_id, ffi.Pointer<ffi.Void> message)>>;
|
||||||
typedef DartDartPostCObjectFnTypeFunction = bool Function(
|
|
||||||
DartDartPort port_id, ffi.Pointer<ffi.Void> message);
|
|
||||||
typedef DartPort = ffi.Int64;
|
typedef DartPort = ffi.Int64;
|
||||||
typedef DartDartPort = int;
|
|
||||||
|
|
||||||
final class wire_cst_list_prim_u_8_strict extends ffi.Struct {
|
final class wire_cst_list_prim_u_8_strict extends ffi.Struct {
|
||||||
external ffi.Pointer<ffi.Uint8> ptr;
|
external ffi.Pointer<ffi.Uint8> ptr;
|
||||||
@@ -1756,6 +1767,11 @@ final class wire_cst_config extends ffi.Struct {
|
|||||||
|
|
||||||
@ffi.Uint64()
|
@ffi.Uint64()
|
||||||
external int payment_timeout_sec;
|
external int payment_timeout_sec;
|
||||||
|
|
||||||
|
@ffi.Float()
|
||||||
|
external double zero_conf_min_fee_rate;
|
||||||
|
|
||||||
|
external ffi.Pointer<ffi.Uint64> zero_conf_max_amount_sat;
|
||||||
}
|
}
|
||||||
|
|
||||||
final class wire_cst_connect_request extends ffi.Struct {
|
final class wire_cst_connect_request extends ffi.Struct {
|
||||||
@@ -1935,3 +1951,9 @@ final class wire_cst_receive_payment_response extends ffi.Struct {
|
|||||||
final class wire_cst_send_payment_response extends ffi.Struct {
|
final class wire_cst_send_payment_response extends ffi.Struct {
|
||||||
external wire_cst_payment payment;
|
external wire_cst_payment payment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const double DEFAULT_ZERO_CONF_MIN_FEE_RATE_TESTNET = 0.1;
|
||||||
|
|
||||||
|
const double DEFAULT_ZERO_CONF_MIN_FEE_RATE_MAINNET = 0.01;
|
||||||
|
|
||||||
|
const int DEFAULT_ZERO_CONF_MAX_SAT = 100000;
|
||||||
|
|||||||
@@ -42,12 +42,21 @@ class Config {
|
|||||||
/// Send payment timeout. See [crate::sdk::LiquidSdk::send_payment]
|
/// Send payment timeout. See [crate::sdk::LiquidSdk::send_payment]
|
||||||
final BigInt paymentTimeoutSec;
|
final BigInt paymentTimeoutSec;
|
||||||
|
|
||||||
|
/// Zero-conf minimum accepted fee-rate in sat/vbyte
|
||||||
|
final double zeroConfMinFeeRate;
|
||||||
|
|
||||||
|
/// Maximum amount in satoshi to accept zero-conf payments with
|
||||||
|
/// Defaults to [crate::receive_swap::DEFAULT_ZERO_CONF_MAX_SAT]
|
||||||
|
final BigInt? zeroConfMaxAmountSat;
|
||||||
|
|
||||||
const Config({
|
const Config({
|
||||||
required this.boltzUrl,
|
required this.boltzUrl,
|
||||||
required this.electrumUrl,
|
required this.electrumUrl,
|
||||||
required this.workingDir,
|
required this.workingDir,
|
||||||
required this.network,
|
required this.network,
|
||||||
required this.paymentTimeoutSec,
|
required this.paymentTimeoutSec,
|
||||||
|
required this.zeroConfMinFeeRate,
|
||||||
|
this.zeroConfMaxAmountSat,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -56,7 +65,9 @@ class Config {
|
|||||||
electrumUrl.hashCode ^
|
electrumUrl.hashCode ^
|
||||||
workingDir.hashCode ^
|
workingDir.hashCode ^
|
||||||
network.hashCode ^
|
network.hashCode ^
|
||||||
paymentTimeoutSec.hashCode;
|
paymentTimeoutSec.hashCode ^
|
||||||
|
zeroConfMinFeeRate.hashCode ^
|
||||||
|
zeroConfMaxAmountSat.hashCode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
@@ -67,7 +78,9 @@ class Config {
|
|||||||
electrumUrl == other.electrumUrl &&
|
electrumUrl == other.electrumUrl &&
|
||||||
workingDir == other.workingDir &&
|
workingDir == other.workingDir &&
|
||||||
network == other.network &&
|
network == other.network &&
|
||||||
paymentTimeoutSec == other.paymentTimeoutSec;
|
paymentTimeoutSec == other.paymentTimeoutSec &&
|
||||||
|
zeroConfMinFeeRate == other.zeroConfMinFeeRate &&
|
||||||
|
zeroConfMaxAmountSat == other.zeroConfMaxAmountSat;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConnectRequest {
|
class ConnectRequest {
|
||||||
|
|||||||
@@ -716,6 +716,11 @@ final class wire_cst_config extends ffi.Struct {
|
|||||||
|
|
||||||
@ffi.Uint64()
|
@ffi.Uint64()
|
||||||
external int payment_timeout_sec;
|
external int payment_timeout_sec;
|
||||||
|
|
||||||
|
@ffi.Float()
|
||||||
|
external double zero_conf_min_fee_rate;
|
||||||
|
|
||||||
|
external ffi.Pointer<ffi.Uint64> zero_conf_max_amount_sat;
|
||||||
}
|
}
|
||||||
|
|
||||||
final class wire_cst_connect_request extends ffi.Struct {
|
final class wire_cst_connect_request extends ffi.Struct {
|
||||||
@@ -898,3 +903,9 @@ 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 double DEFAULT_ZERO_CONF_MIN_FEE_RATE_TESTNET = 0.1;
|
||||||
|
|
||||||
|
const double DEFAULT_ZERO_CONF_MIN_FEE_RATE_MAINNET = 0.01;
|
||||||
|
|
||||||
|
const int DEFAULT_ZERO_CONF_MAX_SAT = 100000;
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ fun asConfig(config: ReadableMap): Config? {
|
|||||||
"workingDir",
|
"workingDir",
|
||||||
"network",
|
"network",
|
||||||
"paymentTimeoutSec",
|
"paymentTimeoutSec",
|
||||||
|
"zeroConfMinFeeRate",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
@@ -52,12 +53,25 @@ fun asConfig(config: ReadableMap): Config? {
|
|||||||
val workingDir = config.getString("workingDir")!!
|
val workingDir = config.getString("workingDir")!!
|
||||||
val network = config.getString("network")?.let { asNetwork(it) }!!
|
val network = config.getString("network")?.let { asNetwork(it) }!!
|
||||||
val paymentTimeoutSec = config.getDouble("paymentTimeoutSec").toULong()
|
val paymentTimeoutSec = config.getDouble("paymentTimeoutSec").toULong()
|
||||||
|
val zeroConfMinFeeRate = config.getDouble("zeroConfMinFeeRate")
|
||||||
|
val zeroConfMaxAmountSat =
|
||||||
|
if (hasNonNullKey(
|
||||||
|
config,
|
||||||
|
"zeroConfMaxAmountSat",
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
config.getDouble("zeroConfMaxAmountSat").toULong()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
return Config(
|
return Config(
|
||||||
boltzUrl,
|
boltzUrl,
|
||||||
electrumUrl,
|
electrumUrl,
|
||||||
workingDir,
|
workingDir,
|
||||||
network,
|
network,
|
||||||
paymentTimeoutSec,
|
paymentTimeoutSec,
|
||||||
|
zeroConfMinFeeRate,
|
||||||
|
zeroConfMaxAmountSat,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +82,8 @@ fun readableMapOf(config: Config): ReadableMap =
|
|||||||
"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,
|
||||||
|
"zeroConfMinFeeRate" to config.zeroConfMinFeeRate,
|
||||||
|
"zeroConfMaxAmountSat" to config.zeroConfMaxAmountSat,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun asConfigList(arr: ReadableArray): List<Config> {
|
fun asConfigList(arr: ReadableArray): List<Config> {
|
||||||
|
|||||||
@@ -56,13 +56,25 @@ enum BreezLiquidSDKMapper {
|
|||||||
guard let paymentTimeoutSec = config["paymentTimeoutSec"] as? UInt64 else {
|
guard let paymentTimeoutSec = config["paymentTimeoutSec"] as? UInt64 else {
|
||||||
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "paymentTimeoutSec", typeName: "Config"))
|
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "paymentTimeoutSec", typeName: "Config"))
|
||||||
}
|
}
|
||||||
|
guard let zeroConfMinFeeRate = config["zeroConfMinFeeRate"] as? Float else {
|
||||||
|
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "zeroConfMinFeeRate", typeName: "Config"))
|
||||||
|
}
|
||||||
|
var zeroConfMaxAmountSat: UInt64?
|
||||||
|
if hasNonNilKey(data: config, key: "zeroConfMaxAmountSat") {
|
||||||
|
guard let zeroConfMaxAmountSatTmp = config["zeroConfMaxAmountSat"] as? UInt64 else {
|
||||||
|
throw LiquidSdkError.Generic(message: errUnexpectedValue(fieldName: "zeroConfMaxAmountSat"))
|
||||||
|
}
|
||||||
|
zeroConfMaxAmountSat = zeroConfMaxAmountSatTmp
|
||||||
|
}
|
||||||
|
|
||||||
return Config(
|
return Config(
|
||||||
boltzUrl: boltzUrl,
|
boltzUrl: boltzUrl,
|
||||||
electrumUrl: electrumUrl,
|
electrumUrl: electrumUrl,
|
||||||
workingDir: workingDir,
|
workingDir: workingDir,
|
||||||
network: network,
|
network: network,
|
||||||
paymentTimeoutSec: paymentTimeoutSec
|
paymentTimeoutSec: paymentTimeoutSec,
|
||||||
|
zeroConfMinFeeRate: zeroConfMinFeeRate,
|
||||||
|
zeroConfMaxAmountSat: zeroConfMaxAmountSat
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +85,8 @@ enum BreezLiquidSDKMapper {
|
|||||||
"workingDir": config.workingDir,
|
"workingDir": config.workingDir,
|
||||||
"network": valueOf(network: config.network),
|
"network": valueOf(network: config.network),
|
||||||
"paymentTimeoutSec": config.paymentTimeoutSec,
|
"paymentTimeoutSec": config.paymentTimeoutSec,
|
||||||
|
"zeroConfMinFeeRate": config.zeroConfMinFeeRate,
|
||||||
|
"zeroConfMaxAmountSat": config.zeroConfMaxAmountSat == nil ? nil : config.zeroConfMaxAmountSat,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ export interface Config {
|
|||||||
workingDir: string
|
workingDir: string
|
||||||
network: Network
|
network: Network
|
||||||
paymentTimeoutSec: number
|
paymentTimeoutSec: number
|
||||||
|
zeroConfMinFeeRate: number
|
||||||
|
zeroConfMaxAmountSat?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConnectRequest {
|
export interface ConnectRequest {
|
||||||
|
|||||||
Reference in New Issue
Block a user