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#[uniffi::remote(Enum)]
51pub enum KeychainKind {
52 External = 0,
54 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#[derive(Debug, uniffi::Enum, Clone)]
71pub enum ChainPosition {
72 Confirmed {
74 confirmation_block_time: ConfirmationBlockTime,
75 transitively: Option<Arc<Txid>>,
79 },
80 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#[derive(Debug, Clone, PartialEq, Eq, std::hash::Hash, uniffi::Record)]
112pub struct ConfirmationBlockTime {
113 pub block_id: BlockId,
115 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#[derive(Debug, Clone, PartialEq, Eq, std::hash::Hash, uniffi::Record)]
139pub struct BlockId {
140 pub height: u32,
142 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#[derive(uniffi::Record)]
166pub struct CanonicalTx {
167 pub transaction: Arc<Transaction>,
169 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#[derive(uniffi::Record)]
184pub struct ScriptAmount {
185 pub script: Arc<Script>,
187 pub amount: Arc<Amount>,
189}
190
191#[derive(uniffi::Record)]
193pub struct AddressInfo {
194 pub index: u32,
196 pub address: Arc<Address>,
198 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#[derive(uniffi::Record)]
214pub struct Balance {
215 pub immature: Arc<Amount>,
217 pub trusted_pending: Arc<Amount>,
219 pub untrusted_pending: Arc<Amount>,
221 pub confirmed: Arc<Amount>,
223 pub trusted_spendable: Arc<Amount>,
228 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#[derive(uniffi::Record)]
247pub struct LocalOutput {
248 pub outpoint: OutPoint,
250 pub txout: TxOut,
252 pub keychain: KeychainKind,
254 pub is_spent: bool,
256 pub derivation_index: u32,
258 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#[uniffi::export(with_foreign)]
283pub trait FullScanScriptInspector: Sync + Send {
284 fn inspect(&self, keychain: KeychainKind, index: u32, script: Arc<Script>);
285}
286
287#[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#[derive(uniffi::Object)]
375pub struct Update(pub(crate) BdkUpdate);
376
377#[derive(uniffi::Record)]
379pub struct SentAndReceivedValues {
380 pub sent: Arc<Amount>,
382 pub received: Arc<Amount>,
384}
385
386#[derive(uniffi::Record)]
388pub struct KeychainAndIndex {
389 pub keychain: KeychainKind,
391 pub index: u32,
393}
394
395#[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#[derive(Debug, Clone, uniffi::Enum)]
512#[uniffi::export(Display)]
513#[non_exhaustive]
514pub enum WalletEvent {
515 ChainTipChanged {
517 old_tip: BlockId,
519 new_tip: BlockId,
521 },
522 TxConfirmed {
530 txid: Arc<Txid>,
532 tx: Arc<Transaction>,
534 block_time: ConfirmationBlockTime,
536 old_block_time: Option<ConfirmationBlockTime>,
538 },
539 TxUnconfirmed {
547 txid: Arc<Txid>,
549 tx: Arc<Transaction>,
551 old_block_time: Option<ConfirmationBlockTime>,
553 },
554 TxReplaced {
562 txid: Arc<Txid>,
564 tx: Arc<Transaction>,
566 conflicts: Vec<Conflict>,
568 },
569 TxDropped {
575 txid: Arc<Txid>,
577 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#[uniffi::export(Display)]
590#[derive(Debug, Clone, uniffi::Record)]
591pub struct Conflict {
592 pub vin: u32,
594 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#[derive(Debug, Clone, uniffi::Record)]
794pub struct Condition {
795 pub csv: Option<u32>,
797 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#[allow(deprecated)]
818#[derive(uniffi::Record)]
819pub struct SignOptions {
820 pub trust_witness_utxo: bool,
833 pub assume_height: Option<u32>,
840 pub allow_all_sighashes: bool,
845 pub try_finalize: bool,
849 pub sign_with_tap_internal_key: bool,
854 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#[derive(uniffi::Record, Debug)]
877pub struct TxStatus {
878 pub confirmed: bool,
880 pub block_height: Option<u32>,
882 pub block_hash: Option<Arc<BlockHash>>,
884 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#[derive(Debug, uniffi::Record)]
941pub struct Tx {
942 pub txid: Arc<Txid>,
944 pub version: i32,
946 pub locktime: u32,
948 pub size: u64,
950 pub weight: u64,
952 pub fee: u64,
954 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#[derive(uniffi::Record)]
975pub struct UnconfirmedTx {
976 pub tx: Arc<Transaction>,
977 pub last_seen: u64,
978}
979
980#[derive(uniffi::Record)]
984pub struct EvictedTx {
985 pub txid: Arc<Txid>,
986 pub evicted_at: u64,
987}
988
989#[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#[derive(Debug, Clone, uniffi::Record)]
1029pub struct ChainChange {
1030 pub height: u32,
1032 pub hash: Option<Arc<BlockHash>>,
1034}
1035
1036#[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 #[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 #[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 #[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 #[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 #[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 #[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 pub fn descriptor(&self) -> Option<Arc<Descriptor>> {
1337 self.descriptor.clone()
1338 }
1339
1340 pub fn change_descriptor(&self) -> Option<Arc<Descriptor>> {
1342 self.change_descriptor.clone()
1343 }
1344
1345 pub fn network(&self) -> Option<bdk_wallet::bitcoin::Network> {
1347 self.network
1348 }
1349
1350 pub fn localchain_changeset(&self) -> LocalChainChangeSet {
1352 self.local_chain.clone()
1353 }
1354
1355 pub fn tx_graph_changeset(&self) -> TxGraphChangeSet {
1357 self.tx_graph.clone()
1358 }
1359
1360 pub fn indexer_changeset(&self) -> IndexerChangeSet {
1362 self.indexer.clone()
1363 }
1364
1365 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#[derive(uniffi::Record, Debug, Clone)]
1427pub struct TxDetails {
1428 pub txid: Arc<Txid>,
1430 pub sent: Arc<Amount>,
1433 pub received: Arc<Amount>,
1435 pub fee: Option<Arc<Amount>>,
1439 pub fee_rate: Option<Arc<FeeRate>>,
1444 pub balance_delta: i64,
1446 pub chain_position: ChainPosition,
1448 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#[derive(uniffi::Enum, PartialEq)]
1469pub enum WildcardType {
1470 Unhardened,
1472 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}