From d9a23e368556a0633b5f42e00b30198c15af10e3 Mon Sep 17 00:00:00 2001 From: alltheseas Date: Thu, 5 Mar 2026 13:50:34 -0600 Subject: [PATCH 1/5] Enrich rustdoc on core public API types and Wallet methods Add detailed documentation with cross-references, tables, and context to Balances, Seed, StorageConfig, ChainSource, WalletConfig, Tunables, PaymentInfo, PaymentInfoBuildError, InitFailure, WalletError, SingleUseReceiveUri, and all Wallet impl methods. Accurately documents close_channels force-close behavior for non-usable channels, list_transactions exclusion of pending inbound invoices and pending rebalances, and from_trusted semantics for amountless receives. Co-Authored-By: Claude Opus 4.6 --- orange-sdk/src/lib.rs | 335 ++++++++++++++++++++++++++++++------------ 1 file changed, 245 insertions(+), 90 deletions(-) diff --git a/orange-sdk/src/lib.rs b/orange-sdk/src/lib.rs index 3388a60..0d0ffcd 100644 --- a/orange-sdk/src/lib.rs +++ b/orange-sdk/src/lib.rs @@ -75,21 +75,43 @@ type Rebalancer = GraduatedRebalancer< Logger, >; -/// Represents the balances of the wallet, including available and pending balances. +/// The wallet's balance breakdown across its different storage layers. +/// +/// Funds in an orange-sdk [`Wallet`] live in up to three places: +/// +/// | Layer | Field | Spendable? | +/// |-------|-------|------------| +/// | Trusted backend (Spark / Cashu) | [`trusted`](Self::trusted) | Yes | +/// | Lightning channel | [`lightning`](Self::lightning) | Yes | +/// | On-chain (pending splice / channel open) | [`pending_balance`](Self::pending_balance) | No | +/// +/// Use [`available_balance`](Self::available_balance) to get the total amount the wallet can +/// spend right now (trusted + lightning). #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Balances { - /// The balance in trusted wallet + /// Balance held in the trusted wallet backend (e.g. Spark or Cashu). + /// + /// These funds are available for instant, low-fee payments but are custodied + /// by a third party. pub trusted: Amount, - /// The balance in lightning wallet available for spending. + /// Balance available in self-custodial Lightning channels. + /// + /// These funds are fully self-custodial and can be spent over Lightning + /// without any third-party trust. pub lightning: Amount, - /// The balance that is pending and not yet spendable. - /// This includes all on-chain balances. The on-chain balance will become - /// available after it has been spliced into a lightning channel. + /// Balance that is not yet spendable. + /// + /// This includes all on-chain balances (e.g. funds waiting for a channel open + /// or splice-in to confirm). Once the on-chain transaction confirms and the + /// channel is ready, these funds move into [`lightning`](Self::lightning). pub pending_balance: Amount, } impl Balances { - /// Returns the total available balance, which is the sum of the lightning and trusted balances. + /// Returns the total spendable balance (trusted + lightning). + /// + /// This excludes [`pending_balance`](Self::pending_balance) since those funds are not + /// yet available for spending. pub fn available_balance(&self) -> Amount { self.lightning.saturating_add(self.trusted) } @@ -137,17 +159,27 @@ pub struct Wallet { inner: Arc, } -/// Represents the seed used for wallet generation. +/// The secret key material used to derive all wallet keys. +/// +/// Two representations are supported: +/// +/// - [`Mnemonic`](Self::Mnemonic) – a standard BIP 39 mnemonic phrase (12 or 24 words). +/// This is the recommended form for end-user wallets because it can be backed up on paper. +/// - [`Seed64`](Self::Seed64) – a raw 64-byte seed, useful for programmatic key derivation +/// or when the seed is already available from another source. +/// +/// The same seed will always produce the same wallet addresses and keys, which is what +/// enables recovery (see [`Wallet::new`] for recovery details). #[derive(Debug, Clone)] pub enum Seed { - /// A BIP 39 mnemonic seed. + /// A BIP 39 mnemonic seed phrase. Mnemonic { - /// The mnemonic phrase. + /// The mnemonic phrase (typically 12 or 24 words). mnemonic: Mnemonic, - /// The passphrase for the mnemonic. + /// Optional BIP 39 passphrase (sometimes called the "25th word"). passphrase: Option, }, - /// A 64-byte seed for the wallet. + /// A raw 64-byte seed for the wallet. Seed64([u8; 64]), } @@ -172,84 +204,137 @@ pub struct VssConfig { pub headers: VssAuth, } -/// Configuration for wallet storage, either local SQLite or VSS. +/// Configuration for wallet persistence. +/// +/// Controls where the wallet stores channel state, transaction metadata, and event history. +/// +/// Currently only local SQLite is supported. A VSS (Versioned Storage Service) backend is +/// planned, which will enable cross-device state synchronization and Lightning channel recovery +/// from seed. #[derive(Debug, Clone)] pub enum StorageConfig { - /// Local SQLite database configuration. + /// Store all data in a local SQLite database at the given directory path. + /// + /// The directory will be created if it does not exist. LocalSQLite(String), // todo VSS(VssConfig), } -/// Configuration for the blockchain data source. +/// The blockchain data source used to monitor on-chain transactions and fee rates. +/// +/// The wallet needs a connection to the Bitcoin network to track on-chain balances, +/// confirm channel opens/closes, and estimate fees. Three backend types are supported: +/// +/// - [`Electrum`](Self::Electrum) – connects to an Electrum server (supports SSL via `ssl://` prefix). +/// - [`Esplora`](Self::Esplora) – connects to an Esplora HTTP API (e.g. `https://blockstream.info/api`). +/// - [`BitcoindRPC`](Self::BitcoindRPC) – connects directly to a Bitcoin Core node via JSON-RPC. #[derive(Debug, Clone)] pub enum ChainSource { - /// Electrum server configuration. + /// Connect to an Electrum server. + /// + /// Use the `ssl://` prefix for TLS connections (e.g. `ssl://electrum.blockstream.info:60002`). Electrum(String), - /// Esplora server configuration. + /// Connect to an Esplora HTTP API, with optional Basic authentication. Esplora { - /// Esplora url + /// The base URL of the Esplora server (e.g. `https://blockstream.info/api`). url: String, - /// Optional for Basic authentication for the Esplora server. + /// Optional username for HTTP Basic authentication. username: Option, - /// Optional for Basic authentication for the Esplora server. + /// Optional password for HTTP Basic authentication. password: Option, }, - /// Bitcoind RPC configuration. + /// Connect directly to a Bitcoin Core node via JSON-RPC. BitcoindRPC { - /// The host of the Bitcoind rpc server (e.g. 127.0.0.1). + /// The host of the Bitcoin Core RPC server (e.g. `127.0.0.1`). host: String, - /// The port of the Bitcoind rpc server (e.g. 8332). + /// The port of the Bitcoin Core RPC server (e.g. `8332` for mainnet). port: u16, - /// The username for the Bitcoind rpc server. + /// The RPC username (configured in `bitcoin.conf`). user: String, - /// The password for the Bitcoind rpc server. + /// The RPC password (configured in `bitcoin.conf`). password: String, }, } -/// Configuration for initializing the wallet. +/// Everything needed to initialize a [`Wallet`]. +/// +/// This struct bundles together all the configuration required to create a wallet instance: +/// storage, networking, keys, thresholds, and the trusted wallet backend. +/// +/// See the [crate-level documentation](crate) for a full configuration example. #[derive(Clone)] pub struct WalletConfig { - /// Configuration for wallet storage. + /// Where the wallet persists its state (channel data, transaction metadata, events). pub storage_config: StorageConfig, - /// The type of logger to use. + /// How the wallet emits log output. pub logger_type: LoggerType, - /// Configuration for the blockchain data source. + /// The blockchain data source for on-chain monitoring and fee estimation. pub chain_source: ChainSource, - /// Lightning Service Provider (LSP) configuration. - /// The address to connect to the LSP, the LSP node id, and an optional auth token. + /// Lightning Service Provider (LSP) connection details. + /// + /// The tuple contains: + /// 1. The LSP's network address (e.g. `127.0.0.1:9735`) + /// 2. The LSP's node public key + /// 3. An optional authentication token + /// + /// The LSP is used to open JIT (Just-In-Time) channels for receiving Lightning payments. pub lsp: (SocketAddress, PublicKey, Option), - /// URL to download a scorer from. This is for the lightning node to get its route - /// scorer from a remote server instead of having to probe and find optimal routes - /// locally. + /// Optional URL to download a pre-built route scorer. + /// + /// When set, the Lightning node fetches its route scorer from this remote server instead of + /// probing and discovering optimal routes locally. This speeds up initial routing decisions. pub scorer_url: Option, - /// URL to Rapid Gossip Sync server to get gossip data from. + /// Optional URL to a [Rapid Gossip Sync](https://docs.rs/lightning-rapid-gossip-sync) server. + /// + /// When set, the Lightning node downloads compressed gossip data from this server instead + /// of learning the network graph through peer gossip, significantly reducing sync time. pub rgs_url: Option, - /// The Bitcoin network the wallet operates on. + /// The Bitcoin network to operate on (e.g. `Network::Bitcoin`, `Network::Testnet`). pub network: Network, - /// The seed used for wallet generation. + /// The secret key material for this wallet. See [`Seed`] for options. pub seed: Seed, - /// Configuration parameters for when the wallet decides to use the lightning or trusted wallet. + /// Thresholds that control when funds move between the trusted and Lightning wallets. pub tunables: Tunables, - /// Extra configuration specific to the trusted wallet implementation. + /// Backend-specific configuration for the trusted wallet (Spark, Cashu, etc.). pub extra_config: ExtraConfig, } -/// Configuration parameters for when the wallet decides to use the lightning or trusted wallet. +/// Thresholds that control the wallet's graduated custody behavior. +/// +/// These parameters govern how the wallet distributes funds between the trusted backend +/// and self-custodial Lightning channels, and how it generates payment URIs. +/// +/// The default values are a reasonable starting point for most wallets: +/// +/// | Parameter | Default | Purpose | +/// |-----------|---------|---------| +/// | `trusted_balance_limit` | 100,000 sats | Trigger rebalance to Lightning above this | +/// | `rebalance_min` | 5,000 sats | Don't bother rebalancing amounts smaller than this | +/// | `onchain_receive_threshold` | 10,000 sats | Include on-chain address in receive URIs above this | +/// | `enable_amountless_receive_on_chain` | `true` | Include on-chain address for open-amount receives | #[derive(Debug, Clone, Copy)] pub struct Tunables { - /// The maximum balance that can be held in the trusted wallet. + /// The maximum balance allowed in the trusted wallet before triggering automatic rebalancing. + /// + /// When the trusted balance exceeds this limit, excess funds are automatically moved into + /// a self-custodial Lightning channel. Set this based on how much you're comfortable + /// holding in the trusted backend. pub trusted_balance_limit: Amount, - /// Trusted balances below this threshold will not be transferred to non-trusted balance - /// even if we have capacity to do so without paying for a new channel. + /// The minimum amount worth rebalancing from trusted to Lightning. /// - /// This avoids unnecessary transfers and fees. + /// Amounts below this threshold won't be transferred even if there's available Lightning + /// capacity, avoiding unnecessary small transfers and their associated fees. pub rebalance_min: Amount, - /// Payment instructions generated using [`Wallet::get_single_use_receive_uri`] for an amount - /// below this threshold will not include an on-chain address. + /// The minimum receive amount for which an on-chain address is included in payment URIs. + /// + /// When generating a receive URI via [`Wallet::get_single_use_receive_uri`], amounts + /// below this threshold will only include a Lightning invoice (no on-chain fallback). + /// This avoids on-chain dust for small payments. pub onchain_receive_threshold: Amount, - /// Payment instructions generated using [`Wallet::get_single_use_receive_uri`] with no amount - /// will only include an on-chain address if this is set. + /// Whether to include an on-chain address in open-amount (no specific amount) receive URIs. + /// + /// When `true`, [`Wallet::get_single_use_receive_uri`] called with `amount: None` will + /// include an on-chain address alongside the Lightning invoice. pub enable_amountless_receive_on_chain: bool, } @@ -264,7 +349,8 @@ impl Default for Tunables { } } -/// Represents errors that can occur when building a [`PaymentInfo`] from [`PaymentInstructions`]. +/// Errors returned by [`PaymentInfo::build`] when the provided amount is incompatible +/// with the [`PaymentInstructions`]. #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum PaymentInfoBuildError { /// The amount given does not match either of the fixed amounts specified in the @@ -290,7 +376,10 @@ pub enum PaymentInfoBuildError { }, } -/// A payable version of [`PaymentInstructions`] (i.e. with a set amount). +/// A validated, ready-to-pay combination of [`PaymentInstructions`] and an [`Amount`]. +/// +/// Created via [`PaymentInfo::build`], which validates that the amount is compatible +/// with the payment instructions. Pass this to [`Wallet::pay`] to initiate the payment. #[derive(Debug, Clone, PartialEq, Eq)] pub struct PaymentInfo { /// The payment instructions (e.g., BOLT 11 invoice, on-chain address). @@ -300,13 +389,16 @@ pub struct PaymentInfo { } impl PaymentInfo { - /// Prepares us to pay a [`PaymentInstructions`] by setting the amount. + /// Creates a [`PaymentInfo`] by pairing [`PaymentInstructions`] with an amount. + /// + /// The amount is validated against the payment instructions: /// - /// If [`PaymentInstructions`] is a [`PaymentInstructions::ConfigurableAmount`], the amount must be - /// within the specified range (if any). + /// - **[`ConfigurableAmount`](PaymentInstructions::ConfigurableAmount):** the amount is + /// required and must fall within the optional min/max range. + /// - **[`FixedAmount`](PaymentInstructions::FixedAmount):** the amount is optional (it + /// defaults to the fixed amount) but if provided must match. /// - /// If [`PaymentInstructions`] is a [`PaymentInstructions::FixedAmount`], the amount must match the - /// fixed on-chain or lightning amount specified. + /// Returns [`PaymentInfoBuildError`] if the amount is missing, out of range, or mismatched. pub fn build( instructions: PaymentInstructions, amount: Option, ) -> Result { @@ -397,16 +489,19 @@ impl PaymentInfo { } } -/// Represents possible failures during wallet initialization. +/// Errors that can occur during [`Wallet::new`]. +/// +/// Initialization can fail due to I/O issues (e.g. storage), Lightning node setup errors, +/// or problems connecting to the trusted wallet backend. #[derive(Debug)] pub enum InitFailure { - /// I/O error during initialization. + /// An I/O error occurred (e.g. failed to create storage directory). IoError(io::Error), - /// Failure to build the LDK node. + /// Failed to build the underlying LDK node (invalid configuration). LdkNodeBuildFailure(BuildError), - /// Failure to start the LDK node. + /// Failed to start the underlying LDK node (e.g. port already in use). LdkNodeStartFailure(NodeError), - /// Failure in the trusted wallet implementation. + /// The trusted wallet backend failed to initialize. TrustedFailure(TrustedError), } @@ -434,12 +529,12 @@ impl From for InitFailure { } } -/// Represents possible errors during wallet operations. +/// Errors that can occur during wallet operations (payments, balance queries, etc.). #[derive(Debug)] pub enum WalletError { - /// Failure in the LDK node. + /// The self-custodial Lightning node encountered an error. LdkNodeFailure(NodeError), - /// Failure in the trusted wallet implementation. + /// The trusted wallet backend encountered an error. TrustedFailure(TrustedError), } @@ -455,18 +550,37 @@ impl From for WalletError { } } -/// Represents a single-use Bitcoin URI for receiving payments. +/// A single-use [BIP 21](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki) Bitcoin +/// URI for receiving a payment. +/// +/// Generated by [`Wallet::get_single_use_receive_uri`]. The URI may contain both an on-chain +/// address and a BOLT 11 Lightning invoice (a "unified" URI), allowing the payer to choose the +/// best payment method. Whether an on-chain address is included depends on the wallet's +/// [`Tunables`] and the requested amount. +/// +/// The [`Display`](std::fmt::Display) implementation formats this as a BIP 21 URI string +/// suitable for encoding in a QR code. #[derive(Debug, Clone, PartialEq, Eq)] pub struct SingleUseReceiveUri { - /// The optional on-chain Bitcoin address. Will be present based on the - /// wallet's configured tunables. + /// The on-chain Bitcoin address, if included. + /// + /// Present when the requested amount exceeds [`Tunables::onchain_receive_threshold`], + /// or when [`Tunables::enable_amountless_receive_on_chain`] is `true` and no amount + /// was specified. pub address: Option, - /// The BOLT 11 Lightning invoice. + /// The BOLT 11 Lightning invoice for this payment. pub invoice: Bolt11Invoice, - /// The optional amount for the payment. + /// The requested amount, if one was specified. pub amount: Option, - /// Whether the URI was generated from a trusted wallet or from - /// the self-custodial LN wallet. + /// Whether the invoice was generated by the trusted wallet backend or the + /// self-custodial LDK node. + /// + /// When `amount` is `Some`, this reflects the routing decision: small amounts + /// use the trusted backend (`true`), larger amounts use the LDK node (`false`). + /// + /// When `amount` is `None` (amountless receive), this is always `false` even + /// though the invoice is generated by the trusted backend, since the wallet + /// cannot predict which layer will ultimately receive the payment. pub from_trusted: bool, } @@ -661,7 +775,11 @@ impl Wallet { Ok(Wallet { inner }) } - /// Sets whether the wallet should automatically rebalance from trusted/onchain to lightning. + /// Enables or disables automatic rebalancing from trusted/on-chain to Lightning. + /// + /// Rebalancing is enabled by default. It is automatically disabled when a channel closes + /// (to avoid an open-close loop). Call this with `true` to re-enable it after handling + /// a [`Event::ChannelClosed`] event. pub async fn set_rebalance_enabled(&self, value: bool) { store::set_rebalance_enabled(self.inner.store.as_ref(), value).await } @@ -671,7 +789,9 @@ impl Wallet { store::get_rebalance_enabled(self.inner.store.as_ref()).await } - /// Returns the lightning wallet's node id. + /// Returns the public key of the underlying Lightning node. + /// + /// This is the node's identity on the Lightning Network and can be shared with peers. pub fn node_id(&self) -> PublicKey { self.inner.ln_wallet.inner.ldk_node.node_id() } @@ -681,12 +801,19 @@ impl Wallet { self.inner.ln_wallet.is_connected_to_lsp() } - /// List our current channels + /// Lists all open Lightning channels and their details (capacity, balance, state, etc.). pub fn channels(&self) -> Vec { self.inner.ln_wallet.inner.ldk_node.list_channels() } - /// Lists the transactions which have been made. + /// Lists completed transactions and in-flight outbound payments, sorted by time. + /// + /// Returns a unified list covering both trusted and self-custodial payments. + /// Internal rebalance transfers are merged into single logical transactions + /// with combined fees. + /// + /// **Note:** Pending inbound invoices (issued but unpaid) and pending rebalances + /// are excluded from the results. pub async fn list_transactions(&self) -> Result, WalletError> { let (trusted_payments, splice_outs) = tokio::join!( self.inner.trusted.list_payments(), @@ -956,7 +1083,9 @@ impl Wallet { Ok(res) } - /// Gets our current total balance + /// Returns the wallet's current balance across all layers (trusted, Lightning, and pending). + /// + /// See [`Balances`] for details on each field. pub async fn get_balance(&self) -> Result { let trusted_balance = self.inner.trusted.get_balance().await?; let ln_balance = self.inner.ln_wallet.get_balance(); @@ -1074,20 +1203,29 @@ impl Wallet { // // } - /// Estimates the fees required to pay a [`PaymentInstructions`] + /// Estimates the fees required to pay a [`PaymentInstructions`]. + /// + /// **Note:** Fee estimation is not yet implemented and currently always returns zero. pub async fn estimate_fee(&self, _payment_info: &PaymentInstructions) -> Amount { // todo implement fee estimation Amount::ZERO } - /// Initiates a payment using the provided [`PaymentInfo`]. This will pay from the trusted - /// wallet if possible, otherwise it will pay from the lightning wallet. + /// Sends a payment using the provided [`PaymentInfo`]. + /// + /// The wallet automatically selects the best funding source using this priority: /// - /// If applicable, this will also initiate a rebalance from the trusted wallet to the - /// lightning wallet based on the resulting balance and configured tunables. + /// 1. **Trusted wallet over Lightning** (BOLT 11/12) – lowest fees for small payments + /// 2. **Self-custodial Lightning** (BOLT 11/12) – if trusted balance is insufficient + /// 3. **Trusted wallet on-chain** – for on-chain payment methods + /// 4. **Self-custodial on-chain** (splice-out) – last resort /// - /// Returns once the payment is pending, however, this does not mean that the - /// payment has been completed. The payment may still fail. + /// After a successful payment, automatic rebalancing may be triggered if the + /// resulting trusted balance exceeds [`Tunables::trusted_balance_limit`]. + /// + /// Returns a [`PaymentId`] once the payment has been **initiated**. The payment + /// may still be in-flight; listen for [`Event::PaymentSuccessful`] or + /// [`Event::PaymentFailed`] to confirm the outcome. pub async fn pay(&self, instructions: &PaymentInfo) -> Result { let trusted_balance = self.inner.trusted.get_balance().await?; let ln_balance = self.inner.ln_wallet.get_balance(); @@ -1295,9 +1433,18 @@ impl Wallet { )) } - /// Initiates closing all channels in the lightning wallet. The channel will not be closed - /// until a [`Event::ChannelClosed`] event is emitted. - /// This will disable rebalancing before closing channels, so that we don't try to reopen them. + /// Initiates closing all open Lightning channels. + /// + /// Usable channels are closed cooperatively; non-usable channels (e.g. peer offline) + /// are force-closed. Force closes have higher on-chain fees and a time-locked delay + /// before funds become spendable. + /// + /// This automatically disables rebalancing (see [`set_rebalance_enabled`](Self::set_rebalance_enabled)) + /// to prevent the wallet from immediately reopening channels. + /// + /// The close is asynchronous — channels are not fully closed until you receive + /// [`Event::ChannelClosed`] events. On-chain funds from the closed channels will + /// appear in [`Balances::pending_balance`] until confirmed. pub async fn close_channels(&self) -> Result<(), WalletError> { // we are explicitly disabling rebalancing here, so that we don't try to // reopen channels after closing them. @@ -1316,7 +1463,7 @@ impl Wallet { // Ok(()) // } - /// Returns the wallet's configured tunables. + /// Returns the [`Tunables`] that were used to configure this wallet. pub fn get_tunables(&self) -> Tunables { self.inner.tunables } @@ -1381,18 +1528,26 @@ impl Wallet { res } - /// Gets the lightning address for this wallet, if one is set. + /// Returns the wallet's registered [Lightning Address](https://lightningaddress.com), + /// if one has been set via [`register_lightning_address`](Self::register_lightning_address). pub async fn get_lightning_address(&self) -> Result, WalletError> { Ok(self.inner.trusted.get_lightning_address().await?) } - /// Attempts to register the lightning address for this wallet. + /// Registers a [Lightning Address](https://lightningaddress.com) (e.g. `name@domain.com`) + /// for this wallet. + /// + /// The `name` parameter is the local part (before the `@`). The domain is determined + /// by the trusted wallet backend configuration. pub async fn register_lightning_address(&self, name: String) -> Result<(), WalletError> { Ok(self.inner.trusted.register_lightning_address(name).await?) } - /// Stops the wallet, which will stop the underlying LDK node and any background tasks. - /// This will ensure that any critical tasks have completed before stopping. + /// Gracefully shuts down the wallet. + /// + /// This waits for any in-progress rebalances to complete, stops the trusted wallet + /// backend, shuts down the Lightning node, and cancels background tasks. Call this + /// before dropping the wallet to ensure data is persisted and resources are released. pub async fn stop(&self) { // wait for the balance mutex to ensure no other tasks are running log_info!(self.inner.logger, "Stopping..."); From 8e2d4fb7ca521c12a2cbe29516b286ca1c71141f Mon Sep 17 00:00:00 2001 From: alltheseas Date: Thu, 5 Mar 2026 13:50:49 -0600 Subject: [PATCH 2/5] Enrich rustdoc on Event enum and EventQueue Add event type summary table, persistence semantics, usage guidance, and cross-references to Wallet methods on Event and EventQueue docs. Co-Authored-By: Claude Opus 4.6 --- orange-sdk/src/event.rs | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/orange-sdk/src/event.rs b/orange-sdk/src/event.rs index 87d179d..4e2d044 100644 --- a/orange-sdk/src/event.rs +++ b/orange-sdk/src/event.rs @@ -23,7 +23,29 @@ pub(crate) const EVENT_QUEUE_PERSISTENCE_PRIMARY_NAMESPACE: &str = ""; pub(crate) const EVENT_QUEUE_PERSISTENCE_SECONDARY_NAMESPACE: &str = ""; pub(crate) const EVENT_QUEUE_PERSISTENCE_KEY: &str = "orange_events"; -/// An event emitted by [`Wallet`], which should be handled by the user. +/// An event emitted by [`Wallet`] that should be handled by the user. +/// +/// Events are retrieved via [`Wallet::next_event`](crate::Wallet::next_event), +/// [`Wallet::next_event_async`](crate::Wallet::next_event_async), or +/// [`Wallet::wait_next_event`](crate::Wallet::wait_next_event). After processing an event, +/// you **must** call [`Wallet::event_handled`](crate::Wallet::event_handled) to advance the queue. +/// +/// Events are persisted, so they will survive process restarts and will be re-delivered +/// until acknowledged. +/// +/// # Event types +/// +/// | Event | Meaning | +/// |-------|---------| +/// | [`PaymentSuccessful`](Self::PaymentSuccessful) | An outgoing payment completed | +/// | [`PaymentFailed`](Self::PaymentFailed) | An outgoing payment failed | +/// | [`PaymentReceived`](Self::PaymentReceived) | An incoming Lightning payment arrived | +/// | [`OnchainPaymentReceived`](Self::OnchainPaymentReceived) | An incoming on-chain payment arrived | +/// | [`ChannelOpened`](Self::ChannelOpened) | A Lightning channel is ready to use | +/// | [`ChannelClosed`](Self::ChannelClosed) | A Lightning channel was closed (rebalancing auto-disabled) | +/// | [`RebalanceInitiated`](Self::RebalanceInitiated) | A trusted-to-Lightning rebalance started | +/// | [`RebalanceSuccessful`](Self::RebalanceSuccessful) | A trusted-to-Lightning rebalance completed | +/// | [`SplicePending`](Self::SplicePending) | An on-chain splice into a channel is pending confirmation | /// /// [`Wallet`]: [`crate::Wallet`] #[derive(Debug, Clone, PartialEq, Eq)] @@ -201,9 +223,16 @@ impl_writeable_tlv_based_enum!(Event, }, ); -/// A queue for events emitted by the [`Wallet`]. +/// A persistent, ordered queue of [`Event`]s emitted by the [`Wallet`]. +/// +/// The queue is backed by the wallet's [`KVStore`] so events survive restarts. Events are +/// delivered in FIFO order and remain at the head of the queue until acknowledged via +/// [`Wallet::event_handled`](crate::Wallet::event_handled). +/// +/// Users typically interact with this through the [`Wallet`] methods rather than directly. /// /// [`Wallet`]: [`crate::Wallet`] +/// [`KVStore`]: ldk_node::lightning::util::persist::KVStore pub struct EventQueue { queue: Arc>>, waker: Arc>>, From 459afbe5aafdb13b1a132739ed60fde8db6326e3 Mon Sep 17 00:00:00 2001 From: alltheseas Date: Thu, 5 Mar 2026 13:52:37 -0600 Subject: [PATCH 3/5] Enrich rustdoc on Transaction, PaymentId, TxStatus, and PaymentType Replace generic descriptions with precise documentation covering lifecycle semantics, cross-references to Wallet methods, and string format details for PaymentId. Co-Authored-By: Claude Opus 4.6 --- orange-sdk/src/store.rs | 70 +++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/orange-sdk/src/store.rs b/orange-sdk/src/store.rs index f6eb15a..5b89515 100644 --- a/orange-sdk/src/store.rs +++ b/orange-sdk/src/store.rs @@ -1,15 +1,9 @@ -//! A library implementing the full backend for a modern, highly usable, Bitcoin wallet focusing on -//! maximizing security and self-custody without trading off user experience. +//! Transaction metadata storage and types. //! -//! This crate should do everything you need to build a great Bitcoin wallet, except the UI. -//! -//! In order to maximize the user experience, small balances are held in a trusted service (XXX -//! which one), avoiding expensive setup fees, while larger balances are moved into on-chain -//! lightning channels, ensuring trust is minimized in the trusted service. -//! -//! Despite funds being stored in multiple places, the full balance can be treated as a single -//! wallet - payments can draw on both balances simultaneously and deposits are automatically -//! shifted to minimize fees and ensure maximal security. +//! This module defines the public types used to represent transactions ([`Transaction`], +//! [`PaymentId`], [`TxStatus`], [`PaymentType`]) and the internal storage layer +//! ([`TxMetadataStore`]) that tracks payment metadata across both trusted and self-custodial +//! wallets. use bitcoin_payment_instructions::amount::Amount; @@ -34,14 +28,14 @@ const STORE_PRIMARY_KEY: &str = "orange_sdk"; const STORE_SECONDARY_KEY: &str = "payment_store"; const SPLICE_OUT_SECONDARY_KEY: &str = "splice_out"; -/// The status of a transaction. This is used to track the state of a transaction +/// The lifecycle state of a [`Transaction`]. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TxStatus { - /// A pending transaction has not yet been paid. + /// The payment has been initiated but not yet settled. Pending, - /// A completed transaction has been paid. + /// The payment settled successfully. Completed, - /// A transaction that has failed. + /// The payment failed (e.g. no route, insufficient funds, timeout). Failed, } @@ -67,28 +61,31 @@ impl Readable for TxStatus { } } -/// A transaction is a record of a payment made or received. It contains information about the -/// transaction, such as the amount, fee, and status. It is used to track the state of a payment -/// and to provide information about the payment to the user. +/// A unified record of a payment made or received, returned by [`Wallet::list_transactions`](crate::Wallet::list_transactions). +/// +/// Transactions cover both trusted and self-custodial payments. Internal rebalance +/// transfers are merged into single entries with combined fees. #[derive(Debug, Clone)] pub struct Transaction { - /// The unique identifier for the payment. + /// Unique identifier for this payment. + /// + /// Use the variant ([`PaymentId::Trusted`] vs [`PaymentId::SelfCustodial`]) to determine + /// which wallet layer handled the payment. pub id: PaymentId, - /// The transaction status, either (Pending, Completed, or Failed) + /// Current lifecycle state of this transaction. pub status: TxStatus, - /// Indicates whether the payment is outbound (`true`) or inbound (`false`). + /// `true` for outbound (sent) payments, `false` for inbound (received). pub outbound: bool, - /// The amount of the payment - /// - /// None if the payment is not yet completed + /// The payment amount, or `None` if not yet known (e.g. pending inbound). pub amount: Option, - /// The fee paid for the payment + /// The fee paid for this transaction, or `None` if not yet known. /// - /// None if the payment is not yet completed + /// For internal rebalance transfers, this is the combined fee across both + /// the trusted and Lightning legs of the transfer. pub fee: Option, - /// Represents the type of payment, including its method and associated metadata. + /// The payment method and associated metadata (Lightning BOLT 11/12, on-chain, etc.). pub payment_type: PaymentType, - /// The time the transaction was created + /// When this transaction was created, as a duration since the Unix epoch. pub time_since_epoch: Duration, } @@ -128,14 +125,16 @@ impl From for StoreTransaction { } } -/// A PaymentId is a unique identifier for a payment. It can be either a Lightning payment or a -/// Trusted payment. It is used to track the state of a payment and to provide information about -/// the payment to the user. +/// A unique identifier for a payment, tagged by which wallet layer handled it. +/// +/// The string representation uses a `SC-` prefix for self-custodial payments and a `TR-` +/// prefix for trusted payments, followed by the hex-encoded 32-byte ID. This format +/// round-trips via [`Display`](std::fmt::Display) and [`FromStr`](std::str::FromStr). #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum PaymentId { - /// A self-custodial payment identifier. + /// A payment handled by the self-custodial Lightning node. SelfCustodial([u8; 32]), - /// A trusted payment identifier. + /// A payment handled by the trusted wallet backend (Spark, Cashu, etc.). Trusted([u8; 32]), } @@ -173,7 +172,10 @@ impl_writeable_tlv_based_enum!(PaymentId, {1, Trusted} => (), ); -/// Represents the type of payment, including its method and associated metadata. +/// The payment method and associated metadata for a [`Transaction`]. +/// +/// For outgoing Lightning payments, the `payment_preimage` field serves as cryptographic +/// proof of payment and is populated once the payment reaches [`TxStatus::Completed`]. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum PaymentType { /// An outgoing Lightning payment paying a BOLT 12 offer. From 436a29d32ffe6d61ce0ce15aa98d19b92d6c3f66 Mon Sep 17 00:00:00 2001 From: alltheseas Date: Thu, 5 Mar 2026 13:53:00 -0600 Subject: [PATCH 4/5] Enrich rustdoc on trusted_wallet module and backends Add module-level docs explaining the graduated custody model and available backends. Enrich TrustedWalletInterface, ExtraConfig, TrustedError, SparkWalletConfig, and CashuConfig with detailed docs, examples, and feature flag guidance. Uses plain backtick references for cfg-gated items (cashu, dummy, ExtraConfig::Cashu, ExtraConfig::Dummy) to avoid broken intra-doc links under default features. Co-Authored-By: Claude Opus 4.6 --- orange-sdk/src/trusted_wallet/cashu/mod.rs | 47 +++++++++++++++-- orange-sdk/src/trusted_wallet/mod.rs | 61 +++++++++++++++++----- orange-sdk/src/trusted_wallet/spark/mod.rs | 52 +++++++++++++++--- 3 files changed, 133 insertions(+), 27 deletions(-) diff --git a/orange-sdk/src/trusted_wallet/cashu/mod.rs b/orange-sdk/src/trusted_wallet/cashu/mod.rs index 404791e..a9454f2 100644 --- a/orange-sdk/src/trusted_wallet/cashu/mod.rs +++ b/orange-sdk/src/trusted_wallet/cashu/mod.rs @@ -1,4 +1,25 @@ -//! An implementation of `TrustedWalletInterface` using the Cashu (CDK) SDK. +//! Cashu ecash trusted wallet backend. +//! +//! This module implements [`TrustedWalletInterface`] using the +//! [Cashu Development Kit (CDK)](https://docs.rs/cdk). Cashu is an ecash protocol where tokens +//! are issued by a mint and can be used for instant, private payments. +//! +//! # Configuration +//! +//! Use [`CashuConfig`] to specify: +//! - `mint_url` – the Cashu mint to connect to +//! - `unit` – the currency unit (typically `CurrencyUnit::Sat`) +//! - `npubcash_url` – optional [npub.cash](https://npub.cash) URL for Lightning address support +//! +//! # Feature flag +//! +//! This module is only available when the `cashu` feature is enabled (disabled by default). +//! Enable it in your `Cargo.toml`: +//! +//! ```toml +//! [dependencies] +//! orange-sdk = { version = "0.1", features = ["cashu"] } +//! ``` use crate::bitcoin::hex::DisplayHex; use crate::logging::Logger; @@ -45,14 +66,30 @@ pub mod cashu_store; use cashu_store::{CashuKvDatabase, read_has_recovered, write_has_recovered}; -/// Configuration for the Cashu wallet +/// Configuration for the Cashu ecash wallet backend. +/// +/// # Example +/// +/// ```rust,no_run +/// use orange_sdk::trusted_wallet::cashu::CashuConfig; +/// use orange_sdk::CurrencyUnit; +/// +/// let config = CashuConfig { +/// mint_url: "https://mint.example.com".to_string(), +/// unit: CurrencyUnit::Sat, +/// npubcash_url: Some("https://npub.cash".to_string()), +/// }; +/// ``` #[derive(Debug, Clone)] pub struct CashuConfig { - /// The mint URL to connect to + /// The URL of the Cashu mint to connect to (e.g. `https://mint.example.com`). pub mint_url: String, - /// The currency unit to use (typically Sat) + /// The currency unit for ecash tokens (typically [`CurrencyUnit::Sat`]). pub unit: CurrencyUnit, - /// Optional npub.cash URL for lightning address support (e.g., `https://npubx.cash`) + /// Optional [npub.cash](https://npub.cash) URL for Lightning address support. + /// + /// When set, the wallet can register and receive payments via a Lightning address + /// backed by the npub.cash service. pub npubcash_url: Option, } diff --git a/orange-sdk/src/trusted_wallet/mod.rs b/orange-sdk/src/trusted_wallet/mod.rs index 5f76082..84fd514 100644 --- a/orange-sdk/src/trusted_wallet/mod.rs +++ b/orange-sdk/src/trusted_wallet/mod.rs @@ -1,4 +1,18 @@ -//! This module defines the `TrustedWalletInterface` trait and its associated types. +//! Trusted wallet backends for the graduated custody model. +//! +//! This module defines the [`TrustedWalletInterface`] trait and provides concrete +//! implementations for different custodial backends: +//! +//! - **`spark`** (feature `spark`, enabled by default) – Uses the [Breez Spark SDK](https://breez.technology) +//! for custodial Lightning payments with low fees and instant settlement. +//! - **`cashu`** (feature `cashu`) – Uses the [Cashu Development Kit (CDK)](https://docs.rs/cdk) +//! for ecash-based custody via a Cashu mint. Supports [npub.cash](https://npub.cash) for +//! Lightning address integration. +//! - **`dummy`** (feature `_test-utils`) – A test-only in-memory implementation. +//! +//! The trusted wallet holds small balances for instant, low-fee payments while the +//! [`Wallet`](crate::Wallet) automatically moves larger amounts into self-custodial +//! Lightning channels via the rebalancer. use crate::store::TxStatus; @@ -43,7 +57,15 @@ pub struct Payment { pub(crate) type DynTrustedWalletInterface = dyn TrustedWalletInterface + Send + Sync; -/// Represents a trait for a trusted wallet interface. +/// The interface that all trusted wallet backends must implement. +/// +/// This trait is **sealed** — it cannot be implemented outside of this crate. The available +/// implementations are `Spark` (feature `spark`), `Cashu` (feature `cashu`), and +/// `DummyTrustedWallet` (feature `_test-utils`, test-only). +/// +/// Users don't interact with this trait directly. Instead, choose a backend via +/// [`ExtraConfig`] when building a [`WalletConfig`](crate::WalletConfig), and the +/// [`Wallet`](crate::Wallet) handles dispatching internally. pub trait TrustedWalletInterface: Send + Sync + private::Sealed { /// Returns the current balance of the wallet. fn get_balance( @@ -134,16 +156,23 @@ impl graduated_rebalancer::TrustedWallet for } } +/// Selects and configures the trusted wallet backend. +/// +/// Pass one of these variants in [`WalletConfig::extra_config`](crate::WalletConfig::extra_config) +/// to choose which custodial backend the wallet uses: +/// +/// - `Spark` – Breez Spark SDK (requires feature `spark`, enabled by default) +/// - `Cashu` – Cashu ecash via CDK (requires feature `cashu`) +/// - `Dummy` – in-memory test backend (requires feature `_test-utils`) #[derive(Clone)] -/// Extra configuration needed for different types of wallets. pub enum ExtraConfig { - /// Configuration for Spark wallet. + /// Use the Spark backend. See [`SparkWalletConfig`](crate::SparkWalletConfig) for options. #[cfg(feature = "spark")] Spark(crate::SparkWalletConfig), - /// Configuration for Cashu wallet. + /// Use the Cashu ecash backend. See [`CashuConfig`](cashu::CashuConfig) for options. #[cfg(feature = "cashu")] Cashu(cashu::CashuConfig), - /// Configuration for dummy wallet (test-only). + /// Use the in-memory dummy backend (test-only). #[cfg(feature = "_test-utils")] Dummy(dummy::DummyTrustedWalletExtraConfig), } @@ -164,22 +193,26 @@ mod private { impl Sealed for super::dummy::DummyTrustedWallet {} } -/// An error type for the Spark wallet implementation. +/// Errors from trusted wallet backend operations. +/// +/// Any of the trusted backends (Spark, Cashu, Dummy) may return these errors. They are +/// surfaced to the caller as [`WalletError::TrustedFailure`](crate::WalletError::TrustedFailure) +/// or [`InitFailure::TrustedFailure`](crate::InitFailure::TrustedFailure). #[derive(Debug)] pub enum TrustedError { - /// Not enough funds to complete the operation. + /// The wallet does not have enough funds to complete the operation. InsufficientFunds, - /// The wallet operation failed with a specific message. + /// A backend-specific operation failed. WalletOperationFailed(String), - /// The provided network is invalid. + /// The configured Bitcoin network does not match the backend's network. InvalidNetwork, - /// The spark wallet does not yet support the operation. + /// The requested operation is not supported by this backend. UnsupportedOperation(String), - /// Failed to convert an amount. + /// An amount conversion error (e.g. overflow or invalid unit). AmountError, - /// An I/O error occurred. + /// An I/O error occurred during a storage or network operation. IOError(ldk_node::lightning::io::Error), - /// An unspecified error occurred. + /// An unspecified error with a descriptive message. Other(String), } diff --git a/orange-sdk/src/trusted_wallet/spark/mod.rs b/orange-sdk/src/trusted_wallet/spark/mod.rs index 5010599..3150968 100644 --- a/orange-sdk/src/trusted_wallet/spark/mod.rs +++ b/orange-sdk/src/trusted_wallet/spark/mod.rs @@ -1,4 +1,18 @@ -//! An implementation of `TrustedWalletInterface` using the Spark SDK. +//! Spark trusted wallet backend. +//! +//! This module implements [`TrustedWalletInterface`] using the +//! [Breez Spark SDK](https://breez.technology). Spark provides custodial Lightning payments +//! with instant settlement and low fees. +//! +//! # Configuration +//! +//! Use [`SparkWalletConfig`] to control sync frequency, payment preferences, and Lightning +//! address domain. The default configuration syncs every 60 seconds and prefers Lightning +//! over Spark-native payments. +//! +//! # Feature flag +//! +//! This module is only available when the `spark` feature is enabled (enabled by default). pub(crate) mod spark_store; @@ -37,17 +51,39 @@ use std::sync::Arc; use std::time::Duration; use uuid::Uuid; -/// Configuration options for the Spark wallet. +/// Configuration for the Spark trusted wallet backend. +/// +/// All fields have reasonable defaults via the [`Default`] implementation. +/// +/// # Example +/// +/// ```rust,no_run +/// use orange_sdk::trusted_wallet::spark::SparkWalletConfig; +/// +/// // Use defaults (60s sync, Lightning-preferred, breez.tips domain) +/// let config = SparkWalletConfig::default(); +/// +/// // Or customize +/// let config = SparkWalletConfig { +/// sync_interval_secs: 30, +/// prefer_spark_over_lightning: true, +/// lnurl_domain: Some("mydomain.com".to_string()), +/// }; +/// ``` #[derive(Debug, Clone)] pub struct SparkWalletConfig { - /// How often to sync the wallet with the blockchain, in seconds. - /// Default is 60 seconds. + /// How often to sync the wallet with the Spark backend, in seconds. + /// + /// Default: `60`. pub sync_interval_secs: u32, - /// When this is set to `true` we will prefer to use spark payments over - /// lightning when sending and receiving. This has the benefit of lower fees - /// but is at the cost of privacy. + /// When `true`, prefer Spark-native payments over standard Lightning. + /// + /// Spark payments have lower fees but less privacy than Lightning. + /// Default: `false`. pub prefer_spark_over_lightning: bool, - /// The domain used for receiving through lnurl-pay and lightning address. + /// The domain used for LNURL-pay and Lightning address receiving. + /// + /// Default: `Some("breez.tips")`. pub lnurl_domain: Option, } From ccf3a7fc6a56d44233298cb2359b11db56f0e326 Mon Sep 17 00:00:00 2001 From: alltheseas Date: Thu, 5 Mar 2026 13:53:36 -0600 Subject: [PATCH 5/5] Enrich graduated-rebalancer crate docs and add Cashu section to README Add comprehensive crate-level documentation to graduated-rebalancer explaining the two rebalance paths, trait-based architecture, and relationship to orange-sdk. Add Cashu backend documentation and code examples to the README, which was previously undocumented despite being a first-class feature. Co-Authored-By: Claude Opus 4.6 --- README.md | 41 +++++++++++++++++++++++++++----- graduated-rebalancer/src/lib.rs | 42 +++++++++++++++++++++++++++++---- 2 files changed, 72 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 9973476..528cc77 100644 --- a/README.md +++ b/README.md @@ -138,20 +138,49 @@ The wallet's behavior is controlled by `Tunables`: ## Supported Backends ### Trusted Wallet Backends -- **Spark** - Custodial Lightning wallet (default) -- **Cashu** - Ecash-based wallet + +#### Spark (default) + +Spark uses the [Breez Spark SDK](https://breez.technology) for custodial Lightning payments with instant settlement and low fees. This is the default backend and is enabled via the `spark` feature flag. + +```rust,no_run +use orange_sdk::trusted_wallet::spark::SparkWalletConfig; +use orange_sdk::ExtraConfig; + +let extra = ExtraConfig::Spark(SparkWalletConfig::default()); +``` + +#### Cashu + +Cashu uses the [Cashu Development Kit (CDK)](https://docs.rs/cdk) for ecash-based custody via a Cashu mint. Enable it with the `cashu` feature flag: + +```toml +[dependencies] +orange-sdk = { version = "0.1", features = ["cashu"] } +``` + +```rust,ignore +use orange_sdk::trusted_wallet::cashu::CashuConfig; +use orange_sdk::{CurrencyUnit, ExtraConfig}; + +let extra = ExtraConfig::Cashu(CashuConfig { + mint_url: "https://mint.example.com".to_string(), + unit: CurrencyUnit::Sat, + npubcash_url: Some("https://npub.cash".to_string()), // optional: enables Lightning address +}); +``` ### Chain Sources -- Electrum servers -- Esplora servers (with optional Basic auth) -- Bitcoin Core RPC +- **Electrum** servers (use `ssl://` prefix for TLS) +- **Esplora** HTTP API servers (with optional Basic auth) +- **Bitcoin Core RPC** (direct JSON-RPC connection) ## Documentation For detailed API documentation, run: ```bash -cargo doc --open +cargo doc --all-features --open ``` ## Contributing diff --git a/graduated-rebalancer/src/lib.rs b/graduated-rebalancer/src/lib.rs index fa65da8..27d35e3 100644 --- a/graduated-rebalancer/src/lib.rs +++ b/graduated-rebalancer/src/lib.rs @@ -1,11 +1,34 @@ #![deny(missing_docs)] #![allow(clippy::type_complexity)] -//! A library for managing graduated rebalancing between trusted and lightning wallets. +//! Graduated rebalancing between trusted and Lightning wallets. //! -//! This crate provides a `GraduatedRebalancer` that automatically manages the balance -//! between trusted wallets (for small amounts) and lightning wallets (for larger amounts) -//! based on configurable thresholds. +//! This crate provides [`GraduatedRebalancer`], a generic engine that automatically moves +//! funds from a trusted wallet backend into self-custodial Lightning channels based on +//! configurable thresholds. +//! +//! # How it works +//! +//! The rebalancer supports two transfer paths: +//! +//! 1. **Trusted → Lightning:** When the trusted wallet balance exceeds the configured limit, +//! the rebalancer pays a Lightning invoice from the trusted wallet to the self-custodial +//! Lightning node, effectively moving funds into self-custody. +//! +//! 2. **On-chain → Lightning:** When on-chain funds are available, the rebalancer opens a +//! new Lightning channel or splices funds into an existing channel with the LSP. +//! +//! Both paths are triggered via the [`RebalanceTrigger`] trait, which determines *when* and +//! *how much* to rebalance. Events are reported via the [`EventHandler`] trait. +//! +//! # Usage +//! +//! This crate is designed to be generic over wallet implementations. Provide types that +//! implement [`TrustedWallet`] and [`LightningWallet`], along with a [`RebalanceTrigger`] +//! and an [`EventHandler`], then construct a [`GraduatedRebalancer`]. +//! +//! The [`orange-sdk`](https://docs.rs/orange-sdk) crate provides a concrete integration +//! of this rebalancer with its wallet infrastructure. use bitcoin_payment_instructions::amount::Amount; use bitcoin_payment_instructions::PaymentMethod; @@ -206,7 +229,16 @@ impl EventHandler for IgnoringEventHandler { } } -/// The main graduated rebalancer that manages balance between trusted and lightning wallets +/// The core rebalancing engine. +/// +/// Manages the automatic transfer of funds from a trusted wallet into self-custodial +/// Lightning channels. It is generic over the wallet implementations, trigger logic, +/// event handling, and logger. +/// +/// All rebalance operations are serialized through an internal mutex to prevent +/// concurrent balance modifications. +/// +/// See the [crate-level documentation](crate) for an overview of how rebalancing works. pub struct GraduatedRebalancer< T: TrustedWallet, L: LightningWallet,