breez_sdk_liquid/swapper/boltz/
bitcoin.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use std::str::FromStr;

use boltz_client::{
    bitcoin::{address::Address, Transaction},
    boltz::SwapTxKind,
    fees::Fee,
    util::secrets::Preimage,
    BtcSwapTx,
};

use crate::{
    ensure_sdk,
    error::{PaymentError, SdkError},
    prelude::{ChainSwap, Direction, Swap, Utxo},
};

use super::{BoltzSwapper, ProxyUrlFetcher};

impl<P: ProxyUrlFetcher> BoltzSwapper<P> {
    pub(crate) async fn new_btc_refund_wrapper(
        &self,
        swap: &Swap,
        refund_address: &str,
    ) -> Result<BtcSwapTx, SdkError> {
        let bitcoin_client = self.get_bitcoin_client()?;
        let refund_wrapper = match swap {
            Swap::Chain(swap) => match swap.direction {
                Direction::Incoming => {
                    let swap_script = swap.get_lockup_swap_script()?;
                    BtcSwapTx::new_refund(
                        swap_script.as_bitcoin_script()?,
                        refund_address,
                        bitcoin_client,
                        self.get_url().await?,
                        swap.id.clone(),
                    )
                    .await
                }
                Direction::Outgoing => {
                    return Err(SdkError::generic(format!(
                        "Cannot create Bitcoin refund wrapper for outgoing Chain swap {}",
                        swap.id
                    )));
                }
            },
            _ => {
                return Err(SdkError::generic(format!(
                    "Cannot create Bitcoin refund wrapper for swap {}",
                    swap.id()
                )));
            }
        }?;
        Ok(refund_wrapper)
    }

    pub(crate) async fn new_btc_refund_tx(
        &self,
        swap: &ChainSwap,
        refund_address: &str,
        utxos: Vec<Utxo>,
        broadcast_fee_rate_sat_per_vb: f64,
        is_cooperative: bool,
    ) -> Result<Transaction, SdkError> {
        ensure_sdk!(
            swap.direction == Direction::Incoming,
            SdkError::generic("Cannot create BTC refund tx for outgoing Chain swaps.")
        );

        let address = Address::from_str(refund_address)
            .map_err(|err| SdkError::generic(format!("Could not parse address: {err:?}")))?;

        ensure_sdk!(
            address.is_valid_for_network(self.config.network.into()),
            SdkError::generic("Address network validation failed")
        );

        let utxos = utxos
            .iter()
            .filter_map(|utxo| utxo.as_bitcoin().cloned())
            .collect();

        let swap_script = swap.get_lockup_swap_script()?.as_bitcoin_script()?;
        let refund_tx = BtcSwapTx {
            kind: SwapTxKind::Refund,
            swap_script,
            output_address: address.assume_checked(),
            utxos,
        };

        let refund_keypair = swap.get_refund_keypair()?;
        let refund_tx_size = refund_tx.size(&refund_keypair, is_cooperative)?;
        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)
                    .await?
            }
            false => None,
        };

        let signed_tx = refund_tx
            .sign_refund(
                &refund_keypair,
                Fee::Absolute(broadcast_fees_sat),
                cooperative,
            )
            .await?;
        Ok(signed_tx)
    }

    pub(crate) async fn new_outgoing_chain_claim_tx(
        &self,
        swap: &ChainSwap,
        claim_address: String,
    ) -> Result<Transaction, PaymentError> {
        let bitcoin_client = self.get_bitcoin_client()?;
        let claim_keypair = swap.get_claim_keypair()?;
        let claim_swap_script = swap.get_claim_swap_script()?.as_bitcoin_script()?;
        let claim_tx_wrapper = BtcSwapTx::new_claim(
            claim_swap_script,
            claim_address,
            bitcoin_client,
            self.get_url().await?,
            swap.id.clone(),
        )
        .await?;

        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))
                    .await?,
            )
            .await?;

        Ok(signed_tx)
    }
}