Skip to main content

fedimint_gateway_common/
lib.rs

1use std::collections::BTreeMap;
2use std::fmt;
3use std::time::{Duration, SystemTime};
4
5use bitcoin::address::NetworkUnchecked;
6use bitcoin::hashes::sha256;
7use bitcoin::secp256k1::PublicKey;
8use bitcoin::{Address, Network, OutPoint};
9use clap::Subcommand;
10use envs::{
11    FM_LDK_ALIAS_ENV, FM_LND_MACAROON_ENV, FM_LND_RPC_ADDR_ENV, FM_LND_TIME_PREF_ENV,
12    FM_LND_TLS_CERT_ENV, FM_PORT_LDK,
13};
14use fedimint_core::config::{FederationId, JsonClientConfig};
15use fedimint_core::encoding::{Decodable, Encodable};
16use fedimint_core::invite_code::InviteCode;
17use fedimint_core::util::{SafeUrl, get_average, get_median};
18use fedimint_core::{Amount, BitcoinAmountOrAll, secp256k1};
19use fedimint_eventlog::{EventKind, EventLogId, PersistedLogEntry, StructuredPaymentEvents};
20use fedimint_lnv2_common::gateway_api::PaymentFee;
21use fedimint_wallet_client::PegOutFees;
22use lightning_invoice::Bolt11Invoice;
23use serde::{Deserialize, Serialize};
24
25pub mod envs;
26
27pub const V1_API_ENDPOINT: &str = "v1";
28
29pub const ADDRESS_ENDPOINT: &str = "/address";
30pub const ADDRESS_RECHECK_ENDPOINT: &str = "/address_recheck";
31pub const BACKUP_ENDPOINT: &str = "/backup";
32pub const CONFIGURATION_ENDPOINT: &str = "/config";
33pub const CONNECT_FED_ENDPOINT: &str = "/connect_fed";
34pub const CREATE_BOLT11_INVOICE_FOR_OPERATOR_ENDPOINT: &str = "/create_bolt11_invoice_for_operator";
35pub const CREATE_BOLT12_OFFER_FOR_OPERATOR_ENDPOINT: &str = "/create_bolt12_offer_for_operator";
36pub const GATEWAY_INFO_ENDPOINT: &str = "/info";
37pub const INVITE_CODES_ENDPOINT: &str = "/invite_codes";
38pub const GET_BALANCES_ENDPOINT: &str = "/balances";
39pub const GET_INVOICE_ENDPOINT: &str = "/get_invoice";
40pub const GET_LN_ONCHAIN_ADDRESS_ENDPOINT: &str = "/get_ln_onchain_address";
41pub const LEAVE_FED_ENDPOINT: &str = "/leave_fed";
42pub const LIST_CHANNELS_ENDPOINT: &str = "/list_channels";
43pub const LIST_TRANSACTIONS_ENDPOINT: &str = "/list_transactions";
44pub const MNEMONIC_ENDPOINT: &str = "/mnemonic";
45pub const OPEN_CHANNEL_ENDPOINT: &str = "/open_channel";
46pub const OPEN_CHANNEL_WITH_PUSH_ENDPOINT: &str = "/open_channel_with_push";
47pub const CLOSE_CHANNELS_WITH_PEER_ENDPOINT: &str = "/close_channels_with_peer";
48pub const PAY_INVOICE_FOR_OPERATOR_ENDPOINT: &str = "/pay_invoice_for_operator";
49pub const PAY_OFFER_FOR_OPERATOR_ENDPOINT: &str = "/pay_offer_for_operator";
50pub const PAYMENT_LOG_ENDPOINT: &str = "/payment_log";
51pub const PAYMENT_SUMMARY_ENDPOINT: &str = "/payment_summary";
52pub const PEGIN_FROM_ONCHAIN_ENDPOINT: &str = "/pegin_from_onchain";
53pub const RECEIVE_ECASH_ENDPOINT: &str = "/receive_ecash";
54pub const SET_CHANNEL_FEES_ENDPOINT: &str = "/set_channel_fees";
55pub const SET_FEES_ENDPOINT: &str = "/set_fees";
56pub const STOP_ENDPOINT: &str = "/stop";
57pub const SEND_ONCHAIN_ENDPOINT: &str = "/send_onchain";
58pub const SPEND_ECASH_ENDPOINT: &str = "/spend_ecash";
59pub const WITHDRAW_ENDPOINT: &str = "/withdraw";
60pub const WITHDRAW_TO_ONCHAIN_ENDPOINT: &str = "/withdraw_to_onchain";
61
62#[derive(Debug, Serialize, Deserialize, Clone)]
63pub struct ConnectFedPayload {
64    pub invite_code: String,
65    pub use_tor: Option<bool>,
66    pub recover: Option<bool>,
67}
68
69#[derive(Debug, Serialize, Deserialize, Clone)]
70pub struct LeaveFedPayload {
71    pub federation_id: FederationId,
72}
73
74#[derive(Debug, Serialize, Deserialize)]
75pub struct InfoPayload;
76
77#[derive(Debug, Serialize, Deserialize)]
78pub struct BackupPayload {
79    pub federation_id: FederationId,
80}
81
82#[derive(Debug, Serialize, Deserialize, Clone)]
83pub struct ConfigPayload {
84    pub federation_id: Option<FederationId>,
85}
86
87#[derive(Debug, Serialize, Deserialize, Clone)]
88pub struct DepositAddressPayload {
89    pub federation_id: FederationId,
90}
91
92#[derive(Debug, Serialize, Deserialize, Clone)]
93pub struct PeginFromOnchainPayload {
94    pub federation_id: FederationId,
95    pub amount: BitcoinAmountOrAll,
96    pub fee_rate_sats_per_vbyte: u64,
97}
98
99#[derive(Debug, Serialize, Deserialize, Clone)]
100pub struct DepositAddressRecheckPayload {
101    pub address: Address<NetworkUnchecked>,
102    pub federation_id: FederationId,
103}
104
105#[derive(Debug, Serialize, Deserialize, Clone)]
106pub struct WithdrawPayload {
107    pub federation_id: FederationId,
108    pub amount: BitcoinAmountOrAll,
109    pub address: Address<NetworkUnchecked>,
110    /// When provided (from UI preview flow), uses these quoted fees.
111    /// When None, fetches current fees from the wallet.
112    #[serde(default)]
113    pub quoted_fees: Option<PegOutFees>,
114}
115
116#[derive(Debug, Serialize, Deserialize, Clone)]
117pub struct WithdrawToOnchainPayload {
118    pub federation_id: FederationId,
119    pub amount: BitcoinAmountOrAll,
120}
121
122#[derive(Debug, Serialize, Deserialize, Clone)]
123pub struct WithdrawResponse {
124    pub txid: bitcoin::Txid,
125    pub fees: PegOutFees,
126}
127
128#[derive(Debug, Serialize, Deserialize, Clone)]
129pub struct WithdrawPreviewPayload {
130    pub federation_id: FederationId,
131    pub amount: BitcoinAmountOrAll,
132    pub address: Address<NetworkUnchecked>,
133}
134
135#[derive(Debug, Serialize, Deserialize, Clone)]
136pub struct WithdrawPreviewResponse {
137    pub withdraw_amount: Amount,
138    pub address: String,
139    pub peg_out_fees: PegOutFees,
140    pub total_cost: Amount,
141    /// Estimated mint fees when withdrawing all. None for partial withdrawals.
142    #[serde(default)]
143    pub mint_fees: Option<Amount>,
144}
145
146/// Deprecated, unused, doesn't do anything
147///
148/// Only here for backward-compat reasons.
149#[allow(deprecated)]
150#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable, Serialize, Deserialize)]
151pub enum ConnectorType {
152    Tcp,
153    Tor,
154}
155
156#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable, Serialize, Deserialize)]
157pub struct FederationConfig {
158    pub invite_code: InviteCode,
159    // Unique integer identifier per-federation that is assigned when the gateways joins a
160    // federation.
161    #[serde(alias = "mint_channel_id")]
162    pub federation_index: u64,
163    pub lightning_fee: PaymentFee,
164    pub transaction_fee: PaymentFee,
165    #[allow(deprecated)] // only here for decoding backward-compat
166    pub _connector: ConnectorType,
167}
168
169/// Information about one of the feds we are connected to
170#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
171pub struct FederationInfo {
172    pub federation_id: FederationId,
173    pub federation_name: Option<String>,
174    pub balance_msat: Amount,
175    pub config: FederationConfig,
176    pub last_backup_time: Option<SystemTime>,
177}
178
179#[derive(Debug, Serialize, Deserialize, PartialEq)]
180pub struct GatewayInfo {
181    pub version_hash: String,
182    pub federations: Vec<FederationInfo>,
183    /// Mapping from short channel id to the federation id that it belongs to.
184    // TODO: Remove this alias once it no longer breaks backwards compatibility.
185    #[serde(alias = "channels")]
186    pub federation_fake_scids: Option<BTreeMap<u64, FederationId>>,
187    pub gateway_state: String,
188    pub lightning_info: LightningInfo,
189    pub lightning_mode: LightningMode,
190    pub registrations: BTreeMap<RegisteredProtocol, (SafeUrl, secp256k1::PublicKey)>,
191}
192
193#[derive(Debug, Serialize, Deserialize, PartialEq)]
194pub struct GatewayFedConfig {
195    pub federations: BTreeMap<FederationId, JsonClientConfig>,
196}
197
198#[derive(Debug, Serialize, Deserialize, Clone)]
199pub struct SetFeesPayload {
200    pub federation_id: Option<FederationId>,
201    pub lightning_base: Option<Amount>,
202    pub lightning_parts_per_million: Option<u64>,
203    pub transaction_base: Option<Amount>,
204    pub transaction_parts_per_million: Option<u64>,
205}
206
207#[derive(Debug, Serialize, Deserialize, Clone)]
208pub struct CreateInvoiceForOperatorPayload {
209    pub amount_msats: u64,
210    pub expiry_secs: Option<u32>,
211    pub description: Option<String>,
212}
213
214#[derive(Debug, Serialize, Deserialize, Clone)]
215pub struct PayInvoiceForOperatorPayload {
216    pub invoice: Bolt11Invoice,
217}
218
219#[derive(Debug, Serialize, Deserialize, Clone)]
220pub struct SpendEcashPayload {
221    /// Federation id of the e-cash to spend
222    pub federation_id: FederationId,
223    /// The amount of e-cash to spend
224    pub amount: Amount,
225}
226
227#[derive(Debug, Serialize, Deserialize, Clone)]
228pub struct SpendEcashResponse {
229    /// OOBNotes.to_string() for v1, base32::encode_prefixed() for v2
230    pub notes: String,
231}
232
233#[derive(Debug, Serialize, Deserialize, Clone)]
234pub struct ReceiveEcashPayload {
235    /// Can be OOBNotes (v1) or ECash (v2)
236    pub notes: String,
237    #[serde(default)]
238    pub wait: bool,
239}
240
241#[derive(Debug, Serialize, Deserialize, Clone)]
242pub struct ReceiveEcashResponse {
243    pub amount: Amount,
244}
245
246#[derive(serde::Serialize, serde::Deserialize, Clone)]
247pub struct GatewayBalances {
248    pub onchain_balance_sats: u64,
249    pub lightning_balance_msats: u64,
250    pub ecash_balances: Vec<FederationBalanceInfo>,
251    pub inbound_lightning_liquidity_msats: u64,
252}
253
254#[derive(serde::Serialize, serde::Deserialize, Clone)]
255pub struct FederationBalanceInfo {
256    pub federation_id: FederationId,
257    pub ecash_balance_msats: Amount,
258}
259
260#[derive(Debug, Serialize, Deserialize, Clone)]
261pub struct MnemonicResponse {
262    pub mnemonic: Vec<String>,
263
264    // Legacy federations are federations that the gateway joined prior to v0.5.0
265    // and do not derive their secrets from the gateway's mnemonic. They also use
266    // a separate database from the gateway's db.
267    pub legacy_federations: Vec<FederationId>,
268}
269
270#[derive(Debug, Serialize, Deserialize, Clone)]
271pub struct PaymentLogPayload {
272    // The position in the log to stop querying. No events will be returned from after
273    // this `EventLogId`. If it is `None`, the last `EventLogId` is used.
274    pub end_position: Option<EventLogId>,
275
276    // The number of events to return
277    pub pagination_size: usize,
278
279    pub federation_id: FederationId,
280
281    /// Filter to only return events of these kinds. If empty, defaults to
282    /// `ALL_GATEWAY_EVENTS` (gateway payment-related events only, not all
283    /// events in the log).
284    ///
285    /// Note: returned event IDs may be non-contiguous because other internal
286    /// events (e.g. `tx-created`, `NoteCreated`) share the same ID space but
287    /// are filtered out.
288    pub event_kinds: Vec<EventKind>,
289}
290
291#[derive(Debug, Serialize, Deserialize, Clone)]
292pub struct PaymentLogResponse(pub Vec<PersistedLogEntry>);
293
294#[derive(Debug, Serialize, Deserialize, Clone)]
295pub struct PaymentSummaryResponse {
296    pub outgoing: PaymentStats,
297    pub incoming: PaymentStats,
298}
299
300#[derive(Debug, Serialize, Deserialize, Clone)]
301pub struct PaymentStats {
302    pub average_latency: Option<Duration>,
303    pub median_latency: Option<Duration>,
304    pub total_fees: Amount,
305    pub total_success: usize,
306    pub total_failure: usize,
307}
308
309impl PaymentStats {
310    /// Computes the payment statistics for the given structured payment events.
311    pub fn compute(events: &StructuredPaymentEvents) -> Self {
312        PaymentStats {
313            average_latency: get_average(&events.latencies_usecs).map(Duration::from_micros),
314            median_latency: get_median(&events.latencies_usecs).map(Duration::from_micros),
315            total_fees: Amount::from_msats(events.fees.iter().map(|a| a.msats).sum()),
316            total_success: events.latencies_usecs.len(),
317            total_failure: events.latencies_failure.len(),
318        }
319    }
320}
321
322#[derive(Debug, Serialize, Deserialize, Clone)]
323pub struct PaymentSummaryPayload {
324    pub start_millis: u64,
325    pub end_millis: u64,
326}
327
328#[derive(Serialize, Deserialize, Debug, Clone)]
329pub struct ChannelInfo {
330    pub remote_pubkey: secp256k1::PublicKey,
331    pub channel_size_sats: u64,
332    pub outbound_liquidity_sats: u64,
333    pub inbound_liquidity_sats: u64,
334    pub is_active: bool,
335    pub funding_outpoint: Option<OutPoint>,
336    pub remote_node_alias: Option<String>,
337    #[serde(default)]
338    pub remote_address: Option<String>,
339    /// The local-side base routing fee (msat) currently advertised for this
340    /// channel. `None` if the backend could not report a fee policy.
341    #[serde(default)]
342    pub base_fee_msat: Option<u64>,
343    /// The local-side proportional routing fee (parts per million) currently
344    /// advertised for this channel. `None` if the backend could not report a
345    /// fee policy.
346    #[serde(default)]
347    pub parts_per_million: Option<u64>,
348}
349
350#[derive(Debug, Serialize, Deserialize, Clone)]
351pub struct OpenChannelRequest {
352    pub pubkey: secp256k1::PublicKey,
353    pub host: String,
354    pub channel_size_sats: u64,
355    pub push_amount_sats: u64,
356    /// Feerate (sat/vB) for the channel-opening on-chain transaction. If
357    /// `None`, the Lightning backend picks a feerate. Not honored by all
358    /// backends (e.g. LDK manages its own fee estimation).
359    #[serde(default, deserialize_with = "empty_string_as_none")]
360    pub fee_rate_sats_per_vbyte: Option<u64>,
361    /// Base routing fee (msat) advertised for the opened channel. If `None`,
362    /// the backend's default policy is used.
363    #[serde(default, deserialize_with = "empty_string_as_none")]
364    pub base_fee_msat: Option<u64>,
365    /// Proportional routing fee (parts per million) advertised for the opened
366    /// channel. If `None`, the backend's default policy is used.
367    #[serde(default, deserialize_with = "empty_string_as_none")]
368    pub parts_per_million: Option<u64>,
369}
370
371#[derive(Debug, Serialize, Deserialize, Clone)]
372pub struct SetChannelFeesRequest {
373    /// Funding outpoint identifying the channel whose advertised routing fees
374    /// should be updated.
375    pub funding_outpoint: OutPoint,
376    /// New base routing fee in millisatoshis.
377    pub base_fee_msat: u64,
378    /// New proportional routing fee in parts per million.
379    pub parts_per_million: u64,
380}
381
382/// Helper for serde: deserializes missing values and empty form strings as
383/// `None`. Lets the same struct be used for JSON payloads and HTMX form
384/// submissions where numeric inputs may be left blank.
385fn empty_string_as_none<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
386where
387    D: serde::Deserializer<'de>,
388    T: std::str::FromStr + Deserialize<'de>,
389    T::Err: std::fmt::Display,
390{
391    #[derive(Deserialize)]
392    #[serde(untagged)]
393    enum StringOrT<T> {
394        String(String),
395        T(T),
396    }
397
398    match Option::<StringOrT<T>>::deserialize(deserializer)? {
399        None => Ok(None),
400        Some(StringOrT::T(value)) => Ok(Some(value)),
401        Some(StringOrT::String(s)) if s.trim().is_empty() => Ok(None),
402        Some(StringOrT::String(s)) => s
403            .trim()
404            .parse::<T>()
405            .map(Some)
406            .map_err(serde::de::Error::custom),
407    }
408}
409
410#[derive(Debug, Serialize, Deserialize, Clone)]
411pub struct SendOnchainRequest {
412    pub address: Address<NetworkUnchecked>,
413    pub amount: BitcoinAmountOrAll,
414    pub fee_rate_sats_per_vbyte: u64,
415}
416
417impl fmt::Display for SendOnchainRequest {
418    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
419        write!(
420            f,
421            "SendOnchainRequest {{ address: {}, amount: {}, fee_rate_sats_per_vbyte: {} }}",
422            self.address.assume_checked_ref(),
423            self.amount,
424            self.fee_rate_sats_per_vbyte
425        )
426    }
427}
428
429#[derive(Debug, Serialize, Deserialize, Clone)]
430pub struct CloseChannelsWithPeerRequest {
431    pub pubkey: secp256k1::PublicKey,
432    #[serde(default)]
433    pub force: bool,
434    pub sats_per_vbyte: Option<u64>,
435}
436
437impl fmt::Display for CloseChannelsWithPeerRequest {
438    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
439        write!(
440            f,
441            "CloseChannelsWithPeerRequest {{ pubkey: {}, force: {}, sats_per_vbyte: {} }}",
442            self.pubkey,
443            self.force,
444            match self.sats_per_vbyte {
445                Some(sats) => sats.to_string(),
446                None => "None".to_string(),
447            }
448        )
449    }
450}
451
452#[derive(Debug, Serialize, Deserialize, Clone)]
453pub struct CloseChannelsWithPeerResponse {
454    pub num_channels_closed: u32,
455}
456
457#[derive(Debug, Serialize, Deserialize, Clone)]
458pub struct GetInvoiceRequest {
459    pub payment_hash: sha256::Hash,
460}
461
462#[derive(Debug, Serialize, Deserialize, Clone)]
463pub struct GetInvoiceResponse {
464    pub preimage: Option<String>,
465    pub payment_hash: Option<sha256::Hash>,
466    pub amount: Amount,
467    pub created_at: SystemTime,
468    pub status: PaymentStatus,
469}
470
471#[derive(Debug, Serialize, Deserialize, Clone)]
472pub struct ListTransactionsPayload {
473    pub start_secs: u64,
474    pub end_secs: u64,
475}
476
477#[derive(Debug, Serialize, Deserialize, Clone)]
478pub struct ListTransactionsResponse {
479    pub transactions: Vec<PaymentDetails>,
480}
481
482#[derive(Debug, Serialize, Deserialize, Clone)]
483pub struct PaymentDetails {
484    pub payment_hash: Option<sha256::Hash>,
485    pub preimage: Option<String>,
486    pub payment_kind: PaymentKind,
487    pub amount: Amount,
488    pub direction: PaymentDirection,
489    pub status: PaymentStatus,
490    pub timestamp_secs: u64,
491}
492
493#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
494pub enum PaymentKind {
495    Bolt11,
496    Bolt12Offer,
497    Bolt12Refund,
498    Onchain,
499}
500
501#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
502pub enum PaymentDirection {
503    Outbound,
504    Inbound,
505}
506
507#[derive(Debug, Serialize, Deserialize, Clone)]
508pub struct CreateOfferPayload {
509    pub amount: Option<Amount>,
510    pub description: Option<String>,
511    pub expiry_secs: Option<u32>,
512    pub quantity: Option<u64>,
513}
514
515#[derive(Debug, Serialize, Deserialize, Clone)]
516pub struct CreateOfferResponse {
517    pub offer: String,
518}
519
520#[derive(Debug, Serialize, Deserialize, Clone)]
521pub struct PayOfferPayload {
522    pub offer: String,
523    pub amount: Option<Amount>,
524    pub quantity: Option<u64>,
525    pub payer_note: Option<String>,
526}
527
528#[derive(Debug, Serialize, Deserialize, Clone)]
529pub struct PayOfferResponse {
530    pub preimage: String,
531}
532
533#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
534pub enum PaymentStatus {
535    Pending,
536    Succeeded,
537    Failed,
538}
539
540/// Default value for LND's `SendPaymentRequest::time_pref`. LND interprets this
541/// as a value in [`LND_TIME_PREF_MIN`, `LND_TIME_PREF_MAX`], where -1 optimizes
542/// purely for fees and 1 optimizes purely for reliability. We default to 0.5 to
543/// favor reliability while still considering fees.
544pub const LND_DEFAULT_TIME_PREF: f64 = 0.5;
545pub const LND_TIME_PREF_MIN: f64 = -1.0;
546pub const LND_TIME_PREF_MAX: f64 = 1.0;
547
548fn parse_lnd_time_pref(s: &str) -> Result<f64, String> {
549    let parsed: f64 = s.parse().map_err(|e| format!("invalid f64: {e}"))?;
550    if !parsed.is_finite() || !(LND_TIME_PREF_MIN..=LND_TIME_PREF_MAX).contains(&parsed) {
551        return Err(format!(
552            "must be a finite value in [{LND_TIME_PREF_MIN}, {LND_TIME_PREF_MAX}]"
553        ));
554    }
555    Ok(parsed)
556}
557
558#[derive(Debug, Clone, Subcommand, Serialize, Deserialize, PartialEq)]
559pub enum LightningMode {
560    #[clap(name = "lnd")]
561    Lnd {
562        /// LND RPC address
563        #[arg(long = "lnd-rpc-host", env = FM_LND_RPC_ADDR_ENV)]
564        lnd_rpc_addr: String,
565
566        /// LND TLS cert file path
567        #[arg(long = "lnd-tls-cert", env = FM_LND_TLS_CERT_ENV)]
568        lnd_tls_cert: String,
569
570        /// LND macaroon file path
571        #[arg(long = "lnd-macaroon", env = FM_LND_MACAROON_ENV)]
572        lnd_macaroon: String,
573
574        /// `time_pref` passed to LND `SendPaymentRequest`. -1.0 optimizes
575        /// purely for fees, 1.0 optimizes purely for reliability.
576        #[arg(
577            long = "lnd-time-pref",
578            env = FM_LND_TIME_PREF_ENV,
579            default_value_t = LND_DEFAULT_TIME_PREF,
580            value_parser = parse_lnd_time_pref,
581        )]
582        lnd_time_pref: f64,
583    },
584    #[clap(name = "ldk")]
585    Ldk {
586        /// LDK lightning server port
587        #[arg(long = "ldk-lightning-port", env = FM_PORT_LDK)]
588        lightning_port: u16,
589
590        /// LDK's Alias
591        #[arg(long = "ldk-alias", env = FM_LDK_ALIAS_ENV)]
592        alias: String,
593    },
594}
595
596#[derive(Clone)]
597pub enum ChainSource {
598    Bitcoind {
599        username: String,
600        password: String,
601        server_url: SafeUrl,
602    },
603    Esplora {
604        server_url: SafeUrl,
605    },
606}
607
608impl fmt::Display for ChainSource {
609    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
610        match self {
611            ChainSource::Bitcoind {
612                username: _,
613                password: _,
614                server_url,
615            } => {
616                write!(f, "Bitcoind source with URL: {server_url}")
617            }
618            ChainSource::Esplora { server_url } => {
619                write!(f, "Esplora source with URL: {server_url}")
620            }
621        }
622    }
623}
624
625#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
626#[serde(rename_all = "snake_case")]
627pub enum LightningInfo {
628    Connected {
629        public_key: PublicKey,
630        alias: String,
631        network: Network,
632        block_height: u64,
633        synced_to_chain: bool,
634    },
635    NotConnected,
636}
637
638#[derive(
639    Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Encodable, Decodable, Serialize, Deserialize,
640)]
641#[serde(rename_all = "snake_case")]
642pub enum RegisteredProtocol {
643    Http,
644    Iroh,
645}
646
647#[derive(Debug, Serialize, Deserialize, Clone)]
648pub struct SetMnemonicPayload {
649    pub words: Option<String>,
650}