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_TLS_CERT_ENV, FM_PORT_LDK,
12};
13use fedimint_core::config::{FederationId, JsonClientConfig};
14use fedimint_core::encoding::{Decodable, Encodable};
15use fedimint_core::invite_code::InviteCode;
16use fedimint_core::util::{SafeUrl, get_average, get_median};
17use fedimint_core::{Amount, BitcoinAmountOrAll, secp256k1};
18use fedimint_eventlog::{EventKind, EventLogId, PersistedLogEntry, StructuredPaymentEvents};
19use fedimint_lnv2_common::gateway_api::PaymentFee;
20use fedimint_mint_client::OOBNotes;
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 CLOSE_CHANNELS_WITH_PEER_ENDPOINT: &str = "/close_channels_with_peer";
47pub const PAY_INVOICE_FOR_OPERATOR_ENDPOINT: &str = "/pay_invoice_for_operator";
48pub const PAY_OFFER_FOR_OPERATOR_ENDPOINT: &str = "/pay_offer_for_operator";
49pub const PAYMENT_LOG_ENDPOINT: &str = "/payment_log";
50pub const PAYMENT_SUMMARY_ENDPOINT: &str = "/payment_summary";
51pub const RECEIVE_ECASH_ENDPOINT: &str = "/receive_ecash";
52pub const SET_FEES_ENDPOINT: &str = "/set_fees";
53pub const STOP_ENDPOINT: &str = "/stop";
54pub const SEND_ONCHAIN_ENDPOINT: &str = "/send_onchain";
55pub const SPEND_ECASH_ENDPOINT: &str = "/spend_ecash";
56pub const WITHDRAW_ENDPOINT: &str = "/withdraw";
57
58#[derive(Debug, Serialize, Deserialize, Clone)]
59pub struct ConnectFedPayload {
60 pub invite_code: String,
61 pub use_tor: Option<bool>,
62 pub recover: Option<bool>,
63}
64
65#[derive(Debug, Serialize, Deserialize, Clone)]
66pub struct LeaveFedPayload {
67 pub federation_id: FederationId,
68}
69
70#[derive(Debug, Serialize, Deserialize)]
71pub struct InfoPayload;
72
73#[derive(Debug, Serialize, Deserialize)]
74pub struct BackupPayload {
75 pub federation_id: FederationId,
76}
77
78#[derive(Debug, Serialize, Deserialize, Clone)]
79pub struct ConfigPayload {
80 pub federation_id: Option<FederationId>,
81}
82
83#[derive(Debug, Serialize, Deserialize, Clone)]
84pub struct DepositAddressPayload {
85 pub federation_id: FederationId,
86}
87
88#[derive(Debug, Serialize, Deserialize, Clone)]
89pub struct DepositAddressRecheckPayload {
90 pub address: Address<NetworkUnchecked>,
91 pub federation_id: FederationId,
92}
93
94#[derive(Debug, Serialize, Deserialize, Clone)]
95pub struct WithdrawPayload {
96 pub federation_id: FederationId,
97 pub amount: BitcoinAmountOrAll,
98 pub address: Address<NetworkUnchecked>,
99 #[serde(default)]
102 pub quoted_fees: Option<PegOutFees>,
103}
104
105#[derive(Debug, Serialize, Deserialize, Clone)]
106pub struct WithdrawResponse {
107 pub txid: bitcoin::Txid,
108 pub fees: PegOutFees,
109}
110
111#[derive(Debug, Serialize, Deserialize, Clone)]
112pub struct WithdrawPreviewPayload {
113 pub federation_id: FederationId,
114 pub amount: BitcoinAmountOrAll,
115 pub address: Address<NetworkUnchecked>,
116}
117
118#[derive(Debug, Serialize, Deserialize, Clone)]
119pub struct WithdrawPreviewResponse {
120 pub withdraw_amount: Amount,
121 pub address: String,
122 pub peg_out_fees: PegOutFees,
123 pub total_cost: Amount,
124 #[serde(default)]
126 pub mint_fees: Option<Amount>,
127}
128
129#[allow(deprecated)]
133#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable, Serialize, Deserialize)]
134pub enum ConnectorType {
135 Tcp,
136 Tor,
137}
138
139#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable, Serialize, Deserialize)]
140pub struct FederationConfig {
141 pub invite_code: InviteCode,
142 #[serde(alias = "mint_channel_id")]
145 pub federation_index: u64,
146 pub lightning_fee: PaymentFee,
147 pub transaction_fee: PaymentFee,
148 #[allow(deprecated)] pub _connector: ConnectorType,
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
154pub struct FederationInfo {
155 pub federation_id: FederationId,
156 pub federation_name: Option<String>,
157 pub balance_msat: Amount,
158 pub config: FederationConfig,
159 pub last_backup_time: Option<SystemTime>,
160}
161
162#[derive(Debug, Serialize, Deserialize, PartialEq)]
163pub struct GatewayInfo {
164 pub version_hash: String,
165 pub federations: Vec<FederationInfo>,
166 #[serde(alias = "channels")]
169 pub federation_fake_scids: Option<BTreeMap<u64, FederationId>>,
170 pub gateway_state: String,
171 pub lightning_info: LightningInfo,
172 pub lightning_mode: LightningMode,
173 pub registrations: BTreeMap<RegisteredProtocol, (SafeUrl, secp256k1::PublicKey)>,
174}
175
176#[derive(Debug, Serialize, Deserialize, PartialEq)]
177pub struct GatewayFedConfig {
178 pub federations: BTreeMap<FederationId, JsonClientConfig>,
179}
180
181#[derive(Debug, Serialize, Deserialize, Clone)]
182pub struct SetFeesPayload {
183 pub federation_id: Option<FederationId>,
184 pub lightning_base: Option<Amount>,
185 pub lightning_parts_per_million: Option<u64>,
186 pub transaction_base: Option<Amount>,
187 pub transaction_parts_per_million: Option<u64>,
188}
189
190#[derive(Debug, Serialize, Deserialize, Clone)]
191pub struct CreateInvoiceForOperatorPayload {
192 pub amount_msats: u64,
193 pub expiry_secs: Option<u32>,
194 pub description: Option<String>,
195}
196
197#[derive(Debug, Serialize, Deserialize, Clone)]
198pub struct PayInvoiceForOperatorPayload {
199 pub invoice: Bolt11Invoice,
200}
201
202#[derive(Debug, Serialize, Deserialize, Clone)]
203pub struct SpendEcashPayload {
204 pub federation_id: FederationId,
206 pub amount: Amount,
208}
209
210#[derive(Debug, Serialize, Deserialize, Clone)]
211pub struct SpendEcashResponse {
212 pub notes: OOBNotes,
213}
214
215#[derive(Debug, Serialize, Deserialize, Clone)]
216pub struct ReceiveEcashPayload {
217 pub notes: OOBNotes,
218 #[serde(default)]
219 pub wait: bool,
220}
221
222#[derive(Debug, Serialize, Deserialize, Clone)]
223pub struct ReceiveEcashResponse {
224 pub amount: Amount,
225}
226
227#[derive(serde::Serialize, serde::Deserialize, Clone)]
228pub struct GatewayBalances {
229 pub onchain_balance_sats: u64,
230 pub lightning_balance_msats: u64,
231 pub ecash_balances: Vec<FederationBalanceInfo>,
232 pub inbound_lightning_liquidity_msats: u64,
233}
234
235#[derive(serde::Serialize, serde::Deserialize, Clone)]
236pub struct FederationBalanceInfo {
237 pub federation_id: FederationId,
238 pub ecash_balance_msats: Amount,
239}
240
241#[derive(Debug, Serialize, Deserialize, Clone)]
242pub struct MnemonicResponse {
243 pub mnemonic: Vec<String>,
244
245 pub legacy_federations: Vec<FederationId>,
249}
250
251#[derive(Debug, Serialize, Deserialize, Clone)]
252pub struct PaymentLogPayload {
253 pub end_position: Option<EventLogId>,
256
257 pub pagination_size: usize,
259
260 pub federation_id: FederationId,
261 pub event_kinds: Vec<EventKind>,
262}
263
264#[derive(Debug, Serialize, Deserialize, Clone)]
265pub struct PaymentLogResponse(pub Vec<PersistedLogEntry>);
266
267#[derive(Debug, Serialize, Deserialize, Clone)]
268pub struct PaymentSummaryResponse {
269 pub outgoing: PaymentStats,
270 pub incoming: PaymentStats,
271}
272
273#[derive(Debug, Serialize, Deserialize, Clone)]
274pub struct PaymentStats {
275 pub average_latency: Option<Duration>,
276 pub median_latency: Option<Duration>,
277 pub total_fees: Amount,
278 pub total_success: usize,
279 pub total_failure: usize,
280}
281
282impl PaymentStats {
283 pub fn compute(events: &StructuredPaymentEvents) -> Self {
285 PaymentStats {
286 average_latency: get_average(&events.latencies_usecs).map(Duration::from_micros),
287 median_latency: get_median(&events.latencies_usecs).map(Duration::from_micros),
288 total_fees: Amount::from_msats(events.fees.iter().map(|a| a.msats).sum()),
289 total_success: events.latencies_usecs.len(),
290 total_failure: events.latencies_failure.len(),
291 }
292 }
293}
294
295#[derive(Debug, Serialize, Deserialize, Clone)]
296pub struct PaymentSummaryPayload {
297 pub start_millis: u64,
298 pub end_millis: u64,
299}
300
301#[derive(Serialize, Deserialize, Debug, Clone)]
302pub struct ChannelInfo {
303 pub remote_pubkey: secp256k1::PublicKey,
304 pub channel_size_sats: u64,
305 pub outbound_liquidity_sats: u64,
306 pub inbound_liquidity_sats: u64,
307 pub is_active: bool,
308 pub funding_outpoint: Option<OutPoint>,
309 pub remote_node_alias: Option<String>,
310}
311
312#[derive(Debug, Serialize, Deserialize, Clone)]
313pub struct OpenChannelRequest {
314 pub pubkey: secp256k1::PublicKey,
315 pub host: String,
316 pub channel_size_sats: u64,
317 pub push_amount_sats: u64,
318}
319
320#[derive(Debug, Serialize, Deserialize, Clone)]
321pub struct SendOnchainRequest {
322 pub address: Address<NetworkUnchecked>,
323 pub amount: BitcoinAmountOrAll,
324 pub fee_rate_sats_per_vbyte: u64,
325}
326
327impl fmt::Display for SendOnchainRequest {
328 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
329 write!(
330 f,
331 "SendOnchainRequest {{ address: {}, amount: {}, fee_rate_sats_per_vbyte: {} }}",
332 self.address.assume_checked_ref(),
333 self.amount,
334 self.fee_rate_sats_per_vbyte
335 )
336 }
337}
338
339#[derive(Debug, Serialize, Deserialize, Clone)]
340pub struct CloseChannelsWithPeerRequest {
341 pub pubkey: secp256k1::PublicKey,
342 #[serde(default)]
343 pub force: bool,
344 pub sats_per_vbyte: Option<u64>,
345}
346
347impl fmt::Display for CloseChannelsWithPeerRequest {
348 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
349 write!(
350 f,
351 "CloseChannelsWithPeerRequest {{ pubkey: {}, force: {}, sats_per_vbyte: {} }}",
352 self.pubkey,
353 self.force,
354 match self.sats_per_vbyte {
355 Some(sats) => sats.to_string(),
356 None => "None".to_string(),
357 }
358 )
359 }
360}
361
362#[derive(Debug, Serialize, Deserialize, Clone)]
363pub struct CloseChannelsWithPeerResponse {
364 pub num_channels_closed: u32,
365}
366
367#[derive(Debug, Serialize, Deserialize, Clone)]
368pub struct GetInvoiceRequest {
369 pub payment_hash: sha256::Hash,
370}
371
372#[derive(Debug, Serialize, Deserialize, Clone)]
373pub struct GetInvoiceResponse {
374 pub preimage: Option<String>,
375 pub payment_hash: Option<sha256::Hash>,
376 pub amount: Amount,
377 pub created_at: SystemTime,
378 pub status: PaymentStatus,
379}
380
381#[derive(Debug, Serialize, Deserialize, Clone)]
382pub struct ListTransactionsPayload {
383 pub start_secs: u64,
384 pub end_secs: u64,
385}
386
387#[derive(Debug, Serialize, Deserialize, Clone)]
388pub struct ListTransactionsResponse {
389 pub transactions: Vec<PaymentDetails>,
390}
391
392#[derive(Debug, Serialize, Deserialize, Clone)]
393pub struct PaymentDetails {
394 pub payment_hash: Option<sha256::Hash>,
395 pub preimage: Option<String>,
396 pub payment_kind: PaymentKind,
397 pub amount: Amount,
398 pub direction: PaymentDirection,
399 pub status: PaymentStatus,
400 pub timestamp_secs: u64,
401}
402
403#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
404pub enum PaymentKind {
405 Bolt11,
406 Bolt12Offer,
407 Bolt12Refund,
408 Onchain,
409}
410
411#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
412pub enum PaymentDirection {
413 Outbound,
414 Inbound,
415}
416
417#[derive(Debug, Serialize, Deserialize, Clone)]
418pub struct CreateOfferPayload {
419 pub amount: Option<Amount>,
420 pub description: Option<String>,
421 pub expiry_secs: Option<u32>,
422 pub quantity: Option<u64>,
423}
424
425#[derive(Debug, Serialize, Deserialize, Clone)]
426pub struct CreateOfferResponse {
427 pub offer: String,
428}
429
430#[derive(Debug, Serialize, Deserialize, Clone)]
431pub struct PayOfferPayload {
432 pub offer: String,
433 pub amount: Option<Amount>,
434 pub quantity: Option<u64>,
435 pub payer_note: Option<String>,
436}
437
438#[derive(Debug, Serialize, Deserialize, Clone)]
439pub struct PayOfferResponse {
440 pub preimage: String,
441}
442
443#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
444pub enum PaymentStatus {
445 Pending,
446 Succeeded,
447 Failed,
448}
449
450#[derive(Debug, Clone, Subcommand, Serialize, Deserialize, Eq, PartialEq)]
451pub enum LightningMode {
452 #[clap(name = "lnd")]
453 Lnd {
454 #[arg(long = "lnd-rpc-host", env = FM_LND_RPC_ADDR_ENV)]
456 lnd_rpc_addr: String,
457
458 #[arg(long = "lnd-tls-cert", env = FM_LND_TLS_CERT_ENV)]
460 lnd_tls_cert: String,
461
462 #[arg(long = "lnd-macaroon", env = FM_LND_MACAROON_ENV)]
464 lnd_macaroon: String,
465 },
466 #[clap(name = "ldk")]
467 Ldk {
468 #[arg(long = "ldk-lightning-port", env = FM_PORT_LDK)]
470 lightning_port: u16,
471
472 #[arg(long = "ldk-alias", env = FM_LDK_ALIAS_ENV)]
474 alias: String,
475 },
476}
477
478#[derive(Clone)]
479pub enum ChainSource {
480 Bitcoind {
481 username: String,
482 password: String,
483 server_url: SafeUrl,
484 },
485 Esplora {
486 server_url: SafeUrl,
487 },
488}
489
490impl fmt::Display for ChainSource {
491 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
492 match self {
493 ChainSource::Bitcoind {
494 username: _,
495 password: _,
496 server_url,
497 } => {
498 write!(f, "Bitcoind source with URL: {server_url}")
499 }
500 ChainSource::Esplora { server_url } => {
501 write!(f, "Esplora source with URL: {server_url}")
502 }
503 }
504 }
505}
506
507#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
508#[serde(rename_all = "snake_case")]
509pub enum LightningInfo {
510 Connected {
511 public_key: PublicKey,
512 alias: String,
513 network: Network,
514 block_height: u64,
515 synced_to_chain: bool,
516 },
517 NotConnected,
518}
519
520#[derive(
521 Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Encodable, Decodable, Serialize, Deserialize,
522)]
523#[serde(rename_all = "snake_case")]
524pub enum RegisteredProtocol {
525 Http,
526 Iroh,
527}
528
529#[derive(Debug, Serialize, Deserialize, Clone)]
530pub struct SetMnemonicPayload {
531 pub words: Option<String>,
532}