fedimint_lnv2_client/
cli.rs1use std::{ffi, iter};
2
3use clap::{Parser, Subcommand};
4use fedimint_api_client::api::FederationError;
5use fedimint_client_module::error::OperationLookupError;
6use fedimint_core::core::OperationId;
7use fedimint_core::util::SafeUrl;
8use fedimint_core::{Amount, PeerId};
9use lightning_invoice::Bolt11Invoice;
10use serde::Serialize;
11use serde_json::Value;
12
13use crate::api::LightningFederationApi;
14use crate::{
15 Bolt11InvoiceDescription, GenerateLnurlError, LightningClientModule, ListGatewaysError,
16 ReceiveError, SelectGatewayError, SendPaymentError,
17};
18
19#[derive(Parser, Serialize)]
20enum Opts {
21 Send {
24 invoice: Bolt11Invoice,
25 #[arg(long)]
26 gateway: Option<SafeUrl>,
27 },
28 AwaitSend { operation_id: OperationId },
30 Receive {
34 amount: Amount,
35 #[arg(long)]
36 gateway: Option<SafeUrl>,
37 },
38 AwaitReceive { operation_id: OperationId },
40 #[command(subcommand)]
42 Lnurl(LnurlOpts),
43 #[command(subcommand)]
45 Gateways(GatewaysOpts),
46}
47
48#[derive(Clone, Subcommand, Serialize)]
49enum LnurlOpts {
50 Generate {
52 recurringd: SafeUrl,
53 #[arg(long)]
54 gateway: Option<SafeUrl>,
55 },
56}
57
58#[derive(Clone, Subcommand, Serialize)]
59enum GatewaysOpts {
60 Map,
64 Select {
66 #[arg(long)]
67 invoice: Option<Bolt11Invoice>,
68 },
69 List {
71 #[arg(long)]
72 peer: Option<PeerId>,
73 },
74 Add { gateway: SafeUrl },
76 Remove { gateway: SafeUrl },
78}
79
80pub(crate) async fn handle_cli_command(
81 lightning: &LightningClientModule,
82 args: &[ffi::OsString],
83) -> Result<serde_json::Value, CliCommandError> {
84 let opts = Opts::parse_from(iter::once(&ffi::OsString::from("lnv2")).chain(args.iter()));
85
86 let value = match opts {
87 Opts::Send { gateway, invoice } => {
88 json(lightning.send(invoice, gateway, Value::Null).await?)
89 }
90 Opts::AwaitSend { operation_id } => json(
91 lightning
92 .await_final_send_operation_state(operation_id)
93 .await?,
94 ),
95 Opts::Receive { amount, gateway } => json(
96 lightning
97 .receive(
98 amount,
99 3600,
100 Bolt11InvoiceDescription::Direct(String::new()),
101 gateway,
102 Value::Null,
103 )
104 .await?,
105 ),
106 Opts::AwaitReceive { operation_id } => json(
107 lightning
108 .await_final_receive_operation_state(operation_id)
109 .await?,
110 ),
111 Opts::Lnurl(lnurl_opts) => match lnurl_opts {
112 LnurlOpts::Generate {
113 recurringd,
114 gateway,
115 } => json(lightning.generate_lnurl(recurringd, gateway).await?),
116 },
117 Opts::Gateways(gateway_opts) => match gateway_opts {
118 #[allow(clippy::unit_arg)]
119 GatewaysOpts::Map => json(lightning.update_gateway_map().await),
120 GatewaysOpts::Select { invoice } => json(lightning.select_gateway(invoice).await?.0),
121 GatewaysOpts::List { peer } => json(lightning.list_gateways(peer).await?),
122 GatewaysOpts::Add { gateway } => {
123 let auth = lightning
124 .admin_auth
125 .clone()
126 .ok_or(CliCommandError::AdminAuthNotSet)?;
127
128 json(lightning.module_api.add_gateway(auth, gateway).await?)
129 }
130 GatewaysOpts::Remove { gateway } => {
131 let auth = lightning
132 .admin_auth
133 .clone()
134 .ok_or(CliCommandError::AdminAuthNotSet)?;
135
136 json(lightning.module_api.remove_gateway(auth, gateway).await?)
137 }
138 },
139 };
140
141 Ok(value)
142}
143
144fn json<T: Serialize>(value: T) -> Value {
145 serde_json::to_value(value).expect("JSON serialization failed")
146}
147
148#[derive(Debug, thiserror::Error)]
150pub(crate) enum CliCommandError {
151 #[error(transparent)]
153 Send(#[from] SendPaymentError),
154
155 #[error(transparent)]
157 OperationLookup(#[from] OperationLookupError),
158
159 #[error(transparent)]
161 Receive(#[from] ReceiveError),
162
163 #[error(transparent)]
165 GenerateLnurl(#[from] GenerateLnurlError),
166
167 #[error(transparent)]
169 SelectGateway(#[from] SelectGatewayError),
170
171 #[error(transparent)]
173 ListGateways(#[from] ListGatewaysError),
174
175 #[error(transparent)]
177 Federation(#[from] FederationError),
178
179 #[error("Admin auth not set")]
182 AdminAuthNotSet,
183}