Skip to content

Commit 1753965

Browse files
ItoroDthunderbiscuit
authored andcommitted
feat: Add wallet event
1 parent 8bfc067 commit 1753965

File tree

2 files changed

+172
-1
lines changed

2 files changed

+172
-1
lines changed

bdk-ffi/src/types.rs

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use bdk_wallet::descriptor::policy::{
2323
Condition as BdkCondition, PkOrF as BdkPkOrF, Policy as BdkPolicy,
2424
Satisfaction as BdkSatisfaction, SatisfiableItem as BdkSatisfiableItem,
2525
};
26+
use bdk_wallet::event::WalletEvent as BdkWalletEvent;
2627
#[allow(deprecated)]
2728
use bdk_wallet::signer::{SignOptions as BdkSignOptions, TapLeavesOptions};
2829
use bdk_wallet::AddressInfo as BdkAddressInfo;
@@ -32,6 +33,7 @@ use bdk_wallet::Update as BdkUpdate;
3233

3334
use std::collections::{BTreeMap, BTreeSet, HashMap};
3435
use std::convert::TryFrom;
36+
use std::fmt::Display;
3537
use std::sync::{Arc, Mutex};
3638

3739
use crate::{impl_from_core_type, impl_into_core_type};
@@ -498,6 +500,157 @@ impl From<BdkSatisfiableItem> for SatisfiableItem {
498500
}
499501
}
500502

503+
/// Events representing changes to wallet transactions.
504+
///
505+
/// Returned after calling
506+
/// [`Wallet::apply_update_events`](crate::wallet::Wallet::apply_update_events).
507+
#[derive(Debug, Clone, uniffi::Enum)]
508+
#[uniffi::export(Display)]
509+
#[non_exhaustive]
510+
pub enum WalletEvent {
511+
/// The latest chain tip known to the wallet changed.
512+
ChainTipChanged {
513+
/// Previous chain tip.
514+
old_tip: BlockId,
515+
/// New chain tip.
516+
new_tip: BlockId,
517+
},
518+
/// A transaction is now confirmed.
519+
///
520+
/// If the transaction was previously unconfirmed `old_block_time` will be `None`.
521+
///
522+
/// If a confirmed transaction is now re-confirmed in a new block `old_block_time` will contain
523+
/// the block id and the time it was previously confirmed. This can happen after a chain
524+
/// reorg.
525+
TxConfirmed {
526+
/// Transaction id.
527+
txid: Arc<Txid>,
528+
/// Transaction.
529+
tx: Arc<Transaction>,
530+
/// Confirmation block time.
531+
block_time: ConfirmationBlockTime,
532+
/// Old confirmation block and time if previously confirmed in a different block.
533+
old_block_time: Option<ConfirmationBlockTime>,
534+
},
535+
/// A transaction is now unconfirmed.
536+
///
537+
/// If the transaction is first seen in the mempool `old_block_time` will be `None`.
538+
///
539+
/// If a previously confirmed transaction is now seen in the mempool `old_block_time` will
540+
/// contain the block id and the time it was previously confirmed. This can happen after a
541+
/// chain reorg.
542+
TxUnconfirmed {
543+
/// Transaction id.
544+
txid: Arc<Txid>,
545+
/// Transaction.
546+
tx: Arc<Transaction>,
547+
/// Old confirmation block and time, if previously confirmed.
548+
old_block_time: Option<ConfirmationBlockTime>,
549+
},
550+
/// An unconfirmed transaction was replaced.
551+
///
552+
/// This can happen after an RBF is broadcast or if a third party double spends an input of
553+
/// a received payment transaction before it is confirmed.
554+
///
555+
/// The conflicts field contains the txid and vin (in which it conflicts) of the conflicting
556+
/// transactions.
557+
TxReplaced {
558+
/// Transaction id.
559+
txid: Arc<Txid>,
560+
/// Transaction.
561+
tx: Arc<Transaction>,
562+
/// Conflicting transactions.
563+
conflicts: Vec<Conflict>,
564+
},
565+
/// Unconfirmed transaction dropped.
566+
///
567+
/// The transaction was dropped from the local mempool. This is generally due to the fee rate
568+
/// being too low. The transaction can still reappear in the mempool in the future resulting in
569+
/// a [`WalletEvent::TxUnconfirmed`] event.
570+
TxDropped {
571+
/// Transaction id.
572+
txid: Arc<Txid>,
573+
/// Transaction.
574+
tx: Arc<Transaction>,
575+
},
576+
}
577+
578+
impl Display for WalletEvent {
579+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
580+
write!(f, "{:?}", self)
581+
}
582+
}
583+
584+
/// Represent a conflict in a replacement transaction.
585+
#[uniffi::export(Display)]
586+
#[derive(Debug, Clone, uniffi::Record)]
587+
pub struct Conflict {
588+
/// The index of the conflicting input.
589+
pub vin: u32,
590+
/// Conflicting transaction id.
591+
pub txid: Arc<Txid>,
592+
}
593+
594+
impl Display for Conflict {
595+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
596+
write!(f, "{:?}", self)
597+
}
598+
}
599+
600+
impl From<BdkWalletEvent> for WalletEvent {
601+
fn from(event: BdkWalletEvent) -> Self {
602+
match event {
603+
BdkWalletEvent::ChainTipChanged { old_tip, new_tip } => WalletEvent::ChainTipChanged {
604+
old_tip: BlockId::from(old_tip),
605+
new_tip: BlockId::from(new_tip),
606+
},
607+
BdkWalletEvent::TxConfirmed {
608+
txid,
609+
tx,
610+
block_time,
611+
old_block_time,
612+
} => WalletEvent::TxConfirmed {
613+
txid: Arc::new(Txid(txid)),
614+
tx: Arc::new(tx.as_ref().clone().into()),
615+
block_time: block_time.into(),
616+
old_block_time: old_block_time.map(|v| v.into()),
617+
},
618+
BdkWalletEvent::TxUnconfirmed {
619+
txid,
620+
tx,
621+
old_block_time,
622+
} => WalletEvent::TxUnconfirmed {
623+
txid: Arc::new(Txid(txid)),
624+
tx: Arc::new(tx.as_ref().clone().into()),
625+
old_block_time: old_block_time.map(|v| v.into()),
626+
},
627+
BdkWalletEvent::TxReplaced {
628+
txid,
629+
tx,
630+
conflicts,
631+
} => {
632+
let conflict_list: Vec<Conflict> = conflicts
633+
.into_iter()
634+
.map(|(vin, conflict)| Conflict {
635+
vin: vin as u32,
636+
txid: Arc::new(Txid(conflict)),
637+
})
638+
.collect();
639+
WalletEvent::TxReplaced {
640+
txid: Arc::new(Txid(txid)),
641+
tx: Arc::new(tx.as_ref().clone().into()),
642+
conflicts: conflict_list,
643+
}
644+
}
645+
BdkWalletEvent::TxDropped { txid, tx } => WalletEvent::TxDropped {
646+
txid: Arc::new(Txid(txid)),
647+
tx: Arc::new(tx.as_ref().clone().into()),
648+
},
649+
_ => unreachable!("WalletEvent variant not covered in conversion"),
650+
}
651+
}
652+
}
653+
501654
#[derive(Debug, Clone, uniffi::Enum)]
502655
pub enum PkOrF {
503656
Pubkey { value: String },

bdk-ffi/src/wallet.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::store::{PersistenceType, Persister};
88
use crate::types::{
99
AddressInfo, Balance, BlockId, CanonicalTx, ChangeSet, EvictedTx, FullScanRequestBuilder,
1010
KeychainAndIndex, LocalOutput, Policy, SentAndReceivedValues, SignOptions, SyncRequestBuilder,
11-
UnconfirmedTx, Update,
11+
UnconfirmedTx, Update, WalletEvent,
1212
};
1313

1414
use bdk_wallet::bitcoin::Network;
@@ -320,6 +320,24 @@ impl Wallet {
320320
.map_err(CannotConnectError::from)
321321
}
322322

323+
/// Applies an update to the wallet, stages the changes, and returns events.
324+
///
325+
/// Usually you create an `update` by interacting with some blockchain data source and inserting
326+
/// transactions related to your wallet into it. Staged changes are NOT persisted.
327+
///
328+
/// After applying updates you should process the events in your app before persisting the
329+
/// staged wallet changes. For an example of how to persist staged wallet changes see
330+
/// [`Wallet::reveal_next_address`].
331+
pub fn apply_update_events(
332+
&self,
333+
update: Arc<Update>,
334+
) -> Result<Vec<WalletEvent>, CannotConnectError> {
335+
match self.get_wallet().apply_update_events(update.0.clone()) {
336+
Ok(events) => Ok(events.into_iter().map(|e| e.into()).collect()),
337+
Err(e) => Err(CannotConnectError::from(e)),
338+
}
339+
}
340+
323341
/// Apply relevant unconfirmed transactions to the wallet.
324342
/// Transactions that are not relevant are filtered out.
325343
pub fn apply_unconfirmed_txs(&self, unconfirmed_txs: Vec<UnconfirmedTx>) {

0 commit comments

Comments
 (0)