bdkffi/
types.rs

1use crate::bitcoin::{
2    Address, Amount, BlockHash, DescriptorId, FeeRate, HashableOutPoint, OutPoint, Script,
3    Transaction, TxOut, Txid,
4};
5use crate::descriptor::Descriptor;
6use crate::error::{CreateTxError, RequestBuilderError};
7
8use bdk_wallet::bitcoin::absolute::LockTime as BdkLockTime;
9use bdk_wallet::chain::spk_client::SyncItem;
10use bdk_wallet::chain::BlockId as BdkBlockId;
11use bdk_wallet::chain::Merge;
12
13use bdk_wallet::bitcoin::Transaction as BdkTransaction;
14use bdk_wallet::chain::spk_client::FullScanRequest as BdkFullScanRequest;
15use bdk_wallet::chain::spk_client::FullScanRequestBuilder as BdkFullScanRequestBuilder;
16use bdk_wallet::chain::spk_client::SyncRequest as BdkSyncRequest;
17use bdk_wallet::chain::spk_client::SyncRequestBuilder as BdkSyncRequestBuilder;
18use bdk_wallet::chain::tx_graph::CanonicalTx as BdkCanonicalTx;
19use bdk_wallet::chain::{
20    ChainPosition as BdkChainPosition, ConfirmationBlockTime as BdkConfirmationBlockTime,
21};
22use bdk_wallet::descriptor::policy::{
23    Condition as BdkCondition, PkOrF as BdkPkOrF, Policy as BdkPolicy,
24    Satisfaction as BdkSatisfaction, SatisfiableItem as BdkSatisfiableItem,
25};
26use bdk_wallet::locked_outpoints::ChangeSet as BdkLockedOutpointsChangeSet;
27#[allow(deprecated)]
28use bdk_wallet::miniscript::descriptor::Wildcard;
29use bdk_wallet::signer::{SignOptions as BdkSignOptions, TapLeavesOptions};
30use bdk_wallet::AddressInfo as BdkAddressInfo;
31use bdk_wallet::Balance as BdkBalance;
32use bdk_wallet::LocalOutput as BdkLocalOutput;
33use bdk_wallet::Update as BdkUpdate;
34use bdk_wallet::WalletEvent as BdkWalletEvent;
35
36use std::collections::{BTreeMap, BTreeSet, HashMap};
37use std::convert::TryFrom;
38use std::fmt::Display;
39use std::sync::{Arc, Mutex};
40
41use crate::{impl_from_core_type, impl_into_core_type};
42use bdk_esplora::esplora_client::api::MerkleProof as BdkMerkleProof;
43use bdk_esplora::esplora_client::api::OutputStatus as BdkOutputStatus;
44use bdk_esplora::esplora_client::api::Tx as BdkTx;
45use bdk_esplora::esplora_client::api::TxStatus as BdkTxStatus;
46
47pub(crate) type KeychainKind = bdk_wallet::KeychainKind;
48
49/// Types of keychains.
50#[uniffi::remote(Enum)]
51pub enum KeychainKind {
52    /// External keychain, used for deriving recipient addresses.
53    External = 0,
54    /// Internal keychain, used for deriving change addresses.
55    Internal = 1,
56}
57
58type WordCount = bdk_wallet::keys::bip39::WordCount;
59
60#[uniffi::remote(Enum)]
61pub enum WordCount {
62    Words12,
63    Words15,
64    Words18,
65    Words21,
66    Words24,
67}
68
69/// Represents the observed position of some chain data.
70#[derive(Debug, uniffi::Enum, Clone)]
71pub enum ChainPosition {
72    /// The chain data is confirmed as it is anchored in the best chain by `A`.
73    Confirmed {
74        confirmation_block_time: ConfirmationBlockTime,
75        /// A child transaction that has been confirmed. Due to incomplete information,
76        /// it is only known that this transaction is confirmed at a chain height less than
77        /// or equal to this child TXID.
78        transitively: Option<Arc<Txid>>,
79    },
80    /// The transaction was last seen in the mempool at this timestamp.
81    Unconfirmed { timestamp: Option<u64> },
82}
83
84impl From<BdkChainPosition<BdkConfirmationBlockTime>> for ChainPosition {
85    fn from(chain_position: BdkChainPosition<BdkConfirmationBlockTime>) -> Self {
86        match chain_position {
87            BdkChainPosition::Confirmed {
88                anchor,
89                transitively,
90            } => {
91                let block_id = BlockId {
92                    height: anchor.block_id.height,
93                    hash: Arc::new(BlockHash(anchor.block_id.hash)),
94                };
95                ChainPosition::Confirmed {
96                    confirmation_block_time: ConfirmationBlockTime {
97                        block_id,
98                        confirmation_time: anchor.confirmation_time,
99                    },
100                    transitively: transitively.map(|t| Arc::new(Txid(t))),
101                }
102            }
103            BdkChainPosition::Unconfirmed { last_seen, .. } => ChainPosition::Unconfirmed {
104                timestamp: last_seen,
105            },
106        }
107    }
108}
109
110/// Represents the confirmation block and time of a transaction.
111#[derive(Debug, Clone, PartialEq, Eq, std::hash::Hash, uniffi::Record)]
112pub struct ConfirmationBlockTime {
113    /// The anchor block.
114    pub block_id: BlockId,
115    /// The confirmation time of the transaction being anchored.
116    pub confirmation_time: u64,
117}
118
119impl From<BdkConfirmationBlockTime> for ConfirmationBlockTime {
120    fn from(value: BdkConfirmationBlockTime) -> Self {
121        Self {
122            block_id: value.block_id.into(),
123            confirmation_time: value.confirmation_time,
124        }
125    }
126}
127
128impl From<ConfirmationBlockTime> for BdkConfirmationBlockTime {
129    fn from(value: ConfirmationBlockTime) -> Self {
130        Self {
131            block_id: value.block_id.into(),
132            confirmation_time: value.confirmation_time,
133        }
134    }
135}
136
137/// A reference to a block in the canonical chain.
138#[derive(Debug, Clone, PartialEq, Eq, std::hash::Hash, uniffi::Record)]
139pub struct BlockId {
140    /// The height of the block.
141    pub height: u32,
142    /// The hash of the block.
143    pub hash: Arc<BlockHash>,
144}
145
146impl From<BdkBlockId> for BlockId {
147    fn from(block_id: BdkBlockId) -> Self {
148        BlockId {
149            height: block_id.height,
150            hash: Arc::new(BlockHash(block_id.hash)),
151        }
152    }
153}
154
155impl From<BlockId> for BdkBlockId {
156    fn from(value: BlockId) -> Self {
157        Self {
158            height: value.height,
159            hash: value.hash.0,
160        }
161    }
162}
163
164/// A transaction that is deemed to be part of the canonical history.
165#[derive(uniffi::Record)]
166pub struct CanonicalTx {
167    /// The transaction.
168    pub transaction: Arc<Transaction>,
169    /// How the transaction is observed in the canonical chain (confirmed or unconfirmed).
170    pub chain_position: ChainPosition,
171}
172
173impl From<BdkCanonicalTx<'_, Arc<BdkTransaction>, BdkConfirmationBlockTime>> for CanonicalTx {
174    fn from(tx: BdkCanonicalTx<'_, Arc<BdkTransaction>, BdkConfirmationBlockTime>) -> Self {
175        CanonicalTx {
176            transaction: Arc::new(Transaction::from(tx.tx_node.tx.as_ref().clone())),
177            chain_position: tx.chain_position.into(),
178        }
179    }
180}
181
182/// A bitcoin script and associated amount.
183#[derive(uniffi::Record)]
184pub struct ScriptAmount {
185    /// The underlying script.
186    pub script: Arc<Script>,
187    /// The amount owned by the script.
188    pub amount: Arc<Amount>,
189}
190
191/// A derived address and the index it was found at.
192#[derive(uniffi::Record)]
193pub struct AddressInfo {
194    /// Child index of this address
195    pub index: u32,
196    /// The address
197    pub address: Arc<Address>,
198    /// Type of keychain
199    pub keychain: KeychainKind,
200}
201
202impl From<BdkAddressInfo> for AddressInfo {
203    fn from(address_info: BdkAddressInfo) -> Self {
204        AddressInfo {
205            index: address_info.index,
206            address: Arc::new(address_info.address.into()),
207            keychain: address_info.keychain,
208        }
209    }
210}
211
212/// Balance, differentiated into various categories.
213#[derive(uniffi::Record)]
214pub struct Balance {
215    /// All coinbase outputs not yet matured
216    pub immature: Arc<Amount>,
217    /// Unconfirmed UTXOs generated by a wallet tx
218    pub trusted_pending: Arc<Amount>,
219    /// Unconfirmed UTXOs received from an external wallet
220    pub untrusted_pending: Arc<Amount>,
221    /// Confirmed and immediately spendable balance
222    pub confirmed: Arc<Amount>,
223    /// Get sum of trusted_pending and confirmed coins.
224    ///
225    /// This is the balance you can spend right now that shouldn't get cancelled via another party
226    /// double spending it.
227    pub trusted_spendable: Arc<Amount>,
228    /// Get the whole balance visible to the wallet.
229    pub total: Arc<Amount>,
230}
231
232impl From<BdkBalance> for Balance {
233    fn from(bdk_balance: BdkBalance) -> Self {
234        Balance {
235            immature: Arc::new(bdk_balance.immature.into()),
236            trusted_pending: Arc::new(bdk_balance.trusted_pending.into()),
237            untrusted_pending: Arc::new(bdk_balance.untrusted_pending.into()),
238            confirmed: Arc::new(bdk_balance.confirmed.into()),
239            trusted_spendable: Arc::new(bdk_balance.trusted_spendable().into()),
240            total: Arc::new(bdk_balance.total().into()),
241        }
242    }
243}
244
245/// An unspent output owned by a [`Wallet`].
246#[derive(uniffi::Record)]
247pub struct LocalOutput {
248    /// Reference to a transaction output
249    pub outpoint: OutPoint,
250    /// Transaction output
251    pub txout: TxOut,
252    /// Type of keychain
253    pub keychain: KeychainKind,
254    /// Whether this UTXO is spent or not
255    pub is_spent: bool,
256    /// The derivation index for the script pubkey in the wallet
257    pub derivation_index: u32,
258    /// The position of the output in the blockchain.
259    pub chain_position: ChainPosition,
260}
261
262impl From<BdkLocalOutput> for LocalOutput {
263    fn from(local_utxo: BdkLocalOutput) -> Self {
264        LocalOutput {
265            outpoint: OutPoint {
266                txid: Arc::new(Txid(local_utxo.outpoint.txid)),
267                vout: local_utxo.outpoint.vout,
268            },
269            txout: TxOut {
270                value: Arc::new(Amount(local_utxo.txout.value)),
271                script_pubkey: Arc::new(Script(local_utxo.txout.script_pubkey)),
272            },
273            keychain: local_utxo.keychain,
274            is_spent: local_utxo.is_spent,
275            derivation_index: local_utxo.derivation_index,
276            chain_position: local_utxo.chain_position.into(),
277        }
278    }
279}
280
281// Callback for the FullScanRequest
282#[uniffi::export(with_foreign)]
283pub trait FullScanScriptInspector: Sync + Send {
284    fn inspect(&self, keychain: KeychainKind, index: u32, script: Arc<Script>);
285}
286
287// Callback for the SyncRequest
288#[uniffi::export(with_foreign)]
289pub trait SyncScriptInspector: Sync + Send {
290    fn inspect(&self, script: Arc<Script>, total: u64);
291}
292
293#[derive(uniffi::Object)]
294pub struct FullScanRequestBuilder(
295    pub(crate) Mutex<Option<BdkFullScanRequestBuilder<KeychainKind>>>,
296);
297
298#[derive(uniffi::Object)]
299pub struct SyncRequestBuilder(pub(crate) Mutex<Option<BdkSyncRequestBuilder<(KeychainKind, u32)>>>);
300
301#[derive(uniffi::Object)]
302pub struct FullScanRequest(pub(crate) Mutex<Option<BdkFullScanRequest<KeychainKind>>>);
303
304#[derive(uniffi::Object)]
305pub struct SyncRequest(pub(crate) Mutex<Option<BdkSyncRequest<(KeychainKind, u32)>>>);
306
307#[uniffi::export]
308impl SyncRequestBuilder {
309    pub fn inspect_spks(
310        &self,
311        inspector: Arc<dyn SyncScriptInspector>,
312    ) -> Result<Arc<Self>, RequestBuilderError> {
313        let guard = self
314            .0
315            .lock()
316            .unwrap()
317            .take()
318            .ok_or(RequestBuilderError::RequestAlreadyConsumed)?;
319        let sync_request_builder = guard.inspect({
320            move |script, progress| {
321                if let SyncItem::Spk(_, spk) = script {
322                    inspector.inspect(Arc::new(Script(spk.to_owned())), progress.total() as u64)
323                }
324            }
325        });
326        Ok(Arc::new(SyncRequestBuilder(Mutex::new(Some(
327            sync_request_builder,
328        )))))
329    }
330
331    pub fn build(&self) -> Result<Arc<SyncRequest>, RequestBuilderError> {
332        let guard = self
333            .0
334            .lock()
335            .unwrap()
336            .take()
337            .ok_or(RequestBuilderError::RequestAlreadyConsumed)?;
338        Ok(Arc::new(SyncRequest(Mutex::new(Some(guard.build())))))
339    }
340}
341
342#[uniffi::export]
343impl FullScanRequestBuilder {
344    pub fn inspect_spks_for_all_keychains(
345        &self,
346        inspector: Arc<dyn FullScanScriptInspector>,
347    ) -> Result<Arc<Self>, RequestBuilderError> {
348        let guard = self
349            .0
350            .lock()
351            .unwrap()
352            .take()
353            .ok_or(RequestBuilderError::RequestAlreadyConsumed)?;
354        let full_scan_request_builder = guard.inspect(move |keychain, index, script| {
355            inspector.inspect(keychain, index, Arc::new(Script(script.to_owned())))
356        });
357        Ok(Arc::new(FullScanRequestBuilder(Mutex::new(Some(
358            full_scan_request_builder,
359        )))))
360    }
361
362    pub fn build(&self) -> Result<Arc<FullScanRequest>, RequestBuilderError> {
363        let guard = self
364            .0
365            .lock()
366            .unwrap()
367            .take()
368            .ok_or(RequestBuilderError::RequestAlreadyConsumed)?;
369        Ok(Arc::new(FullScanRequest(Mutex::new(Some(guard.build())))))
370    }
371}
372
373/// An update for a wallet containing chain, descriptor index, and transaction data.
374#[derive(uniffi::Object)]
375pub struct Update(pub(crate) BdkUpdate);
376
377/// The total value sent and received.
378#[derive(uniffi::Record)]
379pub struct SentAndReceivedValues {
380    /// Amount sent in the transaction.
381    pub sent: Arc<Amount>,
382    /// The amount received in the transaction, possibly as a change output(s).
383    pub received: Arc<Amount>,
384}
385
386/// The keychain kind and the index in that keychain.
387#[derive(uniffi::Record)]
388pub struct KeychainAndIndex {
389    /// Type of keychains.
390    pub keychain: KeychainKind,
391    /// The index in the keychain.
392    pub index: u32,
393}
394
395/// Descriptor spending policy
396#[derive(Debug, PartialEq, Eq, Clone, uniffi::Object)]
397pub struct Policy(BdkPolicy);
398
399impl_from_core_type!(BdkPolicy, Policy);
400impl_into_core_type!(Policy, BdkPolicy);
401
402#[uniffi::export]
403impl Policy {
404    pub fn id(&self) -> String {
405        self.0.id.clone()
406    }
407
408    pub fn as_string(&self) -> String {
409        bdk_wallet::serde_json::to_string(&self.0).unwrap()
410    }
411
412    pub fn requires_path(&self) -> bool {
413        self.0.requires_path()
414    }
415
416    pub fn item(&self) -> SatisfiableItem {
417        self.0.item.clone().into()
418    }
419
420    pub fn satisfaction(&self) -> Satisfaction {
421        self.0.satisfaction.clone().into()
422    }
423
424    pub fn contribution(&self) -> Satisfaction {
425        self.0.contribution.clone().into()
426    }
427}
428
429#[derive(Debug, Clone, uniffi::Enum)]
430pub enum SatisfiableItem {
431    EcdsaSignature {
432        key: PkOrF,
433    },
434    SchnorrSignature {
435        key: PkOrF,
436    },
437    Sha256Preimage {
438        hash: String,
439    },
440    Hash256Preimage {
441        hash: String,
442    },
443    Ripemd160Preimage {
444        hash: String,
445    },
446    Hash160Preimage {
447        hash: String,
448    },
449    AbsoluteTimelock {
450        value: LockTime,
451    },
452    RelativeTimelock {
453        value: u32,
454    },
455    Multisig {
456        keys: Vec<PkOrF>,
457        threshold: u64,
458    },
459    Thresh {
460        items: Vec<Arc<Policy>>,
461        threshold: u64,
462    },
463}
464
465impl From<BdkSatisfiableItem> for SatisfiableItem {
466    fn from(value: BdkSatisfiableItem) -> Self {
467        match value {
468            BdkSatisfiableItem::EcdsaSignature(pk_or_f) => SatisfiableItem::EcdsaSignature {
469                key: pk_or_f.into(),
470            },
471            BdkSatisfiableItem::SchnorrSignature(pk_or_f) => SatisfiableItem::SchnorrSignature {
472                key: pk_or_f.into(),
473            },
474            BdkSatisfiableItem::Sha256Preimage { hash } => SatisfiableItem::Sha256Preimage {
475                hash: hash.to_string(),
476            },
477            BdkSatisfiableItem::Hash256Preimage { hash } => SatisfiableItem::Hash256Preimage {
478                hash: hash.to_string(),
479            },
480            BdkSatisfiableItem::Ripemd160Preimage { hash } => SatisfiableItem::Ripemd160Preimage {
481                hash: hash.to_string(),
482            },
483            BdkSatisfiableItem::Hash160Preimage { hash } => SatisfiableItem::Hash160Preimage {
484                hash: hash.to_string(),
485            },
486            BdkSatisfiableItem::AbsoluteTimelock { value } => SatisfiableItem::AbsoluteTimelock {
487                value: value.into(),
488            },
489            BdkSatisfiableItem::RelativeTimelock { value } => SatisfiableItem::RelativeTimelock {
490                value: value.to_consensus_u32(),
491            },
492            BdkSatisfiableItem::Multisig { keys, threshold } => SatisfiableItem::Multisig {
493                keys: keys.iter().map(|e| e.to_owned().into()).collect(),
494                threshold: threshold as u64,
495            },
496            BdkSatisfiableItem::Thresh { items, threshold } => SatisfiableItem::Thresh {
497                items: items
498                    .iter()
499                    .map(|e| Arc::new(e.to_owned().into()))
500                    .collect(),
501                threshold: threshold as u64,
502            },
503        }
504    }
505}
506
507/// Events representing changes to wallet transactions.
508///
509/// Returned after calling
510/// [`Wallet::apply_update_events`](crate::wallet::Wallet::apply_update_events).
511#[derive(Debug, Clone, uniffi::Enum)]
512#[uniffi::export(Display)]
513#[non_exhaustive]
514pub enum WalletEvent {
515    /// The latest chain tip known to the wallet changed.
516    ChainTipChanged {
517        /// Previous chain tip.
518        old_tip: BlockId,
519        /// New chain tip.
520        new_tip: BlockId,
521    },
522    /// A transaction is now confirmed.
523    ///
524    /// If the transaction was previously unconfirmed `old_block_time` will be `None`.
525    ///
526    /// If a confirmed transaction is now re-confirmed in a new block `old_block_time` will contain
527    /// the block id and the time it was previously confirmed. This can happen after a chain
528    /// reorg.
529    TxConfirmed {
530        /// Transaction id.
531        txid: Arc<Txid>,
532        /// Transaction.
533        tx: Arc<Transaction>,
534        /// Confirmation block time.
535        block_time: ConfirmationBlockTime,
536        /// Old confirmation block and time if previously confirmed in a different block.
537        old_block_time: Option<ConfirmationBlockTime>,
538    },
539    /// A transaction is now unconfirmed.
540    ///
541    /// If the transaction is first seen in the mempool `old_block_time` will be `None`.
542    ///
543    /// If a previously confirmed transaction is now seen in the mempool `old_block_time` will
544    /// contain the block id and the time it was previously confirmed. This can happen after a
545    /// chain reorg.
546    TxUnconfirmed {
547        /// Transaction id.
548        txid: Arc<Txid>,
549        /// Transaction.
550        tx: Arc<Transaction>,
551        /// Old confirmation block and time, if previously confirmed.
552        old_block_time: Option<ConfirmationBlockTime>,
553    },
554    /// An unconfirmed transaction was replaced.
555    ///
556    /// This can happen after an RBF is broadcast or if a third party double spends an input of
557    /// a received payment transaction before it is confirmed.
558    ///
559    /// The conflicts field contains the txid and vin (in which it conflicts) of the conflicting
560    /// transactions.
561    TxReplaced {
562        /// Transaction id.
563        txid: Arc<Txid>,
564        /// Transaction.
565        tx: Arc<Transaction>,
566        /// Conflicting transactions.
567        conflicts: Vec<Conflict>,
568    },
569    /// Unconfirmed transaction dropped.
570    ///
571    /// The transaction was dropped from the local mempool. This is generally due to the fee rate
572    /// being too low. The transaction can still reappear in the mempool in the future resulting in
573    /// a [`WalletEvent::TxUnconfirmed`] event.
574    TxDropped {
575        /// Transaction id.
576        txid: Arc<Txid>,
577        /// Transaction.
578        tx: Arc<Transaction>,
579    },
580}
581
582impl Display for WalletEvent {
583    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
584        write!(f, "{:?}", self)
585    }
586}
587
588/// Represent a conflict in a replacement transaction.
589#[uniffi::export(Display)]
590#[derive(Debug, Clone, uniffi::Record)]
591pub struct Conflict {
592    /// The index of the conflicting input.
593    pub vin: u32,
594    /// Conflicting transaction id.
595    pub txid: Arc<Txid>,
596}
597
598impl Display for Conflict {
599    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
600        write!(f, "{:?}", self)
601    }
602}
603
604impl From<BdkWalletEvent> for WalletEvent {
605    fn from(event: BdkWalletEvent) -> Self {
606        match event {
607            BdkWalletEvent::ChainTipChanged { old_tip, new_tip } => WalletEvent::ChainTipChanged {
608                old_tip: BlockId::from(old_tip),
609                new_tip: BlockId::from(new_tip),
610            },
611            BdkWalletEvent::TxConfirmed {
612                txid,
613                tx,
614                block_time,
615                old_block_time,
616            } => WalletEvent::TxConfirmed {
617                txid: Arc::new(Txid(txid)),
618                tx: Arc::new(tx.as_ref().clone().into()),
619                block_time: block_time.into(),
620                old_block_time: old_block_time.map(|v| v.into()),
621            },
622            BdkWalletEvent::TxUnconfirmed {
623                txid,
624                tx,
625                old_block_time,
626            } => WalletEvent::TxUnconfirmed {
627                txid: Arc::new(Txid(txid)),
628                tx: Arc::new(tx.as_ref().clone().into()),
629                old_block_time: old_block_time.map(|v| v.into()),
630            },
631            BdkWalletEvent::TxReplaced {
632                txid,
633                tx,
634                conflicts,
635            } => {
636                let conflict_list: Vec<Conflict> = conflicts
637                    .into_iter()
638                    .map(|(vin, conflict)| Conflict {
639                        vin: vin as u32,
640                        txid: Arc::new(Txid(conflict)),
641                    })
642                    .collect();
643                WalletEvent::TxReplaced {
644                    txid: Arc::new(Txid(txid)),
645                    tx: Arc::new(tx.as_ref().clone().into()),
646                    conflicts: conflict_list,
647                }
648            }
649            BdkWalletEvent::TxDropped { txid, tx } => WalletEvent::TxDropped {
650                txid: Arc::new(Txid(txid)),
651                tx: Arc::new(tx.as_ref().clone().into()),
652            },
653            _ => unreachable!("WalletEvent variant not covered in conversion"),
654        }
655    }
656}
657
658#[derive(Debug, Clone, uniffi::Enum)]
659pub enum PkOrF {
660    Pubkey { value: String },
661    XOnlyPubkey { value: String },
662    Fingerprint { value: String },
663}
664
665impl From<BdkPkOrF> for PkOrF {
666    fn from(value: BdkPkOrF) -> Self {
667        match value {
668            BdkPkOrF::Pubkey(public_key) => PkOrF::Pubkey {
669                value: public_key.to_string(),
670            },
671            BdkPkOrF::XOnlyPubkey(xonly_public_key) => PkOrF::XOnlyPubkey {
672                value: xonly_public_key.to_string(),
673            },
674            BdkPkOrF::Fingerprint(fingerprint) => PkOrF::Fingerprint {
675                value: fingerprint.to_string(),
676            },
677        }
678    }
679}
680
681#[derive(Debug, Clone, uniffi::Enum)]
682pub enum LockTime {
683    Blocks { height: u32 },
684    Seconds { consensus_time: u32 },
685}
686
687impl From<BdkLockTime> for LockTime {
688    fn from(value: BdkLockTime) -> Self {
689        match value {
690            BdkLockTime::Blocks(height) => LockTime::Blocks {
691                height: height.to_consensus_u32(),
692            },
693            BdkLockTime::Seconds(time) => LockTime::Seconds {
694                consensus_time: time.to_consensus_u32(),
695            },
696        }
697    }
698}
699
700impl TryFrom<&LockTime> for BdkLockTime {
701    type Error = CreateTxError;
702
703    fn try_from(value: &LockTime) -> Result<Self, CreateTxError> {
704        match value {
705            LockTime::Blocks { height } => BdkLockTime::from_height(*height)
706                .map_err(|_| CreateTxError::LockTimeConversionError),
707            LockTime::Seconds { consensus_time } => BdkLockTime::from_time(*consensus_time)
708                .map_err(|_| CreateTxError::LockTimeConversionError),
709        }
710    }
711}
712
713#[derive(Debug, Clone, uniffi::Enum)]
714pub enum Satisfaction {
715    Partial {
716        n: u64,
717        m: u64,
718        items: Vec<u64>,
719        sorted: Option<bool>,
720        conditions: HashMap<u32, Vec<Condition>>,
721    },
722    PartialComplete {
723        n: u64,
724        m: u64,
725        items: Vec<u64>,
726        sorted: Option<bool>,
727        conditions: HashMap<Vec<u32>, Vec<Condition>>,
728    },
729    Complete {
730        condition: Condition,
731    },
732    None {
733        msg: String,
734    },
735}
736
737impl From<BdkSatisfaction> for Satisfaction {
738    fn from(value: BdkSatisfaction) -> Self {
739        match value {
740            BdkSatisfaction::Partial {
741                n,
742                m,
743                items,
744                sorted,
745                conditions,
746            } => Satisfaction::Partial {
747                n: n as u64,
748                m: m as u64,
749                items: items.iter().map(|e| e.to_owned() as u64).collect(),
750                sorted,
751                conditions: conditions
752                    .into_iter()
753                    .map(|(index, conditions)| {
754                        (
755                            index as u32,
756                            conditions.into_iter().map(|e| e.into()).collect(),
757                        )
758                    })
759                    .collect(),
760            },
761            BdkSatisfaction::PartialComplete {
762                n,
763                m,
764                items,
765                sorted,
766                conditions,
767            } => Satisfaction::PartialComplete {
768                n: n as u64,
769                m: m as u64,
770                items: items.iter().map(|e| e.to_owned() as u64).collect(),
771                sorted,
772                conditions: conditions
773                    .into_iter()
774                    .map(|(index, conditions)| {
775                        (
776                            index.iter().map(|e| e.to_owned() as u32).collect(),
777                            conditions.into_iter().map(|e| e.into()).collect(),
778                        )
779                    })
780                    .collect(),
781            },
782            BdkSatisfaction::Complete { condition } => Satisfaction::Complete {
783                condition: condition.into(),
784            },
785            BdkSatisfaction::None => Satisfaction::None {
786                msg: "Cannot satisfy or contribute to the policy item".to_string(),
787            },
788        }
789    }
790}
791
792/// An extra condition that must be satisfied but that is out of control of the user
793#[derive(Debug, Clone, uniffi::Record)]
794pub struct Condition {
795    /// Optional CheckSequenceVerify condition
796    pub csv: Option<u32>,
797    /// Optional timelock condition
798    pub timelock: Option<LockTime>,
799}
800
801impl From<BdkCondition> for Condition {
802    fn from(value: BdkCondition) -> Self {
803        Condition {
804            csv: value.csv.map(|e| e.to_consensus_u32()),
805            timelock: value.timelock.map(|e| e.into()),
806        }
807    }
808}
809
810// This is a wrapper type around the bdk type [SignOptions](https://docs.rs/bdk_wallet/1.0.0/bdk_wallet/signer/struct.SignOptions.html)
811// because we did not want to expose the complexity behind the `TapLeavesOptions` type. When
812// transforming from a SignOption to a BdkSignOptions, we simply use the default values for
813// TapLeavesOptions.
814/// Options for a software signer.
815///
816/// Adjust the behavior of our software signers and the way a transaction is finalized.
817#[allow(deprecated)]
818#[derive(uniffi::Record)]
819pub struct SignOptions {
820    /// Whether the signer should trust the `witness_utxo`, if the `non_witness_utxo` hasn't been
821    /// provided
822    ///
823    /// Defaults to `false` to mitigate the "SegWit bug" which could trick the wallet into
824    /// paying a fee larger than expected.
825    ///
826    /// Some wallets, especially if relatively old, might not provide the `non_witness_utxo` for
827    /// SegWit transactions in the PSBT they generate: in those cases setting this to `true`
828    /// should correctly produce a signature, at the expense of an increased trust in the creator
829    /// of the PSBT.
830    ///
831    /// For more details see: <https://blog.trezor.io/details-of-firmware-updates-for-trezor-one-version-1-9-1-and-trezor-model-t-version-2-3-1-1eba8f60f2dd>
832    pub trust_witness_utxo: bool,
833    /// Whether the wallet should assume a specific height has been reached when trying to finalize
834    /// a transaction
835    ///
836    /// The wallet will only "use" a timelock to satisfy the spending policy of an input if the
837    /// timelock height has already been reached. This option allows overriding the "current height" to let the
838    /// wallet use timelocks in the future to spend a coin.
839    pub assume_height: Option<u32>,
840    /// Whether the signer should use the `sighash_type` set in the PSBT when signing, no matter
841    /// what its value is
842    ///
843    /// Defaults to `false` which will only allow signing using `SIGHASH_ALL`.
844    pub allow_all_sighashes: bool,
845    /// Whether to try finalizing the PSBT after the inputs are signed.
846    ///
847    /// Defaults to `true` which will try finalizing PSBT after inputs are signed.
848    pub try_finalize: bool,
849    /// Whether we should try to sign a taproot transaction with the taproot internal key
850    /// or not. This option is ignored if we're signing a non-taproot PSBT.
851    ///
852    /// Defaults to `true`, i.e., we always try to sign with the taproot internal key.
853    pub sign_with_tap_internal_key: bool,
854    /// Whether we should grind ECDSA signature to ensure signing with low r
855    /// or not.
856    /// Defaults to `true`, i.e., we always grind ECDSA signature to sign with low r.
857    pub allow_grinding: bool,
858}
859
860#[allow(deprecated)]
861impl From<SignOptions> for BdkSignOptions {
862    fn from(options: SignOptions) -> BdkSignOptions {
863        BdkSignOptions {
864            trust_witness_utxo: options.trust_witness_utxo,
865            assume_height: options.assume_height,
866            allow_all_sighashes: options.allow_all_sighashes,
867            try_finalize: options.try_finalize,
868            tap_leaves_options: TapLeavesOptions::default(),
869            sign_with_tap_internal_key: options.sign_with_tap_internal_key,
870            allow_grinding: options.allow_grinding,
871        }
872    }
873}
874
875/// Transaction confirmation metadata.
876#[derive(uniffi::Record, Debug)]
877pub struct TxStatus {
878    /// Is the transaction in a block.
879    pub confirmed: bool,
880    /// Height of the block this transaction was included.
881    pub block_height: Option<u32>,
882    /// Hash of the block.
883    pub block_hash: Option<Arc<BlockHash>>,
884    /// The time shown in the block, not necessarily the same time as when the block was found.
885    pub block_time: Option<u64>,
886}
887
888impl From<BdkTxStatus> for TxStatus {
889    fn from(status: BdkTxStatus) -> Self {
890        TxStatus {
891            confirmed: status.confirmed,
892            block_height: status.block_height,
893            block_hash: status.block_hash.map(|h| Arc::new(BlockHash(h))),
894            block_time: status.block_time,
895        }
896    }
897}
898
899#[derive(uniffi::Record, Debug)]
900pub struct MerkleProof {
901    pub block_height: u32,
902    pub merkle: Vec<Arc<Txid>>,
903    pub pos: u64,
904}
905
906impl From<BdkMerkleProof> for MerkleProof {
907    fn from(proof: BdkMerkleProof) -> Self {
908        MerkleProof {
909            block_height: proof.block_height,
910            merkle: proof
911                .merkle
912                .into_iter()
913                .map(|h| Arc::new(Txid(h)))
914                .collect(),
915            pos: proof.pos as u64,
916        }
917    }
918}
919
920#[derive(uniffi::Record, Debug)]
921pub struct OutputStatus {
922    pub spent: bool,
923    pub txid: Option<Arc<Txid>>,
924    pub vin: Option<u64>,
925    pub status: Option<TxStatus>,
926}
927
928impl From<BdkOutputStatus> for OutputStatus {
929    fn from(status: BdkOutputStatus) -> Self {
930        OutputStatus {
931            spent: status.spent,
932            txid: status.txid.map(|h| Arc::new(Txid(h))),
933            vin: status.vin,
934            status: status.status.map(|s| s.into()),
935        }
936    }
937}
938
939/// Bitcoin transaction metadata.
940#[derive(Debug, uniffi::Record)]
941pub struct Tx {
942    /// The transaction identifier.
943    pub txid: Arc<Txid>,
944    /// The transaction version, of which 0, 1, 2 are standard.
945    pub version: i32,
946    /// The block height or time restriction on the transaction.
947    pub locktime: u32,
948    /// The size of the transaction in bytes.
949    pub size: u64,
950    /// The weight units of this transaction.
951    pub weight: u64,
952    /// The fee of this transaction in satoshis.
953    pub fee: u64,
954    /// Confirmation status and data.
955    pub status: TxStatus,
956}
957
958impl From<BdkTx> for Tx {
959    fn from(tx: BdkTx) -> Self {
960        Self {
961            txid: Arc::new(Txid(tx.txid)),
962            version: tx.version,
963            locktime: tx.locktime,
964            size: tx.size as u64,
965            weight: tx.weight,
966            fee: tx.fee,
967            status: tx.status.into(),
968        }
969    }
970}
971
972/// This type replaces the Rust tuple `(tx, last_seen)` used in the Wallet::apply_unconfirmed_txs` method,
973/// where `last_seen` is the timestamp of when the transaction `tx` was last seen in the mempool.
974#[derive(uniffi::Record)]
975pub struct UnconfirmedTx {
976    pub tx: Arc<Transaction>,
977    pub last_seen: u64,
978}
979
980/// This type replaces the Rust tuple `(txid, evicted_at)` used in the Wallet::apply_evicted_txs` method,
981/// where `evicted_at` is the timestamp of when the transaction `txid` was evicted from the mempool.
982/// Transactions may be evicted for paying a low fee rate or having invalid scripts.
983#[derive(uniffi::Record)]
984pub struct EvictedTx {
985    pub txid: Arc<Txid>,
986    pub evicted_at: u64,
987}
988
989/// Mapping of descriptors to their last revealed index.
990#[derive(Debug, Clone, uniffi::Record)]
991pub struct IndexerChangeSet {
992    pub last_revealed: HashMap<Arc<DescriptorId>, u32>,
993}
994
995impl From<bdk_wallet::chain::indexer::keychain_txout::ChangeSet> for IndexerChangeSet {
996    fn from(mut value: bdk_wallet::chain::indexer::keychain_txout::ChangeSet) -> Self {
997        let mut changes = HashMap::new();
998        for (id, index) in core::mem::take(&mut value.last_revealed) {
999            changes.insert(Arc::new(DescriptorId(id.0)), index);
1000        }
1001        Self {
1002            last_revealed: changes,
1003        }
1004    }
1005}
1006
1007impl From<IndexerChangeSet> for bdk_wallet::chain::indexer::keychain_txout::ChangeSet {
1008    fn from(mut value: IndexerChangeSet) -> Self {
1009        let mut changes = BTreeMap::new();
1010        for (id, index) in core::mem::take(&mut value.last_revealed) {
1011            let descriptor_id = bdk_wallet::chain::DescriptorId(id.0);
1012            changes.insert(descriptor_id, index);
1013        }
1014        Self {
1015            last_revealed: changes,
1016            spk_cache: Default::default(),
1017        }
1018    }
1019}
1020
1021impl Default for IndexerChangeSet {
1022    fn default() -> Self {
1023        bdk_wallet::chain::indexer::keychain_txout::ChangeSet::default().into()
1024    }
1025}
1026
1027/// The hash added or removed at the given height.
1028#[derive(Debug, Clone, uniffi::Record)]
1029pub struct ChainChange {
1030    /// Effected height
1031    pub height: u32,
1032    /// A hash was added or must be removed.
1033    pub hash: Option<Arc<BlockHash>>,
1034}
1035
1036/// Changes to the local chain
1037#[derive(Debug, Clone, uniffi::Record)]
1038pub struct LocalChainChangeSet {
1039    pub changes: Vec<ChainChange>,
1040}
1041
1042impl From<bdk_wallet::chain::local_chain::ChangeSet> for LocalChainChangeSet {
1043    fn from(mut value: bdk_wallet::chain::local_chain::ChangeSet) -> Self {
1044        let mut changes = Vec::with_capacity(value.blocks.len());
1045        for (height, hash) in core::mem::take(&mut value.blocks) {
1046            let hash = hash.map(|h| Arc::new(BlockHash(h)));
1047            let change = ChainChange { height, hash };
1048            changes.push(change);
1049        }
1050        Self { changes }
1051    }
1052}
1053
1054impl From<LocalChainChangeSet> for bdk_wallet::chain::local_chain::ChangeSet {
1055    fn from(mut value: LocalChainChangeSet) -> Self {
1056        let mut changes = BTreeMap::new();
1057        for change in core::mem::take(&mut value.changes) {
1058            let height = change.height;
1059            let hash = change.hash.map(|h| h.0);
1060            changes.insert(height, hash);
1061        }
1062        Self { blocks: changes }
1063    }
1064}
1065
1066impl Default for LocalChainChangeSet {
1067    fn default() -> Self {
1068        bdk_wallet::chain::local_chain::ChangeSet::default().into()
1069    }
1070}
1071
1072#[derive(Debug, Clone, uniffi::Record)]
1073pub struct Anchor {
1074    pub confirmation_block_time: ConfirmationBlockTime,
1075    pub txid: Arc<Txid>,
1076}
1077
1078#[derive(Debug, Clone, uniffi::Record)]
1079pub struct TxGraphChangeSet {
1080    pub txs: Vec<Arc<Transaction>>,
1081    pub txouts: HashMap<Arc<HashableOutPoint>, TxOut>,
1082    pub anchors: Vec<Anchor>,
1083    pub last_seen: HashMap<Arc<Txid>, u64>,
1084    pub first_seen: HashMap<Arc<Txid>, u64>,
1085    pub last_evicted: HashMap<Arc<Txid>, u64>,
1086}
1087
1088impl From<bdk_wallet::chain::tx_graph::ChangeSet<BdkConfirmationBlockTime>> for TxGraphChangeSet {
1089    fn from(mut value: bdk_wallet::chain::tx_graph::ChangeSet<BdkConfirmationBlockTime>) -> Self {
1090        let btree_txs = core::mem::take(&mut value.txs);
1091        let txs = btree_txs
1092            .into_iter()
1093            .map(|tx| Arc::new(tx.as_ref().into()))
1094            .collect::<Vec<Arc<Transaction>>>();
1095        let mut txouts = HashMap::new();
1096        for (outpoint, txout) in core::mem::take(&mut value.txouts) {
1097            txouts.insert(Arc::new(HashableOutPoint(outpoint.into())), txout.into());
1098        }
1099        let mut anchors = Vec::new();
1100        for anchor in core::mem::take(&mut value.anchors) {
1101            let confirmation_block_time = anchor.0.into();
1102            let txid = Arc::new(Txid(anchor.1));
1103            let anchor = Anchor {
1104                confirmation_block_time,
1105                txid,
1106            };
1107            anchors.push(anchor);
1108        }
1109        let mut last_seen = HashMap::new();
1110        for (txid, time) in core::mem::take(&mut value.last_seen) {
1111            last_seen.insert(Arc::new(Txid(txid)), time);
1112        }
1113
1114        let mut first_seen = HashMap::new();
1115        for (txid, time) in core::mem::take(&mut value.first_seen) {
1116            first_seen.insert(Arc::new(Txid(txid)), time);
1117        }
1118
1119        let mut last_evicted = HashMap::new();
1120        for (txid, time) in core::mem::take(&mut value.last_evicted) {
1121            last_evicted.insert(Arc::new(Txid(txid)), time);
1122        }
1123
1124        TxGraphChangeSet {
1125            txs,
1126            txouts,
1127            anchors,
1128            last_seen,
1129            first_seen,
1130            last_evicted,
1131        }
1132    }
1133}
1134
1135impl Default for TxGraphChangeSet {
1136    fn default() -> Self {
1137        bdk_wallet::chain::tx_graph::ChangeSet::default().into()
1138    }
1139}
1140
1141impl From<TxGraphChangeSet> for bdk_wallet::chain::tx_graph::ChangeSet<BdkConfirmationBlockTime> {
1142    fn from(mut value: TxGraphChangeSet) -> Self {
1143        let mut txs = BTreeSet::new();
1144        for tx in core::mem::take(&mut value.txs) {
1145            let tx = Arc::new(tx.as_ref().into());
1146            txs.insert(tx);
1147        }
1148        let mut txouts = BTreeMap::new();
1149        for txout in core::mem::take(&mut value.txouts) {
1150            txouts.insert(txout.0.outpoint().into(), txout.1.into());
1151        }
1152        let mut anchors = BTreeSet::new();
1153        for anchor in core::mem::take(&mut value.anchors) {
1154            let txid = anchor.txid.0;
1155            anchors.insert((anchor.confirmation_block_time.into(), txid));
1156        }
1157        let mut last_seen = BTreeMap::new();
1158        for (txid, time) in core::mem::take(&mut value.last_seen) {
1159            last_seen.insert(txid.0, time);
1160        }
1161
1162        let mut first_seen = BTreeMap::new();
1163        for (txid, time) in core::mem::take(&mut value.first_seen) {
1164            first_seen.insert(txid.0, time);
1165        }
1166
1167        let mut last_evicted = BTreeMap::new();
1168        for (txid, time) in core::mem::take(&mut value.last_evicted) {
1169            last_evicted.insert(txid.0, time);
1170        }
1171
1172        Self {
1173            txs,
1174            txouts,
1175            anchors,
1176            last_seen,
1177            first_seen,
1178            last_evicted,
1179        }
1180    }
1181}
1182
1183#[derive(Debug, Clone, Default)]
1184struct LockedOutpointsChangeSet(HashMap<Arc<HashableOutPoint>, bool>);
1185
1186impl From<LockedOutpointsChangeSet> for BdkLockedOutpointsChangeSet {
1187    fn from(value: LockedOutpointsChangeSet) -> Self {
1188        let outpoints = value
1189            .0
1190            .into_iter()
1191            .map(|(outpoint, is_locked)| (outpoint.outpoint().into(), is_locked))
1192            .collect();
1193
1194        Self { outpoints }
1195    }
1196}
1197
1198impl From<BdkLockedOutpointsChangeSet> for LockedOutpointsChangeSet {
1199    fn from(value: BdkLockedOutpointsChangeSet) -> Self {
1200        Self(
1201            value
1202                .outpoints
1203                .into_iter()
1204                .map(|(outpoint, is_locked)| {
1205                    (Arc::new(HashableOutPoint(outpoint.into())), is_locked)
1206                })
1207                .collect(),
1208        )
1209    }
1210}
1211
1212#[derive(Debug, Clone, uniffi::Object)]
1213pub struct ChangeSet {
1214    descriptor: Option<Arc<Descriptor>>,
1215    change_descriptor: Option<Arc<Descriptor>>,
1216    network: Option<bdk_wallet::bitcoin::Network>,
1217    local_chain: LocalChainChangeSet,
1218    tx_graph: TxGraphChangeSet,
1219    indexer: IndexerChangeSet,
1220    locked_outpoints: LockedOutpointsChangeSet,
1221}
1222
1223#[uniffi::export]
1224impl ChangeSet {
1225    /// Create an empty `ChangeSet`.
1226    #[uniffi::constructor]
1227    #[allow(clippy::new_without_default)]
1228    pub fn new() -> Self {
1229        bdk_wallet::ChangeSet::default().into()
1230    }
1231
1232    #[uniffi::constructor]
1233    pub fn from_aggregate(
1234        descriptor: Option<Arc<Descriptor>>,
1235        change_descriptor: Option<Arc<Descriptor>>,
1236        network: Option<bdk_wallet::bitcoin::Network>,
1237        local_chain: LocalChainChangeSet,
1238        tx_graph: TxGraphChangeSet,
1239        indexer: IndexerChangeSet,
1240    ) -> Self {
1241        Self {
1242            descriptor,
1243            change_descriptor,
1244            network,
1245            local_chain,
1246            tx_graph,
1247            indexer,
1248            locked_outpoints: LockedOutpointsChangeSet::default(),
1249        }
1250    }
1251
1252    #[uniffi::constructor]
1253    pub fn from_aggregate_with_locked_outpoints(
1254        descriptor: Option<Arc<Descriptor>>,
1255        change_descriptor: Option<Arc<Descriptor>>,
1256        network: Option<bdk_wallet::bitcoin::Network>,
1257        local_chain: LocalChainChangeSet,
1258        tx_graph: TxGraphChangeSet,
1259        indexer: IndexerChangeSet,
1260        locked_outpoints: HashMap<Arc<HashableOutPoint>, bool>,
1261    ) -> Self {
1262        Self {
1263            descriptor,
1264            change_descriptor,
1265            network,
1266            local_chain,
1267            tx_graph,
1268            indexer,
1269            locked_outpoints: LockedOutpointsChangeSet(locked_outpoints),
1270        }
1271    }
1272
1273    #[uniffi::constructor]
1274    pub fn from_descriptor_and_network(
1275        descriptor: Option<Arc<Descriptor>>,
1276        change_descriptor: Option<Arc<Descriptor>>,
1277        network: Option<bdk_wallet::bitcoin::Network>,
1278    ) -> Self {
1279        Self {
1280            descriptor,
1281            change_descriptor,
1282            network,
1283            local_chain: LocalChainChangeSet::default(),
1284            tx_graph: TxGraphChangeSet::default(),
1285            indexer: IndexerChangeSet::default(),
1286            locked_outpoints: LockedOutpointsChangeSet::default(),
1287        }
1288    }
1289
1290    /// Start a wallet `ChangeSet` from local chain changes.
1291    #[uniffi::constructor]
1292    pub fn from_local_chain_changes(local_chain_changes: LocalChainChangeSet) -> Self {
1293        let local_chain: bdk_wallet::chain::local_chain::ChangeSet = local_chain_changes.into();
1294        let changeset: bdk_wallet::ChangeSet = local_chain.into();
1295        changeset.into()
1296    }
1297
1298    /// Start a wallet `ChangeSet` from indexer changes.
1299    #[uniffi::constructor]
1300    pub fn from_indexer_changeset(indexer_changes: IndexerChangeSet) -> Self {
1301        let indexer: bdk_wallet::chain::indexer::keychain_txout::ChangeSet = indexer_changes.into();
1302        let changeset: bdk_wallet::ChangeSet = indexer.into();
1303        changeset.into()
1304    }
1305
1306    /// Start a wallet `ChangeSet` from transaction graph changes.
1307    #[uniffi::constructor]
1308    pub fn from_tx_graph_changeset(tx_graph_changeset: TxGraphChangeSet) -> Self {
1309        let tx_graph: bdk_wallet::chain::tx_graph::ChangeSet<BdkConfirmationBlockTime> =
1310            tx_graph_changeset.into();
1311        let changeset: bdk_wallet::ChangeSet = tx_graph.into();
1312        changeset.into()
1313    }
1314
1315    /// Start a wallet `ChangeSet` from locked outpoint changes.
1316    #[uniffi::constructor]
1317    pub fn from_locked_outpoints_changeset(
1318        locked_outpoints_changeset: HashMap<Arc<HashableOutPoint>, bool>,
1319    ) -> Self {
1320        let changeset: bdk_wallet::ChangeSet =
1321            BdkLockedOutpointsChangeSet::from(LockedOutpointsChangeSet(locked_outpoints_changeset))
1322                .into();
1323        changeset.into()
1324    }
1325
1326    /// Build a `ChangeSet` by merging together two `ChangeSet`.
1327    #[uniffi::constructor]
1328    pub fn from_merge(left: Arc<ChangeSet>, right: Arc<ChangeSet>) -> Self {
1329        let mut left: bdk_wallet::ChangeSet = left.as_ref().clone().into();
1330        let right: bdk_wallet::ChangeSet = right.as_ref().clone().into();
1331        left.merge(right);
1332        left.into()
1333    }
1334
1335    /// Get the receiving `Descriptor`.
1336    pub fn descriptor(&self) -> Option<Arc<Descriptor>> {
1337        self.descriptor.clone()
1338    }
1339
1340    /// Get the change `Descriptor`
1341    pub fn change_descriptor(&self) -> Option<Arc<Descriptor>> {
1342        self.change_descriptor.clone()
1343    }
1344
1345    /// Get the `Network`
1346    pub fn network(&self) -> Option<bdk_wallet::bitcoin::Network> {
1347        self.network
1348    }
1349
1350    /// Get the changes to the local chain.
1351    pub fn localchain_changeset(&self) -> LocalChainChangeSet {
1352        self.local_chain.clone()
1353    }
1354
1355    /// Get the changes to the transaction graph.
1356    pub fn tx_graph_changeset(&self) -> TxGraphChangeSet {
1357        self.tx_graph.clone()
1358    }
1359
1360    /// Get the changes to the indexer.
1361    pub fn indexer_changeset(&self) -> IndexerChangeSet {
1362        self.indexer.clone()
1363    }
1364
1365    /// Get the changes to locked outpoints.
1366    pub fn locked_outpoints_changeset(&self) -> HashMap<Arc<HashableOutPoint>, bool> {
1367        self.locked_outpoints.0.clone()
1368    }
1369}
1370
1371impl From<ChangeSet> for bdk_wallet::ChangeSet {
1372    fn from(value: ChangeSet) -> Self {
1373        let descriptor = value.descriptor.map(|d| d.extended_descriptor.clone());
1374        let change_descriptor = value
1375            .change_descriptor
1376            .map(|d| d.extended_descriptor.clone());
1377        let network = value.network;
1378        let local_chain = value.local_chain.into();
1379        let tx_graph = value.tx_graph.into();
1380        let indexer = value.indexer.into();
1381        let locked_outpoints = value.locked_outpoints.into();
1382        Self {
1383            descriptor,
1384            change_descriptor,
1385            network,
1386            local_chain,
1387            tx_graph,
1388            indexer,
1389            locked_outpoints,
1390        }
1391    }
1392}
1393
1394impl From<bdk_wallet::ChangeSet> for ChangeSet {
1395    fn from(value: bdk_wallet::ChangeSet) -> Self {
1396        let descriptor = value.descriptor.map(|d| {
1397            Arc::new(Descriptor {
1398                extended_descriptor: d,
1399                key_map: BTreeMap::new(),
1400            })
1401        });
1402        let change_descriptor = value.change_descriptor.map(|d| {
1403            Arc::new(Descriptor {
1404                extended_descriptor: d,
1405                key_map: BTreeMap::new(),
1406            })
1407        });
1408        let network = value.network;
1409        let local_chain = value.local_chain.into();
1410        let tx_graph = value.tx_graph.into();
1411        let indexer = value.indexer.into();
1412        let locked_outpoints = value.locked_outpoints.into();
1413        Self {
1414            descriptor,
1415            change_descriptor,
1416            network,
1417            local_chain,
1418            tx_graph,
1419            indexer,
1420            locked_outpoints,
1421        }
1422    }
1423}
1424
1425/// Details about a transaction affecting the wallet (relevant and canonical).
1426#[derive(uniffi::Record, Debug, Clone)]
1427pub struct TxDetails {
1428    /// The transaction id.
1429    pub txid: Arc<Txid>,
1430    /// The sum of the transaction input amounts that spend from previous outputs tracked by this
1431    /// wallet.
1432    pub sent: Arc<Amount>,
1433    /// The sum of the transaction outputs that send to script pubkeys tracked by this wallet.
1434    pub received: Arc<Amount>,
1435    /// The fee paid for the transaction. Note that to calculate the fee for a transaction with
1436    /// inputs not owned by this wallet you must manually insert the TxOut(s) into the tx graph
1437    /// using the insert_txout function. If those are not available, the field will be `None`.
1438    pub fee: Option<Arc<Amount>>,
1439    /// The fee rate paid for the transaction. Note that to calculate the fee rate for a
1440    /// transaction with inputs not owned by this wallet you must manually insert the TxOut(s) into
1441    /// the tx graph using the insert_txout function. If those are not available, the field will be
1442    /// `None`.
1443    pub fee_rate: Option<Arc<FeeRate>>,
1444    /// The net effect of the transaction on the balance of the wallet.
1445    pub balance_delta: i64,
1446    /// The position of the transaction in the chain.
1447    pub chain_position: ChainPosition,
1448    /// The complete `Transaction`.
1449    pub tx: Arc<Transaction>,
1450}
1451
1452impl From<bdk_wallet::TxDetails> for TxDetails {
1453    fn from(details: bdk_wallet::TxDetails) -> Self {
1454        TxDetails {
1455            txid: Arc::new(Txid(details.txid)),
1456            sent: Arc::new(details.sent.into()),
1457            received: Arc::new(details.received.into()),
1458            fee: details.fee.map(|f| Arc::new(f.into())),
1459            fee_rate: details.fee_rate.map(|fr| Arc::new(fr.into())),
1460            balance_delta: details.balance_delta.to_sat(),
1461            chain_position: details.chain_position.into(),
1462            tx: Arc::new(Transaction::from(details.tx.as_ref().clone())),
1463        }
1464    }
1465}
1466
1467/// Wildcard types for descriptors
1468#[derive(uniffi::Enum, PartialEq)]
1469pub enum WildcardType {
1470    /// Unhardened wildcard, e.g. *
1471    Unhardened,
1472    /// Hardened wildcard, e.g. *h
1473    Hardened,
1474}
1475
1476impl From<WildcardType> for Wildcard {
1477    fn from(wildcard_type: WildcardType) -> Self {
1478        match wildcard_type {
1479            WildcardType::Unhardened => Wildcard::Unhardened,
1480            WildcardType::Hardened => Wildcard::Hardened,
1481        }
1482    }
1483}