---
# System prepended metadata

title: TxBuilder redesign
tags: [specs]

---

# TxBuilder redesign

Goals of the redesign:
- More intuitive design
- Easier to customize (how group coins, coin selection, etc)
- Less trait heavy (I'm looking at you, CoinSelectionAlgorithm)
- Keep it simple: most users won't customize the cs or coin grouping, the API should still feel lightweight for them
- Avoid unclear behaviors
    - Example: TxBuilder now allows setting both feerate and fee absolute
    - Example: TxBuilder now allows inserting an utxo both in the must be spent and in the unspendable list

Issues with the current tx design:
- Collection issue: [#988](https://github.com/bitcoindevkit/bdk/issues/988)
- CS fallback algorithm can't be changed
- (Somewhat related) Remove Database type paramter from CoinSelectionAlgorithm [#281](https://github.com/bitcoindevkit/bdk/issues/281)
- UTXO locking [#849](https://github.com/bitcoindevkit/bdk/issues/849)
- Maybe controversial, but we should RBF by default [#791](https://github.com/bitcoindevkit/bdk/issues/791)
- Support merging multiple transactions while bumping the fees (https://github.com/bitcoindevkit/bdk/blob/master/crates/bdk/src/wallet/mod.rs#L1017)
- `TxBuilder::add_psbt` [#153](https://github.com/bitcoindevkit/bdk/issues/153)
- Selecting the change addresss type to be same as recipient's address
- 
- TO CHECK: is it possible to put a relative timelock on the tx? I don't see any nsequence param in TxParams

This document assumes a very basic understanding of rust-miniscript planning module. More info here: [rust-miniscript + bdk - planning module](/zNsz5LV9S06l1xTa8-6yqg)

The main idea expressed in [#899](https://github.com/bitcoindevkit/bdk/issues/899) is to split the TxBuilder in stages. I'm copying the stages here for clarity, and adding an obvious first step which was missing in the issue

## 0. User options
- User will use the `TxParams` struct to define how to build the transaction. My proposal for the `TxParams` struct:
```rust
pub struct TxParams<K> {
    pub recipients: Vec<(Script, AmountOrDrain)>,
    pub change_keychain: K /* TODO: document that the change output might not be there */
    /* same as the current txparams: */
    pub fee_policy: FeePolicy,
    pub sighash: psbt::PsbtSighashType,
    pub locktime: LockTime,
    pub rbf: RbfValue,
    pub version: Version,
    pub allow_dust: bool,
    pub current_height: Option<u32>,
}

pub enum AmountOrDrain {
    Amount(Amount),
    Drain(/* ratio */),
}
```
- New fields:
    - `recipients: Vec<(Script, AmountOrDrain)>` 
        - For each script I can either provide an amount to send, or ask bdk to drain the wallet.
        - This is I think pretty powerful as, for example, on can express "Send 10BTC to Alice, and the remaining split between Bob and Charlie" as `{'Alice':'10', 'Bob': 'Drain(1)', 'Charlie': 'Drain(1)'}`.
        - This replaces the current [drain_wallet](https://docs.rs/bdk/latest/bdk/wallet/tx_builder/struct.TxBuilder.html#method.drain_wallet)/[drain_to](https://docs.rs/bdk/latest/bdk/wallet/tx_builder/struct.TxBuilder.html#method.drain_to).
    - `change_keychain: K`
        - where to send the change.
        - TODO: figure out how to provide a nice default. Maybe we want wallets to have one change keychain and N main keychains? Maybe we want keychains with different roles and spend priorities?

## 1. Coin control
- The `CoinControl` structure is useful for assigning a plan to each utxo, and grouping them. 
- Each Utxo can either be a `Planned` one or a `PsbtInput` one:
```rust
pub enum Utxo<P> {
    Planned {
        outpoint: OutPoint,
        txout: TxOut,
        confirmation_time: ConfirmationTime,
        plan_id: Option<P>,
    },
    PsbtInput {
        outpoint: OutPoint,
        satisfaction_weight: u32,
        psbt_input: PsbtInput,
    },
}
```
- Utxos are grouped in groups, in a structure called `CoinGroup`. Each group has a set of "unspendable" utxos, which either don't have a plan, the user explicitly marked as "unspendable", or are locked. This means that some groups are partially spendable - the user might not want to spend a partial group due to privacy loss.
- The `CoinControl` structure will contain all the utxo groups, and each utxo will have a reference to its respective plans. It will also hold a `grouping_fn: Fn(&Utxo) -> G` that is going to be used to assing a specific group to a coin.
- The `grouping_fn` has to be in `CoinControl` and not passed externally every time utxos are added, to make sure that the user will use the same function for all the utxos.
- The default `grouping_fn` will group coins by locking script:
    - For planned utxos, we know the locking script
    - For psbt inputs, we know the locking script if we have either the witness utxo or the non witness utxo. If we don't have either, we have to create a "fake script" that is unique to the coin, so that we can put the coin in a group by itself.
- `CoinControl` is going to be populated through the `add_utxos` method, which accepts a vector of utxos all related to a certain descriptor, and the list of assets that can be used:
```rust
fn add_utxos(&self, utxos: Vec<Utxo>, descriptor: Descriptor<??>, Assets: &[Asset], unspendable: Vec<OutPoint>) {
}
```
- Users are required to pass all the possible utxos to add_utxos, even the ones that they don't want to spend, provided that they also add it to the `unspendable` list
- TODO: figure out coins that can't be spent together. AFAIK we need some additional logic in the coin selection to support this

## 2. Coin selection (WIP)
- Select coins for the actual transaction.
- The coin selection step can be skipped if the user is manually selecting the coins. This means that we don't need any `manually_selected_only` option
- Handling `must_be_used`: user can just ask input a smaller target for the CS and then manually adding the must_be_used coins.
- TODO: coin selection needs to be skipped if `recipients` contains at least one `Drain` (as in that case we don't need to select coins, we just pick them all!). I would like to have a way of making this easier for users...
- Need a way to translate the `CoinControl` struct of the last step into the appropriate parameter for the coin selection function
### PR [#924](https://github.com/bitcoindevkit/bdk/pull/924) [WIP]
- Updates the coin selection
- User now calls `CoinSelector::new(candidates, base_weight).bnb()`

## 3. Transaction preparation
- Can be just a function that will need the selected coins, the tx params, the tx ordering (https://docs.rs/bdk/latest/bdk/wallet/tx_builder/enum.TxOrdering.html) and the drain strategy. The function can then return a `Result<Transaction, Whatever>`
```rust
prepare_tx(
    inputs: Vec<Utxo>,
    tx_params: TxParams,
    drain: Drain,
    ordering: TxOrdering
) -> Result<Transaction, PrepareTxError>
```

## 4. PSBT preparation (Transaction -> PSBT)
- Given the selected coins and respective plans, the function will create the PSBT input
- The function will also accepts some parameters to modify its behaviour:
    - `only_witness_utxo`
    - `add_global_xpub`
    - `include_output_redeem_witness_script`

## 5. Signing
- The function will accept the PSBT and a bdk::Signer and will return the signed PSBT, and a `finalized` bool

## How the API will look like (WIP)
```rust
// Step 0
let tx_params = TxParams { ... };
// Step 1
let plans = TxPlan::default().add_keychain(my_keychain, my_assets)?;
let coins = wallet.list_unspent_by_keychain(my_keychain);
let mut coin_control = CoinControl::default();
coin_control.add_foreign_utxo_with_plan(..);
coin_control.add_local_utxos(coins);
// Step 2: IDK
let (selected_coins, drain) = idk; // ??
let selected_coins_with_plans = idk; // ??
// Step 3:
let tx = prepare_tx(&selected_coins, tx_params, drain, tx_ordering);
// Step 4
let psbt = prepare_psbt(tx, selected_coins_with_plans);
// Step 5
let signer = wallet.get_signers();
let (psbt, finalized) = sign_tx(signers, psbt);
```

## TODO
- [ ] Figure out locking and unlocking
- [ ] Add PSBT for native coinjoin
- [ ] Coins that can't be spent together? :sweat_smile: 
- [ ] Double check Galoy feedback on the old txbuilder
- [ ] Bump txs, CPFP txs, Cancel txs methods