@@ -34,7 +34,7 @@ use bdk_chain::{
3434 SyncResponse ,
3535 } ,
3636 tx_graph:: { self , CalculateFeeError , CanonicalTx , TxGraph , TxUpdate } ,
37- BlockId , CanonicalizationParams , ChainPosition , ConfirmationBlockTime , DescriptorExt ,
37+ Anchor , BlockId , CanonicalizationParams , ChainPosition , ConfirmationBlockTime , DescriptorExt ,
3838 FullTxOut , Indexed , IndexedTxGraph , Indexer , Merge ,
3939} ;
4040use bitcoin:: {
@@ -571,6 +571,107 @@ where
571571 }
572572}
573573
574+ impl < K > Wallet < K >
575+ where
576+ K : Ord + Clone + Debug ,
577+ {
578+ /// Computes the wallet balance.
579+ pub fn balance_with_params_conf_threshold (
580+ & self ,
581+ params : CanonicalizationParams ,
582+ outpoints : impl IntoIterator < Item = ( ( K , u32 ) , OutPoint ) > ,
583+ conf_threshold : u32 ,
584+ trust_predicate : impl Fn ( & FullTxOut < ConfirmationBlockTime > ) -> bool ,
585+ ) -> Balance {
586+ let mut immature = Amount :: ZERO ;
587+ let mut trusted_pending = Amount :: ZERO ;
588+ let mut untrusted_pending = Amount :: ZERO ;
589+ let mut confirmed = Amount :: ZERO ;
590+
591+ let chain = & self . chain ;
592+ let chain_tip = chain. tip ( ) . block_id ( ) ;
593+
594+ for ( _, txo) in self
595+ . tx_graph
596+ . graph ( )
597+ . filter_chain_unspents ( chain, chain_tip, params, outpoints)
598+ {
599+ match & txo. chain_position {
600+ ChainPosition :: Confirmed { anchor, .. } => {
601+ let confirmation_height = anchor. confirmation_height_upper_bound ( ) ;
602+ let confirmations = chain_tip
603+ . height
604+ . saturating_sub ( confirmation_height)
605+ . saturating_add ( 1 ) ;
606+
607+ if confirmations < conf_threshold {
608+ if trust_predicate ( & txo) {
609+ trusted_pending += txo. txout . value ;
610+ } else {
611+ untrusted_pending += txo. txout . value ;
612+ }
613+ } else if txo. is_confirmed_and_spendable ( chain_tip. height ) {
614+ confirmed += txo. txout . value ;
615+ } else if !txo. is_mature ( chain_tip. height ) {
616+ immature += txo. txout . value ;
617+ }
618+ }
619+ ChainPosition :: Unconfirmed { .. } => {
620+ if trust_predicate ( & txo) {
621+ trusted_pending += txo. txout . value ;
622+ } else {
623+ untrusted_pending += txo. txout . value ;
624+ }
625+ }
626+ }
627+ }
628+
629+ Balance {
630+ immature,
631+ trusted_pending,
632+ untrusted_pending,
633+ confirmed,
634+ }
635+ }
636+
637+ /// Computes the wallet balance.
638+ pub fn balance ( & self ) -> Balance {
639+ self . balance_with_params_conf_threshold (
640+ CanonicalizationParams :: default ( ) ,
641+ self . tx_graph . index . outpoints ( ) . clone ( ) ,
642+ 1 ,
643+ |txo| self . is_tx_trusted ( txo. outpoint . txid ) ,
644+ )
645+ }
646+
647+ /// Computes the wallet balance over a keychain range.
648+ pub fn keychain_balance ( & self , keychains : impl core:: ops:: RangeBounds < K > ) -> Balance {
649+ self . balance_with_params_conf_threshold (
650+ CanonicalizationParams :: default ( ) ,
651+ self . tx_graph . index . keychain_outpoints_in_range ( keychains) ,
652+ 1 ,
653+ |txo| self . is_tx_trusted ( txo. outpoint . txid ) ,
654+ )
655+ }
656+
657+ /// Whether the transaction of `txid` is considered trusted by this wallet.
658+ ///
659+ /// Trust is defined as a tx of which all of the inputs are controlled by the wallet, as we can
660+ /// assume it won't be double-spent unintentionally.
661+ pub fn is_tx_trusted ( & self , txid : Txid ) -> bool {
662+ let Some ( tx) = self . tx_graph . graph ( ) . get_tx ( txid) else {
663+ return false ;
664+ } ;
665+ if tx. input . is_empty ( ) {
666+ return false ;
667+ }
668+ tx. input . iter ( ) . all ( |txin| {
669+ let outpoint = txin. previous_output ;
670+ self . tx_graph . index . txout ( outpoint) . is_some ( )
671+ } )
672+ }
673+ }
674+
574675// TODO: replace with `PersistedWallet`
575676#[ cfg( feature = "rusqlite" ) ]
576677impl < K > Wallet < K >
0 commit comments