bdkffi/
tx_builder.rs

1use crate::bitcoin::{Amount, FeeRate, Input, OutPoint, Psbt, Script, Txid};
2use crate::error::{AddForeignUtxoError, CreateTxError, SighashParseError};
3use crate::types::{KeychainKind, LockTime, ScriptAmount};
4use crate::wallet::Wallet;
5
6use bdk_wallet::bitcoin::absolute::{Height as BdkHeight, LockTime as BdkLockTime};
7use bdk_wallet::bitcoin::amount::Amount as BdkAmount;
8use bdk_wallet::bitcoin::psbt::Input as BdkInput;
9use bdk_wallet::bitcoin::psbt::PsbtSighashType as BdkPsbtSighashType;
10use bdk_wallet::bitcoin::script::PushBytesBuf;
11use bdk_wallet::bitcoin::Psbt as BdkPsbt;
12use bdk_wallet::bitcoin::ScriptBuf as BdkScriptBuf;
13use bdk_wallet::bitcoin::{OutPoint as BdkOutPoint, Sequence, Weight as BdkWeight};
14use bdk_wallet::coin_selection::{
15    CoinSelectionAlgorithm as BdkCoinSelectionAlgorithm,
16    LargestFirstCoinSelection as BdkLargestFirstCoinSelection,
17    OldestFirstCoinSelection as BdkOldestFirstCoinSelection,
18    SingleRandomDraw as BdkSingleRandomDraw,
19};
20use bdk_wallet::TxOrdering as BdkTxOrdering;
21
22use std::collections::BTreeMap;
23use std::collections::HashMap;
24use std::convert::{TryFrom, TryInto};
25use std::str::FromStr;
26use std::sync::Arc;
27
28type ChangeSpendPolicy = bdk_wallet::ChangeSpendPolicy;
29
30/// A `TxBuilder` is created by calling `build_tx` on a wallet. After assigning it, you set options on it until finally
31/// calling `finish` to consume the builder and generate the transaction.
32#[derive(Clone, uniffi::Object)]
33pub struct TxBuilder {
34    add_global_xpubs: bool,
35    recipients: Vec<(BdkScriptBuf, BdkAmount)>,
36    utxos: Vec<BdkOutPoint>,
37    unspendable: Vec<BdkOutPoint>,
38    internal_policy_path: Option<BTreeMap<String, Vec<usize>>>,
39    external_policy_path: Option<BTreeMap<String, Vec<usize>>>,
40    change_policy: ChangeSpendPolicy,
41    manually_selected_only: bool,
42    fee_rate: Option<FeeRate>,
43    fee_absolute: Option<Arc<Amount>>,
44    drain_wallet: bool,
45    drain_to: Option<BdkScriptBuf>,
46    sequence: Option<u32>,
47    data: Vec<u8>,
48    current_height: Option<u32>,
49    locktime: Option<LockTime>,
50    allow_dust: bool,
51    version: Option<i32>,
52    sighash: Option<BdkPsbtSighashType>,
53    ordering: TxOrdering,
54    exclude_unconfirmed: bool,
55    exclude_below_confirmations: Option<u32>,
56    only_witness_utxo: bool,
57    foreign_utxos: Vec<(BdkOutPoint, BdkInput, BdkWeight, Option<u32>)>,
58    coin_selection: Option<CoinSelectionAlgorithm>,
59}
60
61#[allow(clippy::new_without_default)]
62#[uniffi::export]
63impl TxBuilder {
64    #[uniffi::constructor]
65    pub fn new() -> Self {
66        TxBuilder {
67            add_global_xpubs: false,
68            recipients: Vec::new(),
69            utxos: Vec::new(),
70            unspendable: Vec::new(),
71            internal_policy_path: None,
72            external_policy_path: None,
73            change_policy: ChangeSpendPolicy::ChangeAllowed,
74            manually_selected_only: false,
75            fee_rate: None,
76            fee_absolute: None,
77            drain_wallet: false,
78            drain_to: None,
79            sequence: None,
80            data: Vec::new(),
81            current_height: None,
82            locktime: None,
83            allow_dust: false,
84            version: None,
85            sighash: None,
86            ordering: TxOrdering::Shuffle,
87            exclude_unconfirmed: false,
88            exclude_below_confirmations: None,
89            only_witness_utxo: false,
90            foreign_utxos: Vec::new(),
91            coin_selection: None,
92        }
93    }
94
95    /// Fill-in the `PSBT_GLOBAL_XPUB` field with the extended keys contained in both the external and internal
96    /// descriptors.
97    ///
98    /// This is useful for offline signers that take part to a multisig. Some hardware wallets like BitBox and ColdCard
99    /// are known to require this.
100    pub fn add_global_xpubs(&self) -> Arc<Self> {
101        Arc::new(TxBuilder {
102            add_global_xpubs: true,
103            ..self.clone()
104        })
105    }
106
107    /// Exclude outpoints whose enclosing transaction is unconfirmed.
108    /// This is a shorthand for exclude_below_confirmations(1).
109    pub fn exclude_unconfirmed(&self) -> Arc<Self> {
110        Arc::new(TxBuilder {
111            exclude_unconfirmed: true,
112            ..self.clone()
113        })
114    }
115
116    /// Excludes any outpoints whose enclosing transaction has fewer than `min_confirms`
117    /// confirmations.
118    ///
119    /// `min_confirms` is the minimum number of confirmations a transaction must have in order for
120    /// its outpoints to remain spendable.
121    /// - Passing `0` will include all transactions (no filtering).
122    /// - Passing `1` will exclude all unconfirmed transactions (equivalent to
123    ///   `exclude_unconfirmed`).
124    /// - Passing `6` will only allow outpoints from transactions with at least 6 confirmations.
125    ///
126    /// If you chain this with other filtering methods, the final set of unspendable outpoints will
127    /// be the union of all filters.
128    pub fn exclude_below_confirmations(&self, min_confirms: u32) -> Arc<Self> {
129        Arc::new(TxBuilder {
130            exclude_below_confirmations: Some(min_confirms),
131            ..self.clone()
132        })
133    }
134
135    /// Add a recipient to the internal list of recipients.
136    pub fn add_recipient(&self, script: &Script, amount: Arc<Amount>) -> Arc<Self> {
137        let mut recipients: Vec<(BdkScriptBuf, BdkAmount)> = self.recipients.clone();
138        recipients.append(&mut vec![(script.0.clone(), amount.0)]);
139
140        Arc::new(TxBuilder {
141            recipients,
142            ..self.clone()
143        })
144    }
145
146    /// Replace the recipients already added with a new list of recipients.
147    pub fn set_recipients(&self, recipients: Vec<ScriptAmount>) -> Arc<Self> {
148        let recipients = recipients
149            .iter()
150            .map(|script_amount| (script_amount.script.0.clone(), script_amount.amount.0)) //;
151            .collect();
152        Arc::new(TxBuilder {
153            recipients,
154            ..self.clone()
155        })
156    }
157
158    /// Add a utxo to the internal list of unspendable utxos.
159    ///
160    /// It’s important to note that the "must-be-spent" utxos added with `TxBuilder::add_utxo` have priority over this.
161    pub fn add_unspendable(&self, unspendable: OutPoint) -> Arc<Self> {
162        let mut unspendable_vec: Vec<BdkOutPoint> = self.unspendable.clone();
163        unspendable_vec.push(unspendable.into());
164
165        Arc::new(TxBuilder {
166            unspendable: unspendable_vec,
167            ..self.clone()
168        })
169    }
170
171    /// Replace the internal list of unspendable utxos with a new list.
172    ///
173    /// It’s important to note that the "must-be-spent" utxos added with `TxBuilder::add_utxo` have priority over these.
174    pub fn unspendable(&self, unspendable: Vec<OutPoint>) -> Arc<Self> {
175        let new_unspendable_vec: Vec<BdkOutPoint> =
176            unspendable.into_iter().map(BdkOutPoint::from).collect();
177
178        Arc::new(TxBuilder {
179            unspendable: new_unspendable_vec,
180            ..self.clone()
181        })
182    }
183
184    /// Add a utxo to the internal list of utxos that must be spent.
185    ///
186    /// These have priority over the "unspendable" utxos, meaning that if a utxo is present both in the "utxos" and the
187    /// "unspendable" list, it will be spent.
188    pub fn add_utxo(&self, outpoint: OutPoint) -> Arc<Self> {
189        self.add_utxos(vec![outpoint])
190    }
191
192    /// Add the list of outpoints to the internal list of UTXOs that must be spent.
193    //
194    // If an error occurs while adding any of the UTXOs then none of them are added and the error is returned.
195    //
196    // These have priority over the “unspendable” utxos, meaning that if a utxo is present both in the “utxos” and the “unspendable” list, it will be spent.
197    pub fn add_utxos(&self, outpoints: Vec<OutPoint>) -> Arc<Self> {
198        let mut utxos: Vec<BdkOutPoint> = self.utxos.clone();
199        utxos.extend(outpoints.into_iter().map(BdkOutPoint::from));
200        Arc::new(TxBuilder {
201            utxos,
202            ..self.clone()
203        })
204    }
205
206    /// The TxBuilder::policy_path is a complex API. See the Rust docs for complete       information: https://docs.rs/bdk_wallet/latest/bdk_wallet/struct.TxBuilder.html#method.policy_path
207    pub fn policy_path(
208        &self,
209        policy_path: HashMap<String, Vec<u64>>,
210        keychain: KeychainKind,
211    ) -> Arc<Self> {
212        let mut updated_self = self.clone();
213        let to_update = match keychain {
214            KeychainKind::Internal => &mut updated_self.internal_policy_path,
215            KeychainKind::External => &mut updated_self.external_policy_path,
216        };
217        *to_update = Some(
218            policy_path
219                .into_iter()
220                .map(|(key, value)| (key, value.into_iter().map(|x| x as usize).collect()))
221                .collect::<BTreeMap<String, Vec<usize>>>(),
222        );
223        Arc::new(updated_self)
224    }
225
226    /// Set a specific `ChangeSpendPolicy`. See `TxBuilder::do_not_spend_change` and `TxBuilder::only_spend_change` for
227    /// some shortcuts. This method assumes the presence of an internal keychain, otherwise it has no effect.
228    pub fn change_policy(&self, change_policy: ChangeSpendPolicy) -> Arc<Self> {
229        Arc::new(TxBuilder {
230            change_policy,
231            ..self.clone()
232        })
233    }
234
235    /// Do not spend change outputs.
236    ///
237    /// This effectively adds all the change outputs to the "unspendable" list. See `TxBuilder::unspendable`. This method
238    /// assumes the presence of an internal keychain, otherwise it has no effect.
239    pub fn do_not_spend_change(&self) -> Arc<Self> {
240        Arc::new(TxBuilder {
241            change_policy: ChangeSpendPolicy::ChangeForbidden,
242            ..self.clone()
243        })
244    }
245
246    /// Only spend change outputs.
247    ///
248    /// This effectively adds all the non-change outputs to the "unspendable" list. See `TxBuilder::unspendable`. This
249    /// method assumes the presence of an internal keychain, otherwise it has no effect.
250    pub fn only_spend_change(&self) -> Arc<Self> {
251        Arc::new(TxBuilder {
252            change_policy: ChangeSpendPolicy::OnlyChange,
253            ..self.clone()
254        })
255    }
256
257    /// Only spend utxos added by `TxBuilder::add_utxo`.
258    ///
259    /// The wallet will not add additional utxos to the transaction even if they are needed to make the transaction valid.
260    pub fn manually_selected_only(&self) -> Arc<Self> {
261        Arc::new(TxBuilder {
262            manually_selected_only: true,
263            ..self.clone()
264        })
265    }
266
267    /// Set a custom fee rate.
268    ///
269    /// This method sets the mining fee paid by the transaction as a rate on its size. This means that the total fee paid
270    /// is equal to fee_rate times the size of the transaction. Default is 1 sat/vB in accordance with Bitcoin Core’s
271    /// default relay policy.
272    ///
273    /// Note that this is really a minimum feerate – it’s possible to overshoot it slightly since adding a change output
274    /// to drain the remaining excess might not be viable.
275    pub fn fee_rate(&self, fee_rate: &FeeRate) -> Arc<Self> {
276        Arc::new(TxBuilder {
277            fee_rate: Some(fee_rate.clone()),
278            fee_absolute: None,
279            ..self.clone()
280        })
281    }
282
283    /// Set an absolute fee The `fee_absolute` method refers to the absolute transaction fee in `Amount`. If anyone sets
284    /// both the `fee_absolute` method and the `fee_rate` method, the `FeePolicy` enum will be set by whichever method was
285    /// called last, as the `FeeRate` and `FeeAmount` are mutually exclusive.
286    ///
287    /// Note that this is really a minimum absolute fee – it’s possible to overshoot it slightly since adding a change output to drain the remaining excess might not be viable.
288    pub fn fee_absolute(&self, fee_amount: Arc<Amount>) -> Arc<Self> {
289        Arc::new(TxBuilder {
290            fee_rate: None,
291            fee_absolute: Some(fee_amount),
292            ..self.clone()
293        })
294    }
295
296    /// Spend all the available inputs. This respects filters like `TxBuilder::unspendable` and the change policy.
297    pub fn drain_wallet(&self) -> Arc<Self> {
298        Arc::new(TxBuilder {
299            drain_wallet: true,
300            ..self.clone()
301        })
302    }
303
304    /// Sets the address to drain excess coins to.
305    ///
306    /// Usually, when there are excess coins they are sent to a change address generated by the wallet. This option
307    /// replaces the usual change address with an arbitrary script_pubkey of your choosing. Just as with a change output,
308    /// if the drain output is not needed (the excess coins are too small) it will not be included in the resulting
309    /// transaction. The only difference is that it is valid to use `drain_to` without setting any ordinary recipients
310    /// with `add_recipient` (but it is perfectly fine to add recipients as well).
311    ///
312    /// If you choose not to set any recipients, you should provide the utxos that the transaction should spend via
313    /// `add_utxos`. `drain_to` is very useful for draining all the coins in a wallet with `drain_wallet` to a single
314    /// address.
315    pub fn drain_to(&self, script: &Script) -> Arc<Self> {
316        Arc::new(TxBuilder {
317            drain_to: Some(script.0.clone()),
318            ..self.clone()
319        })
320    }
321
322    /// Set an exact `nSequence` value.
323    ///
324    /// This can cause conflicts if the wallet’s descriptors contain an "older" (`OP_CSV`) operator and the given
325    /// `nsequence` is lower than the CSV value.
326    pub fn set_exact_sequence(&self, nsequence: u32) -> Arc<Self> {
327        Arc::new(TxBuilder {
328            sequence: Some(nsequence),
329            ..self.clone()
330        })
331    }
332
333    /// Add data as an output using `OP_RETURN`.
334    pub fn add_data(&self, data: Vec<u8>) -> Arc<Self> {
335        Arc::new(TxBuilder {
336            data,
337            ..self.clone()
338        })
339    }
340
341    /// Set the current blockchain height.
342    ///
343    /// This will be used to:
344    ///
345    /// 1. Set the `nLockTime` for preventing fee sniping. Note: This will be ignored if you manually specify a
346    ///    `nlocktime` using `TxBuilder::nlocktime`.
347    ///
348    /// 2. Decide whether coinbase outputs are mature or not. If the coinbase outputs are not mature
349    ///    at spending height, which is `current_height` + 1, we ignore them in the coin selection.
350    ///    If you want to create a transaction that spends immature coinbase inputs,
351    ///    manually add them using `TxBuilder::add_utxos`.
352    ///    In both cases, if you don’t provide a current height, we use the last sync height.
353    pub fn current_height(&self, height: u32) -> Arc<Self> {
354        Arc::new(TxBuilder {
355            current_height: Some(height),
356            ..self.clone()
357        })
358    }
359
360    /// Use a specific nLockTime while creating the transaction.
361    ///
362    /// This can cause conflicts if the wallet’s descriptors contain an "after" (`OP_CLTV`) operator.
363    pub fn nlocktime(&self, locktime: LockTime) -> Arc<Self> {
364        Arc::new(TxBuilder {
365            locktime: Some(locktime),
366            ..self.clone()
367        })
368    }
369
370    /// Set whether or not the dust limit is checked.
371    ///
372    /// Note: by avoiding a dust limit check you may end up with a transaction that is non-standard.
373    pub fn allow_dust(&self, allow_dust: bool) -> Arc<Self> {
374        Arc::new(TxBuilder {
375            allow_dust,
376            ..self.clone()
377        })
378    }
379
380    /// Build a transaction with a specific version.
381    ///
382    /// The version should always be greater than 0 and greater than 1 if the wallet's descriptors contain an "older"
383    /// (`OP_CSV`) operator.
384    pub fn version(&self, version: i32) -> Arc<Self> {
385        Arc::new(TxBuilder {
386            version: Some(version),
387            ..self.clone()
388        })
389    }
390
391    /// Sign with a specific sig hash
392    ///
393    /// **Use this option very carefully**
394    pub fn sighash(&self, sighash: String) -> Result<Arc<Self>, SighashParseError> {
395        let sighash = parse_sighash_type(&sighash)?;
396        Ok(Arc::new(TxBuilder {
397            sighash: Some(sighash),
398            ..self.clone()
399        }))
400    }
401
402    /// Choose the ordering for inputs and outputs of the transaction
403    ///
404    /// When [TxBuilder::ordering] is set to [TxOrdering::Untouched], the insertion order of
405    /// recipients and manually selected UTXOs is preserved and reflected exactly in transaction's
406    /// output and input vectors respectively. If algorithmically selected UTXOs are included, they
407    /// will be placed after all the manually selected ones in the transaction's input vector.
408    pub fn ordering(&self, ordering: TxOrdering) -> Arc<Self> {
409        Arc::new(TxBuilder {
410            ordering,
411            ..self.clone()
412        })
413    }
414
415    /// Choose the coin selection algorithm
416    pub fn coin_selection(&self, coin_selection: CoinSelectionAlgorithm) -> Arc<Self> {
417        Arc::new(TxBuilder {
418            coin_selection: Some(coin_selection),
419            ..self.clone()
420        })
421    }
422
423    /// Only Fill-in the [`psbt::Input::witness_utxo`](bitcoin::psbt::Input::witness_utxo) field
424    /// when spending from SegWit descriptors.
425    ///
426    /// This reduces the size of the PSBT, but some signers might reject them due to the lack of
427    /// the `non_witness_utxo`.
428    pub fn only_witness_utxo(&self) -> Arc<Self> {
429        Arc::new(TxBuilder {
430            only_witness_utxo: true,
431            ..self.clone()
432        })
433    }
434
435    /// Add a foreign UTXO i.e. a UTXO not known by this wallet.
436    ///
437    /// Foreign UTXOs are not prioritized over local UTXOs. If a local UTXO is added to the
438    /// manually selected list, it will replace any conflicting foreign UTXOs. However, a foreign
439    /// UTXO cannot replace a conflicting local UTXO.
440    ///
441    /// There might be cases where the UTXO belongs to the wallet but it doesn't have knowledge of
442    /// it. This is possible if the wallet is not synced or its not being use to track
443    /// transactions. In those cases is the responsibility of the user to add any possible local
444    /// UTXOs through the [`TxBuilder::add_utxo`] method.
445    /// A manually added local UTXO will always have greater precedence than a foreign UTXO. No
446    /// matter if it was added before or after the foreign UTXO.
447    ///
448    /// At a minimum to add a foreign UTXO we need:
449    ///
450    /// 1. `outpoint`: To add it to the raw transaction.
451    /// 2. `psbt_input`: To know the value.
452    /// 3. `satisfaction_weight`: To know how much weight/vbytes the input will add to the
453    ///    transaction for fee calculation.
454    ///
455    /// There are several security concerns about adding foreign UTXOs that application
456    /// developers should consider. First, how do you know the value of the input is correct? If a
457    /// `non_witness_utxo` is provided in the `psbt_input` then this method implicitly verifies the
458    /// value by checking it against the transaction. If only a `witness_utxo` is provided then this
459    /// method doesn't verify the value but just takes it as a given -- it is up to you to check
460    /// that whoever sent you the `input_psbt` was not lying!
461    ///
462    /// Secondly, you must somehow provide `satisfaction_weight` of the input. Depending on your
463    /// application it may be important that this be known precisely. If not, a malicious
464    /// counterparty may fool you into putting in a value that is too low, giving the transaction a
465    /// lower than expected feerate. They could also fool you into putting a value that is too high
466    /// causing you to pay a fee that is too high. The party who is broadcasting the transaction can
467    /// of course check the real input weight matches the expected weight prior to broadcasting.
468    ///
469    /// To guarantee the `max_weight_to_satisfy` is correct, you can require the party providing the
470    /// `psbt_input` provide a miniscript descriptor for the input so you can check it against the
471    /// `script_pubkey` and then ask it for the [`max_weight_to_satisfy`].
472    ///
473    /// This is an **EXPERIMENTAL** feature, API and other major changes are expected.
474    ///
475    /// In order to use [`Wallet::calculate_fee`] or [`Wallet::calculate_fee_rate`] for a
476    /// transaction created with foreign UTXO(s) you must manually insert the corresponding
477    /// TxOut(s) into the tx graph using the [`Wallet::insert_txout`] function.
478    ///
479    /// # Errors
480    ///
481    /// This method returns errors in the following circumstances:
482    ///
483    /// 1. The `psbt_input` does not contain a `witness_utxo` or `non_witness_utxo`.
484    /// 2. The data in `non_witness_utxo` does not match what is in `outpoint`.
485    ///
486    /// Note unless you set [`only_witness_utxo`] any non-taproot `psbt_input` you pass to this
487    /// method must have `non_witness_utxo` set otherwise you will get an error when [`finish`]
488    /// is called.
489    ///
490    /// [`only_witness_utxo`]: Self::only_witness_utxo
491    /// [`finish`]: Self::finish
492    /// [`max_weight_to_satisfy`]: miniscript::Descriptor::max_weight_to_satisfy
493    pub fn add_foreign_utxo(
494        &self,
495        outpoint: OutPoint,
496        psbt_input: Input,
497        satisfaction_weight: u64,
498    ) -> Result<Arc<Self>, AddForeignUtxoError> {
499        let bdk_outpoint: BdkOutPoint = outpoint.into();
500        let bdk_input: BdkInput = psbt_input.try_into()?;
501
502        if let Some(tx) = bdk_input.non_witness_utxo.as_ref() {
503            if tx.compute_txid() != bdk_outpoint.txid {
504                return Err(AddForeignUtxoError::InvalidTxid);
505            }
506            if tx.output.len() <= bdk_outpoint.vout as usize {
507                return Err(AddForeignUtxoError::InvalidOutpoint {
508                    outpoint: bdk_outpoint.to_string(),
509                });
510            }
511        } else if bdk_input.witness_utxo.is_none() {
512            return Err(AddForeignUtxoError::MissingUtxo);
513        }
514
515        let bdk_weight = BdkWeight::from_wu(satisfaction_weight);
516
517        let mut foreign_utxos = self.foreign_utxos.clone();
518        foreign_utxos.push((bdk_outpoint, bdk_input, bdk_weight, None));
519
520        Ok(Arc::new(TxBuilder {
521            foreign_utxos,
522            ..self.clone()
523        }))
524    }
525
526    /// Same as [add_foreign_utxo](TxBuilder::add_foreign_utxo) but allows to set the nSequence
527    /// value.
528    pub fn add_foreign_utxo_with_sequence(
529        &self,
530        outpoint: OutPoint,
531        psbt_input: Input,
532        satisfaction_weight: u64,
533        sequence: u32,
534    ) -> Result<Arc<Self>, AddForeignUtxoError> {
535        let bdk_outpoint: BdkOutPoint = outpoint.into();
536        let bdk_input: BdkInput = psbt_input.try_into()?;
537
538        if let Some(tx) = bdk_input.non_witness_utxo.as_ref() {
539            if tx.compute_txid() != bdk_outpoint.txid {
540                return Err(AddForeignUtxoError::InvalidTxid);
541            }
542            if tx.output.len() <= bdk_outpoint.vout as usize {
543                return Err(AddForeignUtxoError::InvalidOutpoint {
544                    outpoint: bdk_outpoint.to_string(),
545                });
546            }
547        } else if bdk_input.witness_utxo.is_none() {
548            return Err(AddForeignUtxoError::MissingUtxo);
549        }
550
551        let bdk_weight = BdkWeight::from_wu(satisfaction_weight);
552
553        let mut foreign_utxos = self.foreign_utxos.clone();
554        foreign_utxos.push((bdk_outpoint, bdk_input, bdk_weight, Some(sequence)));
555
556        Ok(Arc::new(TxBuilder {
557            foreign_utxos,
558            ..self.clone()
559        }))
560    }
561
562    /// Finish building the transaction.
563    ///
564    /// Uses the thread-local random number generator (rng).
565    ///
566    /// Returns a new `Psbt` per BIP174.
567    ///
568    /// WARNING: To avoid change address reuse you must persist the changes resulting from one or more calls to this
569    /// method before closing the wallet. See `Wallet::reveal_next_address`.
570    pub fn finish(&self, wallet: &Arc<Wallet>) -> Result<Arc<Psbt>, CreateTxError> {
571        // TODO: I had to change the wallet here to be mutable. Why is that now required with the 1.0 API?
572        let mut wallet = wallet.get_wallet();
573        let tx_builder = wallet.build_tx();
574        match self.coin_selection {
575            None | Some(CoinSelectionAlgorithm::BranchAndBound) => {
576                self.finish_with_builder(tx_builder)
577            }
578            Some(CoinSelectionAlgorithm::LargestFirst) => {
579                self.finish_with_builder(tx_builder.coin_selection(BdkLargestFirstCoinSelection))
580            }
581            Some(CoinSelectionAlgorithm::OldestFirst) => {
582                self.finish_with_builder(tx_builder.coin_selection(BdkOldestFirstCoinSelection))
583            }
584            Some(CoinSelectionAlgorithm::SingleRandomDraw) => {
585                self.finish_with_builder(tx_builder.coin_selection(BdkSingleRandomDraw))
586            }
587        }
588    }
589}
590
591impl TxBuilder {
592    fn finish_with_builder<Cs>(
593        &self,
594        mut tx_builder: bdk_wallet::TxBuilder<'_, Cs>,
595    ) -> Result<Arc<Psbt>, CreateTxError>
596    where
597        Cs: BdkCoinSelectionAlgorithm,
598    {
599        if self.add_global_xpubs {
600            tx_builder.add_global_xpubs();
601        }
602        for (script, amount) in &self.recipients {
603            tx_builder.add_recipient(script.clone(), *amount);
604        }
605        if let Some(policy_path) = &self.external_policy_path {
606            tx_builder.policy_path(policy_path.clone(), KeychainKind::External);
607        }
608        if let Some(policy_path) = &self.internal_policy_path {
609            tx_builder.policy_path(policy_path.clone(), KeychainKind::Internal);
610        }
611        tx_builder.change_policy(self.change_policy);
612        if !self.utxos.is_empty() {
613            tx_builder
614                .add_utxos(&self.utxos)
615                .map_err(CreateTxError::from)?;
616        }
617        if !self.unspendable.is_empty() {
618            tx_builder.unspendable(self.unspendable.clone());
619        }
620        if self.manually_selected_only {
621            tx_builder.manually_selected_only();
622        }
623        if let Some(fee_rate) = &self.fee_rate {
624            tx_builder.fee_rate(fee_rate.0);
625        }
626        if let Some(fee_amount) = &self.fee_absolute {
627            tx_builder.fee_absolute(fee_amount.0);
628        }
629        if self.drain_wallet {
630            tx_builder.drain_wallet();
631        }
632        if let Some(script) = &self.drain_to {
633            tx_builder.drain_to(script.clone());
634        }
635        if let Some(sequence) = self.sequence {
636            tx_builder.set_exact_sequence(Sequence(sequence));
637        }
638        if !&self.data.is_empty() {
639            let push_bytes = PushBytesBuf::try_from(self.data.clone())?;
640            tx_builder.add_data(&push_bytes);
641        }
642        if let Some(height) = self.current_height {
643            let height = BdkHeight::from_consensus(height)
644                .map_err(|_| CreateTxError::LockTimeConversionError)?;
645            tx_builder.current_height(height.to_consensus_u32());
646        }
647        if let Some(locktime) = &self.locktime {
648            let bdk_locktime: BdkLockTime = locktime.try_into()?;
649            tx_builder.nlocktime(bdk_locktime);
650        }
651        if self.allow_dust {
652            tx_builder.allow_dust(self.allow_dust);
653        }
654        if let Some(version) = self.version {
655            tx_builder.version(version);
656        }
657        if let Some(sighash) = self.sighash {
658            tx_builder.sighash(sighash);
659        }
660        tx_builder.ordering(self.ordering.into());
661        if self.exclude_unconfirmed {
662            tx_builder.exclude_unconfirmed();
663        }
664        if let Some(min_confirms) = self.exclude_below_confirmations {
665            tx_builder.exclude_below_confirmations(min_confirms);
666        }
667        if self.only_witness_utxo {
668            tx_builder.only_witness_utxo();
669        }
670        for (outpoint, input, weight, sequence) in &self.foreign_utxos {
671            match sequence {
672                Some(sequence) => tx_builder
673                    .add_foreign_utxo_with_sequence(
674                        *outpoint,
675                        input.clone(),
676                        *weight,
677                        Sequence(*sequence),
678                    )
679                    .map_err(AddForeignUtxoError::from)?,
680                None => tx_builder
681                    .add_foreign_utxo(*outpoint, input.clone(), *weight)
682                    .map_err(AddForeignUtxoError::from)?,
683            };
684        }
685        let psbt = tx_builder.finish().map_err(CreateTxError::from)?;
686
687        Ok(Arc::new(psbt.into()))
688    }
689}
690
691/// A `BumpFeeTxBuilder` is created by calling `build_fee_bump` on a wallet. After assigning it, you set options on it
692/// until finally calling `finish` to consume the builder and generate the transaction.
693#[derive(Clone, uniffi::Object)]
694pub struct BumpFeeTxBuilder {
695    txid: Arc<Txid>,
696    fee_rate: Arc<FeeRate>,
697    sequence: Option<u32>,
698    current_height: Option<u32>,
699    locktime: Option<LockTime>,
700    allow_dust: bool,
701    version: Option<i32>,
702    sighash: Option<BdkPsbtSighashType>,
703    ordering: TxOrdering,
704}
705
706#[uniffi::export]
707impl BumpFeeTxBuilder {
708    #[uniffi::constructor]
709    pub fn new(txid: Arc<Txid>, fee_rate: Arc<FeeRate>) -> Self {
710        BumpFeeTxBuilder {
711            txid,
712            fee_rate,
713            sequence: None,
714            current_height: None,
715            locktime: None,
716            allow_dust: false,
717            version: None,
718            sighash: None,
719            ordering: TxOrdering::Shuffle,
720        }
721    }
722
723    /// Set an exact `nSequence` value.
724    ///
725    /// This can cause conflicts if the wallet’s descriptors contain an "older" (`OP_CSV`) operator and the given
726    /// `nsequence` is lower than the CSV value.
727    pub fn set_exact_sequence(&self, nsequence: u32) -> Arc<Self> {
728        Arc::new(BumpFeeTxBuilder {
729            sequence: Some(nsequence),
730            ..self.clone()
731        })
732    }
733
734    /// Set the current blockchain height.
735    ///
736    /// This will be used to:
737    ///
738    /// 1. Set the `nLockTime` for preventing fee sniping. Note: This will be ignored if you manually specify a
739    ///    `nlocktime` using `TxBuilder::nlocktime`.
740    ///
741    /// 2. Decide whether coinbase outputs are mature or not. If the coinbase outputs are not mature
742    ///    at spending height, which is `current_height` + 1, we ignore them in the coin selection.
743    ///    If you want to create a transaction that spends immature coinbase inputs,
744    ///    manually add them using `TxBuilder::add_utxos`.
745    ///    In both cases, if you don’t provide a current height, we use the last sync height.
746    pub fn current_height(&self, height: u32) -> Arc<Self> {
747        Arc::new(BumpFeeTxBuilder {
748            current_height: Some(height),
749            ..self.clone()
750        })
751    }
752
753    /// Use a specific nLockTime while creating the transaction.
754    ///
755    /// This can cause conflicts if the wallet’s descriptors contain an "after" (`OP_CLTV`) operator.
756    pub fn nlocktime(&self, locktime: LockTime) -> Arc<Self> {
757        Arc::new(BumpFeeTxBuilder {
758            locktime: Some(locktime),
759            ..self.clone()
760        })
761    }
762
763    /// Set whether the dust limit is checked.
764    ///
765    /// Note: by avoiding a dust limit check you may end up with a transaction that is non-standard.
766    pub fn allow_dust(&self, allow_dust: bool) -> Arc<Self> {
767        Arc::new(BumpFeeTxBuilder {
768            allow_dust,
769            ..self.clone()
770        })
771    }
772
773    /// Build a transaction with a specific version.
774    ///
775    /// The version should always be greater than 0 and greater than 1 if the wallet’s descriptors contain an "older"
776    /// (`OP_CSV`) operator.
777    pub fn version(&self, version: i32) -> Arc<Self> {
778        Arc::new(BumpFeeTxBuilder {
779            version: Some(version),
780            ..self.clone()
781        })
782    }
783
784    /// Sign with a specific sig hash
785    ///
786    /// **Use this option very carefully**
787    pub fn sighash(&self, sighash: String) -> Result<Arc<Self>, SighashParseError> {
788        let sighash = parse_sighash_type(&sighash)?;
789        Ok(Arc::new(BumpFeeTxBuilder {
790            sighash: Some(sighash),
791            ..self.clone()
792        }))
793    }
794
795    /// Choose the ordering for inputs and outputs of the transaction
796    ///
797    /// When [TxBuilder::ordering] is set to [TxOrdering::Untouched], the insertion order of
798    /// recipients and manually selected UTXOs is preserved and reflected exactly in transaction's
799    /// output and input vectors respectively. If algorithmically selected UTXOs are included, they
800    /// will be placed after all the manually selected ones in the transaction's input vector.
801    pub fn ordering(&self, ordering: TxOrdering) -> Arc<Self> {
802        Arc::new(BumpFeeTxBuilder {
803            ordering,
804            ..self.clone()
805        })
806    }
807
808    /// Finish building the transaction.
809    ///
810    /// Uses the thread-local random number generator (rng).
811    ///
812    /// Returns a new `Psbt` per BIP174.
813    ///
814    /// WARNING: To avoid change address reuse you must persist the changes resulting from one or more calls to this
815    /// method before closing the wallet. See `Wallet::reveal_next_address`.
816    pub fn finish(&self, wallet: &Arc<Wallet>) -> Result<Arc<Psbt>, CreateTxError> {
817        let mut wallet = wallet.get_wallet();
818        let mut tx_builder = wallet
819            .build_fee_bump(self.txid.0)
820            .map_err(CreateTxError::from)?;
821        tx_builder.fee_rate(self.fee_rate.0);
822        if let Some(sequence) = self.sequence {
823            tx_builder.set_exact_sequence(Sequence(sequence));
824        }
825        if let Some(height) = self.current_height {
826            let height = BdkHeight::from_consensus(height)
827                .map_err(|_| CreateTxError::LockTimeConversionError)?;
828            tx_builder.current_height(height.to_consensus_u32());
829        }
830        if let Some(locktime) = &self.locktime {
831            let bdk_locktime: BdkLockTime = locktime.try_into()?;
832            tx_builder.nlocktime(bdk_locktime);
833        }
834        if self.allow_dust {
835            tx_builder.allow_dust(self.allow_dust);
836        }
837        if let Some(version) = self.version {
838            tx_builder.version(version);
839        }
840        if let Some(sighash) = self.sighash {
841            tx_builder.sighash(sighash);
842        }
843        tx_builder.ordering(self.ordering.into());
844
845        let psbt: BdkPsbt = tx_builder.finish()?;
846
847        Ok(Arc::new(psbt.into()))
848    }
849}
850
851/// Policy regarding the use of change outputs when creating a transaction.
852#[uniffi::remote(Enum)]
853pub enum ChangeSpendPolicy {
854    /// Use both change and non-change outputs (default).
855    #[default]
856    ChangeAllowed,
857    /// Only use change outputs (see [`bdk_wallet::TxBuilder::only_spend_change`]).
858    OnlyChange,
859    /// Only use non-change outputs (see [`bdk_wallet::TxBuilder::do_not_spend_change`]).
860    ChangeForbidden,
861}
862
863/// Coin selection algorithm to use when creating a transaction.
864#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, uniffi::Enum)]
865pub enum CoinSelectionAlgorithm {
866    /// Branch and bound with single random draw fallback.
867    #[default]
868    BranchAndBound,
869    /// Select largest UTXOs first until the target is reached.
870    LargestFirst,
871    /// Select oldest local UTXOs first until the target is reached.
872    OldestFirst,
873    /// Shuffle optional UTXOs and select randomly until the target is reached.
874    SingleRandomDraw,
875}
876
877/// Ordering of the transaction's inputs and outputs.
878#[derive(Clone, Copy, Debug, Default, uniffi::Enum)]
879pub enum TxOrdering {
880    /// Randomized (default)
881    #[default]
882    Shuffle,
883    /// Untouched
884    ///
885    /// Untouched insertion order for recipients and for manually added UTXOs. This guarantees all
886    /// recipients preserve insertion order in the transaction's output vector and manually added
887    /// UTXOs preserve insertion order in the transaction's input vector, but does not make any
888    /// guarantees about algorithmically selected UTXOs. However, by design they will always be
889    /// placed after the manually selected ones.
890    Untouched,
891}
892
893impl From<TxOrdering> for BdkTxOrdering {
894    fn from(value: TxOrdering) -> Self {
895        match value {
896            TxOrdering::Shuffle => BdkTxOrdering::Shuffle,
897            TxOrdering::Untouched => BdkTxOrdering::Untouched,
898        }
899    }
900}
901
902fn parse_sighash_type(sighash: &str) -> Result<BdkPsbtSighashType, SighashParseError> {
903    BdkPsbtSighashType::from_str(sighash).map_err(|error| SighashParseError::Invalid {
904        error_message: error.to_string(),
905    })
906}
907
908#[cfg(test)]
909mod tests {
910    use super::{CoinSelectionAlgorithm, TxBuilder};
911    use crate::bitcoin::{Amount, FeeRate};
912    use std::sync::Arc;
913
914    #[test]
915    fn tx_builder_defaults_to_upstream_coin_selection() {
916        assert_eq!(TxBuilder::new().coin_selection, None);
917    }
918
919    #[test]
920    fn tx_builder_coin_selection_sets_algorithm_and_keeps_chainable_state() {
921        let tx_builder = TxBuilder::new()
922            .coin_selection(CoinSelectionAlgorithm::LargestFirst)
923            .manually_selected_only();
924
925        assert_eq!(
926            tx_builder.coin_selection,
927            Some(CoinSelectionAlgorithm::LargestFirst)
928        );
929        assert!(tx_builder.manually_selected_only);
930    }
931
932    #[test]
933    fn tx_builder_coin_selection_accepts_all_supported_algorithms() {
934        let algorithms = [
935            CoinSelectionAlgorithm::BranchAndBound,
936            CoinSelectionAlgorithm::LargestFirst,
937            CoinSelectionAlgorithm::OldestFirst,
938            CoinSelectionAlgorithm::SingleRandomDraw,
939        ];
940
941        for algorithm in algorithms {
942            let tx_builder = TxBuilder::new().coin_selection(algorithm);
943            assert_eq!(tx_builder.coin_selection, Some(algorithm));
944        }
945    }
946
947    #[test]
948    fn tx_builder_fee_absolute_overrides_fee_rate() {
949        let fee_rate = FeeRate::from_sat_per_vb(2).unwrap();
950        let tx_builder = TxBuilder::new()
951            .fee_rate(&fee_rate)
952            .fee_absolute(Arc::new(Amount::from_sat(500)));
953
954        assert!(tx_builder.fee_rate.is_none());
955        assert_eq!(
956            tx_builder.fee_absolute.as_ref().map(|fee| fee.to_sat()),
957            Some(500)
958        );
959    }
960
961    #[test]
962    fn tx_builder_fee_rate_overrides_fee_absolute() {
963        let fee_rate = FeeRate::from_sat_per_vb(2).unwrap();
964        let tx_builder = TxBuilder::new()
965            .fee_absolute(Arc::new(Amount::from_sat(500)))
966            .fee_rate(&fee_rate);
967
968        assert!(tx_builder.fee_absolute.is_none());
969        assert_eq!(
970            tx_builder
971                .fee_rate
972                .as_ref()
973                .map(FeeRate::to_sat_per_vb_ceil),
974            Some(2)
975        );
976    }
977}