mirror of
https://github.com/aljazceru/breez-sdk-liquid.git
synced 2025-12-24 01:14:22 +01:00
Add zero-amount Receive Chain Swap (#538)
Add support for Zero-Amount Receive Chain Swaps
This commit is contained in:
4
cli/Cargo.lock
generated
4
cli/Cargo.lock
generated
@@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
@@ -609,7 +609,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "boltz-client"
|
||||
version = "0.1.3"
|
||||
source = "git+https://github.com/SatoshiPortal/boltz-rust?branch=trunk#19e01071f5722c21f80ae9b34edef5c3972e9e0b"
|
||||
source = "git+https://github.com/SatoshiPortal/boltz-rust?branch=trunk#68ff15b0f80abfa54d8dca4c11c8c2794e4b6921"
|
||||
dependencies = [
|
||||
"bip39",
|
||||
"bitcoin 0.31.2",
|
||||
|
||||
@@ -271,13 +271,21 @@ pub(crate) async fn handle_command(
|
||||
})
|
||||
.await?;
|
||||
|
||||
wait_confirmation!(
|
||||
let fees = prepare_response.fees_sat;
|
||||
let confirmation_msg = match payer_amount_sat {
|
||||
Some(_) => format!("Fees: {fees} sat. Are the fees acceptable? (y/N)"),
|
||||
None => {
|
||||
let min = prepare_response.min_payer_amount_sat;
|
||||
let max = prepare_response.max_payer_amount_sat;
|
||||
let service_feerate = prepare_response.service_feerate;
|
||||
format!(
|
||||
"Fees: {} sat. Are the fees acceptable? (y/N) ",
|
||||
prepare_response.fees_sat
|
||||
),
|
||||
"Payment receive halted"
|
||||
);
|
||||
"Fees: {fees} sat + {service_feerate:?}% of the sent amount. \
|
||||
Sender should send between {min:?} sat and {max:?} sat. \
|
||||
Are the fees acceptable? (y/N)"
|
||||
)
|
||||
}
|
||||
};
|
||||
wait_confirmation!(confirmation_msg, "Payment receive halted");
|
||||
|
||||
let response = sdk
|
||||
.receive_payment(&ReceivePaymentRequest {
|
||||
|
||||
4
lib/Cargo.lock
generated
4
lib/Cargo.lock
generated
@@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
@@ -723,7 +723,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "boltz-client"
|
||||
version = "0.1.3"
|
||||
source = "git+https://github.com/SatoshiPortal/boltz-rust?branch=trunk#19e01071f5722c21f80ae9b34edef5c3972e9e0b"
|
||||
source = "git+https://github.com/SatoshiPortal/boltz-rust?branch=trunk#68ff15b0f80abfa54d8dca4c11c8c2794e4b6921"
|
||||
dependencies = [
|
||||
"bip39",
|
||||
"bitcoin 0.31.2",
|
||||
|
||||
@@ -367,6 +367,9 @@ typedef struct wire_cst_prepare_receive_response {
|
||||
int32_t payment_method;
|
||||
uint64_t *payer_amount_sat;
|
||||
uint64_t fees_sat;
|
||||
uint64_t *min_payer_amount_sat;
|
||||
uint64_t *max_payer_amount_sat;
|
||||
double *swapper_feerate;
|
||||
} wire_cst_prepare_receive_response;
|
||||
|
||||
typedef struct wire_cst_receive_payment_request {
|
||||
@@ -1194,6 +1197,8 @@ struct wire_cst_check_message_request *frbgen_breez_liquid_cst_new_box_autoadd_c
|
||||
|
||||
struct wire_cst_connect_request *frbgen_breez_liquid_cst_new_box_autoadd_connect_request(void);
|
||||
|
||||
double *frbgen_breez_liquid_cst_new_box_autoadd_f_64(double value);
|
||||
|
||||
struct wire_cst_get_payment_request *frbgen_breez_liquid_cst_new_box_autoadd_get_payment_request(void);
|
||||
|
||||
int64_t *frbgen_breez_liquid_cst_new_box_autoadd_i_64(int64_t value);
|
||||
@@ -1306,6 +1311,7 @@ static int64_t dummy_method_to_enforce_bundling(void) {
|
||||
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_buy_bitcoin_request);
|
||||
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_check_message_request);
|
||||
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_connect_request);
|
||||
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_f_64);
|
||||
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_get_payment_request);
|
||||
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_i_64);
|
||||
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_liquid_address_data);
|
||||
|
||||
@@ -431,9 +431,12 @@ dictionary PrepareReceiveRequest {
|
||||
};
|
||||
|
||||
dictionary PrepareReceiveResponse {
|
||||
u64? payer_amount_sat;
|
||||
PaymentMethod payment_method;
|
||||
u64 fees_sat;
|
||||
u64? payer_amount_sat;
|
||||
u64? min_payer_amount_sat;
|
||||
u64? max_payer_amount_sat;
|
||||
f64? swapper_feerate;
|
||||
};
|
||||
|
||||
dictionary ReceivePaymentRequest {
|
||||
|
||||
@@ -436,6 +436,20 @@ impl ChainSwapHandler {
|
||||
| ChainSwapStates::TransactionLockupFailed
|
||||
| ChainSwapStates::TransactionRefunded
|
||||
| ChainSwapStates::SwapExpired => {
|
||||
// Zero-amount Receive Chain Swaps also get to TransactionLockupFailed when user locks up funds
|
||||
let is_zero_amount = swap.payer_amount_sat == 0;
|
||||
if matches!(swap_state, ChainSwapStates::TransactionLockupFailed) && is_zero_amount
|
||||
{
|
||||
match self.handle_amountless_update(swap).await {
|
||||
Ok(_) => {
|
||||
// We successfully accepted the quote, the swap should continue as normal
|
||||
return Ok(()); // Break from TxLockupFailed branch
|
||||
}
|
||||
// In case of error, we continue and mark it as refundable
|
||||
Err(e) => error!("Failed to accept the quote for swap {}: {e:?}", &swap.id),
|
||||
}
|
||||
}
|
||||
|
||||
match swap.refund_tx_id.clone() {
|
||||
None => {
|
||||
warn!("Chain Swap {id} is in an unrecoverable state: {swap_state:?}");
|
||||
@@ -453,7 +467,7 @@ impl ChainSwapHandler {
|
||||
}
|
||||
}
|
||||
Some(refund_tx_id) => warn!(
|
||||
"Refund tx for Chain Swap {id} was already broadcast: txid {refund_tx_id}"
|
||||
"Refund for Chain Swap {id} was already broadcast: txid {refund_tx_id}"
|
||||
),
|
||||
};
|
||||
Ok(())
|
||||
@@ -466,6 +480,82 @@ impl ChainSwapHandler {
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_amountless_update(&self, swap: &ChainSwap) -> Result<(), PaymentError> {
|
||||
let quote = self
|
||||
.swapper
|
||||
.get_zero_amount_chain_swap_quote(&swap.id)
|
||||
.map(|quote| quote.to_sat())?;
|
||||
info!("Got quote of {quote} sat for swap {}", &swap.id);
|
||||
|
||||
self.validate_and_update_amountless_swap(swap, quote)
|
||||
.await?;
|
||||
self.swapper
|
||||
.accept_zero_amount_chain_swap_quote(&swap.id, quote)
|
||||
}
|
||||
|
||||
async fn validate_and_update_amountless_swap(
|
||||
&self,
|
||||
swap: &ChainSwap,
|
||||
quote_server_lockup_amount_sat: u64,
|
||||
) -> Result<(), PaymentError> {
|
||||
debug!("Validating {swap:?}");
|
||||
|
||||
ensure_sdk!(
|
||||
matches!(swap.direction, Direction::Incoming),
|
||||
PaymentError::generic(&format!(
|
||||
"Only an incoming chain swap can be a zero-amount swap. Swap ID: {}",
|
||||
&swap.id
|
||||
))
|
||||
);
|
||||
|
||||
let script_pubkey = swap.get_receive_lockup_swap_script_pubkey(self.config.network)?;
|
||||
let script_balance = self
|
||||
.bitcoin_chain_service
|
||||
.lock()
|
||||
.await
|
||||
.script_get_balance(script_pubkey.as_script())?;
|
||||
debug!("Found lockup balance {script_balance:?}");
|
||||
let user_lockup_amount_sat = match script_balance.confirmed > 0 {
|
||||
true => script_balance.confirmed,
|
||||
false => match script_balance.unconfirmed > 0 {
|
||||
true => script_balance.unconfirmed.unsigned_abs(),
|
||||
false => 0,
|
||||
},
|
||||
};
|
||||
ensure_sdk!(
|
||||
user_lockup_amount_sat > 0,
|
||||
PaymentError::generic("Lockup address has no confirmed or unconfirmed balance")
|
||||
);
|
||||
|
||||
let pair = swap.get_boltz_pair()?;
|
||||
let swapper_service_feerate = pair.fees.percentage;
|
||||
let swapper_server_fees_sat = pair.fees.server();
|
||||
let service_fees_sat =
|
||||
((swapper_service_feerate / 100.0) * user_lockup_amount_sat as f64).ceil() as u64;
|
||||
let fees_sat = swapper_server_fees_sat + service_fees_sat;
|
||||
ensure_sdk!(
|
||||
user_lockup_amount_sat > fees_sat,
|
||||
PaymentError::generic(&format!("Invalid quote: fees ({fees_sat} sat) are higher than user lockup ({user_lockup_amount_sat} sat)"))
|
||||
);
|
||||
|
||||
let expected_server_lockup_amount_sat = user_lockup_amount_sat - fees_sat;
|
||||
debug!("user_lockup_amount_sat = {}, service_fees_sat = {}, server_fees_sat = {}, expected_server_lockup_amount_sat = {}, quote_server_lockup_amount_sat = {}",
|
||||
user_lockup_amount_sat, service_fees_sat, swapper_server_fees_sat, expected_server_lockup_amount_sat, quote_server_lockup_amount_sat);
|
||||
ensure_sdk!(
|
||||
expected_server_lockup_amount_sat <= quote_server_lockup_amount_sat,
|
||||
PaymentError::generic(&format!("Invalid quote: expected at least {expected_server_lockup_amount_sat} sat, got {quote_server_lockup_amount_sat} sat"))
|
||||
);
|
||||
|
||||
let receiver_amount_sat = quote_server_lockup_amount_sat - swap.claim_fees_sat;
|
||||
self.persister.update_zero_amount_swap_values(
|
||||
&swap.id,
|
||||
user_lockup_amount_sat,
|
||||
receiver_amount_sat,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn on_new_outgoing_status(&self, swap: &ChainSwap, update: &boltz::Update) -> Result<()> {
|
||||
let id = &update.id;
|
||||
let status = &update.status;
|
||||
|
||||
@@ -3291,6 +3291,17 @@ impl SseDecode for Option<bool> {
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for Option<f64> {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
if (<bool>::sse_decode(deserializer)) {
|
||||
return Some(<f64>::sse_decode(deserializer));
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for Option<i64> {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
@@ -3747,10 +3758,16 @@ impl SseDecode for crate::model::PrepareReceiveResponse {
|
||||
let mut var_paymentMethod = <crate::model::PaymentMethod>::sse_decode(deserializer);
|
||||
let mut var_payerAmountSat = <Option<u64>>::sse_decode(deserializer);
|
||||
let mut var_feesSat = <u64>::sse_decode(deserializer);
|
||||
let mut var_minPayerAmountSat = <Option<u64>>::sse_decode(deserializer);
|
||||
let mut var_maxPayerAmountSat = <Option<u64>>::sse_decode(deserializer);
|
||||
let mut var_swapperFeerate = <Option<f64>>::sse_decode(deserializer);
|
||||
return crate::model::PrepareReceiveResponse {
|
||||
payment_method: var_paymentMethod,
|
||||
payer_amount_sat: var_payerAmountSat,
|
||||
fees_sat: var_feesSat,
|
||||
min_payer_amount_sat: var_minPayerAmountSat,
|
||||
max_payer_amount_sat: var_maxPayerAmountSat,
|
||||
swapper_feerate: var_swapperFeerate,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -5807,6 +5824,9 @@ impl flutter_rust_bridge::IntoDart for crate::model::PrepareReceiveResponse {
|
||||
self.payment_method.into_into_dart().into_dart(),
|
||||
self.payer_amount_sat.into_into_dart().into_dart(),
|
||||
self.fees_sat.into_into_dart().into_dart(),
|
||||
self.min_payer_amount_sat.into_into_dart().into_dart(),
|
||||
self.max_payer_amount_sat.into_into_dart().into_dart(),
|
||||
self.swapper_feerate.into_into_dart().into_dart(),
|
||||
]
|
||||
.into_dart()
|
||||
}
|
||||
@@ -7282,6 +7302,16 @@ impl SseEncode for Option<bool> {
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for Option<f64> {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
<bool>::sse_encode(self.is_some(), serializer);
|
||||
if let Some(value) = self {
|
||||
<f64>::sse_encode(value, serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for Option<i64> {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
@@ -7680,6 +7710,9 @@ impl SseEncode for crate::model::PrepareReceiveResponse {
|
||||
<crate::model::PaymentMethod>::sse_encode(self.payment_method, serializer);
|
||||
<Option<u64>>::sse_encode(self.payer_amount_sat, serializer);
|
||||
<u64>::sse_encode(self.fees_sat, serializer);
|
||||
<Option<u64>>::sse_encode(self.min_payer_amount_sat, serializer);
|
||||
<Option<u64>>::sse_encode(self.max_payer_amount_sat, serializer);
|
||||
<Option<f64>>::sse_encode(self.swapper_feerate, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8290,6 +8323,12 @@ mod io {
|
||||
CstDecode::<crate::model::ConnectRequest>::cst_decode(*wrap).into()
|
||||
}
|
||||
}
|
||||
impl CstDecode<f64> for *mut f64 {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
fn cst_decode(self) -> f64 {
|
||||
unsafe { *flutter_rust_bridge::for_generated::box_from_leak_ptr(self) }
|
||||
}
|
||||
}
|
||||
impl CstDecode<crate::model::GetPaymentRequest> for *mut wire_cst_get_payment_request {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
fn cst_decode(self) -> crate::model::GetPaymentRequest {
|
||||
@@ -9554,6 +9593,9 @@ mod io {
|
||||
payment_method: self.payment_method.cst_decode(),
|
||||
payer_amount_sat: self.payer_amount_sat.cst_decode(),
|
||||
fees_sat: self.fees_sat.cst_decode(),
|
||||
min_payer_amount_sat: self.min_payer_amount_sat.cst_decode(),
|
||||
max_payer_amount_sat: self.max_payer_amount_sat.cst_decode(),
|
||||
swapper_feerate: self.swapper_feerate.cst_decode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10702,6 +10744,9 @@ mod io {
|
||||
payment_method: Default::default(),
|
||||
payer_amount_sat: core::ptr::null_mut(),
|
||||
fees_sat: Default::default(),
|
||||
min_payer_amount_sat: core::ptr::null_mut(),
|
||||
max_payer_amount_sat: core::ptr::null_mut(),
|
||||
swapper_feerate: core::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11481,6 +11526,11 @@ mod io {
|
||||
)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn frbgen_breez_liquid_cst_new_box_autoadd_f_64(value: f64) -> *mut f64 {
|
||||
flutter_rust_bridge::for_generated::new_leak_box_ptr(value)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn frbgen_breez_liquid_cst_new_box_autoadd_get_payment_request(
|
||||
) -> *mut wire_cst_get_payment_request {
|
||||
@@ -12840,6 +12890,9 @@ mod io {
|
||||
payment_method: i32,
|
||||
payer_amount_sat: *mut u64,
|
||||
fees_sat: u64,
|
||||
min_payer_amount_sat: *mut u64,
|
||||
max_payer_amount_sat: *mut u64,
|
||||
swapper_feerate: *mut f64,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::path::PathBuf;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
|
||||
use boltz_client::boltz::ChainPair;
|
||||
use boltz_client::{
|
||||
bitcoin::ScriptBuf,
|
||||
network::Chain,
|
||||
@@ -302,7 +303,31 @@ pub struct PrepareReceiveRequest {
|
||||
pub struct PrepareReceiveResponse {
|
||||
pub payment_method: PaymentMethod,
|
||||
pub payer_amount_sat: Option<u64>,
|
||||
|
||||
/// Generally represents the total fees that would be paid to send or receive this payment.
|
||||
///
|
||||
/// In case of Zero-Amount Receive Chain swaps, the swapper service fee (`swapper_feerate` times
|
||||
/// the amount) is paid in addition to `fees_sat`. The swapper service feerate is already known
|
||||
/// in the beginning, but the exact swapper service fee will only be known when the
|
||||
/// `payer_amount_sat` is known.
|
||||
///
|
||||
/// In all other types of swaps, the swapper service fee is included in `fees_sat`.
|
||||
pub fees_sat: u64,
|
||||
|
||||
/// The minimum amount the payer can send for this swap to succeed.
|
||||
///
|
||||
/// When the method is [PaymentMethod::LiquidAddress], this is empty.
|
||||
pub min_payer_amount_sat: Option<u64>,
|
||||
|
||||
/// The maximum amount the payer can send for this swap to succeed.
|
||||
///
|
||||
/// When the method is [PaymentMethod::LiquidAddress], this is empty.
|
||||
pub max_payer_amount_sat: Option<u64>,
|
||||
|
||||
/// The percentage of the sent amount that will count towards the service fee.
|
||||
///
|
||||
/// When the method is [PaymentMethod::LiquidAddress], this is empty.
|
||||
pub swapper_feerate: Option<f64>,
|
||||
}
|
||||
|
||||
/// An argument when calling [crate::sdk::LiquidSdk::receive_payment].
|
||||
@@ -671,6 +696,13 @@ impl ChainSwap {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn get_boltz_pair(&self) -> Result<ChainPair> {
|
||||
let pair: ChainPair = serde_json::from_str(&self.pair_fees_json)
|
||||
.map_err(|e| anyhow!("Failed to deserialize ChainPair: {e:?}"))?;
|
||||
|
||||
Ok(pair)
|
||||
}
|
||||
|
||||
pub(crate) fn get_claim_swap_script(&self) -> SdkResult<SwapScriptV2> {
|
||||
let chain_swap_details = self.get_boltz_create_response()?.claim_details;
|
||||
let our_pubkey = self.get_claim_keypair()?.public_key();
|
||||
|
||||
@@ -267,6 +267,32 @@ impl Persister {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Used for Zero-amount Receive Chain swaps, when we fetched the quote and we know how much
|
||||
/// the sender locked up
|
||||
pub(crate) fn update_zero_amount_swap_values(
|
||||
&self,
|
||||
swap_id: &str,
|
||||
payer_amount_sat: u64,
|
||||
receiver_amount_sat: u64,
|
||||
) -> Result<(), PaymentError> {
|
||||
log::info!("Updating chain swap {swap_id}: payer_amount_sat = {payer_amount_sat}, receiver_amount_sat = {receiver_amount_sat}");
|
||||
let con: Connection = self.get_connection()?;
|
||||
con.execute(
|
||||
"UPDATE chain_swaps
|
||||
SET
|
||||
payer_amount_sat = :payer_amount_sat,
|
||||
receiver_amount_sat = :receiver_amount_sat
|
||||
WHERE
|
||||
id = :id",
|
||||
named_params! {
|
||||
":id": swap_id,
|
||||
":payer_amount_sat": payer_amount_sat,
|
||||
":receiver_amount_sat": receiver_amount_sat,
|
||||
},
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Only set the Chain Swap claim_tx_id if not set, otherwise return an error
|
||||
pub(crate) fn set_chain_swap_claim_tx_id(
|
||||
&self,
|
||||
|
||||
@@ -706,10 +706,12 @@ impl LiquidSdk {
|
||||
fn get_and_validate_chain_pair(
|
||||
&self,
|
||||
direction: Direction,
|
||||
user_lockup_amount_sat: u64,
|
||||
user_lockup_amount_sat: Option<u64>,
|
||||
) -> Result<ChainPair, PaymentError> {
|
||||
let pair = self.get_chain_pair(direction)?;
|
||||
if let Some(user_lockup_amount_sat) = user_lockup_amount_sat {
|
||||
self.validate_user_lockup_amount_for_chain_pair(&pair, user_lockup_amount_sat)?;
|
||||
}
|
||||
Ok(pair)
|
||||
}
|
||||
|
||||
@@ -1631,6 +1633,9 @@ impl LiquidSdk {
|
||||
) -> Result<PrepareReceiveResponse, PaymentError> {
|
||||
self.ensure_is_started().await?;
|
||||
|
||||
let mut min_payer_amount_sat = None;
|
||||
let mut max_payer_amount_sat = None;
|
||||
let mut swapper_feerate = None;
|
||||
let fees_sat;
|
||||
match req.payment_method {
|
||||
PaymentMethod::Lightning => {
|
||||
@@ -1651,29 +1656,35 @@ impl LiquidSdk {
|
||||
.within(payer_amount_sat)
|
||||
.map_err(|_| PaymentError::AmountOutOfRange)?;
|
||||
|
||||
min_payer_amount_sat = Some(reverse_pair.limits.minimal);
|
||||
max_payer_amount_sat = Some(reverse_pair.limits.maximal);
|
||||
swapper_feerate = Some(reverse_pair.fees.percentage);
|
||||
|
||||
debug!(
|
||||
"Preparing Lightning Receive Swap with: payer_amount_sat {payer_amount_sat} sat, fees_sat {fees_sat} sat"
|
||||
);
|
||||
}
|
||||
PaymentMethod::BitcoinAddress => {
|
||||
let Some(payer_amount_sat) = req.payer_amount_sat else {
|
||||
return Err(PaymentError::AmountMissing { err: "`payer_amount_sat` must be specified when `PaymentMethod::BitcoinAddress` is used.".to_string() });
|
||||
};
|
||||
let payer_amount_sat = req.payer_amount_sat;
|
||||
let pair =
|
||||
self.get_and_validate_chain_pair(Direction::Incoming, payer_amount_sat)?;
|
||||
let claim_fees_sat = pair.fees.claim_estimate();
|
||||
let server_fees_sat = pair.fees.server();
|
||||
fees_sat = pair.fees.boltz(payer_amount_sat) + claim_fees_sat + server_fees_sat;
|
||||
debug!(
|
||||
"Preparing Chain Receive Swap with: payer_amount_sat {payer_amount_sat} sat, fees_sat {fees_sat} sat"
|
||||
);
|
||||
let service_fees_sat = payer_amount_sat
|
||||
.map(|user_lockup_amount_sat| pair.fees.boltz(user_lockup_amount_sat))
|
||||
.unwrap_or_default();
|
||||
|
||||
min_payer_amount_sat = Some(pair.limits.minimal);
|
||||
max_payer_amount_sat = Some(pair.limits.maximal);
|
||||
swapper_feerate = Some(pair.fees.percentage);
|
||||
|
||||
fees_sat = service_fees_sat + claim_fees_sat + server_fees_sat;
|
||||
debug!("Preparing Chain Receive Swap with: payer_amount_sat {payer_amount_sat:?}, fees_sat {fees_sat}");
|
||||
}
|
||||
PaymentMethod::LiquidAddress => {
|
||||
fees_sat = 0;
|
||||
debug!(
|
||||
"Preparing Liquid Receive Swap with: amount_sat {:?} sat, fees_sat {fees_sat} sat",
|
||||
req.payer_amount_sat
|
||||
);
|
||||
let payer_amount_sat = req.payer_amount_sat;
|
||||
debug!("Preparing Liquid Receive Swap with: amount_sat {payer_amount_sat:?}, fees_sat {fees_sat}");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1681,6 +1692,9 @@ impl LiquidSdk {
|
||||
payer_amount_sat: req.payer_amount_sat,
|
||||
fees_sat,
|
||||
payment_method: req.payment_method.clone(),
|
||||
min_payer_amount_sat,
|
||||
max_payer_amount_sat,
|
||||
swapper_feerate,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1708,6 +1722,7 @@ impl LiquidSdk {
|
||||
payment_method,
|
||||
payer_amount_sat: amount_sat,
|
||||
fees_sat,
|
||||
..
|
||||
} = &req.prepare_response;
|
||||
|
||||
match payment_method {
|
||||
@@ -1733,12 +1748,7 @@ impl LiquidSdk {
|
||||
self.create_receive_swap(*amount_sat, *fees_sat, description, description_hash)
|
||||
.await
|
||||
}
|
||||
PaymentMethod::BitcoinAddress => {
|
||||
let Some(amount_sat) = amount_sat else {
|
||||
return Err(PaymentError::AmountMissing { err: "`amount_sat` must be specified when `PaymentMethod::BitcoinAddress` is used.".to_string() });
|
||||
};
|
||||
self.receive_onchain(*amount_sat, *fees_sat).await
|
||||
}
|
||||
PaymentMethod::BitcoinAddress => self.receive_onchain(*amount_sat, *fees_sat).await,
|
||||
PaymentMethod::LiquidAddress => {
|
||||
let address = self.onchain_wallet.next_unused_address().await?.to_string();
|
||||
|
||||
@@ -1898,15 +1908,19 @@ impl LiquidSdk {
|
||||
|
||||
async fn create_receive_chain_swap(
|
||||
&self,
|
||||
user_lockup_amount_sat: u64,
|
||||
user_lockup_amount_sat: Option<u64>,
|
||||
fees_sat: u64,
|
||||
) -> Result<ChainSwap, PaymentError> {
|
||||
let pair = self.get_and_validate_chain_pair(Direction::Incoming, user_lockup_amount_sat)?;
|
||||
let claim_fees_sat = pair.fees.claim_estimate();
|
||||
let server_fees_sat = pair.fees.server();
|
||||
// Service fees are 0 if this is a zero-amount swap
|
||||
let service_fees_sat = user_lockup_amount_sat
|
||||
.map(|user_lockup_amount_sat| pair.fees.boltz(user_lockup_amount_sat))
|
||||
.unwrap_or_default();
|
||||
|
||||
ensure_sdk!(
|
||||
fees_sat == pair.fees.boltz(user_lockup_amount_sat) + claim_fees_sat + server_fees_sat,
|
||||
fees_sat == service_fees_sat + claim_fees_sat + server_fees_sat,
|
||||
PaymentError::InvalidOrExpiredFees
|
||||
);
|
||||
|
||||
@@ -1938,7 +1952,7 @@ impl LiquidSdk {
|
||||
preimage_hash: preimage.sha256,
|
||||
claim_public_key: Some(claim_public_key),
|
||||
refund_public_key: Some(refund_public_key),
|
||||
user_lock_amount: Some(user_lockup_amount_sat),
|
||||
user_lock_amount: user_lockup_amount_sat,
|
||||
server_lock_amount: None,
|
||||
pair_hash: Some(pair.hash.clone()),
|
||||
referral_id: None,
|
||||
@@ -1949,8 +1963,12 @@ impl LiquidSdk {
|
||||
let create_response_json =
|
||||
ChainSwap::from_boltz_struct_to_json(&create_response, &swap_id)?;
|
||||
|
||||
let accept_zero_conf = user_lockup_amount_sat <= pair.limits.maximal_zero_conf;
|
||||
let receiver_amount_sat = user_lockup_amount_sat - fees_sat;
|
||||
let accept_zero_conf = user_lockup_amount_sat
|
||||
.map(|user_lockup_amount_sat| user_lockup_amount_sat <= pair.limits.maximal_zero_conf)
|
||||
.unwrap_or(false);
|
||||
let receiver_amount_sat = user_lockup_amount_sat
|
||||
.map(|user_lockup_amount_sat| user_lockup_amount_sat - fees_sat)
|
||||
.unwrap_or(0);
|
||||
|
||||
let swap = ChainSwap {
|
||||
id: swap_id.clone(),
|
||||
@@ -1960,7 +1978,7 @@ impl LiquidSdk {
|
||||
timeout_block_height: create_response.lockup_details.timeout_block_height,
|
||||
preimage: preimage_str,
|
||||
description: Some("Bitcoin transfer".to_string()),
|
||||
payer_amount_sat: user_lockup_amount_sat,
|
||||
payer_amount_sat: user_lockup_amount_sat.unwrap_or(0),
|
||||
receiver_amount_sat,
|
||||
claim_fees_sat,
|
||||
pair_fees_json: serde_json::to_string(&pair).map_err(|e| {
|
||||
@@ -1983,15 +2001,18 @@ impl LiquidSdk {
|
||||
}
|
||||
|
||||
/// Receive from a Bitcoin transaction via a chain swap.
|
||||
///
|
||||
/// If no `user_lockup_amount_sat` is specified, this is an amountless swap and `fees_sat` exclude
|
||||
/// the service fees.
|
||||
async fn receive_onchain(
|
||||
&self,
|
||||
payer_amount_sat: u64,
|
||||
user_lockup_amount_sat: Option<u64>,
|
||||
fees_sat: u64,
|
||||
) -> Result<ReceivePaymentResponse, PaymentError> {
|
||||
self.ensure_is_started().await?;
|
||||
|
||||
let swap = self
|
||||
.create_receive_chain_swap(payer_amount_sat, fees_sat)
|
||||
.create_receive_chain_swap(user_lockup_amount_sat, fees_sat)
|
||||
.await?;
|
||||
let create_response = swap.get_boltz_create_response()?;
|
||||
let address = create_response.lockup_details.lockup_address;
|
||||
@@ -2170,7 +2191,7 @@ impl LiquidSdk {
|
||||
|
||||
let swap = self
|
||||
.create_receive_chain_swap(
|
||||
req.prepare_response.amount_sat,
|
||||
Some(req.prepare_response.amount_sat),
|
||||
req.prepare_response.fees_sat,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -8,6 +8,7 @@ use boltz_client::{
|
||||
elements::secp256k1_zkp::{MusigPartialSignature, MusigPubNonce},
|
||||
network::{electrum::ElectrumConfig, Chain},
|
||||
util::secrets::Preimage,
|
||||
Amount,
|
||||
};
|
||||
use log::info;
|
||||
use url::Url;
|
||||
@@ -179,6 +180,23 @@ impl Swapper for BoltzSwapper {
|
||||
Ok((pair_outgoing, pair_incoming))
|
||||
}
|
||||
|
||||
fn get_zero_amount_chain_swap_quote(&self, swap_id: &str) -> Result<Amount, PaymentError> {
|
||||
self.client
|
||||
.get_quote(swap_id)
|
||||
.map(|r| Amount::from_sat(r.amount))
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn accept_zero_amount_chain_swap_quote(
|
||||
&self,
|
||||
swap_id: &str,
|
||||
server_lockup_sat: u64,
|
||||
) -> Result<(), PaymentError> {
|
||||
self.client
|
||||
.accept_quote(swap_id, server_lockup_sat)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Get a submarine pair information
|
||||
fn get_submarine_pairs(&self) -> Result<Option<SubmarinePair>, PaymentError> {
|
||||
Ok(self.client.get_submarine_pairs()?.get_lbtc_to_btc_pair())
|
||||
|
||||
@@ -8,6 +8,7 @@ use boltz_client::{
|
||||
SubmarineClaimTxResponse, SubmarinePair,
|
||||
},
|
||||
network::Chain,
|
||||
Amount,
|
||||
};
|
||||
use tokio::sync::{broadcast, watch};
|
||||
|
||||
@@ -40,6 +41,19 @@ pub trait Swapper: Send + Sync {
|
||||
/// Get the current rate, limits and fees for both swap directions
|
||||
fn get_chain_pairs(&self) -> Result<(Option<ChainPair>, Option<ChainPair>), PaymentError>;
|
||||
|
||||
/// Get the quote for a Zero-Amount Receive Chain Swap.
|
||||
///
|
||||
/// If the user locked-up funds in the valid range this will return that amount. In all other
|
||||
/// cases, this will return an error.
|
||||
fn get_zero_amount_chain_swap_quote(&self, swap_id: &str) -> Result<Amount, PaymentError>;
|
||||
|
||||
/// Accept a specific quote for a Zero-Amount Receive Chain Swap
|
||||
fn accept_zero_amount_chain_swap_quote(
|
||||
&self,
|
||||
swap_id: &str,
|
||||
server_lockup_sat: u64,
|
||||
) -> Result<(), PaymentError>;
|
||||
|
||||
/// Get a submarine pair information
|
||||
fn get_submarine_pairs(&self) -> Result<Option<SubmarinePair>, PaymentError>;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use boltz_client::{
|
||||
SubmarinePair, SwapTree,
|
||||
},
|
||||
util::secrets::Preimage,
|
||||
PublicKey,
|
||||
Amount, PublicKey,
|
||||
};
|
||||
use sdk_common::invoice::parse_invoice;
|
||||
|
||||
@@ -313,4 +313,16 @@ impl Swapper for MockSwapper {
|
||||
fn get_bolt12_invoice(&self, _offer: &str, _amount_sat: u64) -> Result<String, PaymentError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_zero_amount_chain_swap_quote(&self, swap_id: &str) -> Result<Amount, PaymentError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn accept_zero_amount_chain_swap_quote(
|
||||
&self,
|
||||
_swap_id: &str,
|
||||
_server_lockup_sat: u64,
|
||||
) -> Result<(), PaymentError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1411,6 +1411,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return dco_decode_connect_request(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
double dco_decode_box_autoadd_f_64(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return raw as double;
|
||||
}
|
||||
|
||||
@protected
|
||||
GetPaymentRequest dco_decode_box_autoadd_get_payment_request(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@@ -2379,6 +2385,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return raw == null ? null : dco_decode_box_autoadd_bool(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
double? dco_decode_opt_box_autoadd_f_64(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return raw == null ? null : dco_decode_box_autoadd_f_64(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
PlatformInt64? dco_decode_opt_box_autoadd_i_64(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@@ -2691,11 +2703,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
PrepareReceiveResponse dco_decode_prepare_receive_response(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
final arr = raw as List<dynamic>;
|
||||
if (arr.length != 3) throw Exception('unexpected arr length: expect 3 but see ${arr.length}');
|
||||
if (arr.length != 6) throw Exception('unexpected arr length: expect 6 but see ${arr.length}');
|
||||
return PrepareReceiveResponse(
|
||||
paymentMethod: dco_decode_payment_method(arr[0]),
|
||||
payerAmountSat: dco_decode_opt_box_autoadd_u_64(arr[1]),
|
||||
feesSat: dco_decode_u_64(arr[2]),
|
||||
minPayerAmountSat: dco_decode_opt_box_autoadd_u_64(arr[3]),
|
||||
maxPayerAmountSat: dco_decode_opt_box_autoadd_u_64(arr[4]),
|
||||
swapperFeerate: dco_decode_opt_box_autoadd_f_64(arr[5]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3291,6 +3306,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return (sse_decode_connect_request(deserializer));
|
||||
}
|
||||
|
||||
@protected
|
||||
double sse_decode_box_autoadd_f_64(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
return (sse_decode_f_64(deserializer));
|
||||
}
|
||||
|
||||
@protected
|
||||
GetPaymentRequest sse_decode_box_autoadd_get_payment_request(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -4303,6 +4324,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
double? sse_decode_opt_box_autoadd_f_64(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
|
||||
if (sse_decode_bool(deserializer)) {
|
||||
return (sse_decode_box_autoadd_f_64(deserializer));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
PlatformInt64? sse_decode_opt_box_autoadd_i_64(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -4664,8 +4696,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
var var_paymentMethod = sse_decode_payment_method(deserializer);
|
||||
var var_payerAmountSat = sse_decode_opt_box_autoadd_u_64(deserializer);
|
||||
var var_feesSat = sse_decode_u_64(deserializer);
|
||||
var var_minPayerAmountSat = sse_decode_opt_box_autoadd_u_64(deserializer);
|
||||
var var_maxPayerAmountSat = sse_decode_opt_box_autoadd_u_64(deserializer);
|
||||
var var_swapperFeerate = sse_decode_opt_box_autoadd_f_64(deserializer);
|
||||
return PrepareReceiveResponse(
|
||||
paymentMethod: var_paymentMethod, payerAmountSat: var_payerAmountSat, feesSat: var_feesSat);
|
||||
paymentMethod: var_paymentMethod,
|
||||
payerAmountSat: var_payerAmountSat,
|
||||
feesSat: var_feesSat,
|
||||
minPayerAmountSat: var_minPayerAmountSat,
|
||||
maxPayerAmountSat: var_maxPayerAmountSat,
|
||||
swapperFeerate: var_swapperFeerate);
|
||||
}
|
||||
|
||||
@protected
|
||||
@@ -5308,6 +5348,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
sse_encode_connect_request(self, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_f_64(double self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_f_64(self, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_get_payment_request(GetPaymentRequest self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -6175,6 +6221,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_opt_box_autoadd_f_64(double? self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
|
||||
sse_encode_bool(self != null, serializer);
|
||||
if (self != null) {
|
||||
sse_encode_box_autoadd_f_64(self, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_opt_box_autoadd_i_64(PlatformInt64? self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -6494,6 +6550,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
sse_encode_payment_method(self.paymentMethod, serializer);
|
||||
sse_encode_opt_box_autoadd_u_64(self.payerAmountSat, serializer);
|
||||
sse_encode_u_64(self.feesSat, serializer);
|
||||
sse_encode_opt_box_autoadd_u_64(self.minPayerAmountSat, serializer);
|
||||
sse_encode_opt_box_autoadd_u_64(self.maxPayerAmountSat, serializer);
|
||||
sse_encode_opt_box_autoadd_f_64(self.swapperFeerate, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
|
||||
@@ -107,6 +107,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
ConnectRequest dco_decode_box_autoadd_connect_request(dynamic raw);
|
||||
|
||||
@protected
|
||||
double dco_decode_box_autoadd_f_64(dynamic raw);
|
||||
|
||||
@protected
|
||||
GetPaymentRequest dco_decode_box_autoadd_get_payment_request(dynamic raw);
|
||||
|
||||
@@ -398,6 +401,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
bool? dco_decode_opt_box_autoadd_bool(dynamic raw);
|
||||
|
||||
@protected
|
||||
double? dco_decode_opt_box_autoadd_f_64(dynamic raw);
|
||||
|
||||
@protected
|
||||
PlatformInt64? dco_decode_opt_box_autoadd_i_64(dynamic raw);
|
||||
|
||||
@@ -654,6 +660,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
ConnectRequest sse_decode_box_autoadd_connect_request(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
double sse_decode_box_autoadd_f_64(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
GetPaymentRequest sse_decode_box_autoadd_get_payment_request(SseDeserializer deserializer);
|
||||
|
||||
@@ -945,6 +954,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
bool? sse_decode_opt_box_autoadd_bool(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
double? sse_decode_opt_box_autoadd_f_64(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
PlatformInt64? sse_decode_opt_box_autoadd_i_64(SseDeserializer deserializer);
|
||||
|
||||
@@ -1243,6 +1255,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@protected
|
||||
ffi.Pointer<ffi.Double> cst_encode_box_autoadd_f_64(double raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return wire.cst_new_box_autoadd_f_64(cst_encode_f_64(raw));
|
||||
}
|
||||
|
||||
@protected
|
||||
ffi.Pointer<wire_cst_get_payment_request> cst_encode_box_autoadd_get_payment_request(
|
||||
GetPaymentRequest raw) {
|
||||
@@ -1708,6 +1726,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
return raw == null ? ffi.nullptr : cst_encode_box_autoadd_bool(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
ffi.Pointer<ffi.Double> cst_encode_opt_box_autoadd_f_64(double? raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return raw == null ? ffi.nullptr : cst_encode_box_autoadd_f_64(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
ffi.Pointer<ffi.Int64> cst_encode_opt_box_autoadd_i_64(PlatformInt64? raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
@@ -2895,6 +2919,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
wireObj.payment_method = cst_encode_payment_method(apiObj.paymentMethod);
|
||||
wireObj.payer_amount_sat = cst_encode_opt_box_autoadd_u_64(apiObj.payerAmountSat);
|
||||
wireObj.fees_sat = cst_encode_u_64(apiObj.feesSat);
|
||||
wireObj.min_payer_amount_sat = cst_encode_opt_box_autoadd_u_64(apiObj.minPayerAmountSat);
|
||||
wireObj.max_payer_amount_sat = cst_encode_opt_box_autoadd_u_64(apiObj.maxPayerAmountSat);
|
||||
wireObj.swapper_feerate = cst_encode_opt_box_autoadd_f_64(apiObj.swapperFeerate);
|
||||
}
|
||||
|
||||
@protected
|
||||
@@ -3308,6 +3335,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_encode_box_autoadd_connect_request(ConnectRequest self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_f_64(double self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_get_payment_request(GetPaymentRequest self, SseSerializer serializer);
|
||||
|
||||
@@ -3607,6 +3637,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_encode_opt_box_autoadd_bool(bool? self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_opt_box_autoadd_f_64(double? self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_opt_box_autoadd_i_64(PlatformInt64? self, SseSerializer serializer);
|
||||
|
||||
@@ -4689,6 +4722,20 @@ class RustLibWire implements BaseWire {
|
||||
late final _cst_new_box_autoadd_connect_request =
|
||||
_cst_new_box_autoadd_connect_requestPtr.asFunction<ffi.Pointer<wire_cst_connect_request> Function()>();
|
||||
|
||||
ffi.Pointer<ffi.Double> cst_new_box_autoadd_f_64(
|
||||
double value,
|
||||
) {
|
||||
return _cst_new_box_autoadd_f_64(
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
late final _cst_new_box_autoadd_f_64Ptr =
|
||||
_lookup<ffi.NativeFunction<ffi.Pointer<ffi.Double> Function(ffi.Double)>>(
|
||||
'frbgen_breez_liquid_cst_new_box_autoadd_f_64');
|
||||
late final _cst_new_box_autoadd_f_64 =
|
||||
_cst_new_box_autoadd_f_64Ptr.asFunction<ffi.Pointer<ffi.Double> Function(double)>();
|
||||
|
||||
ffi.Pointer<wire_cst_get_payment_request> cst_new_box_autoadd_get_payment_request() {
|
||||
return _cst_new_box_autoadd_get_payment_request();
|
||||
}
|
||||
@@ -5744,6 +5791,12 @@ final class wire_cst_prepare_receive_response extends ffi.Struct {
|
||||
|
||||
@ffi.Uint64()
|
||||
external int fees_sat;
|
||||
|
||||
external ffi.Pointer<ffi.Uint64> min_payer_amount_sat;
|
||||
|
||||
external ffi.Pointer<ffi.Uint64> max_payer_amount_sat;
|
||||
|
||||
external ffi.Pointer<ffi.Double> swapper_feerate;
|
||||
}
|
||||
|
||||
final class wire_cst_receive_payment_request extends ffi.Struct {
|
||||
|
||||
@@ -936,16 +936,49 @@ class PrepareReceiveRequest {
|
||||
class PrepareReceiveResponse {
|
||||
final PaymentMethod paymentMethod;
|
||||
final BigInt? payerAmountSat;
|
||||
|
||||
/// Generally represents the total fees that would be paid to send or receive this payment.
|
||||
///
|
||||
/// In case of Zero-Amount Receive Chain swaps, the swapper service fee (`swapper_feerate` times
|
||||
/// the amount) is paid in addition to `fees_sat`. The swapper service feerate is already known
|
||||
/// in the beginning, but the exact swapper service fee will only be known when the
|
||||
/// `payer_amount_sat` is known.
|
||||
///
|
||||
/// In all other types of swaps, the swapper service fee is included in `fees_sat`.
|
||||
final BigInt feesSat;
|
||||
|
||||
/// The minimum amount the payer can send for this swap to succeed.
|
||||
///
|
||||
/// When the method is [PaymentMethod::LiquidAddress], this is empty.
|
||||
final BigInt? minPayerAmountSat;
|
||||
|
||||
/// The maximum amount the payer can send for this swap to succeed.
|
||||
///
|
||||
/// When the method is [PaymentMethod::LiquidAddress], this is empty.
|
||||
final BigInt? maxPayerAmountSat;
|
||||
|
||||
/// The percentage of the sent amount that will count towards the service fee.
|
||||
///
|
||||
/// When the method is [PaymentMethod::LiquidAddress], this is empty.
|
||||
final double? swapperFeerate;
|
||||
|
||||
const PrepareReceiveResponse({
|
||||
required this.paymentMethod,
|
||||
this.payerAmountSat,
|
||||
required this.feesSat,
|
||||
this.minPayerAmountSat,
|
||||
this.maxPayerAmountSat,
|
||||
this.swapperFeerate,
|
||||
});
|
||||
|
||||
@override
|
||||
int get hashCode => paymentMethod.hashCode ^ payerAmountSat.hashCode ^ feesSat.hashCode;
|
||||
int get hashCode =>
|
||||
paymentMethod.hashCode ^
|
||||
payerAmountSat.hashCode ^
|
||||
feesSat.hashCode ^
|
||||
minPayerAmountSat.hashCode ^
|
||||
maxPayerAmountSat.hashCode ^
|
||||
swapperFeerate.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
@@ -954,7 +987,10 @@ class PrepareReceiveResponse {
|
||||
runtimeType == other.runtimeType &&
|
||||
paymentMethod == other.paymentMethod &&
|
||||
payerAmountSat == other.payerAmountSat &&
|
||||
feesSat == other.feesSat;
|
||||
feesSat == other.feesSat &&
|
||||
minPayerAmountSat == other.minPayerAmountSat &&
|
||||
maxPayerAmountSat == other.maxPayerAmountSat &&
|
||||
swapperFeerate == other.swapperFeerate;
|
||||
}
|
||||
|
||||
/// An argument when calling [crate::sdk::LiquidSdk::prepare_refund].
|
||||
|
||||
@@ -939,6 +939,20 @@ class FlutterBreezLiquidBindings {
|
||||
_frbgen_breez_liquid_cst_new_box_autoadd_connect_requestPtr
|
||||
.asFunction<ffi.Pointer<wire_cst_connect_request> Function()>();
|
||||
|
||||
ffi.Pointer<ffi.Double> frbgen_breez_liquid_cst_new_box_autoadd_f_64(
|
||||
double value,
|
||||
) {
|
||||
return _frbgen_breez_liquid_cst_new_box_autoadd_f_64(
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
late final _frbgen_breez_liquid_cst_new_box_autoadd_f_64Ptr =
|
||||
_lookup<ffi.NativeFunction<ffi.Pointer<ffi.Double> Function(ffi.Double)>>(
|
||||
'frbgen_breez_liquid_cst_new_box_autoadd_f_64');
|
||||
late final _frbgen_breez_liquid_cst_new_box_autoadd_f_64 =
|
||||
_frbgen_breez_liquid_cst_new_box_autoadd_f_64Ptr.asFunction<ffi.Pointer<ffi.Double> Function(double)>();
|
||||
|
||||
ffi.Pointer<wire_cst_get_payment_request> frbgen_breez_liquid_cst_new_box_autoadd_get_payment_request() {
|
||||
return _frbgen_breez_liquid_cst_new_box_autoadd_get_payment_request();
|
||||
}
|
||||
@@ -4327,6 +4341,12 @@ final class wire_cst_prepare_receive_response extends ffi.Struct {
|
||||
|
||||
@ffi.Uint64()
|
||||
external int fees_sat;
|
||||
|
||||
external ffi.Pointer<ffi.Uint64> min_payer_amount_sat;
|
||||
|
||||
external ffi.Pointer<ffi.Uint64> max_payer_amount_sat;
|
||||
|
||||
external ffi.Pointer<ffi.Double> swapper_feerate;
|
||||
}
|
||||
|
||||
final class wire_cst_receive_payment_request extends ffi.Struct {
|
||||
|
||||
@@ -1687,6 +1687,8 @@ fun asPrepareReceiveResponse(prepareReceiveResponse: ReadableMap): PrepareReceiv
|
||||
) {
|
||||
return null
|
||||
}
|
||||
val paymentMethod = prepareReceiveResponse.getString("paymentMethod")?.let { asPaymentMethod(it) }!!
|
||||
val feesSat = prepareReceiveResponse.getDouble("feesSat").toULong()
|
||||
val payerAmountSat =
|
||||
if (hasNonNullKey(
|
||||
prepareReceiveResponse,
|
||||
@@ -1697,16 +1699,47 @@ fun asPrepareReceiveResponse(prepareReceiveResponse: ReadableMap): PrepareReceiv
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val paymentMethod = prepareReceiveResponse.getString("paymentMethod")?.let { asPaymentMethod(it) }!!
|
||||
val feesSat = prepareReceiveResponse.getDouble("feesSat").toULong()
|
||||
return PrepareReceiveResponse(payerAmountSat, paymentMethod, feesSat)
|
||||
val minPayerAmountSat =
|
||||
if (hasNonNullKey(
|
||||
prepareReceiveResponse,
|
||||
"minPayerAmountSat",
|
||||
)
|
||||
) {
|
||||
prepareReceiveResponse.getDouble("minPayerAmountSat").toULong()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val maxPayerAmountSat =
|
||||
if (hasNonNullKey(
|
||||
prepareReceiveResponse,
|
||||
"maxPayerAmountSat",
|
||||
)
|
||||
) {
|
||||
prepareReceiveResponse.getDouble("maxPayerAmountSat").toULong()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val swapperFeerate =
|
||||
if (hasNonNullKey(
|
||||
prepareReceiveResponse,
|
||||
"swapperFeerate",
|
||||
)
|
||||
) {
|
||||
prepareReceiveResponse.getDouble("swapperFeerate")
|
||||
} else {
|
||||
null
|
||||
}
|
||||
return PrepareReceiveResponse(paymentMethod, feesSat, payerAmountSat, minPayerAmountSat, maxPayerAmountSat, swapperFeerate)
|
||||
}
|
||||
|
||||
fun readableMapOf(prepareReceiveResponse: PrepareReceiveResponse): ReadableMap =
|
||||
readableMapOf(
|
||||
"payerAmountSat" to prepareReceiveResponse.payerAmountSat,
|
||||
"paymentMethod" to prepareReceiveResponse.paymentMethod.name.lowercase(),
|
||||
"feesSat" to prepareReceiveResponse.feesSat,
|
||||
"payerAmountSat" to prepareReceiveResponse.payerAmountSat,
|
||||
"minPayerAmountSat" to prepareReceiveResponse.minPayerAmountSat,
|
||||
"maxPayerAmountSat" to prepareReceiveResponse.maxPayerAmountSat,
|
||||
"swapperFeerate" to prepareReceiveResponse.swapperFeerate,
|
||||
)
|
||||
|
||||
fun asPrepareReceiveResponseList(arr: ReadableArray): List<PrepareReceiveResponse> {
|
||||
|
||||
@@ -1972,13 +1972,6 @@ enum BreezSDKLiquidMapper {
|
||||
}
|
||||
|
||||
static func asPrepareReceiveResponse(prepareReceiveResponse: [String: Any?]) throws -> PrepareReceiveResponse {
|
||||
var payerAmountSat: UInt64?
|
||||
if hasNonNilKey(data: prepareReceiveResponse, key: "payerAmountSat") {
|
||||
guard let payerAmountSatTmp = prepareReceiveResponse["payerAmountSat"] as? UInt64 else {
|
||||
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "payerAmountSat"))
|
||||
}
|
||||
payerAmountSat = payerAmountSatTmp
|
||||
}
|
||||
guard let paymentMethodTmp = prepareReceiveResponse["paymentMethod"] as? String else {
|
||||
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "paymentMethod", typeName: "PrepareReceiveResponse"))
|
||||
}
|
||||
@@ -1987,15 +1980,46 @@ enum BreezSDKLiquidMapper {
|
||||
guard let feesSat = prepareReceiveResponse["feesSat"] as? UInt64 else {
|
||||
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "feesSat", typeName: "PrepareReceiveResponse"))
|
||||
}
|
||||
var payerAmountSat: UInt64?
|
||||
if hasNonNilKey(data: prepareReceiveResponse, key: "payerAmountSat") {
|
||||
guard let payerAmountSatTmp = prepareReceiveResponse["payerAmountSat"] as? UInt64 else {
|
||||
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "payerAmountSat"))
|
||||
}
|
||||
payerAmountSat = payerAmountSatTmp
|
||||
}
|
||||
var minPayerAmountSat: UInt64?
|
||||
if hasNonNilKey(data: prepareReceiveResponse, key: "minPayerAmountSat") {
|
||||
guard let minPayerAmountSatTmp = prepareReceiveResponse["minPayerAmountSat"] as? UInt64 else {
|
||||
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "minPayerAmountSat"))
|
||||
}
|
||||
minPayerAmountSat = minPayerAmountSatTmp
|
||||
}
|
||||
var maxPayerAmountSat: UInt64?
|
||||
if hasNonNilKey(data: prepareReceiveResponse, key: "maxPayerAmountSat") {
|
||||
guard let maxPayerAmountSatTmp = prepareReceiveResponse["maxPayerAmountSat"] as? UInt64 else {
|
||||
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "maxPayerAmountSat"))
|
||||
}
|
||||
maxPayerAmountSat = maxPayerAmountSatTmp
|
||||
}
|
||||
var swapperFeerate: Double?
|
||||
if hasNonNilKey(data: prepareReceiveResponse, key: "swapperFeerate") {
|
||||
guard let swapperFeerateTmp = prepareReceiveResponse["swapperFeerate"] as? Double else {
|
||||
throw SdkError.Generic(message: errUnexpectedValue(fieldName: "swapperFeerate"))
|
||||
}
|
||||
swapperFeerate = swapperFeerateTmp
|
||||
}
|
||||
|
||||
return PrepareReceiveResponse(payerAmountSat: payerAmountSat, paymentMethod: paymentMethod, feesSat: feesSat)
|
||||
return PrepareReceiveResponse(paymentMethod: paymentMethod, feesSat: feesSat, payerAmountSat: payerAmountSat, minPayerAmountSat: minPayerAmountSat, maxPayerAmountSat: maxPayerAmountSat, swapperFeerate: swapperFeerate)
|
||||
}
|
||||
|
||||
static func dictionaryOf(prepareReceiveResponse: PrepareReceiveResponse) -> [String: Any?] {
|
||||
return [
|
||||
"payerAmountSat": prepareReceiveResponse.payerAmountSat == nil ? nil : prepareReceiveResponse.payerAmountSat,
|
||||
"paymentMethod": valueOf(paymentMethod: prepareReceiveResponse.paymentMethod),
|
||||
"feesSat": prepareReceiveResponse.feesSat,
|
||||
"payerAmountSat": prepareReceiveResponse.payerAmountSat == nil ? nil : prepareReceiveResponse.payerAmountSat,
|
||||
"minPayerAmountSat": prepareReceiveResponse.minPayerAmountSat == nil ? nil : prepareReceiveResponse.minPayerAmountSat,
|
||||
"maxPayerAmountSat": prepareReceiveResponse.maxPayerAmountSat == nil ? nil : prepareReceiveResponse.maxPayerAmountSat,
|
||||
"swapperFeerate": prepareReceiveResponse.swapperFeerate == nil ? nil : prepareReceiveResponse.swapperFeerate,
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -299,9 +299,12 @@ export interface PrepareReceiveRequest {
|
||||
}
|
||||
|
||||
export interface PrepareReceiveResponse {
|
||||
payerAmountSat?: number
|
||||
paymentMethod: PaymentMethod
|
||||
feesSat: number
|
||||
payerAmountSat?: number
|
||||
minPayerAmountSat?: number
|
||||
maxPayerAmountSat?: number
|
||||
swapperFeerate?: number
|
||||
}
|
||||
|
||||
export interface PrepareRefundRequest {
|
||||
|
||||
Reference in New Issue
Block a user