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