mirror of
https://github.com/aljazceru/breez-sdk-liquid.git
synced 2026-02-21 05:54:20 +01:00
Merge pull request #332 from breez/lowbal-fees-fixes
Use lowball fees only on lockup and cooperative refund
This commit is contained in:
@@ -11,7 +11,7 @@ use reqwest::Response;
|
||||
use serde::Deserialize;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::model::Config;
|
||||
use crate::model::{Config, LiquidNetwork};
|
||||
|
||||
const LIQUID_ESPLORA_URL: &str = "https://lq1.breez.technology/liquid/api";
|
||||
|
||||
@@ -44,13 +44,17 @@ struct Status {
|
||||
|
||||
pub(crate) struct HybridLiquidChainService {
|
||||
electrum_client: ElectrumClient,
|
||||
network: LiquidNetwork,
|
||||
}
|
||||
|
||||
impl HybridLiquidChainService {
|
||||
pub(crate) fn new(config: Config) -> Result<Self> {
|
||||
let electrum_client =
|
||||
ElectrumClient::new(&ElectrumUrl::new(&config.liquid_electrum_url, true, true))?;
|
||||
Ok(Self { electrum_client })
|
||||
Ok(Self {
|
||||
electrum_client,
|
||||
network: config.network,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,17 +65,22 @@ impl LiquidChainService for HybridLiquidChainService {
|
||||
}
|
||||
|
||||
async fn broadcast(&self, tx: &Transaction, swap_id: Option<&str>) -> Result<Txid> {
|
||||
let tx_bytes = tx.serialize();
|
||||
info!("Broadcasting Liquid tx: {}", tx_bytes.to_hex());
|
||||
let client = reqwest::Client::new();
|
||||
let response = client
|
||||
.post(format!("{LIQUID_ESPLORA_URL}/tx"))
|
||||
.header("Swap-ID", swap_id.unwrap_or_default())
|
||||
.body(tx_bytes.to_hex())
|
||||
.send()
|
||||
.await?;
|
||||
let txid = Txid::from_str(&response.text().await?)?;
|
||||
Ok(txid)
|
||||
match self.network {
|
||||
LiquidNetwork::Mainnet => {
|
||||
let tx_bytes = tx.serialize();
|
||||
info!("Broadcasting Liquid tx: {}", tx_bytes.to_hex());
|
||||
let client = reqwest::Client::new();
|
||||
let response = client
|
||||
.post(format!("{LIQUID_ESPLORA_URL}/tx"))
|
||||
.header("Swap-ID", swap_id.unwrap_or_default())
|
||||
.body(tx_bytes.to_hex())
|
||||
.send()
|
||||
.await?;
|
||||
let txid = Txid::from_str(&response.text().await?)?;
|
||||
Ok(txid)
|
||||
}
|
||||
LiquidNetwork::Testnet => Ok(self.electrum_client.broadcast(tx)?),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_transactions(&self, txids: &[Txid]) -> Result<Vec<Transaction>> {
|
||||
@@ -79,17 +88,26 @@ impl LiquidChainService for HybridLiquidChainService {
|
||||
}
|
||||
|
||||
async fn get_script_history(&self, script: &Script) -> Result<Vec<History>> {
|
||||
let script = lwk_wollet::elements::bitcoin::Script::from_bytes(script.as_bytes());
|
||||
let script_hash = sha256::Hash::hash(script.as_bytes())
|
||||
.to_byte_array()
|
||||
.to_hex();
|
||||
let url = format!("{}/scripthash/{}/txs", LIQUID_ESPLORA_URL, script_hash);
|
||||
// TODO must handle paging -> https://github.com/blockstream/esplora/blob/master/API.md#addresses
|
||||
let response = get_with_retry(&url, 3).await?;
|
||||
let json: Vec<EsploraTx> = response.json().await?;
|
||||
match self.network {
|
||||
LiquidNetwork::Mainnet => {
|
||||
let script = lwk_wollet::elements::bitcoin::Script::from_bytes(script.as_bytes());
|
||||
let script_hash = sha256::Hash::hash(script.as_bytes())
|
||||
.to_byte_array()
|
||||
.to_hex();
|
||||
let url = format!("{}/scripthash/{}/txs", LIQUID_ESPLORA_URL, script_hash);
|
||||
// TODO must handle paging -> https://github.com/blockstream/esplora/blob/master/API.md#addresses
|
||||
let response = get_with_retry(&url, 3).await?;
|
||||
let json: Vec<EsploraTx> = response.json().await?;
|
||||
|
||||
let history: Vec<History> = json.into_iter().map(Into::into).collect();
|
||||
Ok(history)
|
||||
let history: Vec<History> = json.into_iter().map(Into::into).collect();
|
||||
Ok(history)
|
||||
}
|
||||
LiquidNetwork::Testnet => {
|
||||
let mut history_vec = self.electrum_client.get_scripts_history(&[script])?;
|
||||
let h = history_vec.pop();
|
||||
Ok(h.unwrap_or(vec![]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,13 @@ impl Config {
|
||||
self.zero_conf_max_amount_sat
|
||||
.unwrap_or(DEFAULT_ZERO_CONF_MAX_SAT)
|
||||
}
|
||||
|
||||
pub(crate) fn lowball_fee_rate(&self) -> Option<f32> {
|
||||
match self.network {
|
||||
LiquidNetwork::Mainnet => Some(LOWBALL_FEE_RATE_SAT_PER_VBYTE * 1000.0),
|
||||
LiquidNetwork::Testnet => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Network chosen for this Liquid SDK instance. Note that it represents both the Liquid and the
|
||||
|
||||
@@ -582,14 +582,15 @@ impl LiquidSdk {
|
||||
}
|
||||
|
||||
/// Estimate the onchain fee for sending the given amount to the given destination address
|
||||
async fn estimate_onchain_tx_fee(&self, amount_sat: u64, address: &str) -> Result<u64> {
|
||||
async fn estimate_onchain_tx_fee(
|
||||
&self,
|
||||
amount_sat: u64,
|
||||
address: &str,
|
||||
fee_rate: Option<f32>,
|
||||
) -> Result<u64> {
|
||||
Ok(self
|
||||
.onchain_wallet
|
||||
.build_tx(
|
||||
Some(LOWBALL_FEE_RATE_SAT_PER_VBYTE * 1000.0),
|
||||
address,
|
||||
amount_sat,
|
||||
)
|
||||
.build_tx(fee_rate, address, amount_sat)
|
||||
.await?
|
||||
.all_fees()
|
||||
.values()
|
||||
@@ -604,7 +605,7 @@ impl LiquidSdk {
|
||||
LiquidNetwork::Testnet => "tlq1pq0wqu32e2xacxeyps22x8gjre4qk3u6r70pj4r62hzczxeyz8x3yxucrpn79zy28plc4x37aaf33kwt6dz2nn6gtkya6h02mwpzy4eh69zzexq7cf5y5"
|
||||
};
|
||||
|
||||
self.estimate_onchain_tx_fee(amount_sat, temp_p2tr_addr)
|
||||
self.estimate_onchain_tx_fee(amount_sat, temp_p2tr_addr, self.config.lowball_fee_rate())
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -625,8 +626,12 @@ impl LiquidSdk {
|
||||
|
||||
let fees_sat = match self.swapper.check_for_mrh(&req.invoice)? {
|
||||
Some((lbtc_address, _)) => {
|
||||
self.estimate_onchain_tx_fee(receiver_amount_sat, &lbtc_address)
|
||||
.await?
|
||||
self.estimate_onchain_tx_fee(
|
||||
receiver_amount_sat,
|
||||
&lbtc_address,
|
||||
self.config.lowball_fee_rate(),
|
||||
)
|
||||
.await?
|
||||
}
|
||||
None => {
|
||||
let lockup_fees_sat = self.estimate_lockup_tx_fee(receiver_amount_sat).await?;
|
||||
@@ -675,17 +680,22 @@ impl LiquidSdk {
|
||||
async fn refund_send(&self, swap: &SendSwap) -> Result<String, PaymentError> {
|
||||
let amount_sat = get_invoice_amount!(swap.invoice);
|
||||
let output_address = self.onchain_wallet.next_unused_address().await?.to_string();
|
||||
let refund_tx_fees_sat = self
|
||||
.estimate_onchain_tx_fee(amount_sat, &output_address)
|
||||
let cooperative_refund_tx_fees_sat = self
|
||||
.estimate_onchain_tx_fee(amount_sat, &output_address, self.config.lowball_fee_rate())
|
||||
.await?;
|
||||
let refund_res =
|
||||
self.swapper
|
||||
.refund_send_swap_cooperative(swap, &output_address, refund_tx_fees_sat);
|
||||
let refund_res = self.swapper.refund_send_swap_cooperative(
|
||||
swap,
|
||||
&output_address,
|
||||
cooperative_refund_tx_fees_sat,
|
||||
);
|
||||
match refund_res {
|
||||
Ok(res) => Ok(res),
|
||||
Err(e) => {
|
||||
let non_cooperative_refund_tx_fees_sat = self
|
||||
.estimate_onchain_tx_fee(swap.receiver_amount_sat, &output_address, None)
|
||||
.await?;
|
||||
warn!("Cooperative refund failed: {:?}", e);
|
||||
self.refund_send_non_cooperative(swap, refund_tx_fees_sat)
|
||||
self.refund_send_non_cooperative(swap, non_cooperative_refund_tx_fees_sat)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ use tokio::sync::{broadcast, Mutex};
|
||||
|
||||
use crate::chain::liquid::LiquidChainService;
|
||||
use crate::model::PaymentState::{Complete, Created, Failed, Pending, Refundable, TimedOut};
|
||||
use crate::model::{Config, SendSwap, LOWBALL_FEE_RATE_SAT_PER_VBYTE};
|
||||
use crate::model::{Config, SendSwap};
|
||||
use crate::swapper::Swapper;
|
||||
use crate::wallet::OnchainWallet;
|
||||
use crate::{ensure_sdk, get_invoice_amount};
|
||||
@@ -196,7 +196,7 @@ impl SendSwapStateHandler {
|
||||
let lockup_tx = self
|
||||
.onchain_wallet
|
||||
.build_tx(
|
||||
Some(LOWBALL_FEE_RATE_SAT_PER_VBYTE * 1000.0),
|
||||
self.config.lowball_fee_rate(),
|
||||
&create_response.address,
|
||||
create_response.expected_amount,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user