Skip to main content

bark/onchain/
mod.rs

1//! Onchain wallet integration interfaces.
2//!
3//! This module defines the traits and types that an external onchain wallet must
4//! implement to be used by the library. The goal is to let integrators plug in
5//! their own wallet implementation, so features like boarding (moving onchain funds
6//! into Ark) and unilateral exit (claiming VTXOs onchain without server cooperation)
7//! are supported.
8//!
9//! Key concepts exposed here:
10//! - [Utxo], [LocalUtxo] & [SpendableExit]: lightweight types representing wallet UTXOs and
11//!   spendable exit outputs.
12//! - [OnchainWalletTrait]: unified interface covering balance, address, PSBT construction and signing,
13//!   transaction lookups, and CPFP fee-bumping for unilateral exits.
14//!
15//! A reference implementation based on BDK is available behind the `onchain-bdk`
16//! cargo feature. Enable it to use the provided [OnchainWallet] implementation.
17//! You can use all features from BDK because [bdk_wallet] is re-exported.
18
19#[cfg(feature = "onchain-bdk")]
20mod bdk;
21
22#[cfg(feature = "onchain-bdk")]
23pub use bdk_wallet;
24
25pub use bitcoin_ext::cpfp::{CpfpError, MakeCpfpFees};
26
27/// BDK-backed onchain wallet implementation.
28///
29/// Available only when the `onchain-bdk` feature is enabled.
30#[cfg(feature = "onchain-bdk")]
31pub use crate::onchain::bdk::{OnchainWallet, TxBuilderExt};
32
33use std::sync::Arc;
34
35use bitcoin::{
36	Address, Amount, FeeRate, OutPoint, Psbt, Script, SignedAmount, Transaction, Txid,
37};
38
39use ark::Vtxo;
40use ark::vtxo::Full;
41use bitcoin_ext::{BlockHeight, BlockRef};
42
43use crate::chain::ChainSource;
44
45
46/// Summary of a wallet transaction produced by [OnchainWallet::list_transaction_infos].
47#[derive(Debug, Clone)]
48pub struct WalletTxInfo {
49	pub txid: Txid,
50	pub tx: Arc<Transaction>,
51	/// Total fee paid by the transaction, when computable. `None` for inbound or
52	/// collaboratively-funded txs whose foreign prevouts BDK has not indexed
53	/// (e.g. after a bitcoind-rpc sync — esplora syncs populate prevouts).
54	pub onchain_fees: Option<Amount>,
55	/// Net change to the wallet's balance: `received - sent` over wallet-owned outputs.
56	pub balance_change: SignedAmount,
57	/// `Some` if the transaction is confirmed in a block, `None` if still in the mempool.
58	pub confirmation: Option<BlockRef>,
59	/// `true` when this tx spends a P2A fee anchor — i.e. it is a CPFP child
60	/// bumping the parent that created the anchor.
61	pub is_cpfp: bool,
62}
63
64/// Represents an onchain UTXO known to the wallet.
65///
66/// This can be either:
67/// - `Local`: a standard wallet UTXO
68/// - `Exit`: a spendable exit output produced by the Ark exit mechanism
69#[derive(Debug, Clone)]
70pub enum Utxo {
71	Local(LocalUtxo),
72	Exit(SpendableExit),
73}
74
75/// A standard wallet [Utxo] owned by the local wallet implementation.
76#[derive(Debug, Clone)]
77pub struct LocalUtxo {
78	/// The outpoint referencing the UTXO.
79	pub outpoint: OutPoint,
80	/// The amount contained in the UTXO.
81	pub amount: Amount,
82	/// Optional confirmation height; `None` if unconfirmed.
83	pub confirmation_height: Option<BlockHeight>,
84}
85
86/// A spendable unilateral exit of a [Vtxo] which can be claimed onchain.
87///
88/// When exiting unilaterally, the wallet will end up with onchain outputs that correspond to
89/// previously-held VTXOs. These can be claimed and used for further spending.
90#[derive(Debug, Clone)]
91pub struct SpendableExit {
92	/// The VTXO being exited.
93	pub vtxo: Vtxo<Full>,
94	/// The block height associated with the exits' validity window.
95	pub height: BlockHeight,
96}
97
98#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
99#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
100pub trait OnchainWalletTrait: std::any::Any + Send + Sync {
101	/// Get the total balance of the wallet
102	async fn balance(&self) -> Amount;
103
104	/// Get an onchain receive address from the wallet
105	async fn address(&mut self) -> anyhow::Result<Address>;
106
107	/// Sync the wallet with the onchain network.
108	async fn sync(&mut self, chain: &ChainSource) -> anyhow::Result<()>;
109
110	/// Returns `true` if the given script pubkey belongs to the wallet's keychains.
111	async fn is_mine(&self, spk: &Script) -> anyhow::Result<bool>;
112
113	/// Register an unconfirmed transaction relevant to the wallet
114	async fn register_tx(&mut self, tx: &Transaction) -> anyhow::Result<()>;
115
116	/// Prepare a [Transaction] which will send to the given destinations
117	async fn prepare_tx(
118		&mut self,
119		destinations: &[(Address, Amount)],
120		fee_rate: FeeRate,
121	) -> anyhow::Result<Psbt>;
122
123	/// Prepare a [Transaction] for sending all wallet funds to the given destination
124	async fn prepare_drain_tx(
125		&mut self,
126		destination: Address,
127		fee_rate: FeeRate,
128	) -> anyhow::Result<Psbt>;
129
130	/// Consume a [Psbt] and return a fully signed [Psbt] with all witnesses filled in
131	///
132	/// Useful when the signed [Psbt] is needed after signing, e.g. to compute fees
133	/// via [Psbt::fee] before extracting the final [Transaction].
134	///
135	/// Wallets should apply all necessary signatures and finalize inputs according
136	/// to their internal key management and policies.
137	async fn finish_psbt(&mut self, psbt: Psbt) -> anyhow::Result<Psbt>;
138
139	/// Creates a signed Child Pays for Parent (CPFP) transaction using a Pay-to-Anchor (P2A) output
140	/// to broadcast unilateral exits and other TRUC transactions.
141	///
142	/// For more information please see [BIP431](https://github.com/bitcoin/bips/blob/master/bip-0431.mediawiki#topologically-restricted-until-confirmation).
143	///
144	/// # Arguments
145	///
146	/// * `tx` - A parent `Transaction` that is guaranteed to have one P2A output which
147	///          implementations must spend so that both the parent and child transactions can be
148	///          broadcast to the network as a v3 transaction package.
149	/// * `fees` - Informs the implementation how fees should be paid by the child transaction. Note
150	///            that an effective fee rate should be calculated using the weight of both the
151	///            parent and child transactions.
152	///
153	/// # Returns
154	///
155	/// Returns a `Result` containing:
156	/// * `Transaction` - The signed CPFP transaction ready to be broadcasted to the network with
157	///                   the given parent transaction if construction and signing were successful.
158	/// * `CpfpError` - An error indicating the reason for failure in constructing the CPFP
159	///                 transaction (e.g., insufficient funds, invalid parent transaction, or
160	///                 signing failure).
161	async fn make_signed_p2a_cpfp(
162		&mut self,
163		tx: &Transaction,
164		fees: MakeCpfpFees,
165	) -> Result<Transaction, CpfpError>;
166
167	/// Persist the signed CPFP transaction so it can be rebroadcast or retrieved as needed.
168	async fn store_signed_p2a_cpfp(&mut self, tx: &Transaction) -> anyhow::Result<(), CpfpError>;
169}