Skip to content

Commit 1fa0f75

Browse files
feat: add Wallet::get_tx_details method
1 parent 7cdc985 commit 1fa0f75

File tree

4 files changed

+73
-6
lines changed

4 files changed

+73
-6
lines changed

wallet/src/wallet/mod.rs

+29-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use bitcoin::{
4141
secp256k1::Secp256k1,
4242
sighash::{EcdsaSighashType, TapSighashType},
4343
transaction, Address, Amount, Block, BlockHash, FeeRate, Network, OutPoint, Psbt, ScriptBuf,
44-
Sequence, Transaction, TxOut, Txid, Weight, Witness,
44+
Sequence, SignedAmount, Transaction, TxOut, Txid, Weight, Witness,
4545
};
4646
use miniscript::{
4747
descriptor::KeyMap,
@@ -72,7 +72,7 @@ use crate::wallet::{
7272
error::{BuildFeeBumpError, CreateTxError, MiniscriptPsbtError},
7373
signer::{SignOptions, SignerError, SignerOrdering, SignersContainer, TransactionSigner},
7474
tx_builder::{FeePolicy, TxBuilder, TxParams},
75-
utils::{check_nsequence_rbf, After, Older, SecpCtx},
75+
utils::{check_nsequence_rbf, After, Older, SecpCtx, TxDetails},
7676
};
7777

7878
// re-exports
@@ -822,6 +822,33 @@ impl Wallet {
822822
.map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
823823
}
824824

825+
/// Build a [`TxDetails`] struct for a given transaction.
826+
///
827+
/// If the transaction with txid [`Txid`] cannot be found in the wallet's transaction graph,
828+
/// `None` is returned.
829+
pub fn get_tx_details(&self, txid: Txid) -> Option<TxDetails> {
830+
let tx: WalletTx = self.transactions().find(|c| c.tx_node.txid == txid)?;
831+
832+
let (sent, received) = self.sent_and_received(&tx.tx_node.tx);
833+
let fee: Option<Amount> = self.calculate_fee(&tx.tx_node.tx).ok();
834+
let fee_rate: Option<FeeRate> = self.calculate_fee_rate(&tx.tx_node.tx).ok();
835+
let balance_delta: SignedAmount = self.indexed_graph.index.net_value(&tx.tx_node.tx, ..);
836+
let chain_position = tx.chain_position;
837+
838+
let tx_details: TxDetails = TxDetails {
839+
txid,
840+
received,
841+
sent,
842+
fee,
843+
fee_rate,
844+
balance_delta,
845+
chain_position,
846+
tx: tx.tx_node.tx,
847+
};
848+
849+
Some(tx_details)
850+
}
851+
825852
/// List all relevant outputs (includes both spent and unspent, confirmed and unconfirmed).
826853
///
827854
/// To list only unspent outputs (UTXOs), use [`Wallet::list_unspent`] instead.

wallet/src/wallet/signer.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ fn sign_psbt_schnorr(
577577
let keypair = match leaf_hash {
578578
None => keypair
579579
.tap_tweak(secp, psbt_input.tap_merkle_root)
580-
.to_inner(),
580+
.to_keypair(),
581581
Some(_) => keypair, // no tweak for script spend
582582
};
583583

wallet/src/wallet/utils.rs

+18-2
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
// You may not use this file except in accordance with one or both of these
1010
// licenses.
1111

12+
use alloc::sync::Arc;
1213
use bitcoin::secp256k1::{All, Secp256k1};
13-
use bitcoin::{absolute, relative, Amount, Script, Sequence};
14-
14+
use bitcoin::{
15+
absolute, relative, Amount, FeeRate, Script, Sequence, SignedAmount, Transaction, Txid,
16+
};
17+
use chain::{ChainPosition, ConfirmationBlockTime};
1518
use miniscript::{MiniscriptKey, Satisfier, ToPublicKey};
1619

1720
use rand_core::RngCore;
@@ -133,6 +136,19 @@ pub(crate) fn shuffle_slice<T>(list: &mut [T], rng: &mut impl RngCore) {
133136

134137
pub(crate) type SecpCtx = Secp256k1<All>;
135138

139+
/// Details about a transaction the wallet knows about.
140+
#[derive(Debug)]
141+
pub struct TxDetails {
142+
pub txid: Txid,
143+
pub received: Amount,
144+
pub sent: Amount,
145+
pub fee: Option<Amount>,
146+
pub fee_rate: Option<FeeRate>,
147+
pub balance_delta: SignedAmount,
148+
pub chain_position: ChainPosition<ConfirmationBlockTime>,
149+
pub tx: Arc<Transaction>,
150+
}
151+
136152
#[cfg(test)]
137153
mod test {
138154
// When nSequence is lower than this flag the timelock is interpreted as block-height-based,

wallet/tests/wallet.rs

+25-1
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ use bdk_wallet::{KeychainKind, LoadError, LoadMismatch, LoadWithPersistError};
1919
use bitcoin::constants::{ChainHash, COINBASE_MATURITY};
2020
use bitcoin::hashes::Hash;
2121
use bitcoin::key::Secp256k1;
22-
use bitcoin::psbt;
2322
use bitcoin::script::PushBytesBuf;
2423
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
2524
use bitcoin::taproot::TapNodeHash;
2625
use bitcoin::{
2726
absolute, transaction, Address, Amount, BlockHash, FeeRate, Network, OutPoint, ScriptBuf,
2827
Sequence, Transaction, TxIn, TxOut, Txid, Weight,
2928
};
29+
use bitcoin::{psbt, SignedAmount};
3030
use miniscript::{descriptor::KeyMap, Descriptor, DescriptorPublicKey};
3131
use rand::rngs::StdRng;
3232
use rand::SeedableRng;
@@ -4314,3 +4314,27 @@ fn test_wallet_transactions_relevant() {
43144314
assert!(full_tx_count_before < full_tx_count_after);
43154315
assert!(canonical_tx_count_before < canonical_tx_count_after);
43164316
}
4317+
4318+
#[test]
4319+
fn test_tx_details_method() {
4320+
let (test_wallet, txid_1) = get_funded_wallet_wpkh();
4321+
let tx_details_1_option = test_wallet.get_tx_details(txid_1);
4322+
dbg!(&tx_details_1_option);
4323+
4324+
assert!(tx_details_1_option.is_some());
4325+
let tx_details_1 = tx_details_1_option.unwrap();
4326+
4327+
assert_eq!(
4328+
tx_details_1.txid.to_string(),
4329+
"f2a03cdfe1bb6a295b0a4bb4385ca42f95e4b2c6d9a7a59355d32911f957a5b3"
4330+
);
4331+
assert_eq!(tx_details_1.received, Amount::from_sat(50000));
4332+
assert_eq!(tx_details_1.sent, Amount::from_sat(76000));
4333+
assert_eq!(tx_details_1.fee.unwrap(), Amount::from_sat(1000));
4334+
assert_eq!(tx_details_1.delta, SignedAmount::from_sat(-26000));
4335+
4336+
// Transaction id not part of the TxGraph
4337+
let txid_2 = Txid::from_raw_hash(Hash::all_zeros());
4338+
let tx_details_2_option = test_wallet.get_tx_details(txid_2);
4339+
assert!(tx_details_2_option.is_none());
4340+
}

0 commit comments

Comments
 (0)