mirror of
https://github.com/aljazceru/breez-sdk-liquid.git
synced 2026-02-22 06:24:20 +01:00
fix: incoming Chain swap refund (#522)
This commit is contained in:
@@ -179,7 +179,6 @@ impl BitcoinChainService for HybridBitcoinChainService {
|
||||
}
|
||||
|
||||
async fn get_script_utxos(&self, script: &Script) -> Result<Vec<Utxo>> {
|
||||
let script_pubkey = script.to_p2sh();
|
||||
let utxos = self
|
||||
.client
|
||||
.script_list_unspent(script)?
|
||||
@@ -195,7 +194,7 @@ impl BitcoinChainService for HybridBitcoinChainService {
|
||||
OutPoint::new(*tx_hash, *tx_pos as u32),
|
||||
TxOut {
|
||||
value: Amount::from_sat(*value),
|
||||
script_pubkey: script_pubkey.clone(),
|
||||
script_pubkey: script.into(),
|
||||
},
|
||||
))
|
||||
},
|
||||
|
||||
@@ -129,12 +129,17 @@ impl LiquidChainService for HybridLiquidChainService {
|
||||
}
|
||||
|
||||
async fn get_transaction_hex(&self, txid: &Txid) -> Result<Option<Transaction>> {
|
||||
let url = format!("{}/tx/{}/hex", LIQUID_ESPLORA_URL, txid.to_hex());
|
||||
let response = get_with_retry(&url, 3).await?;
|
||||
Ok(match response.status() {
|
||||
StatusCode::OK => Some(utils::deserialize_tx_hex(&response.text().await?)?),
|
||||
_ => None,
|
||||
})
|
||||
match self.network {
|
||||
LiquidNetwork::Mainnet => {
|
||||
let url = format!("{}/tx/{}/hex", LIQUID_ESPLORA_URL, txid.to_hex());
|
||||
let response = get_with_retry(&url, 3).await?;
|
||||
Ok(match response.status() {
|
||||
StatusCode::OK => Some(utils::deserialize_tx_hex(&response.text().await?)?),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
LiquidNetwork::Testnet => Ok(self.get_transactions(&[*txid]).await?.first().cloned()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_transactions(&self, txids: &[Txid]) -> Result<Vec<Transaction>> {
|
||||
|
||||
@@ -4,7 +4,7 @@ use boltz_client::{
|
||||
bitcoin::{address::Address, Transaction},
|
||||
boltz::SwapTxKind,
|
||||
util::secrets::Preimage,
|
||||
BtcSwapScript, BtcSwapTx, Keypair,
|
||||
BtcSwapTx,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -54,17 +54,21 @@ impl BoltzSwapper {
|
||||
Ok(refund_wrapper)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new_btc_refund_tx(
|
||||
&self,
|
||||
swap_id: String,
|
||||
swap_script: BtcSwapScript,
|
||||
swap: &ChainSwap,
|
||||
refund_address: &str,
|
||||
refund_keypair: &Keypair,
|
||||
utxos: Vec<Utxo>,
|
||||
broadcast_fee_rate_sat_per_vb: f64,
|
||||
is_cooperative: bool,
|
||||
) -> Result<Transaction, SdkError> {
|
||||
ensure_sdk!(
|
||||
swap.direction == Direction::Incoming,
|
||||
SdkError::Generic {
|
||||
err: "Cannot create BTC refund tx for outgoing Chain swaps.".to_string()
|
||||
}
|
||||
);
|
||||
|
||||
let address = Address::from_str(refund_address).map_err(|err| SdkError::Generic {
|
||||
err: format!("Could not parse address: {err:?}"),
|
||||
})?;
|
||||
@@ -83,6 +87,7 @@ impl BoltzSwapper {
|
||||
err: "No UTXO found".to_string(),
|
||||
})?;
|
||||
|
||||
let swap_script = swap.get_lockup_swap_script()?.as_bitcoin_script()?;
|
||||
let refund_tx = BtcSwapTx {
|
||||
kind: SwapTxKind::Refund,
|
||||
swap_script,
|
||||
@@ -90,15 +95,17 @@ impl BoltzSwapper {
|
||||
utxo,
|
||||
};
|
||||
|
||||
let refund_tx_size = refund_tx.size(refund_keypair, &Preimage::new())?;
|
||||
let refund_keypair = swap.get_refund_keypair()?;
|
||||
let preimage = Preimage::from_str(&swap.preimage)?;
|
||||
let refund_tx_size = refund_tx.size(&refund_keypair, &preimage)?;
|
||||
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, None, None),
|
||||
true => self.get_cooperative_details(swap.id.clone(), None, None),
|
||||
false => None,
|
||||
};
|
||||
|
||||
let signed_tx = refund_tx.sign_refund(refund_keypair, broadcast_fees_sat, cooperative)?;
|
||||
let signed_tx = refund_tx.sign_refund(&refund_keypair, broadcast_fees_sat, cooperative)?;
|
||||
Ok(signed_tx)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,13 +4,17 @@ use boltz_client::{
|
||||
boltz::SwapTxKind,
|
||||
elements::Transaction,
|
||||
util::{liquid_genesis_hash, secrets::Preimage},
|
||||
Amount, Bolt11Invoice, ElementsAddress as Address, Keypair, LBtcSwapScript, LBtcSwapTx,
|
||||
Amount, Bolt11Invoice, ElementsAddress as Address, LBtcSwapTx,
|
||||
};
|
||||
use log::info;
|
||||
|
||||
use crate::{
|
||||
ensure_sdk,
|
||||
error::{PaymentError, SdkError},
|
||||
prelude::{ChainSwap, Direction, ReceiveSwap, Swap, Utxo, LOWBALL_FEE_RATE_SAT_PER_VBYTE},
|
||||
prelude::{
|
||||
ChainSwap, Direction, LiquidNetwork, ReceiveSwap, Swap, Utxo,
|
||||
LOWBALL_FEE_RATE_SAT_PER_VBYTE, STANDARD_FEE_RATE_SAT_PER_VBYTE,
|
||||
},
|
||||
};
|
||||
|
||||
use super::BoltzSwapper;
|
||||
@@ -92,8 +96,10 @@ impl BoltzSwapper {
|
||||
}
|
||||
|
||||
fn calculate_refund_fees(&self, refund_tx_size: usize) -> u64 {
|
||||
// Testnet not supports lowball as well, see https://blog.blockstream.com/elements-23-2-3-discounted-fees-for-confidential-transactions/
|
||||
let fee_rate = LOWBALL_FEE_RATE_SAT_PER_VBYTE;
|
||||
let fee_rate = match self.config.network {
|
||||
LiquidNetwork::Mainnet => LOWBALL_FEE_RATE_SAT_PER_VBYTE,
|
||||
LiquidNetwork::Testnet => STANDARD_FEE_RATE_SAT_PER_VBYTE,
|
||||
};
|
||||
(refund_tx_size as f64 * fee_rate).ceil() as u64
|
||||
}
|
||||
|
||||
@@ -147,13 +153,39 @@ impl BoltzSwapper {
|
||||
|
||||
pub(crate) fn new_lbtc_refund_tx(
|
||||
&self,
|
||||
swap_id: String,
|
||||
swap_script: LBtcSwapScript,
|
||||
swap: &Swap,
|
||||
refund_address: &str,
|
||||
refund_keypair: &Keypair,
|
||||
utxos: Vec<Utxo>,
|
||||
is_cooperative: bool,
|
||||
) -> Result<Transaction, SdkError> {
|
||||
let (swap_script, refund_keypair, preimage) = match swap {
|
||||
Swap::Chain(swap) => {
|
||||
ensure_sdk!(
|
||||
swap.direction == Direction::Outgoing,
|
||||
SdkError::Generic {
|
||||
err: "Cannot create LBTC refund tx for incoming Chain swaps".to_string()
|
||||
}
|
||||
);
|
||||
|
||||
(
|
||||
swap.get_lockup_swap_script()?.as_liquid_script()?,
|
||||
swap.get_refund_keypair()?,
|
||||
Preimage::from_str(&swap.preimage)?,
|
||||
)
|
||||
}
|
||||
Swap::Send(swap) => (
|
||||
swap.get_swap_script()?,
|
||||
swap.get_refund_keypair()?,
|
||||
Preimage::new(),
|
||||
),
|
||||
Swap::Receive(_) => {
|
||||
return Err(SdkError::Generic {
|
||||
err: "Cannot create LBTC refund tx for Receive swaps.".to_string(),
|
||||
});
|
||||
}
|
||||
};
|
||||
let swap_id = swap.id();
|
||||
|
||||
let address = Address::from_str(refund_address).map_err(|err| SdkError::Generic {
|
||||
err: format!("Could not parse address: {err:?}"),
|
||||
})?;
|
||||
@@ -176,16 +208,16 @@ impl BoltzSwapper {
|
||||
genesis_hash,
|
||||
};
|
||||
|
||||
let refund_tx_size = refund_tx.size(refund_keypair, &Preimage::new())?;
|
||||
let refund_tx_size = refund_tx.size(&refund_keypair, &preimage)?;
|
||||
let broadcast_fees_sat = self.calculate_refund_fees(refund_tx_size);
|
||||
|
||||
let cooperative = match is_cooperative {
|
||||
true => self.get_cooperative_details(swap_id, None, None),
|
||||
true => self.get_cooperative_details(swap_id.clone(), None, None),
|
||||
false => None,
|
||||
};
|
||||
|
||||
let signed_tx = refund_tx.sign_refund(
|
||||
refund_keypair,
|
||||
&refund_keypair,
|
||||
Amount::from_sat(broadcast_fees_sat),
|
||||
cooperative,
|
||||
)?;
|
||||
|
||||
@@ -329,60 +329,43 @@ impl Swapper for BoltzSwapper {
|
||||
broadcast_fee_rate_sat_per_vb: Option<f64>,
|
||||
is_cooperative: bool,
|
||||
) -> Result<Transaction, PaymentError> {
|
||||
let swap_id = swap.id();
|
||||
let refund_address = &refund_address.to_string();
|
||||
let tx = match &swap {
|
||||
Swap::Chain(swap) => {
|
||||
let swap_id = swap.id.clone();
|
||||
let swap_script = swap.get_lockup_swap_script()?;
|
||||
let refund_keypair = swap.get_refund_keypair()?;
|
||||
|
||||
match swap.direction {
|
||||
Direction::Incoming => {
|
||||
let Some(broadcast_fee_rate_sat_per_vb) = broadcast_fee_rate_sat_per_vb
|
||||
else {
|
||||
return Err(PaymentError::Generic {
|
||||
let tx = match &swap {
|
||||
Swap::Chain(chain_swap) => match chain_swap.direction {
|
||||
Direction::Incoming => {
|
||||
let Some(broadcast_fee_rate_sat_per_vb) = broadcast_fee_rate_sat_per_vb else {
|
||||
return Err(PaymentError::Generic {
|
||||
err: format!("No broadcast fee rate provided when refunding incoming Chain Swap {swap_id}")
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
Transaction::Bitcoin(self.new_btc_refund_tx(
|
||||
swap_id,
|
||||
swap_script.as_bitcoin_script()?,
|
||||
refund_address,
|
||||
&refund_keypair,
|
||||
utxos,
|
||||
broadcast_fee_rate_sat_per_vb,
|
||||
is_cooperative,
|
||||
)?)
|
||||
}
|
||||
Direction::Outgoing => Transaction::Liquid(self.new_lbtc_refund_tx(
|
||||
swap_id,
|
||||
swap_script.as_liquid_script()?,
|
||||
Transaction::Bitcoin(self.new_btc_refund_tx(
|
||||
chain_swap,
|
||||
refund_address,
|
||||
&refund_keypair,
|
||||
utxos,
|
||||
broadcast_fee_rate_sat_per_vb,
|
||||
is_cooperative,
|
||||
)?),
|
||||
)?)
|
||||
}
|
||||
}
|
||||
Swap::Send(swap) => {
|
||||
let swap_script = swap.get_swap_script()?;
|
||||
let refund_keypair = swap.get_refund_keypair()?;
|
||||
|
||||
Transaction::Liquid(self.new_lbtc_refund_tx(
|
||||
swap.id.clone(),
|
||||
swap_script,
|
||||
Direction::Outgoing => Transaction::Liquid(self.new_lbtc_refund_tx(
|
||||
&swap,
|
||||
refund_address,
|
||||
&refund_keypair,
|
||||
utxos,
|
||||
is_cooperative,
|
||||
)?)
|
||||
}
|
||||
Swap::Receive(swap) => {
|
||||
)?),
|
||||
},
|
||||
Swap::Send(_) => Transaction::Liquid(self.new_lbtc_refund_tx(
|
||||
&swap,
|
||||
refund_address,
|
||||
utxos,
|
||||
is_cooperative,
|
||||
)?),
|
||||
Swap::Receive(_) => {
|
||||
return Err(PaymentError::Generic {
|
||||
err: format!(
|
||||
"Failed to create refund tx for Receive swap {}: invalid swap type",
|
||||
swap.id
|
||||
"Failed to create refund tx for Receive swap {swap_id}: invalid swap type",
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user