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