mirror of
https://github.com/aljazceru/breez-sdk-liquid.git
synced 2026-01-18 05:24:25 +01:00
feat: remove swapper initialization at startup (#712)
This commit is contained in:
@@ -402,6 +402,7 @@ impl ChainSwapHandler {
|
||||
let quote = self
|
||||
.swapper
|
||||
.get_zero_amount_chain_swap_quote(&id)
|
||||
.await
|
||||
.map(|quote| quote.to_sat())?;
|
||||
info!("Got quote of {quote} sat for swap {}", &id);
|
||||
|
||||
@@ -420,7 +421,8 @@ impl ChainSwapHandler {
|
||||
.inspect_err(|e| {
|
||||
error!("Failed to accept zero-amount swap {id} quote: {e} - trying to erase the accepted receiver amount...");
|
||||
let _ = self.persister.update_accepted_receiver_amount(&id, None);
|
||||
})?;
|
||||
})
|
||||
.await?;
|
||||
self.persister.set_chain_swap_auto_accepted_fees(&id)
|
||||
}
|
||||
ValidateAmountlessSwapResult::RequiresUserAction {
|
||||
@@ -828,7 +830,8 @@ impl ChainSwapHandler {
|
||||
};
|
||||
let claim_tx = self
|
||||
.swapper
|
||||
.create_claim_tx(Swap::Chain(swap.clone()), claim_address.clone())?;
|
||||
.create_claim_tx(Swap::Chain(swap.clone()), claim_address.clone())
|
||||
.await?;
|
||||
|
||||
// Set the swap claim_tx_id before broadcasting.
|
||||
// If another claim_tx_id has been set in the meantime, don't broadcast the claim tx
|
||||
@@ -841,26 +844,26 @@ impl ChainSwapHandler {
|
||||
let broadcast_res = match claim_tx {
|
||||
// We attempt broadcasting via chain service, then fallback to Boltz
|
||||
SdkTransaction::Liquid(tx) => {
|
||||
self.liquid_chain_service
|
||||
.broadcast(&tx)
|
||||
.await
|
||||
.map(|tx_id| tx_id.to_hex())
|
||||
.or_else(|err| {
|
||||
match self.liquid_chain_service.broadcast(&tx).await {
|
||||
Ok(tx_id) => Ok(tx_id.to_hex()),
|
||||
Err(err) => {
|
||||
debug!(
|
||||
"Could not broadcast claim tx via chain service for Chain swap {swap_id}: {err:?}"
|
||||
);
|
||||
"Could not broadcast claim tx via chain service for Chain swap {swap_id}: {err:?}"
|
||||
);
|
||||
let claim_tx_hex = tx.serialize().to_lower_hex_string();
|
||||
self.swapper.broadcast_tx(self.config.network.into(), &claim_tx_hex)
|
||||
})
|
||||
}
|
||||
SdkTransaction::Bitcoin(tx) => {
|
||||
self.bitcoin_chain_service
|
||||
.broadcast(&tx)
|
||||
.map(|tx_id| tx_id.to_hex())
|
||||
.map_err(|err| PaymentError::Generic {
|
||||
err: err.to_string(),
|
||||
})
|
||||
self.swapper
|
||||
.broadcast_tx(self.config.network.into(), &claim_tx_hex)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
SdkTransaction::Bitcoin(tx) => self
|
||||
.bitcoin_chain_service
|
||||
.broadcast(&tx)
|
||||
.map(|tx_id| tx_id.to_hex())
|
||||
.map_err(|err| PaymentError::Generic {
|
||||
err: err.to_string(),
|
||||
}),
|
||||
};
|
||||
|
||||
match broadcast_res {
|
||||
@@ -937,11 +940,14 @@ impl ChainSwapHandler {
|
||||
);
|
||||
}
|
||||
|
||||
let (refund_tx_size, refund_tx_fees_sat) = self.swapper.estimate_refund_broadcast(
|
||||
Swap::Chain(swap),
|
||||
refund_address,
|
||||
Some(fee_rate_sat_per_vb as f64),
|
||||
)?;
|
||||
let (refund_tx_size, refund_tx_fees_sat) = self
|
||||
.swapper
|
||||
.estimate_refund_broadcast(
|
||||
Swap::Chain(swap),
|
||||
refund_address,
|
||||
Some(fee_rate_sat_per_vb as f64),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok((refund_tx_size, refund_tx_fees_sat, refund_tx_id))
|
||||
}
|
||||
@@ -985,13 +991,16 @@ impl ChainSwapHandler {
|
||||
.get_script_utxos(&script_pk)
|
||||
.await?;
|
||||
|
||||
let SdkTransaction::Bitcoin(refund_tx) = self.swapper.create_refund_tx(
|
||||
Swap::Chain(swap.clone()),
|
||||
refund_address,
|
||||
utxos,
|
||||
Some(broadcast_fee_rate_sat_per_vb as f64),
|
||||
is_cooperative,
|
||||
)?
|
||||
let SdkTransaction::Bitcoin(refund_tx) = self
|
||||
.swapper
|
||||
.create_refund_tx(
|
||||
Swap::Chain(swap.clone()),
|
||||
refund_address,
|
||||
utxos,
|
||||
Some(broadcast_fee_rate_sat_per_vb as f64),
|
||||
is_cooperative,
|
||||
)
|
||||
.await?
|
||||
else {
|
||||
return Err(PaymentError::Generic {
|
||||
err: format!("Unexpected refund tx type returned for incoming Chain swap {id}",),
|
||||
@@ -1054,13 +1063,16 @@ impl ChainSwapHandler {
|
||||
.await?;
|
||||
|
||||
let refund_address = self.onchain_wallet.next_unused_address().await?.to_string();
|
||||
let SdkTransaction::Liquid(refund_tx) = self.swapper.create_refund_tx(
|
||||
Swap::Chain(swap.clone()),
|
||||
&refund_address,
|
||||
utxos,
|
||||
None,
|
||||
is_cooperative,
|
||||
)?
|
||||
let SdkTransaction::Liquid(refund_tx) = self
|
||||
.swapper
|
||||
.create_refund_tx(
|
||||
Swap::Chain(swap.clone()),
|
||||
&refund_address,
|
||||
utxos,
|
||||
None,
|
||||
is_cooperative,
|
||||
)
|
||||
.await?
|
||||
else {
|
||||
return Err(PaymentError::Generic {
|
||||
err: format!(
|
||||
|
||||
@@ -5,7 +5,7 @@ use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use boltz_client::{
|
||||
bitcoin::ScriptBuf,
|
||||
boltz::ChainPair,
|
||||
boltz::{ChainPair, BOLTZ_MAINNET_URL_V2, BOLTZ_TESTNET_URL_V2},
|
||||
network::Chain,
|
||||
swaps::boltz::{
|
||||
CreateChainResponse, CreateReverseResponse, CreateSubmarineResponse, Leaf, Side, SwapTree,
|
||||
@@ -164,6 +164,13 @@ impl Config {
|
||||
|
||||
external_input_parsers
|
||||
}
|
||||
|
||||
pub(crate) fn default_boltz_url(&self) -> &str {
|
||||
match self.network {
|
||||
LiquidNetwork::Mainnet => BOLTZ_MAINNET_URL_V2,
|
||||
LiquidNetwork::Testnet => BOLTZ_TESTNET_URL_V2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Network chosen for this Liquid SDK instance. Note that it represents both the Liquid and the
|
||||
|
||||
@@ -1019,12 +1019,11 @@ mod tests {
|
||||
false,
|
||||
)?;
|
||||
|
||||
assert!(storage
|
||||
assert!(!storage
|
||||
.get_payments(&ListPaymentsRequest {
|
||||
..Default::default()
|
||||
})?
|
||||
.first()
|
||||
.is_some());
|
||||
.is_empty());
|
||||
assert!(storage.get_payment(&payment_tx_data.tx_id)?.is_some());
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -341,7 +341,8 @@ impl ReceiveSwapHandler {
|
||||
let claim_address = self.onchain_wallet.next_unused_address().await?.to_string();
|
||||
let crate::prelude::Transaction::Liquid(claim_tx) = self
|
||||
.swapper
|
||||
.create_claim_tx(Swap::Receive(swap.clone()), Some(claim_address))?
|
||||
.create_claim_tx(Swap::Receive(swap.clone()), Some(claim_address))
|
||||
.await?
|
||||
else {
|
||||
return Err(PaymentError::Generic {
|
||||
err: format!("Constructed invalid transaction for Receive swap {swap_id}"),
|
||||
@@ -354,18 +355,18 @@ impl ReceiveSwapHandler {
|
||||
match self.persister.set_receive_swap_claim_tx_id(swap_id, &tx_id) {
|
||||
Ok(_) => {
|
||||
// We attempt broadcasting via chain service, then fallback to Boltz
|
||||
let broadcast_res = self.liquid_chain_service
|
||||
.broadcast(&claim_tx)
|
||||
.await
|
||||
.map(|tx_id| tx_id.to_hex())
|
||||
.or_else(|err| {
|
||||
let broadcast_res = match self.liquid_chain_service.broadcast(&claim_tx).await {
|
||||
Ok(tx_id) => Ok(tx_id.to_hex()),
|
||||
Err(err) => {
|
||||
debug!(
|
||||
"Could not broadcast claim tx via chain service for Receive swap {swap_id}: {err:?}"
|
||||
);
|
||||
let claim_tx_hex = claim_tx.serialize().to_lower_hex_string();
|
||||
self.swapper.broadcast_tx(self.config.network.into(), &claim_tx_hex)
|
||||
});
|
||||
|
||||
self.swapper
|
||||
.broadcast_tx(self.config.network.into(), &claim_tx_hex)
|
||||
.await
|
||||
}
|
||||
};
|
||||
match broadcast_res {
|
||||
Ok(claim_tx_id) => {
|
||||
// We insert a pseudo-claim-tx in case LWK fails to pick up the new mempool tx for a while
|
||||
|
||||
@@ -49,7 +49,7 @@ impl Recoverer {
|
||||
})
|
||||
}
|
||||
|
||||
fn recover_cooperative_preimages(
|
||||
async fn recover_cooperative_preimages(
|
||||
&self,
|
||||
recovered_send_data: &mut HashMap<String, &mut RecoveredOnchainDataSend>,
|
||||
) -> HashMap<String, Txid> {
|
||||
@@ -59,7 +59,7 @@ impl Recoverer {
|
||||
continue;
|
||||
};
|
||||
|
||||
match self.swapper.get_submarine_preimage(swap_id) {
|
||||
match self.swapper.get_submarine_preimage(swap_id).await {
|
||||
Ok(preimage) => recovered_data.preimage = Some(preimage),
|
||||
Err(err) => {
|
||||
warn!("Could not recover Send swap {swap_id} preimage cooperatively: {err:?}");
|
||||
@@ -118,7 +118,9 @@ impl Recoverer {
|
||||
mut recovered_send_data: HashMap<String, &mut RecoveredOnchainDataSend>,
|
||||
) -> Result<()> {
|
||||
// Recover the preimages by querying the swapper, only if there is a claim_tx_id
|
||||
let failed_cooperative = self.recover_cooperative_preimages(&mut recovered_send_data);
|
||||
let failed_cooperative = self
|
||||
.recover_cooperative_preimages(&mut recovered_send_data)
|
||||
.await;
|
||||
|
||||
// For those which failed, recover the preimages by querying onchain (non-cooperative case)
|
||||
self.recover_non_cooperative_preimages(&mut recovered_send_data, failed_cooperative)
|
||||
|
||||
@@ -26,6 +26,7 @@ use sdk_common::input_parser::InputType;
|
||||
use sdk_common::liquid::LiquidAddressData;
|
||||
use sdk_common::prelude::{FiatAPI, FiatCurrency, LnUrlPayError, LnUrlWithdrawError, Rate};
|
||||
use signer::SdkSigner;
|
||||
use swapper::boltz::proxy::BoltzProxyFetcher;
|
||||
use tokio::sync::{watch, RwLock};
|
||||
use tokio::time::MissedTickBehavior;
|
||||
use tokio_stream::wrappers::BroadcastStream;
|
||||
@@ -123,16 +124,7 @@ impl LiquidSdk {
|
||||
req: ConnectWithSignerRequest,
|
||||
signer: Box<dyn Signer>,
|
||||
) -> Result<Arc<LiquidSdk>> {
|
||||
let maybe_swapper_proxy_url =
|
||||
match BreezServer::new("https://bs1.breez.technology:443".into(), None) {
|
||||
Ok(breez_server) => breez_server
|
||||
.fetch_boltz_swapper_urls()
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|swapper_urls| swapper_urls.first().cloned()),
|
||||
Err(_) => None,
|
||||
};
|
||||
let sdk = LiquidSdk::new(req.config, maybe_swapper_proxy_url, Arc::new(signer))?;
|
||||
let sdk = LiquidSdk::new(req.config, Arc::new(signer))?;
|
||||
sdk.start()
|
||||
.inspect_err(|e| error!("Failed to start an SDK instance: {:?}", e))
|
||||
.await?;
|
||||
@@ -164,11 +156,7 @@ impl LiquidSdk {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn new(
|
||||
config: Config,
|
||||
swapper_proxy_url: Option<String>,
|
||||
signer: Arc<Box<dyn Signer>>,
|
||||
) -> Result<Arc<Self>> {
|
||||
fn new(config: Config, signer: Arc<Box<dyn Signer>>) -> Result<Arc<Self>> {
|
||||
if let Some(breez_api_key) = &config.breez_api_key {
|
||||
Self::validate_breez_api_key(breez_api_key)?
|
||||
}
|
||||
@@ -199,11 +187,8 @@ impl LiquidSdk {
|
||||
let event_manager = Arc::new(EventManager::new());
|
||||
let (shutdown_sender, shutdown_receiver) = watch::channel::<()>(());
|
||||
|
||||
if let Some(swapper_proxy_url) = swapper_proxy_url {
|
||||
persister.set_swapper_proxy_url(swapper_proxy_url)?;
|
||||
}
|
||||
let cached_swapper_proxy_url = persister.get_swapper_proxy_url()?;
|
||||
let swapper = Arc::new(BoltzSwapper::new(config.clone(), cached_swapper_proxy_url));
|
||||
let proxy_url_fetcher = Arc::new(BoltzProxyFetcher::new(persister.clone()));
|
||||
let swapper = Arc::new(BoltzSwapper::new(config.clone(), proxy_url_fetcher));
|
||||
let status_stream = Arc::<dyn SwapperStatusStream>::from(swapper.create_status_stream());
|
||||
|
||||
let recoverer = Arc::new(Recoverer::new(
|
||||
@@ -316,13 +301,12 @@ impl LiquidSdk {
|
||||
));
|
||||
self.status_stream
|
||||
.clone()
|
||||
.start(reconnect_handler, self.shutdown_receiver.clone())
|
||||
.await;
|
||||
.start(reconnect_handler, self.shutdown_receiver.clone());
|
||||
if let Some(sync_service) = self.sync_service.clone() {
|
||||
sync_service.start(self.shutdown_receiver.clone()).await?;
|
||||
sync_service.start(self.shutdown_receiver.clone());
|
||||
}
|
||||
self.track_new_blocks().await;
|
||||
self.track_swap_updates().await;
|
||||
self.track_new_blocks();
|
||||
self.track_swap_updates();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -345,7 +329,7 @@ impl LiquidSdk {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn track_new_blocks(self: &Arc<LiquidSdk>) {
|
||||
fn track_new_blocks(self: &Arc<LiquidSdk>) {
|
||||
let cloned = self.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut current_liquid_block: u32 = 0;
|
||||
@@ -431,7 +415,7 @@ impl LiquidSdk {
|
||||
});
|
||||
}
|
||||
|
||||
async fn track_swap_updates(self: &Arc<LiquidSdk>) {
|
||||
fn track_swap_updates(self: &Arc<LiquidSdk>) {
|
||||
let cloned = self.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut shutdown_receiver = cloned.shutdown_receiver.clone();
|
||||
@@ -756,13 +740,14 @@ impl LiquidSdk {
|
||||
|
||||
/// For submarine swaps (Liquid -> LN), the output amount (invoice amount) is checked if it fits
|
||||
/// the pair limits. This is unlike all the other swap types, where the input amount is checked.
|
||||
fn validate_submarine_pairs(
|
||||
async fn validate_submarine_pairs(
|
||||
&self,
|
||||
receiver_amount_sat: u64,
|
||||
) -> Result<SubmarinePair, PaymentError> {
|
||||
let lbtc_pair = self
|
||||
.swapper
|
||||
.get_submarine_pairs()?
|
||||
.get_submarine_pairs()
|
||||
.await?
|
||||
.ok_or(PaymentError::PairsNotFound)?;
|
||||
|
||||
lbtc_pair.limits.within(receiver_amount_sat)?;
|
||||
@@ -777,9 +762,10 @@ impl LiquidSdk {
|
||||
Ok(lbtc_pair)
|
||||
}
|
||||
|
||||
fn get_chain_pair(&self, direction: Direction) -> Result<ChainPair, PaymentError> {
|
||||
async fn get_chain_pair(&self, direction: Direction) -> Result<ChainPair, PaymentError> {
|
||||
self.swapper
|
||||
.get_chain_pair(direction)?
|
||||
.get_chain_pair(direction)
|
||||
.await?
|
||||
.ok_or(PaymentError::PairsNotFound)
|
||||
}
|
||||
|
||||
@@ -800,12 +786,12 @@ impl LiquidSdk {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_and_validate_chain_pair(
|
||||
async fn get_and_validate_chain_pair(
|
||||
&self,
|
||||
direction: Direction,
|
||||
user_lockup_amount_sat: Option<u64>,
|
||||
) -> Result<ChainPair, PaymentError> {
|
||||
let pair = self.get_chain_pair(direction)?;
|
||||
let pair = self.get_chain_pair(direction).await?;
|
||||
if let Some(user_lockup_amount_sat) = user_lockup_amount_sat {
|
||||
self.validate_user_lockup_amount_for_chain_pair(&pair, user_lockup_amount_sat)?;
|
||||
}
|
||||
@@ -1074,10 +1060,11 @@ impl LiquidSdk {
|
||||
);
|
||||
}
|
||||
|
||||
let lbtc_pair = self.validate_submarine_pairs(invoice_amount_sat)?;
|
||||
let lbtc_pair = self.validate_submarine_pairs(invoice_amount_sat).await?;
|
||||
let mrh_address = self
|
||||
.swapper
|
||||
.check_for_mrh(&invoice.bolt11)?
|
||||
.check_for_mrh(&invoice.bolt11)
|
||||
.await?
|
||||
.map(|(address, _)| address);
|
||||
asset_id = self.config.lbtc_asset_id();
|
||||
(receiver_amount_sat, fees_sat, payment_destination) =
|
||||
@@ -1153,7 +1140,7 @@ impl LiquidSdk {
|
||||
);
|
||||
}
|
||||
|
||||
let lbtc_pair = self.validate_submarine_pairs(receiver_amount_sat)?;
|
||||
let lbtc_pair = self.validate_submarine_pairs(receiver_amount_sat).await?;
|
||||
|
||||
let boltz_fees_total = lbtc_pair.fees.total(receiver_amount_sat);
|
||||
let lockup_fees_sat = self
|
||||
@@ -1265,7 +1252,8 @@ impl LiquidSdk {
|
||||
} => {
|
||||
let bolt12_invoice = self
|
||||
.swapper
|
||||
.get_bolt12_invoice(&offer.offer, *receiver_amount_sat)?;
|
||||
.get_bolt12_invoice(&offer.offer, *receiver_amount_sat)
|
||||
.await?;
|
||||
self.pay_bolt12_invoice(offer, *receiver_amount_sat, &bolt12_invoice, *fees_sat)
|
||||
.await
|
||||
}
|
||||
@@ -1292,7 +1280,7 @@ impl LiquidSdk {
|
||||
Bolt11InvoiceDescription::Hash(_) => None,
|
||||
};
|
||||
|
||||
match self.swapper.check_for_mrh(invoice)? {
|
||||
match self.swapper.check_for_mrh(invoice).await? {
|
||||
// If we find a valid MRH, extract the BIP21 address and pay to it via onchain tx
|
||||
Some((address, _)) => {
|
||||
info!("Found MRH for L-BTC address {address}, invoice amount_sat {amount_sat}");
|
||||
@@ -1458,7 +1446,7 @@ impl LiquidSdk {
|
||||
receiver_amount_sat: u64,
|
||||
fees_sat: u64,
|
||||
) -> Result<SendPaymentResponse, PaymentError> {
|
||||
let lbtc_pair = self.validate_submarine_pairs(receiver_amount_sat)?;
|
||||
let lbtc_pair = self.validate_submarine_pairs(receiver_amount_sat).await?;
|
||||
let boltz_fees_total = lbtc_pair.fees.total(receiver_amount_sat);
|
||||
let user_lockup_amount_sat = receiver_amount_sat + boltz_fees_total;
|
||||
let lockup_tx_fees_sat = self
|
||||
@@ -1512,15 +1500,18 @@ impl LiquidSdk {
|
||||
SubSwapStates::TransactionLockupFailed,
|
||||
]),
|
||||
});
|
||||
let create_response = self.swapper.create_send_swap(CreateSubmarineRequest {
|
||||
from: "L-BTC".to_string(),
|
||||
to: "BTC".to_string(),
|
||||
invoice: invoice.to_string(),
|
||||
refund_public_key,
|
||||
pair_hash: Some(lbtc_pair.hash.clone()),
|
||||
referral_id: None,
|
||||
webhook,
|
||||
})?;
|
||||
let create_response = self
|
||||
.swapper
|
||||
.create_send_swap(CreateSubmarineRequest {
|
||||
from: "L-BTC".to_string(),
|
||||
to: "BTC".to_string(),
|
||||
invoice: invoice.to_string(),
|
||||
refund_public_key,
|
||||
pair_hash: Some(lbtc_pair.hash.clone()),
|
||||
referral_id: None,
|
||||
webhook,
|
||||
})
|
||||
.await?;
|
||||
|
||||
let swap_id = &create_response.id;
|
||||
let create_response_json =
|
||||
@@ -1530,7 +1521,7 @@ impl LiquidSdk {
|
||||
|
||||
let payer_amount_sat = fees_sat + receiver_amount_sat;
|
||||
let swap = SendSwap {
|
||||
id: swap_id.clone(),
|
||||
id: swap_id.to_string(),
|
||||
invoice: invoice.to_string(),
|
||||
bolt12_offer,
|
||||
payment_hash: Some(payment_hash.to_string()),
|
||||
@@ -1575,13 +1566,15 @@ impl LiquidSdk {
|
||||
|
||||
let submarine_pair = self
|
||||
.swapper
|
||||
.get_submarine_pairs()?
|
||||
.get_submarine_pairs()
|
||||
.await?
|
||||
.ok_or(PaymentError::PairsNotFound)?;
|
||||
let send_limits = submarine_pair.limits;
|
||||
|
||||
let reverse_pair = self
|
||||
.swapper
|
||||
.get_reverse_swap_pairs()?
|
||||
.get_reverse_swap_pairs()
|
||||
.await?
|
||||
.ok_or(PaymentError::PairsNotFound)?;
|
||||
let receive_limits = reverse_pair.limits;
|
||||
|
||||
@@ -1603,7 +1596,7 @@ impl LiquidSdk {
|
||||
pub async fn fetch_onchain_limits(&self) -> Result<OnchainPaymentLimitsResponse, PaymentError> {
|
||||
self.ensure_is_started().await?;
|
||||
|
||||
let (pair_outgoing, pair_incoming) = self.swapper.get_chain_pairs()?;
|
||||
let (pair_outgoing, pair_incoming) = self.swapper.get_chain_pairs().await?;
|
||||
let send_limits = pair_outgoing
|
||||
.ok_or(PaymentError::PairsNotFound)
|
||||
.map(|pair| pair.limits)?;
|
||||
@@ -1640,7 +1633,7 @@ impl LiquidSdk {
|
||||
self.ensure_is_started().await?;
|
||||
|
||||
let get_info_res = self.get_info().await?;
|
||||
let pair = self.get_chain_pair(Direction::Outgoing)?;
|
||||
let pair = self.get_chain_pair(Direction::Outgoing).await?;
|
||||
let claim_fees_sat = match req.fee_rate_sat_per_vbyte {
|
||||
Some(sat_per_vbyte) => ESTIMATED_BTC_CLAIM_TX_VSIZE * sat_per_vbyte as u64,
|
||||
None => pair.clone().fees.claim_estimate(),
|
||||
@@ -1744,7 +1737,7 @@ impl LiquidSdk {
|
||||
let claim_address = self.validate_bitcoin_address(&req.address).await?;
|
||||
let balance_sat = self.get_info().await?.wallet_info.balance_sat;
|
||||
let receiver_amount_sat = req.prepare_response.receiver_amount_sat;
|
||||
let pair = self.get_chain_pair(Direction::Outgoing)?;
|
||||
let pair = self.get_chain_pair(Direction::Outgoing).await?;
|
||||
let claim_fees_sat = req.prepare_response.claim_fees_sat;
|
||||
let server_fees_sat = pair.fees.server();
|
||||
let server_lockup_amount_sat = receiver_amount_sat + claim_fees_sat;
|
||||
@@ -1800,18 +1793,21 @@ impl LiquidSdk {
|
||||
ChainSwapStates::TransactionServerConfirmed,
|
||||
]),
|
||||
});
|
||||
let create_response = self.swapper.create_chain_swap(CreateChainRequest {
|
||||
from: "L-BTC".to_string(),
|
||||
to: "BTC".to_string(),
|
||||
preimage_hash: preimage.sha256,
|
||||
claim_public_key: Some(claim_public_key),
|
||||
refund_public_key: Some(refund_public_key),
|
||||
user_lock_amount: None,
|
||||
server_lock_amount: Some(server_lockup_amount_sat),
|
||||
pair_hash: Some(pair.hash.clone()),
|
||||
referral_id: None,
|
||||
webhook,
|
||||
})?;
|
||||
let create_response = self
|
||||
.swapper
|
||||
.create_chain_swap(CreateChainRequest {
|
||||
from: "L-BTC".to_string(),
|
||||
to: "BTC".to_string(),
|
||||
preimage_hash: preimage.sha256,
|
||||
claim_public_key: Some(claim_public_key),
|
||||
refund_public_key: Some(refund_public_key),
|
||||
user_lock_amount: None,
|
||||
server_lock_amount: Some(server_lockup_amount_sat),
|
||||
pair_hash: Some(pair.hash.clone()),
|
||||
referral_id: None,
|
||||
webhook,
|
||||
})
|
||||
.await?;
|
||||
|
||||
let create_response_json =
|
||||
ChainSwap::from_boltz_struct_to_json(&create_response, &create_response.id)?;
|
||||
@@ -1953,7 +1949,8 @@ impl LiquidSdk {
|
||||
};
|
||||
let reverse_pair = self
|
||||
.swapper
|
||||
.get_reverse_swap_pairs()?
|
||||
.get_reverse_swap_pairs()
|
||||
.await?
|
||||
.ok_or(PaymentError::PairsNotFound)?;
|
||||
|
||||
fees_sat = reverse_pair.fees.total(payer_amount_sat);
|
||||
@@ -1983,8 +1980,9 @@ impl LiquidSdk {
|
||||
Some(ReceiveAmount::Bitcoin { payer_amount_sat }) => Some(payer_amount_sat),
|
||||
None => None,
|
||||
};
|
||||
let pair =
|
||||
self.get_and_validate_chain_pair(Direction::Incoming, payer_amount_sat)?;
|
||||
let pair = self
|
||||
.get_and_validate_chain_pair(Direction::Incoming, payer_amount_sat)
|
||||
.await?;
|
||||
let claim_fees_sat = pair.fees.claim_estimate();
|
||||
let server_fees_sat = pair.fees.server();
|
||||
let service_fees_sat = payer_amount_sat
|
||||
@@ -2145,7 +2143,8 @@ impl LiquidSdk {
|
||||
) -> Result<ReceivePaymentResponse, PaymentError> {
|
||||
let reverse_pair = self
|
||||
.swapper
|
||||
.get_reverse_swap_pairs()?
|
||||
.get_reverse_swap_pairs()
|
||||
.await?
|
||||
.ok_or(PaymentError::PairsNotFound)?;
|
||||
let new_fees_sat = reverse_pair.fees.total(payer_amount_sat);
|
||||
ensure_sdk!(fees_sat == new_fees_sat, PaymentError::InvalidOrExpiredFees);
|
||||
@@ -2192,7 +2191,7 @@ impl LiquidSdk {
|
||||
referral_id: None,
|
||||
webhook,
|
||||
};
|
||||
let create_response = self.swapper.create_receive_swap(v2_req)?;
|
||||
let create_response = self.swapper.create_receive_swap(v2_req).await?;
|
||||
|
||||
// Reserve this address until the timeout block height
|
||||
self.persister.insert_or_update_reserved_address(
|
||||
@@ -2203,7 +2202,8 @@ impl LiquidSdk {
|
||||
// Check if correct MRH was added to the invoice by Boltz
|
||||
let (bip21_lbtc_address, _bip21_amount_btc) = self
|
||||
.swapper
|
||||
.check_for_mrh(&create_response.invoice)?
|
||||
.check_for_mrh(&create_response.invoice)
|
||||
.await?
|
||||
.ok_or(PaymentError::receive_error("Invoice has no MRH"))?;
|
||||
ensure_sdk!(
|
||||
bip21_lbtc_address == mrh_addr_str,
|
||||
@@ -2277,7 +2277,9 @@ impl LiquidSdk {
|
||||
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 pair = self
|
||||
.get_and_validate_chain_pair(Direction::Incoming, user_lockup_amount_sat)
|
||||
.await?;
|
||||
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
|
||||
@@ -2312,18 +2314,21 @@ impl LiquidSdk {
|
||||
ChainSwapStates::TransactionServerConfirmed,
|
||||
]),
|
||||
});
|
||||
let create_response = self.swapper.create_chain_swap(CreateChainRequest {
|
||||
from: "BTC".to_string(),
|
||||
to: "L-BTC".to_string(),
|
||||
preimage_hash: preimage.sha256,
|
||||
claim_public_key: Some(claim_public_key),
|
||||
refund_public_key: Some(refund_public_key),
|
||||
user_lock_amount: user_lockup_amount_sat,
|
||||
server_lock_amount: None,
|
||||
pair_hash: Some(pair.hash.clone()),
|
||||
referral_id: None,
|
||||
webhook,
|
||||
})?;
|
||||
let create_response = self
|
||||
.swapper
|
||||
.create_chain_swap(CreateChainRequest {
|
||||
from: "BTC".to_string(),
|
||||
to: "L-BTC".to_string(),
|
||||
preimage_hash: preimage.sha256,
|
||||
claim_public_key: Some(claim_public_key),
|
||||
refund_public_key: Some(refund_public_key),
|
||||
user_lock_amount: user_lockup_amount_sat,
|
||||
server_lock_amount: None,
|
||||
pair_hash: Some(pair.hash.clone()),
|
||||
referral_id: None,
|
||||
webhook,
|
||||
})
|
||||
.await?;
|
||||
|
||||
let swap_id = create_response.id.clone();
|
||||
let create_response_json =
|
||||
@@ -2909,7 +2914,8 @@ impl LiquidSdk {
|
||||
|
||||
let server_lockup_quote = self
|
||||
.swapper
|
||||
.get_zero_amount_chain_swap_quote(&req.swap_id)?;
|
||||
.get_zero_amount_chain_swap_quote(&req.swap_id)
|
||||
.await?;
|
||||
|
||||
let actual_payer_amount_sat =
|
||||
chain_swap
|
||||
@@ -2957,7 +2963,10 @@ impl LiquidSdk {
|
||||
}
|
||||
);
|
||||
|
||||
let server_lockup_quote = self.swapper.get_zero_amount_chain_swap_quote(&swap_id)?;
|
||||
let server_lockup_quote = self
|
||||
.swapper
|
||||
.get_zero_amount_chain_swap_quote(&swap_id)
|
||||
.await?;
|
||||
|
||||
ensure_sdk!(
|
||||
fees_sat == payer_amount_sat - server_lockup_quote.to_sat() + chain_swap.claim_fees_sat,
|
||||
@@ -2973,7 +2982,7 @@ impl LiquidSdk {
|
||||
let _ = self
|
||||
.persister
|
||||
.update_accepted_receiver_amount(&swap_id, None);
|
||||
})?;
|
||||
}).await?;
|
||||
self.chain_swap_handler.update_swap_info(&ChainSwapUpdate {
|
||||
swap_id,
|
||||
to_state: Pending,
|
||||
@@ -3096,7 +3105,8 @@ impl LiquidSdk {
|
||||
);
|
||||
let lbtc_pair = self
|
||||
.swapper
|
||||
.get_submarine_pairs()?
|
||||
.get_submarine_pairs()
|
||||
.await?
|
||||
.ok_or(PaymentError::PairsNotFound)?;
|
||||
let drain_fees_sat = self.estimate_drain_tx_fee(None, None).await?;
|
||||
let drain_amount_sat = get_info_res.wallet_info.balance_sat - drain_fees_sat;
|
||||
@@ -3634,7 +3644,7 @@ mod tests {
|
||||
None,
|
||||
)?);
|
||||
|
||||
LiquidSdk::track_swap_updates(&sdk).await;
|
||||
LiquidSdk::track_swap_updates(&sdk);
|
||||
|
||||
// We spawn a new thread since updates can only be sent when called via async runtimes
|
||||
tokio::spawn(async move {
|
||||
@@ -3745,7 +3755,7 @@ mod tests {
|
||||
status_stream.clone(),
|
||||
)?);
|
||||
|
||||
LiquidSdk::track_swap_updates(&sdk).await;
|
||||
LiquidSdk::track_swap_updates(&sdk);
|
||||
|
||||
// We spawn a new thread since updates can only be sent when called via async runtimes
|
||||
tokio::spawn(async move {
|
||||
@@ -3806,7 +3816,7 @@ mod tests {
|
||||
None,
|
||||
)?);
|
||||
|
||||
LiquidSdk::track_swap_updates(&sdk).await;
|
||||
LiquidSdk::track_swap_updates(&sdk);
|
||||
|
||||
// We spawn a new thread since updates can only be sent when called via async runtimes
|
||||
tokio::spawn(async move {
|
||||
@@ -4039,7 +4049,7 @@ mod tests {
|
||||
None,
|
||||
)?);
|
||||
|
||||
LiquidSdk::track_swap_updates(&sdk).await;
|
||||
LiquidSdk::track_swap_updates(&sdk);
|
||||
|
||||
// We spawn a new thread since updates can only be sent when called via async runtimes
|
||||
tokio::spawn(async move {
|
||||
@@ -4100,7 +4110,7 @@ mod tests {
|
||||
Some(onchain_fee_rate_leeway_sat_per_vbyte),
|
||||
)?);
|
||||
|
||||
LiquidSdk::track_swap_updates(&sdk).await;
|
||||
LiquidSdk::track_swap_updates(&sdk);
|
||||
|
||||
let max_fee_increase_for_auto_accept_sat =
|
||||
onchain_fee_rate_leeway_sat_per_vbyte as u64 * ESTIMATED_BTC_LOCKUP_TX_VSIZE;
|
||||
|
||||
@@ -355,7 +355,7 @@ impl SendSwapHandler {
|
||||
&send_swap.id
|
||||
);
|
||||
let output_address = self.onchain_wallet.next_unused_address().await?.to_string();
|
||||
let claim_tx_details = self.swapper.get_send_claim_tx_details(send_swap)?;
|
||||
let claim_tx_details = self.swapper.get_send_claim_tx_details(send_swap).await?;
|
||||
self.update_swap_info(
|
||||
&send_swap.id,
|
||||
Complete,
|
||||
@@ -364,7 +364,8 @@ impl SendSwapHandler {
|
||||
None,
|
||||
)?;
|
||||
self.swapper
|
||||
.claim_send_swap_cooperative(send_swap, claim_tx_details, &output_address)?;
|
||||
.claim_send_swap_cooperative(send_swap, claim_tx_details, &output_address)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -447,13 +448,16 @@ impl SendSwapHandler {
|
||||
.to_unconfidential()
|
||||
.script_pubkey();
|
||||
let utxos = self.chain_service.get_script_utxos(&script_pk).await?;
|
||||
let SdkTransaction::Liquid(refund_tx) = self.swapper.create_refund_tx(
|
||||
Swap::Send(swap.clone()),
|
||||
&refund_address,
|
||||
utxos,
|
||||
None,
|
||||
is_cooperative,
|
||||
)?
|
||||
let SdkTransaction::Liquid(refund_tx) = self
|
||||
.swapper
|
||||
.create_refund_tx(
|
||||
Swap::Send(swap.clone()),
|
||||
&refund_address,
|
||||
utxos,
|
||||
None,
|
||||
is_cooperative,
|
||||
)
|
||||
.await?
|
||||
else {
|
||||
return Err(PaymentError::Generic {
|
||||
err: format!(
|
||||
|
||||
@@ -14,10 +14,10 @@ use crate::{
|
||||
prelude::{ChainSwap, Direction, Swap, Utxo},
|
||||
};
|
||||
|
||||
use super::BoltzSwapper;
|
||||
use super::{BoltzSwapper, ProxyUrlFetcher};
|
||||
|
||||
impl BoltzSwapper {
|
||||
pub(crate) fn new_btc_refund_wrapper(
|
||||
impl<P: ProxyUrlFetcher> BoltzSwapper<P> {
|
||||
pub(crate) async fn new_btc_refund_wrapper(
|
||||
&self,
|
||||
swap: &Swap,
|
||||
refund_address: &str,
|
||||
@@ -30,7 +30,7 @@ impl BoltzSwapper {
|
||||
swap_script.as_bitcoin_script()?,
|
||||
refund_address,
|
||||
&self.bitcoin_electrum_config,
|
||||
self.boltz_url.clone(),
|
||||
self.get_url().await?,
|
||||
swap.id.clone(),
|
||||
)
|
||||
}
|
||||
@@ -51,7 +51,7 @@ impl BoltzSwapper {
|
||||
Ok(refund_wrapper)
|
||||
}
|
||||
|
||||
pub(crate) fn new_btc_refund_tx(
|
||||
pub(crate) async fn new_btc_refund_tx(
|
||||
&self,
|
||||
swap: &ChainSwap,
|
||||
refund_address: &str,
|
||||
@@ -91,7 +91,10 @@ impl BoltzSwapper {
|
||||
let broadcast_fees_sat = (refund_tx_size as f64 * broadcast_fee_rate_sat_per_vb) as u64;
|
||||
|
||||
let cooperative = match is_cooperative {
|
||||
true => self.get_cooperative_details(swap.id.clone(), None, None),
|
||||
true => {
|
||||
self.get_cooperative_details(swap.id.clone(), None, None)
|
||||
.await?
|
||||
}
|
||||
false => None,
|
||||
};
|
||||
|
||||
@@ -103,7 +106,7 @@ impl BoltzSwapper {
|
||||
Ok(signed_tx)
|
||||
}
|
||||
|
||||
pub(crate) fn new_outgoing_chain_claim_tx(
|
||||
pub(crate) async fn new_outgoing_chain_claim_tx(
|
||||
&self,
|
||||
swap: &ChainSwap,
|
||||
claim_address: String,
|
||||
@@ -114,17 +117,18 @@ impl BoltzSwapper {
|
||||
claim_swap_script,
|
||||
claim_address,
|
||||
&self.bitcoin_electrum_config,
|
||||
self.boltz_url.clone(),
|
||||
self.get_url().await?,
|
||||
swap.id.clone(),
|
||||
)?;
|
||||
|
||||
let (partial_sig, pub_nonce) = self.get_claim_partial_sig(swap)?;
|
||||
let (partial_sig, pub_nonce) = self.get_claim_partial_sig(swap).await?;
|
||||
|
||||
let signed_tx = claim_tx_wrapper.sign_claim(
|
||||
&claim_keypair,
|
||||
&Preimage::from_str(&swap.preimage)?,
|
||||
Fee::Absolute(swap.claim_fees_sat),
|
||||
self.get_cooperative_details(swap.id.clone(), Some(pub_nonce), Some(partial_sig)),
|
||||
self.get_cooperative_details(swap.id.clone(), Some(pub_nonce), Some(partial_sig))
|
||||
.await?,
|
||||
)?;
|
||||
|
||||
Ok(signed_tx)
|
||||
|
||||
@@ -16,9 +16,9 @@ use crate::{
|
||||
utils,
|
||||
};
|
||||
|
||||
use super::BoltzSwapper;
|
||||
use super::{BoltzSwapper, ProxyUrlFetcher};
|
||||
|
||||
impl BoltzSwapper {
|
||||
impl<P: ProxyUrlFetcher> BoltzSwapper<P> {
|
||||
pub(crate) fn validate_send_swap_preimage(
|
||||
&self,
|
||||
swap_id: &str,
|
||||
@@ -30,7 +30,7 @@ impl BoltzSwapper {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn new_receive_claim_tx(
|
||||
pub(crate) async fn new_receive_claim_tx(
|
||||
&self,
|
||||
swap: &ReceiveSwap,
|
||||
claim_address: String,
|
||||
@@ -41,7 +41,7 @@ impl BoltzSwapper {
|
||||
swap_script,
|
||||
claim_address,
|
||||
&self.liquid_electrum_config,
|
||||
self.boltz_url.clone(),
|
||||
self.get_url().await?,
|
||||
swap.id.clone(),
|
||||
)?;
|
||||
|
||||
@@ -49,14 +49,15 @@ impl BoltzSwapper {
|
||||
&swap.get_claim_keypair()?,
|
||||
&Preimage::from_str(&swap.preimage)?,
|
||||
Fee::Absolute(swap.claim_fees_sat),
|
||||
self.get_cooperative_details(swap.id.clone(), None, None),
|
||||
self.get_cooperative_details(swap.id.clone(), None, None)
|
||||
.await?,
|
||||
true,
|
||||
)?;
|
||||
|
||||
Ok(signed_tx)
|
||||
}
|
||||
|
||||
pub(crate) fn new_incoming_chain_claim_tx(
|
||||
pub(crate) async fn new_incoming_chain_claim_tx(
|
||||
&self,
|
||||
swap: &ChainSwap,
|
||||
claim_address: String,
|
||||
@@ -67,17 +68,18 @@ impl BoltzSwapper {
|
||||
swap_script,
|
||||
claim_address,
|
||||
&self.liquid_electrum_config,
|
||||
self.boltz_url.clone(),
|
||||
self.get_url().await?,
|
||||
swap.id.clone(),
|
||||
)?;
|
||||
|
||||
let (partial_sig, pub_nonce) = self.get_claim_partial_sig(swap)?;
|
||||
let (partial_sig, pub_nonce) = self.get_claim_partial_sig(swap).await?;
|
||||
|
||||
let signed_tx = claim_tx_wrapper.sign_claim(
|
||||
&claim_keypair,
|
||||
&Preimage::from_str(&swap.preimage)?,
|
||||
Fee::Absolute(swap.claim_fees_sat),
|
||||
self.get_cooperative_details(swap.id.clone(), Some(pub_nonce), Some(partial_sig)),
|
||||
self.get_cooperative_details(swap.id.clone(), Some(pub_nonce), Some(partial_sig))
|
||||
.await?,
|
||||
true,
|
||||
)?;
|
||||
|
||||
@@ -88,7 +90,7 @@ impl BoltzSwapper {
|
||||
(refund_tx_size as f64 * LIQUID_FEE_RATE_SAT_PER_VBYTE).ceil() as u64
|
||||
}
|
||||
|
||||
pub(crate) fn new_lbtc_refund_wrapper(
|
||||
pub(crate) async fn new_lbtc_refund_wrapper(
|
||||
&self,
|
||||
swap: &Swap,
|
||||
refund_address: &str,
|
||||
@@ -107,7 +109,7 @@ impl BoltzSwapper {
|
||||
swap_script.as_liquid_script()?,
|
||||
refund_address,
|
||||
&self.liquid_electrum_config,
|
||||
self.boltz_url.clone(),
|
||||
self.get_url().await?,
|
||||
swap.id.clone(),
|
||||
)
|
||||
}
|
||||
@@ -118,7 +120,7 @@ impl BoltzSwapper {
|
||||
swap_script,
|
||||
refund_address,
|
||||
&self.liquid_electrum_config,
|
||||
self.boltz_url.clone(),
|
||||
self.get_url().await?,
|
||||
swap.id.clone(),
|
||||
)
|
||||
}
|
||||
@@ -132,7 +134,7 @@ impl BoltzSwapper {
|
||||
Ok(refund_wrapper)
|
||||
}
|
||||
|
||||
pub(crate) fn new_lbtc_refund_tx(
|
||||
pub(crate) async fn new_lbtc_refund_tx(
|
||||
&self,
|
||||
swap: &Swap,
|
||||
refund_address: &str,
|
||||
@@ -189,7 +191,10 @@ impl BoltzSwapper {
|
||||
let broadcast_fees_sat = self.calculate_refund_fees(refund_tx_size);
|
||||
|
||||
let cooperative = match is_cooperative {
|
||||
true => self.get_cooperative_details(swap_id.clone(), None, None),
|
||||
true => {
|
||||
self.get_cooperative_details(swap_id.clone(), None, None)
|
||||
.await?
|
||||
}
|
||||
false => None,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
use std::str::FromStr;
|
||||
use std::{
|
||||
str::FromStr,
|
||||
sync::{Arc, OnceLock},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::{PaymentError, SdkError},
|
||||
model::LIQUID_FEE_RATE_SAT_PER_VBYTE,
|
||||
prelude::{ChainSwap, Config, Direction, LiquidNetwork, SendSwap, Swap, Transaction, Utxo},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use boltz_client::{
|
||||
boltz::{
|
||||
BoltzApiClientV2, ChainPair, Cooperative, CreateChainRequest, CreateChainResponse,
|
||||
CreateReverseRequest, CreateReverseResponse, CreateSubmarineRequest,
|
||||
CreateSubmarineResponse, ReversePair, SubmarineClaimTxResponse, SubmarinePair,
|
||||
BOLTZ_MAINNET_URL_V2, BOLTZ_TESTNET_URL_V2,
|
||||
},
|
||||
elements::secp256k1_zkp::{MusigPartialSignature, MusigPubNonce},
|
||||
network::{electrum::ElectrumConfig, Chain},
|
||||
@@ -13,63 +22,35 @@ use boltz_client::{
|
||||
Amount,
|
||||
};
|
||||
use log::info;
|
||||
use url::Url;
|
||||
|
||||
use crate::{
|
||||
error::{PaymentError, SdkError},
|
||||
model::LIQUID_FEE_RATE_SAT_PER_VBYTE,
|
||||
prelude::{ChainSwap, Config, Direction, LiquidNetwork, SendSwap, Swap, Transaction, Utxo},
|
||||
};
|
||||
use proxy::split_proxy_url;
|
||||
|
||||
use self::status_stream::BoltzStatusStream;
|
||||
use super::{Swapper, SwapperStatusStream};
|
||||
use super::{ProxyUrlFetcher, Swapper, SwapperStatusStream};
|
||||
|
||||
pub(crate) mod bitcoin;
|
||||
pub(crate) mod liquid;
|
||||
pub(crate) mod proxy;
|
||||
pub mod status_stream;
|
||||
|
||||
pub struct BoltzSwapper {
|
||||
client: BoltzApiClientV2,
|
||||
boltz_url: String,
|
||||
pub(crate) struct BoltzClient {
|
||||
url: String,
|
||||
referral_id: Option<String>,
|
||||
config: Config,
|
||||
liquid_electrum_config: ElectrumConfig,
|
||||
bitcoin_electrum_config: ElectrumConfig,
|
||||
inner: BoltzApiClientV2,
|
||||
}
|
||||
|
||||
impl BoltzSwapper {
|
||||
pub fn new(config: Config, swapper_proxy_url: Option<String>) -> Self {
|
||||
let (boltz_api_base_url, referral_id) = match &config.network {
|
||||
LiquidNetwork::Testnet => (None, None),
|
||||
LiquidNetwork::Mainnet => match &swapper_proxy_url {
|
||||
Some(swapper_proxy_url) => Url::parse(swapper_proxy_url)
|
||||
.map(|url| match url.query() {
|
||||
None => (None, None),
|
||||
Some(query) => {
|
||||
let api_base_url =
|
||||
url.domain().map(|domain| format!("https://{domain}/v2"));
|
||||
let parts: Vec<String> = query.split('=').map(Into::into).collect();
|
||||
let referral_id = parts.get(1).cloned();
|
||||
(api_base_url, referral_id)
|
||||
}
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
None => (None, None),
|
||||
},
|
||||
};
|
||||
|
||||
let boltz_url = boltz_api_base_url.unwrap_or(
|
||||
match config.network {
|
||||
LiquidNetwork::Mainnet => BOLTZ_MAINNET_URL_V2,
|
||||
LiquidNetwork::Testnet => BOLTZ_TESTNET_URL_V2,
|
||||
}
|
||||
.to_string(),
|
||||
);
|
||||
pub struct BoltzSwapper<P: ProxyUrlFetcher> {
|
||||
config: Config,
|
||||
client: OnceLock<BoltzClient>,
|
||||
liquid_electrum_config: ElectrumConfig,
|
||||
bitcoin_electrum_config: ElectrumConfig,
|
||||
proxy_url: Arc<P>,
|
||||
}
|
||||
|
||||
impl<P: ProxyUrlFetcher> BoltzSwapper<P> {
|
||||
pub fn new(config: Config, proxy_url: Arc<P>) -> Self {
|
||||
Self {
|
||||
client: BoltzApiClientV2::new(&boltz_url),
|
||||
boltz_url,
|
||||
referral_id,
|
||||
proxy_url,
|
||||
client: OnceLock::new(),
|
||||
config: config.clone(),
|
||||
liquid_electrum_config: ElectrumConfig::new(
|
||||
config.network.into(),
|
||||
@@ -88,7 +69,34 @@ impl BoltzSwapper {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_claim_partial_sig(
|
||||
async fn get_client(&self) -> Result<&BoltzClient> {
|
||||
if let Some(client) = self.client.get() {
|
||||
return Ok(client);
|
||||
}
|
||||
|
||||
let (boltz_api_base_url, referral_id) = match &self.config.network {
|
||||
LiquidNetwork::Testnet => (None, None),
|
||||
LiquidNetwork::Mainnet => match self.proxy_url.fetch().await {
|
||||
Ok(Some(swapper_proxy_url)) => split_proxy_url(swapper_proxy_url),
|
||||
_ => (None, None),
|
||||
},
|
||||
};
|
||||
|
||||
let boltz_url = boltz_api_base_url.unwrap_or(self.config.default_boltz_url().to_string());
|
||||
|
||||
let client = self.client.get_or_init(|| BoltzClient {
|
||||
inner: BoltzApiClientV2::new(&boltz_url),
|
||||
url: boltz_url,
|
||||
referral_id,
|
||||
});
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
async fn get_url(&self) -> Result<String> {
|
||||
Ok(self.get_client().await?.url.clone())
|
||||
}
|
||||
|
||||
async fn get_claim_partial_sig(
|
||||
&self,
|
||||
swap: &ChainSwap,
|
||||
) -> Result<(MusigPartialSignature, MusigPubNonce), PaymentError> {
|
||||
@@ -98,11 +106,16 @@ impl BoltzSwapper {
|
||||
// We need it to calculate the musig partial sig for the claim tx from the other chain
|
||||
let lockup_address = &swap.lockup_address;
|
||||
|
||||
let claim_tx_details = self.client.get_chain_claim_tx_details(&swap.id)?;
|
||||
let claim_tx_details = self
|
||||
.get_client()
|
||||
.await?
|
||||
.inner
|
||||
.get_chain_claim_tx_details(&swap.id)?;
|
||||
match swap.direction {
|
||||
Direction::Incoming => {
|
||||
let refund_tx_wrapper =
|
||||
self.new_btc_refund_wrapper(&Swap::Chain(swap.clone()), lockup_address)?;
|
||||
let refund_tx_wrapper = self
|
||||
.new_btc_refund_wrapper(&Swap::Chain(swap.clone()), lockup_address)
|
||||
.await?;
|
||||
|
||||
refund_tx_wrapper.partial_sign(
|
||||
&refund_keypair,
|
||||
@@ -111,8 +124,9 @@ impl BoltzSwapper {
|
||||
)
|
||||
}
|
||||
Direction::Outgoing => {
|
||||
let refund_tx_wrapper =
|
||||
self.new_lbtc_refund_wrapper(&Swap::Chain(swap.clone()), lockup_address)?;
|
||||
let refund_tx_wrapper = self
|
||||
.new_lbtc_refund_wrapper(&Swap::Chain(swap.clone()), lockup_address)
|
||||
.await?;
|
||||
|
||||
refund_tx_wrapper.partial_sign(
|
||||
&refund_keypair,
|
||||
@@ -124,48 +138,54 @@ impl BoltzSwapper {
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn get_cooperative_details(
|
||||
async fn get_cooperative_details(
|
||||
&self,
|
||||
swap_id: String,
|
||||
pub_nonce: Option<MusigPubNonce>,
|
||||
partial_sig: Option<MusigPartialSignature>,
|
||||
) -> Option<Cooperative> {
|
||||
Some(Cooperative {
|
||||
boltz_api: &self.client,
|
||||
) -> Result<Option<Cooperative>> {
|
||||
Ok(Some(Cooperative {
|
||||
boltz_api: &self.get_client().await?.inner,
|
||||
swap_id,
|
||||
pub_nonce,
|
||||
partial_sig,
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Swapper for BoltzSwapper {
|
||||
#[async_trait]
|
||||
impl<P: ProxyUrlFetcher> Swapper for BoltzSwapper<P> {
|
||||
/// Create a new chain swap
|
||||
fn create_chain_swap(
|
||||
async fn create_chain_swap(
|
||||
&self,
|
||||
req: CreateChainRequest,
|
||||
) -> Result<CreateChainResponse, PaymentError> {
|
||||
let client = self.get_client().await?;
|
||||
let modified_req = CreateChainRequest {
|
||||
referral_id: self.referral_id.clone(),
|
||||
referral_id: client.referral_id.clone(),
|
||||
..req.clone()
|
||||
};
|
||||
Ok(self.client.post_chain_req(modified_req)?)
|
||||
Ok(client.inner.post_chain_req(modified_req)?)
|
||||
}
|
||||
|
||||
/// Create a new send swap
|
||||
fn create_send_swap(
|
||||
async fn create_send_swap(
|
||||
&self,
|
||||
req: CreateSubmarineRequest,
|
||||
) -> Result<CreateSubmarineResponse, PaymentError> {
|
||||
let client = self.get_client().await?;
|
||||
let modified_req = CreateSubmarineRequest {
|
||||
referral_id: self.referral_id.clone(),
|
||||
referral_id: client.referral_id.clone(),
|
||||
..req.clone()
|
||||
};
|
||||
Ok(self.client.post_swap_req(&modified_req)?)
|
||||
Ok(client.inner.post_swap_req(&modified_req)?)
|
||||
}
|
||||
|
||||
fn get_chain_pair(&self, direction: Direction) -> Result<Option<ChainPair>, PaymentError> {
|
||||
let pairs = self.client.get_chain_pairs()?;
|
||||
async fn get_chain_pair(
|
||||
&self,
|
||||
direction: Direction,
|
||||
) -> Result<Option<ChainPair>, PaymentError> {
|
||||
let pairs = self.get_client().await?.inner.get_chain_pairs()?;
|
||||
let pair = match direction {
|
||||
Direction::Incoming => pairs.get_btc_to_lbtc_pair(),
|
||||
Direction::Outgoing => pairs.get_lbtc_to_btc_pair(),
|
||||
@@ -173,48 +193,68 @@ impl Swapper for BoltzSwapper {
|
||||
Ok(pair)
|
||||
}
|
||||
|
||||
fn get_chain_pairs(&self) -> Result<(Option<ChainPair>, Option<ChainPair>), PaymentError> {
|
||||
let pairs = self.client.get_chain_pairs()?;
|
||||
async fn get_chain_pairs(
|
||||
&self,
|
||||
) -> Result<(Option<ChainPair>, Option<ChainPair>), PaymentError> {
|
||||
let pairs = self.get_client().await?.inner.get_chain_pairs()?;
|
||||
let pair_outgoing = pairs.get_lbtc_to_btc_pair();
|
||||
let pair_incoming = pairs.get_btc_to_lbtc_pair();
|
||||
Ok((pair_outgoing, pair_incoming))
|
||||
}
|
||||
|
||||
fn get_zero_amount_chain_swap_quote(&self, swap_id: &str) -> Result<Amount, SdkError> {
|
||||
self.client
|
||||
async fn get_zero_amount_chain_swap_quote(&self, swap_id: &str) -> Result<Amount, SdkError> {
|
||||
self.get_client()
|
||||
.await?
|
||||
.inner
|
||||
.get_quote(swap_id)
|
||||
.map(|r| Amount::from_sat(r.amount))
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn accept_zero_amount_chain_swap_quote(
|
||||
async fn accept_zero_amount_chain_swap_quote(
|
||||
&self,
|
||||
swap_id: &str,
|
||||
server_lockup_sat: u64,
|
||||
) -> Result<(), PaymentError> {
|
||||
self.client
|
||||
self.get_client()
|
||||
.await?
|
||||
.inner
|
||||
.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())
|
||||
async fn get_submarine_pairs(&self) -> Result<Option<SubmarinePair>, PaymentError> {
|
||||
Ok(self
|
||||
.get_client()
|
||||
.await?
|
||||
.inner
|
||||
.get_submarine_pairs()?
|
||||
.get_lbtc_to_btc_pair())
|
||||
}
|
||||
|
||||
/// Get a submarine swap's preimage
|
||||
fn get_submarine_preimage(&self, swap_id: &str) -> Result<String, PaymentError> {
|
||||
Ok(self.client.get_submarine_preimage(swap_id)?.preimage)
|
||||
async fn get_submarine_preimage(&self, swap_id: &str) -> Result<String, PaymentError> {
|
||||
Ok(self
|
||||
.get_client()
|
||||
.await?
|
||||
.inner
|
||||
.get_submarine_preimage(swap_id)?
|
||||
.preimage)
|
||||
}
|
||||
|
||||
/// Get claim tx details which includes the preimage as a proof of payment.
|
||||
/// It is used to validate the preimage before claiming which is the reason why we need to separate
|
||||
/// the claim into two steps.
|
||||
fn get_send_claim_tx_details(
|
||||
async fn get_send_claim_tx_details(
|
||||
&self,
|
||||
swap: &SendSwap,
|
||||
) -> Result<SubmarineClaimTxResponse, PaymentError> {
|
||||
let claim_tx_response = self.client.get_submarine_claim_tx_details(&swap.id)?;
|
||||
let claim_tx_response = self
|
||||
.get_client()
|
||||
.await?
|
||||
.inner
|
||||
.get_submarine_claim_tx_details(&swap.id)?;
|
||||
info!("Received claim tx details: {:?}", &claim_tx_response);
|
||||
|
||||
self.validate_send_swap_preimage(&swap.id, &swap.invoice, &claim_tx_response.preimage)?;
|
||||
@@ -223,7 +263,7 @@ impl Swapper for BoltzSwapper {
|
||||
|
||||
/// Claim send swap cooperatively. Here the remote swapper is the one that claims.
|
||||
/// We are helping to use key spend path for cheaper fees.
|
||||
fn claim_send_swap_cooperative(
|
||||
async fn claim_send_swap_cooperative(
|
||||
&self,
|
||||
swap: &SendSwap,
|
||||
claim_tx_response: SubmarineClaimTxResponse,
|
||||
@@ -231,8 +271,9 @@ impl Swapper for BoltzSwapper {
|
||||
) -> Result<(), PaymentError> {
|
||||
let swap_id = &swap.id;
|
||||
let keypair = swap.get_refund_keypair()?;
|
||||
let refund_tx_wrapper =
|
||||
self.new_lbtc_refund_wrapper(&Swap::Send(swap.clone()), refund_address)?;
|
||||
let refund_tx_wrapper = self
|
||||
.new_lbtc_refund_wrapper(&Swap::Send(swap.clone()), refund_address)
|
||||
.await?;
|
||||
|
||||
self.validate_send_swap_preimage(swap_id, &swap.invoice, &claim_tx_response.preimage)?;
|
||||
|
||||
@@ -242,34 +283,39 @@ impl Swapper for BoltzSwapper {
|
||||
&claim_tx_response.transaction_hash,
|
||||
)?;
|
||||
|
||||
self.client.post_submarine_claim_tx_details(
|
||||
&swap_id.to_string(),
|
||||
pub_nonce,
|
||||
partial_sig,
|
||||
)?;
|
||||
self.get_client()
|
||||
.await?
|
||||
.inner
|
||||
.post_submarine_claim_tx_details(&swap_id.to_string(), pub_nonce, partial_sig)?;
|
||||
info!("Successfully sent claim details for swap-in {swap_id}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Create a new receive swap
|
||||
fn create_receive_swap(
|
||||
async fn create_receive_swap(
|
||||
&self,
|
||||
req: CreateReverseRequest,
|
||||
) -> Result<CreateReverseResponse, PaymentError> {
|
||||
let client = self.get_client().await?;
|
||||
let modified_req = CreateReverseRequest {
|
||||
referral_id: self.referral_id.clone(),
|
||||
referral_id: client.referral_id.clone(),
|
||||
..req.clone()
|
||||
};
|
||||
Ok(self.client.post_reverse_req(modified_req)?)
|
||||
Ok(client.inner.post_reverse_req(modified_req)?)
|
||||
}
|
||||
|
||||
// Get a reverse pair information
|
||||
fn get_reverse_swap_pairs(&self) -> Result<Option<ReversePair>, PaymentError> {
|
||||
Ok(self.client.get_reverse_pairs()?.get_btc_to_lbtc_pair())
|
||||
async fn get_reverse_swap_pairs(&self) -> Result<Option<ReversePair>, PaymentError> {
|
||||
Ok(self
|
||||
.get_client()
|
||||
.await?
|
||||
.inner
|
||||
.get_reverse_pairs()?
|
||||
.get_btc_to_lbtc_pair())
|
||||
}
|
||||
|
||||
/// Create a claim transaction for a receive or chain swap
|
||||
fn create_claim_tx(
|
||||
async fn create_claim_tx(
|
||||
&self,
|
||||
swap: Swap,
|
||||
claim_address: Option<String>,
|
||||
@@ -285,12 +331,14 @@ impl Swapper for BoltzSwapper {
|
||||
});
|
||||
};
|
||||
match swap.direction {
|
||||
Direction::Incoming => {
|
||||
Transaction::Liquid(self.new_incoming_chain_claim_tx(swap, claim_address)?)
|
||||
}
|
||||
Direction::Outgoing => {
|
||||
Transaction::Bitcoin(self.new_outgoing_chain_claim_tx(swap, claim_address)?)
|
||||
}
|
||||
Direction::Incoming => Transaction::Liquid(
|
||||
self.new_incoming_chain_claim_tx(swap, claim_address)
|
||||
.await?,
|
||||
),
|
||||
Direction::Outgoing => Transaction::Bitcoin(
|
||||
self.new_outgoing_chain_claim_tx(swap, claim_address)
|
||||
.await?,
|
||||
),
|
||||
}
|
||||
}
|
||||
Swap::Receive(swap) => {
|
||||
@@ -302,7 +350,7 @@ impl Swapper for BoltzSwapper {
|
||||
),
|
||||
});
|
||||
};
|
||||
Transaction::Liquid(self.new_receive_claim_tx(swap, claim_address)?)
|
||||
Transaction::Liquid(self.new_receive_claim_tx(swap, claim_address).await?)
|
||||
}
|
||||
Swap::Send(swap) => {
|
||||
return Err(PaymentError::Generic {
|
||||
@@ -318,7 +366,7 @@ impl Swapper for BoltzSwapper {
|
||||
}
|
||||
|
||||
/// Estimate the refund broadcast transaction size and fees in sats for a send or chain swap
|
||||
fn estimate_refund_broadcast(
|
||||
async fn estimate_refund_broadcast(
|
||||
&self,
|
||||
swap: Swap,
|
||||
refund_address: &str,
|
||||
@@ -339,10 +387,10 @@ impl Swapper for BoltzSwapper {
|
||||
}
|
||||
};
|
||||
|
||||
let refund_tx_size = match self.new_lbtc_refund_wrapper(&swap, refund_address) {
|
||||
let refund_tx_size = match self.new_lbtc_refund_wrapper(&swap, refund_address).await {
|
||||
Ok(refund_tx_wrapper) => refund_tx_wrapper.size(&refund_keypair, &preimage, true)?,
|
||||
Err(_) => {
|
||||
let refund_tx_wrapper = self.new_btc_refund_wrapper(&swap, refund_address)?;
|
||||
let refund_tx_wrapper = self.new_btc_refund_wrapper(&swap, refund_address).await?;
|
||||
refund_tx_wrapper.size(&refund_keypair, &preimage)?
|
||||
}
|
||||
} as u32;
|
||||
@@ -354,7 +402,7 @@ impl Swapper for BoltzSwapper {
|
||||
}
|
||||
|
||||
/// Create a refund transaction for a send or chain swap
|
||||
fn create_refund_tx(
|
||||
async fn create_refund_tx(
|
||||
&self,
|
||||
swap: Swap,
|
||||
refund_address: &str,
|
||||
@@ -374,27 +422,26 @@ impl Swapper for BoltzSwapper {
|
||||
});
|
||||
};
|
||||
|
||||
Transaction::Bitcoin(self.new_btc_refund_tx(
|
||||
chain_swap,
|
||||
refund_address,
|
||||
utxos,
|
||||
broadcast_fee_rate_sat_per_vb,
|
||||
is_cooperative,
|
||||
)?)
|
||||
Transaction::Bitcoin(
|
||||
self.new_btc_refund_tx(
|
||||
chain_swap,
|
||||
refund_address,
|
||||
utxos,
|
||||
broadcast_fee_rate_sat_per_vb,
|
||||
is_cooperative,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
Direction::Outgoing => Transaction::Liquid(self.new_lbtc_refund_tx(
|
||||
&swap,
|
||||
refund_address,
|
||||
utxos,
|
||||
is_cooperative,
|
||||
)?),
|
||||
Direction::Outgoing => Transaction::Liquid(
|
||||
self.new_lbtc_refund_tx(&swap, refund_address, utxos, is_cooperative)
|
||||
.await?,
|
||||
),
|
||||
},
|
||||
Swap::Send(_) => Transaction::Liquid(self.new_lbtc_refund_tx(
|
||||
&swap,
|
||||
refund_address,
|
||||
utxos,
|
||||
is_cooperative,
|
||||
)?),
|
||||
Swap::Send(_) => Transaction::Liquid(
|
||||
self.new_lbtc_refund_tx(&swap, refund_address, utxos, is_cooperative)
|
||||
.await?,
|
||||
),
|
||||
Swap::Receive(_) => {
|
||||
return Err(PaymentError::Generic {
|
||||
err: format!(
|
||||
@@ -407,8 +454,12 @@ impl Swapper for BoltzSwapper {
|
||||
Ok(tx)
|
||||
}
|
||||
|
||||
fn broadcast_tx(&self, chain: Chain, tx_hex: &str) -> Result<String, PaymentError> {
|
||||
let response = self.client.broadcast_tx(chain, &tx_hex.into())?;
|
||||
async fn broadcast_tx(&self, chain: Chain, tx_hex: &str) -> Result<String, PaymentError> {
|
||||
let response = self
|
||||
.get_client()
|
||||
.await?
|
||||
.inner
|
||||
.broadcast_tx(chain, &tx_hex.into())?;
|
||||
let err = format!("Unexpected response from Boltz server: {response}");
|
||||
let tx_id = response
|
||||
.as_object()
|
||||
@@ -422,23 +473,34 @@ impl Swapper for BoltzSwapper {
|
||||
}
|
||||
|
||||
fn create_status_stream(&self) -> Box<dyn SwapperStatusStream> {
|
||||
Box::new(BoltzStatusStream::new(&self.boltz_url))
|
||||
Box::new(BoltzStatusStream::new(
|
||||
self.config.clone(),
|
||||
self.proxy_url.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn check_for_mrh(
|
||||
async fn check_for_mrh(
|
||||
&self,
|
||||
invoice: &str,
|
||||
) -> Result<Option<(String, boltz_client::bitcoin::Amount)>, PaymentError> {
|
||||
boltz_client::swaps::magic_routing::check_for_mrh(
|
||||
&self.client,
|
||||
&self.get_client().await?.inner,
|
||||
invoice,
|
||||
self.config.network.into(),
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn get_bolt12_invoice(&self, offer: &str, amount_sat: u64) -> Result<String, PaymentError> {
|
||||
let invoice_res = self.client.get_bolt12_invoice(offer, amount_sat)?;
|
||||
async fn get_bolt12_invoice(
|
||||
&self,
|
||||
offer: &str,
|
||||
amount_sat: u64,
|
||||
) -> Result<String, PaymentError> {
|
||||
let invoice_res = self
|
||||
.get_client()
|
||||
.await?
|
||||
.inner
|
||||
.get_bolt12_invoice(offer, amount_sat)?;
|
||||
info!("Received BOLT12 invoice response: {invoice_res:?}");
|
||||
Ok(invoice_res.invoice)
|
||||
}
|
||||
|
||||
66
lib/core/src/swapper/boltz/proxy.rs
Normal file
66
lib/core/src/swapper/boltz/proxy.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use sdk_common::prelude::BreezServer;
|
||||
use url::Url;
|
||||
|
||||
use crate::{persist::Persister, swapper::ProxyUrlFetcher};
|
||||
|
||||
pub(crate) struct BoltzProxyFetcher {
|
||||
url: OnceLock<Option<String>>,
|
||||
persister: Arc<Persister>,
|
||||
}
|
||||
|
||||
pub(crate) fn split_proxy_url(url: &str) -> (Option<String>, Option<String>) {
|
||||
Url::parse(url)
|
||||
.map(|url| {
|
||||
let api_base_url = url.domain().map(|domain| format!("https://{domain}/v2"));
|
||||
let referral_id = url.query().and_then(|q| {
|
||||
q.split('=')
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>()
|
||||
.get(1)
|
||||
.cloned()
|
||||
});
|
||||
(api_base_url, referral_id)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
impl BoltzProxyFetcher {
|
||||
pub(crate) fn new(persister: Arc<Persister>) -> Self {
|
||||
Self {
|
||||
url: OnceLock::new(),
|
||||
persister,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ProxyUrlFetcher for BoltzProxyFetcher {
|
||||
async fn fetch(&self) -> Result<&Option<String>> {
|
||||
if let Some(swapper_proxy_url) = self.url.get() {
|
||||
return Ok(swapper_proxy_url);
|
||||
}
|
||||
|
||||
let maybe_swapper_proxy_url =
|
||||
match BreezServer::new("https://bs1.breez.technology:443".into(), None) {
|
||||
Ok(breez_server) => {
|
||||
let maybe_swapper_proxy_url = breez_server
|
||||
.fetch_boltz_swapper_urls()
|
||||
.await
|
||||
.map(|swapper_urls| swapper_urls.first().cloned())?;
|
||||
|
||||
if let Some(swapper_proxy_url) = maybe_swapper_proxy_url.clone() {
|
||||
self.persister.set_swapper_proxy_url(swapper_proxy_url)?;
|
||||
}
|
||||
maybe_swapper_proxy_url
|
||||
}
|
||||
Err(_) => self.persister.get_swapper_proxy_url().unwrap_or(None),
|
||||
};
|
||||
|
||||
let swapper_proxy_url = self.url.get_or_init(|| maybe_swapper_proxy_url);
|
||||
Ok(swapper_proxy_url)
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use boltz_client::swaps::boltz::{self, Subscription, SwapUpdate};
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use log::{debug, error, info, warn};
|
||||
@@ -13,28 +12,39 @@ use tokio_tungstenite::tungstenite::Message;
|
||||
use tokio_tungstenite::{connect_async, MaybeTlsStream, WebSocketStream};
|
||||
use url::Url;
|
||||
|
||||
use crate::model::Config;
|
||||
use crate::swapper::{ReconnectHandler, SwapperStatusStream};
|
||||
|
||||
use super::{split_proxy_url, ProxyUrlFetcher};
|
||||
|
||||
pub(crate) struct BoltzStatusStream {
|
||||
url: String,
|
||||
config: Config,
|
||||
proxy_url: Arc<dyn ProxyUrlFetcher>,
|
||||
subscription_notifier: broadcast::Sender<String>,
|
||||
update_notifier: broadcast::Sender<boltz::Update>,
|
||||
}
|
||||
|
||||
impl BoltzStatusStream {
|
||||
pub(crate) fn new(url: &str) -> Self {
|
||||
pub(crate) fn new(config: Config, proxy_url: Arc<dyn ProxyUrlFetcher>) -> Self {
|
||||
let (subscription_notifier, _) = broadcast::channel::<String>(30);
|
||||
let (update_notifier, _) = broadcast::channel::<boltz::Update>(30);
|
||||
|
||||
Self {
|
||||
url: url.replace("http", "ws") + "/ws",
|
||||
config,
|
||||
proxy_url,
|
||||
subscription_notifier,
|
||||
update_notifier,
|
||||
}
|
||||
}
|
||||
|
||||
async fn connect(&self) -> Result<WebSocketStream<MaybeTlsStream<TcpStream>>> {
|
||||
let (socket, _) = connect_async(Url::parse(&self.url)?)
|
||||
let default_url = self.config.default_boltz_url().to_string();
|
||||
let url = match self.proxy_url.fetch().await {
|
||||
Ok(Some(url)) => split_proxy_url(url).0.unwrap_or(default_url),
|
||||
_ => default_url,
|
||||
};
|
||||
let url = url.replace("http", "ws") + "/ws";
|
||||
let (socket, _) = connect_async(Url::parse(&url)?)
|
||||
.await
|
||||
.map_err(|e| anyhow!("Failed to connect to websocket: {e:?}"))?;
|
||||
Ok(socket)
|
||||
@@ -58,7 +68,6 @@ impl BoltzStatusStream {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl SwapperStatusStream for BoltzStatusStream {
|
||||
fn track_swap_id(&self, swap_id: &str) -> Result<()> {
|
||||
let _ = self.subscription_notifier.send(swap_id.to_string());
|
||||
@@ -69,7 +78,7 @@ impl SwapperStatusStream for BoltzStatusStream {
|
||||
self.update_notifier.subscribe()
|
||||
}
|
||||
|
||||
async fn start(
|
||||
fn start(
|
||||
self: Arc<Self>,
|
||||
callback: Box<dyn ReconnectHandler>,
|
||||
mut shutdown: watch::Receiver<()>,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use boltz_client::{
|
||||
boltz::{
|
||||
@@ -22,55 +23,58 @@ pub(crate) use reconnect_handler::*;
|
||||
pub(crate) mod boltz;
|
||||
pub(crate) mod reconnect_handler;
|
||||
|
||||
#[async_trait]
|
||||
pub trait Swapper: Send + Sync {
|
||||
/// Create a new chain swap
|
||||
fn create_chain_swap(
|
||||
async fn create_chain_swap(
|
||||
&self,
|
||||
req: CreateChainRequest,
|
||||
) -> Result<CreateChainResponse, PaymentError>;
|
||||
|
||||
/// Create a new send swap
|
||||
fn create_send_swap(
|
||||
async fn create_send_swap(
|
||||
&self,
|
||||
req: CreateSubmarineRequest,
|
||||
) -> Result<CreateSubmarineResponse, PaymentError>;
|
||||
|
||||
/// Get the current rate, limits and fees for a given swap direction
|
||||
fn get_chain_pair(&self, direction: Direction) -> Result<Option<ChainPair>, PaymentError>;
|
||||
async fn get_chain_pair(&self, direction: Direction)
|
||||
-> Result<Option<ChainPair>, PaymentError>;
|
||||
|
||||
/// Get the current rate, limits and fees for both swap directions
|
||||
fn get_chain_pairs(&self) -> Result<(Option<ChainPair>, Option<ChainPair>), PaymentError>;
|
||||
async 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, SdkError>;
|
||||
async fn get_zero_amount_chain_swap_quote(&self, swap_id: &str) -> Result<Amount, SdkError>;
|
||||
|
||||
/// Accept a specific quote for a Zero-Amount Receive Chain Swap
|
||||
fn accept_zero_amount_chain_swap_quote(
|
||||
async 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>;
|
||||
async fn get_submarine_pairs(&self) -> Result<Option<SubmarinePair>, PaymentError>;
|
||||
|
||||
/// Get a submarine swap's preimage
|
||||
fn get_submarine_preimage(&self, swap_id: &str) -> Result<String, PaymentError>;
|
||||
async fn get_submarine_preimage(&self, swap_id: &str) -> Result<String, PaymentError>;
|
||||
|
||||
/// Get send swap claim tx details which includes the preimage as a proof of payment.
|
||||
/// It is used to validate the preimage before claiming which is the reason why we need to separate
|
||||
/// the claim into two steps.
|
||||
fn get_send_claim_tx_details(
|
||||
async fn get_send_claim_tx_details(
|
||||
&self,
|
||||
swap: &SendSwap,
|
||||
) -> Result<SubmarineClaimTxResponse, PaymentError>;
|
||||
|
||||
/// Claim send swap cooperatively. Here the remote swapper is the one that claims.
|
||||
/// We are helping to use key spend path for cheaper fees.
|
||||
fn claim_send_swap_cooperative(
|
||||
async fn claim_send_swap_cooperative(
|
||||
&self,
|
||||
swap: &SendSwap,
|
||||
claim_tx_response: SubmarineClaimTxResponse,
|
||||
@@ -78,23 +82,23 @@ pub trait Swapper: Send + Sync {
|
||||
) -> Result<(), PaymentError>;
|
||||
|
||||
/// Create a new receive swap
|
||||
fn create_receive_swap(
|
||||
async fn create_receive_swap(
|
||||
&self,
|
||||
req: CreateReverseRequest,
|
||||
) -> Result<CreateReverseResponse, PaymentError>;
|
||||
|
||||
/// Get a reverse pair information
|
||||
fn get_reverse_swap_pairs(&self) -> Result<Option<ReversePair>, PaymentError>;
|
||||
async fn get_reverse_swap_pairs(&self) -> Result<Option<ReversePair>, PaymentError>;
|
||||
|
||||
/// Create a claim transaction for a receive or chain swap
|
||||
fn create_claim_tx(
|
||||
async fn create_claim_tx(
|
||||
&self,
|
||||
swap: Swap,
|
||||
claim_address: Option<String>,
|
||||
) -> Result<crate::prelude::Transaction, PaymentError>;
|
||||
|
||||
/// Estimate the refund broadcast transaction size and fees in sats for a send or chain swap
|
||||
fn estimate_refund_broadcast(
|
||||
async fn estimate_refund_broadcast(
|
||||
&self,
|
||||
swap: Swap,
|
||||
refund_address: &str,
|
||||
@@ -102,7 +106,7 @@ pub trait Swapper: Send + Sync {
|
||||
) -> Result<(u32, u64), SdkError>;
|
||||
|
||||
/// Create a refund transaction for a send or chain swap
|
||||
fn create_refund_tx(
|
||||
async fn create_refund_tx(
|
||||
&self,
|
||||
swap: Swap,
|
||||
refund_address: &str,
|
||||
@@ -112,26 +116,30 @@ pub trait Swapper: Send + Sync {
|
||||
) -> Result<crate::prelude::Transaction, PaymentError>;
|
||||
|
||||
/// Broadcasts a transaction and returns its id
|
||||
fn broadcast_tx(&self, chain: Chain, tx_hex: &str) -> Result<String, PaymentError>;
|
||||
async fn broadcast_tx(&self, chain: Chain, tx_hex: &str) -> Result<String, PaymentError>;
|
||||
|
||||
fn create_status_stream(&self) -> Box<dyn SwapperStatusStream>;
|
||||
|
||||
/// Look for a valid Magic Routing Hint. If found, validate it and extract the BIP21 info (amount, address).
|
||||
fn check_for_mrh(
|
||||
async fn check_for_mrh(
|
||||
&self,
|
||||
invoice: &str,
|
||||
) -> Result<Option<(String, boltz_client::bitcoin::Amount)>, PaymentError>;
|
||||
|
||||
fn get_bolt12_invoice(&self, offer: &str, amount_sat: u64) -> Result<String, PaymentError>;
|
||||
async fn get_bolt12_invoice(
|
||||
&self,
|
||||
offer: &str,
|
||||
amount_sat: u64,
|
||||
) -> Result<String, PaymentError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait SwapperStatusStream: Send + Sync {
|
||||
async fn start(
|
||||
self: Arc<Self>,
|
||||
callback: Box<dyn ReconnectHandler>,
|
||||
shutdown: watch::Receiver<()>,
|
||||
);
|
||||
fn start(self: Arc<Self>, callback: Box<dyn ReconnectHandler>, shutdown: watch::Receiver<()>);
|
||||
fn track_swap_id(&self, swap_id: &str) -> anyhow::Result<()>;
|
||||
fn subscribe_swap_updates(&self) -> broadcast::Receiver<boltz_client::boltz::Update>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub(crate) trait ProxyUrlFetcher: Send + Sync + 'static {
|
||||
async fn fetch(&self) -> Result<&Option<String>>;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ impl SyncService {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn start(self: Arc<Self>, mut shutdown: watch::Receiver<()>) -> Result<()> {
|
||||
pub(crate) fn start(self: Arc<Self>, mut shutdown: watch::Receiver<()>) {
|
||||
tokio::spawn(async move {
|
||||
if let Err(err) = self.client.connect(self.remote_url.clone()).await {
|
||||
log::warn!("Could not connect to sync service: {err:?}");
|
||||
@@ -111,8 +111,6 @@ impl SyncService {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn commit_record(&self, decryption_info: &DecryptionInfo, swap: Option<Swap>) -> Result<()> {
|
||||
|
||||
@@ -247,7 +247,7 @@ impl BitcoinChainService for MockBitcoinChainService {
|
||||
_retries: u64,
|
||||
) -> Result<electrum_client::GetBalanceRes> {
|
||||
Ok(GetBalanceRes {
|
||||
confirmed: self.script_balance_sat.lock().unwrap().clone(),
|
||||
confirmed: *self.script_balance_sat.lock().unwrap(),
|
||||
unconfirmed: 0,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ use crate::{
|
||||
use super::{
|
||||
chain::{MockBitcoinChainService, MockLiquidChainService},
|
||||
generate_random_string,
|
||||
swapper::MockProxyUrlFetcher,
|
||||
wallet::{MockSigner, MockWallet},
|
||||
};
|
||||
|
||||
@@ -31,7 +32,10 @@ pub(crate) fn new_chain_swap_handler(persister: Arc<Persister>) -> Result<ChainS
|
||||
let config = Config::testnet(None);
|
||||
let signer: Arc<Box<dyn Signer>> = Arc::new(Box::new(MockSigner::new()?));
|
||||
let onchain_wallet = Arc::new(MockWallet::new(signer)?);
|
||||
let swapper = Arc::new(BoltzSwapper::new(config.clone(), None));
|
||||
let swapper = Arc::new(BoltzSwapper::new(
|
||||
config.clone(),
|
||||
Arc::new(MockProxyUrlFetcher::new()),
|
||||
));
|
||||
let liquid_chain_service = Arc::new(MockLiquidChainService::new());
|
||||
let bitcoin_chain_service = Arc::new(MockBitcoinChainService::new());
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#![cfg(test)]
|
||||
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use boltz_client::boltz;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -29,9 +28,8 @@ impl MockStatusStream {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl SwapperStatusStream for MockStatusStream {
|
||||
async fn start(
|
||||
fn start(
|
||||
self: Arc<Self>,
|
||||
_callback: Box<dyn ReconnectHandler>,
|
||||
_shutdown: watch::Receiver<()>,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#![cfg(test)]
|
||||
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use boltz_client::{
|
||||
boltz::{
|
||||
ChainFees, ChainMinerFees, ChainPair, ChainSwapDetails, CreateChainResponse,
|
||||
@@ -17,7 +19,7 @@ use crate::{
|
||||
ensure_sdk,
|
||||
error::{PaymentError, SdkError},
|
||||
model::{Direction, SendSwap, Swap, Transaction as SdkTransaction, Utxo},
|
||||
swapper::Swapper,
|
||||
swapper::{ProxyUrlFetcher, Swapper},
|
||||
test_utils::generate_random_string,
|
||||
utils,
|
||||
};
|
||||
@@ -75,25 +77,45 @@ impl MockSwapper {
|
||||
*self.zero_amount_swap_mock_config.lock().unwrap() = config;
|
||||
}
|
||||
|
||||
fn get_zero_amount_swap_server_lockup_sat(&self) -> u64 {
|
||||
fn new_chain_pair() -> ChainPair {
|
||||
ChainPair {
|
||||
hash: generate_random_string(10),
|
||||
rate: 0.0,
|
||||
limits: PairLimits {
|
||||
maximal: u64::MAX,
|
||||
minimal: 0,
|
||||
maximal_zero_conf: 100_000,
|
||||
},
|
||||
fees: ChainFees {
|
||||
percentage: 0.1,
|
||||
miner_fees: ChainMinerFees {
|
||||
server: 100,
|
||||
user: PairMinerFees {
|
||||
lockup: 100,
|
||||
claim: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_zero_amount_swap_server_lockup_sat(&self) -> u64 {
|
||||
let zero_amount_swap_mock_config = self.zero_amount_swap_mock_config.lock().unwrap();
|
||||
|
||||
let pair = self
|
||||
.get_chain_pair(Direction::Incoming)
|
||||
.expect("mock get_chain_pair failed")
|
||||
.expect("no chainpair in mock");
|
||||
|
||||
let pair = Self::new_chain_pair();
|
||||
let fees = pair
|
||||
.fees
|
||||
.boltz(zero_amount_swap_mock_config.user_lockup_sat)
|
||||
+ pair.fees.server()
|
||||
+ zero_amount_swap_mock_config.onchain_fee_increase_sat;
|
||||
|
||||
zero_amount_swap_mock_config.user_lockup_sat - fees
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Swapper for MockSwapper {
|
||||
fn create_chain_swap(
|
||||
async fn create_chain_swap(
|
||||
&self,
|
||||
_req: boltz_client::swaps::boltz::CreateChainRequest,
|
||||
) -> Result<CreateChainResponse, PaymentError> {
|
||||
@@ -104,7 +126,7 @@ impl Swapper for MockSwapper {
|
||||
})
|
||||
}
|
||||
|
||||
fn create_send_swap(
|
||||
async fn create_send_swap(
|
||||
&self,
|
||||
req: boltz_client::swaps::boltz::CreateSubmarineRequest,
|
||||
) -> Result<CreateSubmarineResponse, PaymentError> {
|
||||
@@ -130,32 +152,16 @@ impl Swapper for MockSwapper {
|
||||
})
|
||||
}
|
||||
|
||||
fn get_chain_pair(
|
||||
async fn get_chain_pair(
|
||||
&self,
|
||||
_direction: Direction,
|
||||
) -> anyhow::Result<Option<ChainPair>, PaymentError> {
|
||||
Ok(Some(ChainPair {
|
||||
hash: generate_random_string(10),
|
||||
rate: 0.0,
|
||||
limits: PairLimits {
|
||||
maximal: u64::MAX,
|
||||
minimal: 0,
|
||||
maximal_zero_conf: 100_000,
|
||||
},
|
||||
fees: ChainFees {
|
||||
percentage: 0.1,
|
||||
miner_fees: ChainMinerFees {
|
||||
server: 100,
|
||||
user: PairMinerFees {
|
||||
lockup: 100,
|
||||
claim: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
Ok(Some(Self::new_chain_pair()))
|
||||
}
|
||||
|
||||
fn get_chain_pairs(&self) -> Result<(Option<ChainPair>, Option<ChainPair>), PaymentError> {
|
||||
async fn get_chain_pairs(
|
||||
&self,
|
||||
) -> Result<(Option<ChainPair>, Option<ChainPair>), PaymentError> {
|
||||
let test_pair = Some(ChainPair {
|
||||
hash: generate_random_string(10),
|
||||
rate: 0.0,
|
||||
@@ -178,11 +184,11 @@ impl Swapper for MockSwapper {
|
||||
Ok((test_pair.clone(), test_pair))
|
||||
}
|
||||
|
||||
fn get_submarine_preimage(&self, _swap_id: &str) -> Result<String, PaymentError> {
|
||||
async fn get_submarine_preimage(&self, _swap_id: &str) -> Result<String, PaymentError> {
|
||||
Ok(Preimage::new().to_string().unwrap())
|
||||
}
|
||||
|
||||
fn get_submarine_pairs(&self) -> Result<Option<SubmarinePair>, PaymentError> {
|
||||
async fn get_submarine_pairs(&self) -> Result<Option<SubmarinePair>, PaymentError> {
|
||||
Ok(Some(SubmarinePair {
|
||||
hash: generate_random_string(10),
|
||||
rate: 0.0,
|
||||
@@ -198,7 +204,7 @@ impl Swapper for MockSwapper {
|
||||
}))
|
||||
}
|
||||
|
||||
fn get_send_claim_tx_details(
|
||||
async fn get_send_claim_tx_details(
|
||||
&self,
|
||||
_swap: &SendSwap,
|
||||
) -> Result<SubmarineClaimTxResponse, PaymentError> {
|
||||
@@ -212,7 +218,7 @@ impl Swapper for MockSwapper {
|
||||
})
|
||||
}
|
||||
|
||||
fn create_claim_tx(
|
||||
async fn create_claim_tx(
|
||||
&self,
|
||||
swap: Swap,
|
||||
_claim_address: Option<String>,
|
||||
@@ -240,7 +246,7 @@ impl Swapper for MockSwapper {
|
||||
})
|
||||
}
|
||||
|
||||
fn estimate_refund_broadcast(
|
||||
async fn estimate_refund_broadcast(
|
||||
&self,
|
||||
_swap: Swap,
|
||||
_refund_address: &str,
|
||||
@@ -249,7 +255,7 @@ impl Swapper for MockSwapper {
|
||||
Ok((0, 0))
|
||||
}
|
||||
|
||||
fn create_refund_tx(
|
||||
async fn create_refund_tx(
|
||||
&self,
|
||||
swap: Swap,
|
||||
_refund_address: &str,
|
||||
@@ -280,7 +286,7 @@ impl Swapper for MockSwapper {
|
||||
})
|
||||
}
|
||||
|
||||
fn claim_send_swap_cooperative(
|
||||
async fn claim_send_swap_cooperative(
|
||||
&self,
|
||||
_swap: &SendSwap,
|
||||
_claim_tx_response: boltz_client::swaps::boltz::SubmarineClaimTxResponse,
|
||||
@@ -289,7 +295,7 @@ impl Swapper for MockSwapper {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_receive_swap(
|
||||
async fn create_receive_swap(
|
||||
&self,
|
||||
_req: boltz_client::swaps::boltz::CreateReverseRequest,
|
||||
) -> Result<CreateReverseResponse, PaymentError> {
|
||||
@@ -305,7 +311,7 @@ impl Swapper for MockSwapper {
|
||||
})
|
||||
}
|
||||
|
||||
fn get_reverse_swap_pairs(&self) -> Result<Option<ReversePair>, PaymentError> {
|
||||
async fn get_reverse_swap_pairs(&self) -> Result<Option<ReversePair>, PaymentError> {
|
||||
Ok(Some(ReversePair {
|
||||
hash: "".to_string(),
|
||||
rate: 0.0,
|
||||
@@ -323,7 +329,7 @@ impl Swapper for MockSwapper {
|
||||
}))
|
||||
}
|
||||
|
||||
fn broadcast_tx(
|
||||
async fn broadcast_tx(
|
||||
&self,
|
||||
_chain: boltz_client::network::Chain,
|
||||
tx_hex: &str,
|
||||
@@ -336,7 +342,7 @@ impl Swapper for MockSwapper {
|
||||
Box::new(MockStatusStream::new())
|
||||
}
|
||||
|
||||
fn check_for_mrh(
|
||||
async fn check_for_mrh(
|
||||
&self,
|
||||
_invoice: &str,
|
||||
) -> Result<Option<(String, boltz_client::bitcoin::Amount)>, PaymentError> {
|
||||
@@ -344,24 +350,43 @@ impl Swapper for MockSwapper {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_bolt12_invoice(&self, _offer: &str, _amount_sat: u64) -> Result<String, PaymentError> {
|
||||
async 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, SdkError> {
|
||||
let server_lockup_amount_sat = self.get_zero_amount_swap_server_lockup_sat();
|
||||
async fn get_zero_amount_chain_swap_quote(&self, _swap_id: &str) -> Result<Amount, SdkError> {
|
||||
let server_lockup_amount_sat = self.get_zero_amount_swap_server_lockup_sat().await;
|
||||
Ok(Amount::from_sat(server_lockup_amount_sat))
|
||||
}
|
||||
|
||||
fn accept_zero_amount_chain_swap_quote(
|
||||
async fn accept_zero_amount_chain_swap_quote(
|
||||
&self,
|
||||
_swap_id: &str,
|
||||
server_lockup_sat: u64,
|
||||
) -> Result<(), PaymentError> {
|
||||
ensure_sdk!(
|
||||
server_lockup_sat == self.get_zero_amount_swap_server_lockup_sat(),
|
||||
server_lockup_sat == self.get_zero_amount_swap_server_lockup_sat().await,
|
||||
PaymentError::InvalidOrExpiredFees
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct MockProxyUrlFetcher {}
|
||||
|
||||
impl MockProxyUrlFetcher {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ProxyUrlFetcher for MockProxyUrlFetcher {
|
||||
async fn fetch(&self) -> Result<&Option<String>> {
|
||||
Ok(&None)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user