@@ -459,6 +459,45 @@ impl<'a, Cs> TxBuilder<'a, Cs> {
459
459
self
460
460
}
461
461
462
+ /// Excludes any outpoints whose enclosing transaction has fewer than `min_confirms`
463
+ /// confirmations.
464
+ ///
465
+ /// `min_confirms` is the minimum number of confirmations a transaction must have in order for
466
+ /// its outpoints to remain spendable.
467
+ /// - Passing `0` will include all transactions (no filtering).
468
+ /// - Passing `1` will exclude all unconfirmed transactions (equivalent to
469
+ /// `exclude_unconfirmed`).
470
+ /// - Passing `6` will only allow outpoints from transactions with at least 6 confirmations.
471
+ ///
472
+ /// If you chain this with other filtering methods, the final set of unspendable outpoints will
473
+ /// be the union of all filters.
474
+ pub fn exclude_below_confirmations ( & mut self , min_confirms : u32 ) -> & mut Self {
475
+ let tip_height = self . wallet . latest_checkpoint ( ) . height ( ) ;
476
+ let to_exclude = self
477
+ . wallet
478
+ . list_unspent ( )
479
+ . filter ( |utxo| {
480
+ utxo. chain_position
481
+ . confirmation_height_upper_bound ( )
482
+ . map_or ( 0 , |h| tip_height. saturating_add ( 1 ) . saturating_sub ( h) )
483
+ < min_confirms
484
+ } )
485
+ . map ( |utxo| utxo. outpoint ) ;
486
+ for op in to_exclude {
487
+ self . params . unspendable . insert ( op) ;
488
+ }
489
+ self
490
+ }
491
+
492
+ /// Exclude outpoints whose enclosing transaction is unconfirmed.
493
+ ///
494
+ /// This is a shorthand for [`exclude_below_confirmations(1)`].
495
+ ///
496
+ /// [`exclude_below_confirmations(1)`]: Self::exclude_below_confirmations
497
+ pub fn exclude_unconfirmed ( & mut self ) -> & mut Self {
498
+ self . exclude_below_confirmations ( 1 )
499
+ }
500
+
462
501
/// Sign with a specific sig hash
463
502
///
464
503
/// **Use this option very carefully**
@@ -1088,6 +1127,96 @@ mod test {
1088
1127
assert_eq ! ( filtered[ 0 ] . keychain, KeychainKind :: Internal ) ;
1089
1128
}
1090
1129
1130
+ #[ test]
1131
+ fn test_exclude_unconfirmed ( ) {
1132
+ use crate :: test_utils:: * ;
1133
+ use bdk_chain:: BlockId ;
1134
+ use bitcoin:: { hashes:: Hash , BlockHash , Network } ;
1135
+
1136
+ let mut wallet = Wallet :: create_single ( get_test_tr_single_sig ( ) )
1137
+ . network ( Network :: Regtest )
1138
+ . create_wallet_no_persist ( )
1139
+ . unwrap ( ) ;
1140
+ let recipient = wallet. next_unused_address ( KeychainKind :: External ) . address ;
1141
+
1142
+ insert_checkpoint (
1143
+ & mut wallet,
1144
+ BlockId {
1145
+ height : 1 ,
1146
+ hash : BlockHash :: all_zeros ( ) ,
1147
+ } ,
1148
+ ) ;
1149
+ insert_checkpoint (
1150
+ & mut wallet,
1151
+ BlockId {
1152
+ height : 2 ,
1153
+ hash : BlockHash :: all_zeros ( ) ,
1154
+ } ,
1155
+ ) ;
1156
+ receive_output (
1157
+ & mut wallet,
1158
+ Amount :: ONE_BTC ,
1159
+ ReceiveTo :: Block ( chain:: ConfirmationBlockTime {
1160
+ block_id : BlockId {
1161
+ height : 1 ,
1162
+ hash : BlockHash :: all_zeros ( ) ,
1163
+ } ,
1164
+ confirmation_time : 1 ,
1165
+ } ) ,
1166
+ ) ;
1167
+ receive_output (
1168
+ & mut wallet,
1169
+ Amount :: ONE_BTC * 2 ,
1170
+ ReceiveTo :: Block ( chain:: ConfirmationBlockTime {
1171
+ block_id : BlockId {
1172
+ height : 2 ,
1173
+ hash : BlockHash :: all_zeros ( ) ,
1174
+ } ,
1175
+ confirmation_time : 2 ,
1176
+ } ) ,
1177
+ ) ;
1178
+ receive_output ( & mut wallet, Amount :: ONE_BTC * 3 , ReceiveTo :: Mempool ( 100 ) ) ;
1179
+
1180
+ // Exclude nothing.
1181
+ {
1182
+ let mut builder = wallet. build_tx ( ) ;
1183
+ builder
1184
+ . fee_rate ( FeeRate :: ZERO )
1185
+ . exclude_below_confirmations ( 0 )
1186
+ . drain_wallet ( )
1187
+ . drain_to ( recipient. script_pubkey ( ) ) ;
1188
+ let tx = builder. finish ( ) . unwrap ( ) ;
1189
+ let output = tx. unsigned_tx . output . first ( ) . expect ( "must have one output" ) ;
1190
+ assert_eq ! ( output. value, Amount :: ONE_BTC * 6 ) ;
1191
+ }
1192
+
1193
+ // Exclude < 1 conf (a.k.a exclude unconfirmed).
1194
+ {
1195
+ let mut builder = wallet. build_tx ( ) ;
1196
+ builder
1197
+ . fee_rate ( FeeRate :: ZERO )
1198
+ . exclude_below_confirmations ( 1 )
1199
+ . drain_wallet ( )
1200
+ . drain_to ( recipient. script_pubkey ( ) ) ;
1201
+ let tx = builder. finish ( ) . unwrap ( ) ;
1202
+ let output = tx. unsigned_tx . output . first ( ) . expect ( "must have one output" ) ;
1203
+ assert_eq ! ( output. value, Amount :: ONE_BTC * 3 ) ;
1204
+ }
1205
+
1206
+ // Exclude < 2 conf (a.k.a need at least 2 conf)
1207
+ {
1208
+ let mut builder = wallet. build_tx ( ) ;
1209
+ builder
1210
+ . fee_rate ( FeeRate :: ZERO )
1211
+ . exclude_below_confirmations ( 2 )
1212
+ . drain_wallet ( )
1213
+ . drain_to ( recipient. script_pubkey ( ) ) ;
1214
+ let tx = builder. finish ( ) . unwrap ( ) ;
1215
+ let output = tx. unsigned_tx . output . first ( ) . expect ( "must have one output" ) ;
1216
+ assert_eq ! ( output. value, Amount :: ONE_BTC ) ;
1217
+ }
1218
+ }
1219
+
1091
1220
#[ test]
1092
1221
fn test_build_fee_bump_remove_change_output_single_desc ( ) {
1093
1222
use crate :: test_utils:: * ;
0 commit comments