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#[uniffi::export(Display)]
214#[derive(uniffi::Record)]
215pub struct WalletKeychain {
216 pub keychain: KeychainKind,
218 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#[derive(uniffi::Record)]
230pub struct Balance {
231 pub immature: Arc<Amount>,
233 pub trusted_pending: Arc<Amount>,
235 pub untrusted_pending: Arc<Amount>,
237 pub confirmed: Arc<Amount>,
239 pub trusted_spendable: Arc<Amount>,
244 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#[derive(uniffi::Record)]
263pub struct LocalOutput {
264 pub outpoint: OutPoint,
266 pub txout: TxOut,
268 pub keychain: KeychainKind,
270 pub is_spent: bool,
272 pub derivation_index: u32,
274 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#[uniffi::export(with_foreign)]
299pub trait FullScanScriptInspector: Sync + Send {
300 fn inspect(&self, keychain: KeychainKind, index: u32, script: Arc<Script>);
301}
302
303#[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#[derive(uniffi::Object)]
391pub struct Update(pub(crate) BdkUpdate);
392
393#[derive(uniffi::Record)]
395pub struct SentAndReceivedValues {
396 pub sent: Arc<Amount>,
398 pub received: Arc<Amount>,
400}
401
402#[derive(uniffi::Record)]
404pub struct KeychainAndIndex {
405 pub keychain: KeychainKind,
407 pub index: u32,
409}
410
411#[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#[derive(Debug, Clone, uniffi::Enum)]
528#[uniffi::export(Display)]
529#[non_exhaustive]
530pub enum WalletEvent {
531 ChainTipChanged {
533 old_tip: BlockId,
535 new_tip: BlockId,
537 },
538 TxConfirmed {
546 txid: Arc<Txid>,
548 tx: Arc<Transaction>,
550 block_time: ConfirmationBlockTime,
552 old_block_time: Option<ConfirmationBlockTime>,
554 },
555 TxUnconfirmed {
563 txid: Arc<Txid>,
565 tx: Arc<Transaction>,
567 old_block_time: Option<ConfirmationBlockTime>,
569 },
570 TxReplaced {
578 txid: Arc<Txid>,
580 tx: Arc<Transaction>,
582 conflicts: Vec<Conflict>,
584 },
585 TxDropped {
591 txid: Arc<Txid>,
593 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#[uniffi::export(Display)]
606#[derive(Debug, Clone, uniffi::Record)]
607pub struct Conflict {
608 pub vin: u32,
610 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#[derive(Debug, Clone, uniffi::Record)]
810pub struct Condition {
811 pub csv: Option<u32>,
813 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#[allow(deprecated)]
834#[derive(uniffi::Record)]
835pub struct SignOptions {
836 pub trust_witness_utxo: bool,
849 pub assume_height: Option<u32>,
856 pub allow_all_sighashes: bool,
861 pub try_finalize: bool,
865 pub sign_with_tap_internal_key: bool,
870 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#[derive(uniffi::Record, Debug)]
893pub struct TxStatus {
894 pub confirmed: bool,
896 pub block_height: Option<u32>,
898 pub block_hash: Option<Arc<BlockHash>>,
900 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#[derive(Debug, uniffi::Record)]
957pub struct Tx {
958 pub txid: Arc<Txid>,
960 pub version: i32,
962 pub locktime: u32,
964 pub size: u64,
966 pub weight: u64,
968 pub fee: u64,
970 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#[derive(uniffi::Record)]
991pub struct UnconfirmedTx {
992 pub tx: Arc<Transaction>,
993 pub last_seen: u64,
994}
995
996#[derive(uniffi::Record)]
1000pub struct EvictedTx {
1001 pub txid: Arc<Txid>,
1002 pub evicted_at: u64,
1003}
1004
1005#[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#[derive(Debug, Clone, uniffi::Record)]
1045pub struct ChainChange {
1046 pub height: u32,
1048 pub hash: Option<Arc<BlockHash>>,
1050}
1051
1052#[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 #[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 #[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 #[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 #[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 #[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 #[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 pub fn descriptor(&self) -> Option<Arc<Descriptor>> {
1353 self.descriptor.clone()
1354 }
1355
1356 pub fn change_descriptor(&self) -> Option<Arc<Descriptor>> {
1358 self.change_descriptor.clone()
1359 }
1360
1361 pub fn network(&self) -> Option<bdk_wallet::bitcoin::Network> {
1363 self.network
1364 }
1365
1366 pub fn localchain_changeset(&self) -> LocalChainChangeSet {
1368 self.local_chain.clone()
1369 }
1370
1371 pub fn tx_graph_changeset(&self) -> TxGraphChangeSet {
1373 self.tx_graph.clone()
1374 }
1375
1376 pub fn indexer_changeset(&self) -> IndexerChangeSet {
1378 self.indexer.clone()
1379 }
1380
1381 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#[derive(uniffi::Record, Debug, Clone)]
1443pub struct TxDetails {
1444 pub txid: Arc<Txid>,
1446 pub sent: Arc<Amount>,
1449 pub received: Arc<Amount>,
1451 pub fee: Option<Arc<Amount>>,
1455 pub fee_rate: Option<Arc<FeeRate>>,
1460 pub balance_delta: i64,
1462 pub chain_position: ChainPosition,
1464 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#[derive(uniffi::Enum, PartialEq)]
1485pub enum WildcardType {
1486 Unhardened,
1488 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}