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}