MultiMintWallet

Struct MultiMintWallet 

Source
pub struct MultiMintWallet { /* private fields */ }
Expand description

Multi Mint Wallet

A wallet that manages multiple mints but supports only one currency unit. This simplifies the interface by removing the need to specify both mint and unit.

§Examples

§Creating and using a multi-mint wallet

// Create a multi-mint wallet with a database
// For real usage, you would use cdk_sqlite::wallet::memory::empty().await? or similar
let seed = [0u8; 64];  // Use a secure random seed in production
let database = cdk_sqlite::wallet::memory::empty().await?;

let wallet = MultiMintWallet::new(
    Arc::new(database),
    seed,
    CurrencyUnit::Sat,
).await?;

// Add mints to the wallet
let mint_url1: MintUrl = "https://mint1.example.com".parse()?;
let mint_url2: MintUrl = "https://mint2.example.com".parse()?;
wallet.add_mint(mint_url1.clone()).await?;
wallet.add_mint(mint_url2).await?;

// Check total balance across all mints
let balance = wallet.total_balance().await?;
println!("Total balance: {} sats", balance);

// Send tokens from a specific mint
let prepared = wallet.prepare_send(
    mint_url1,
    Amount::from(100),
    Default::default()
).await?;
let token = prepared.confirm(None).await?;

Implementations§

Source§

impl MultiMintWallet

Source

pub async fn new( localstore: Arc<dyn WalletDatabase<Err = Error> + Send + Sync>, seed: [u8; 64], unit: CurrencyUnit, ) -> Result<Self, Error>

Create a new MultiMintWallet for a specific currency unit

Source

pub async fn new_with_proxy( localstore: Arc<dyn WalletDatabase<Err = Error> + Send + Sync>, seed: [u8; 64], unit: CurrencyUnit, proxy_url: Url, ) -> Result<Self, Error>

Create a new MultiMintWallet with proxy configuration

All wallets in this MultiMintWallet will use the specified proxy. This allows you to route all mint connections through a proxy server.

Source

pub async fn add_mint(&self, mint_url: MintUrl) -> Result<(), Error>

Adds a mint to this MultiMintWallet

Creates a wallet for the specified mint using default or global settings. For custom configuration, use add_mint_with_config().

Source

pub async fn add_mint_with_config( &self, mint_url: MintUrl, config: WalletConfig, ) -> Result<(), Error>

Adds a mint to this MultiMintWallet with custom configuration

The provided configuration is used to create the wallet with custom connectors and settings. Configuration is stored within the Wallet instance itself.

Source

pub async fn set_mint_config( &self, mint_url: MintUrl, config: WalletConfig, ) -> Result<(), Error>

Set or update configuration for a mint

If the wallet already exists, it will be updated with the new config. If the wallet doesn’t exist, it will be created with the specified config.

Source

pub async fn set_auth_client( &self, mint_url: &MintUrl, auth_wallet: Option<AuthWallet>, ) -> Result<(), Error>

Set the auth client (AuthWallet) for a specific mint

This allows updating the auth wallet for an existing mint wallet without recreating it.

Source

pub async fn remove_mint(&self, mint_url: &MintUrl)

Remove mint from MultiMintWallet

Source

pub async fn get_wallets(&self) -> Vec<Wallet>

Get Wallets from MultiMintWallet

Source

pub async fn get_wallet(&self, mint_url: &MintUrl) -> Option<Wallet>

Get Wallet from MultiMintWallet

Source

pub async fn has_mint(&self, mint_url: &MintUrl) -> bool

Check if mint is in wallet

Source

pub fn unit(&self) -> &CurrencyUnit

Get the currency unit for this wallet

Source

pub async fn get_balances(&self) -> Result<BTreeMap<MintUrl, Amount>, Error>

Get wallet balances for all mints

Source

pub async fn list_proofs(&self) -> Result<BTreeMap<MintUrl, Vec<Proof>>, Error>

List proofs.

Source

pub async fn list_transactions( &self, direction: Option<TransactionDirection>, ) -> Result<Vec<Transaction>, Error>

List transactions

Source

pub async fn total_balance(&self) -> Result<Amount, Error>

Get total balance across all wallets (since all wallets use the same currency unit)

Source

pub async fn prepare_send( &self, mint_url: MintUrl, amount: Amount, opts: MultiMintSendOptions, ) -> Result<PreparedSend, Error>

Prepare to send tokens from a specific mint with optional transfer from other mints

This method ensures that sends always happen from only one mint. If the specified mint doesn’t have sufficient balance and allow_transfer is enabled in options, it will first transfer funds from other mints to the target mint.

Source

pub async fn transfer( &self, source_mint_url: &MintUrl, target_mint_url: &MintUrl, mode: TransferMode, ) -> Result<TransferResult, Error>

Transfer funds from a single source wallet to target mint using Lightning Network (melt/mint)

This function properly accounts for fees by handling different transfer modes:

  • ExactReceive: Target receives exactly the specified amount, source pays amount + fees
  • FullBalance: All source balance is transferred, target receives balance - fees
Source

pub async fn mint_quote( &self, mint_url: &MintUrl, amount: Amount, description: Option<String>, ) -> Result<MintQuote, Error>

Mint quote for wallet

Source

pub async fn check_mint_quote( &self, mint_url: &MintUrl, quote_id: &str, ) -> Result<MintQuote, Error>

Check a specific mint quote status

Source

pub async fn check_all_mint_quotes( &self, mint_url: Option<MintUrl>, ) -> Result<Amount, Error>

Check all mint quotes If quote is paid, wallet will mint

Source

pub async fn mint( &self, mint_url: &MintUrl, quote_id: &str, conditions: Option<SpendingConditions>, ) -> Result<Proofs, Error>

Mint a specific quote

Source

pub async fn wait_for_mint_quote( &self, mint_url: &MintUrl, quote_id: &str, split_target: SplitTarget, conditions: Option<SpendingConditions>, timeout_secs: u64, ) -> Result<Proofs, Error>

Wait for a mint quote to be paid and automatically mint the proofs

Source

pub async fn receive( &self, encoded_token: &str, opts: MultiMintReceiveOptions, ) -> Result<Amount, Error>

Receive token with multi-mint options

This method can:

  • Receive tokens from trusted mints (already added to the wallet)
  • Optionally receive from untrusted mints by adding them to the wallet
  • Optionally transfer tokens from untrusted mints to a trusted mint (and remove the untrusted mint)
§Examples
// Receive from a trusted mint
let token = "cashuAey...";
let amount = wallet
    .receive(token, MultiMintReceiveOptions::default())
    .await?;

// Receive from untrusted mint and add it to the wallet
let options = MultiMintReceiveOptions::default().allow_untrusted(true);
let amount = wallet.receive(token, options).await?;

// Receive from untrusted mint, transfer to trusted mint, then remove untrusted mint
let trusted_mint: MintUrl = "https://trusted.mint".parse()?;
let options = MultiMintReceiveOptions::default().transfer_to_mint(Some(trusted_mint));
let amount = wallet.receive(token, options).await?;
Source

pub async fn restore(&self, mint_url: &MintUrl) -> Result<Amount, Error>

Restore

Source

pub async fn verify_token_p2pk( &self, token: &Token, conditions: SpendingConditions, ) -> Result<(), Error>

Verify token matches p2pk conditions

Source

pub async fn verify_token_dleq(&self, token: &Token) -> Result<(), Error>

Verifys all proofs in token have valid dleq proof

Source

pub async fn melt_quote( &self, mint_url: &MintUrl, bolt11: String, options: Option<MeltOptions>, ) -> Result<MeltQuote, Error>

Create a melt quote for a specific mint

Source

pub async fn melt_with_mint( &self, mint_url: &MintUrl, quote_id: &str, ) -> Result<Melted, Error>

Melt (pay invoice) from a specific mint using a quote ID

Source

pub async fn mpp_melt_quote( &self, bolt11: String, mint_amounts: Vec<(MintUrl, Amount)>, ) -> Result<Vec<(MintUrl, MeltQuote)>, Error>

Create MPP (Multi-Path Payment) melt quotes from multiple mints

This function allows manual specification of which mints and amounts to use for MPP. Returns a vector of (MintUrl, MeltQuote) pairs.

Source

pub async fn mpp_melt( &self, quotes: Vec<(MintUrl, String)>, ) -> Result<Vec<(MintUrl, Melted)>, Error>

Execute MPP melts using previously obtained quotes

Source

pub async fn melt( &self, bolt11: &str, options: Option<MeltOptions>, max_fee: Option<Amount>, ) -> Result<Melted, Error>

Melt (pay invoice) with automatic wallet selection (deprecated, use specific mint functions for better control)

Automatically selects the best wallet to pay from based on:

  • Available balance
  • Fees
§Examples
// Pay a lightning invoice from any mint with sufficient balance
let invoice = "lnbc100n1p...";

let result = wallet.melt(invoice, None, None).await?;
println!("Paid {} sats, fee was {} sats", result.amount, result.fee_paid);
Source

pub async fn swap( &self, amount: Option<Amount>, conditions: Option<SpendingConditions>, ) -> Result<Option<Proofs>, Error>

Swap proofs with automatic wallet selection

Source

pub async fn consolidate(&self) -> Result<Amount, Error>

Consolidate proofs from multiple wallets into fewer, larger proofs This can help reduce the number of proofs and optimize wallet performance

Source

pub async fn mint_blind_auth( &self, mint_url: &MintUrl, amount: Amount, ) -> Result<Proofs, Error>

Mint blind auth tokens for a specific mint

This is a convenience method that calls the underlying wallet’s mint_blind_auth.

Source

pub async fn get_unspent_auth_proofs( &self, mint_url: &MintUrl, ) -> Result<Vec<AuthProof>, Error>

Get unspent auth proofs for a specific mint

This is a convenience method that calls the underlying wallet’s get_unspent_auth_proofs.

Source

pub async fn set_cat( &self, mint_url: &MintUrl, cat: String, ) -> Result<(), Error>

Set Clear Auth Token (CAT) for authentication at a specific mint

This is a convenience method that calls the underlying wallet’s set_cat.

Source

pub async fn set_refresh_token( &self, mint_url: &MintUrl, refresh_token: String, ) -> Result<(), Error>

Set refresh token for authentication at a specific mint

This is a convenience method that calls the underlying wallet’s set_refresh_token.

Source

pub async fn refresh_access_token( &self, mint_url: &MintUrl, ) -> Result<(), Error>

Refresh CAT token for a specific mint

This is a convenience method that calls the underlying wallet’s refresh_access_token.

Source

pub async fn fetch_mint_info( &self, mint_url: &MintUrl, ) -> Result<Option<MintInfo>, Error>

Query mint for current mint information

This is a convenience method that calls the underlying wallet’s fetch_mint_info.

Source

pub async fn melt_bip353_quote( &self, mint_url: &MintUrl, bip353_address: &str, amount_msat: impl Into<Amount>, ) -> Result<MeltQuote, Error>

Melt Quote for BIP353 human-readable address

This method resolves a BIP353 address (e.g., “alice@example.com”) to a Lightning offer and then creates a melt quote for that offer at the specified mint.

§Arguments
  • mint_url - The mint to use for creating the melt quote
  • bip353_address - Human-readable address in the format “user@domain.com”
  • amount_msat - Amount to pay in millisatoshis
§Returns

A MeltQuote that can be used to execute the payment

Source

pub async fn melt_lightning_address_quote( &self, mint_url: &MintUrl, lightning_address: &str, amount_msat: impl Into<Amount>, ) -> Result<MeltQuote, Error>

Melt Quote for Lightning address

This method resolves a Lightning address (e.g., “alice@example.com”) to a Lightning invoice and then creates a melt quote for that invoice at the specified mint.

§Arguments
  • mint_url - The mint to use for creating the melt quote
  • lightning_address - Lightning address in the format “user@domain.com”
  • amount_msat - Amount to pay in millisatoshis
§Returns

A MeltQuote that can be used to execute the payment

Source

pub async fn melt_human_readable_quote( &self, mint_url: &MintUrl, address: &str, amount_msat: impl Into<Amount>, ) -> Result<MeltQuote, Error>

Get a melt quote for a human-readable address

This method accepts a human-readable address that could be either a BIP353 address or a Lightning address. It intelligently determines which to try based on mint support:

  1. If the mint supports Bolt12, it tries BIP353 first
  2. Falls back to Lightning address only if BIP353 DNS resolution fails
  3. If BIP353 resolves but fails at the mint, it does NOT fall back to Lightning address
  4. If the mint doesn’t support Bolt12, it tries Lightning address directly
§Arguments
  • mint_url - The mint to use for creating the melt quote
  • address - Human-readable address (BIP353 or Lightning address)
  • amount_msat - Amount to pay in millisatoshis
Source§

impl MultiMintWallet

Source

pub async fn create_request( &self, params: CreateRequestParams, ) -> Result<(PaymentRequest, Option<NostrWaitInfo>), Error>

Create a NUT-18 PaymentRequest from high-level parameters.

Why:

  • Ensures the CLI and SDKs construct requests consistently using wallet context.
  • Advertises available mints for the chosen unit so payers can select compatible proofs.
  • Optionally embeds a transport; Nostr is preferred to reduce IP exposure for the payer.

Behavior summary (focus on rationale rather than steps):

  • Uses unit to discover mints with balances as a hint to senders (helps route payments without leaking more data than necessary).
  • Translates P2PK/multisig and HTLC inputs (pubkeys/num_sigs/hash/preimage) into a NUT-10 secret request so the receiver can enforce spending constraints.
  • For transport == "nostr", generates ephemeral keys and an nprofile pointing at the chosen relays; returns NostrWaitInfo so callers can wait for the incoming payment without coupling construction and reception logic.
  • For transport == "http", attaches the provided endpoint; for none or unknown, omits transports to let the caller deliver out-of-band.

Returns:

  • (PaymentRequest, Some(NostrWaitInfo)) when transport == "nostr".
  • (PaymentRequest, None) otherwise.

Errors when:

  • unit cannot be parsed, relay URLs are invalid, or P2PK/HTLC parameters are malformed.

Notes:

  • Sets single_use = true to discourage replays.
  • Ephemeral Nostr keys are intentional; keep NostrWaitInfo only as long as needed for reception.
Source

pub async fn wait_for_nostr_payment( &self, info: NostrWaitInfo, ) -> Result<Amount>

Wait for a Nostr payment for the previously constructed PaymentRequest and receive it into the wallet.

Trait Implementations§

Source§

impl Clone for MultiMintWallet

Source§

fn clone(&self) -> MultiMintWallet

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Drop for MultiMintWallet

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> IntoRequest<T> for T

Source§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
Source§

impl<L> LayerExt<L> for L

Source§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where L: Layer<S>,

Applies the layer to a service and wraps it in Layered.
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more