1use bitcoin::hashes::sha256;
2use chrono::{DateTime, Utc};
3use clap::Subcommand;
4use fedimint_core::Amount;
5use fedimint_gateway_client::{
6 close_channels_with_peer, create_invoice_for_self, create_offer, get_invoice, list_channels,
7 list_transactions, open_channel, pay_invoice, pay_offer,
8};
9use fedimint_gateway_common::{
10 CloseChannelsWithPeerRequest, CreateInvoiceForOperatorPayload, CreateOfferPayload,
11 GetInvoiceRequest, ListTransactionsPayload, OpenChannelRequest, PayInvoiceForOperatorPayload,
12 PayOfferPayload,
13};
14use fedimint_ln_common::client::GatewayRpcClient;
15use lightning_invoice::Bolt11Invoice;
16
17use crate::print_response;
18
19#[derive(Subcommand)]
23pub enum LightningCommands {
24 CreateInvoice {
26 amount_msats: u64,
27
28 #[clap(long)]
29 expiry_secs: Option<u32>,
30
31 #[clap(long)]
32 description: Option<String>,
33 },
34 PayInvoice { invoice: Bolt11Invoice },
36 OpenChannel {
38 #[clap(long)]
40 pubkey: bitcoin::secp256k1::PublicKey,
41
42 #[clap(long)]
43 host: String,
44
45 #[clap(long)]
47 channel_size_sats: u64,
48
49 #[clap(long)]
51 push_amount_sats: Option<u64>,
52 },
53 CloseChannelsWithPeer {
56 #[clap(long)]
58 pubkey: bitcoin::secp256k1::PublicKey,
59
60 #[clap(long)]
61 force: bool,
62 },
63 ListChannels,
65 ListTransactions {
68 #[arg(long, value_parser = parse_datetime)]
71 start_time: DateTime<Utc>,
72
73 #[arg(long, value_parser = parse_datetime)]
76 end_time: DateTime<Utc>,
77 },
78 GetInvoice {
80 #[clap(long)]
82 payment_hash: sha256::Hash,
83 },
84 CreateOffer {
85 #[clap(long)]
86 amount_msat: Option<u64>,
87
88 #[clap(long)]
89 description: Option<String>,
90
91 #[clap(long)]
92 expiry_secs: Option<u32>,
93
94 #[clap(long)]
95 quantity: Option<u64>,
96 },
97 PayOffer {
98 #[clap(long)]
99 offer: String,
100
101 #[clap(long)]
102 amount_msat: Option<u64>,
103
104 #[clap(long)]
105 quantity: Option<u64>,
106
107 #[clap(long)]
108 payer_note: Option<String>,
109 },
110}
111
112fn parse_datetime(s: &str) -> Result<DateTime<Utc>, chrono::ParseError> {
113 s.parse::<DateTime<Utc>>()
114}
115
116impl LightningCommands {
117 #![allow(clippy::too_many_lines)]
118 pub async fn handle(self, client: &GatewayRpcClient) -> anyhow::Result<()> {
119 match self {
120 Self::CreateInvoice {
121 amount_msats,
122 expiry_secs,
123 description,
124 } => {
125 let response = create_invoice_for_self(
126 client,
127 CreateInvoiceForOperatorPayload {
128 amount_msats,
129 expiry_secs,
130 description,
131 },
132 )
133 .await?;
134 println!("{response}");
135 }
136 Self::PayInvoice { invoice } => {
137 let response =
138 pay_invoice(client, PayInvoiceForOperatorPayload { invoice }).await?;
139 println!("{response}");
140 }
141 Self::OpenChannel {
142 pubkey,
143 host,
144 channel_size_sats,
145 push_amount_sats,
146 } => {
147 let funding_txid = open_channel(
148 client,
149 OpenChannelRequest {
150 pubkey,
151 host,
152 channel_size_sats,
153 push_amount_sats: push_amount_sats.unwrap_or(0),
154 },
155 )
156 .await?;
157 println!("{funding_txid}");
158 }
159 Self::CloseChannelsWithPeer { pubkey, force } => {
160 let response = close_channels_with_peer(
161 client,
162 CloseChannelsWithPeerRequest { pubkey, force },
163 )
164 .await?;
165 print_response(response);
166 }
167 Self::ListChannels => {
168 let response = list_channels(client).await?;
169 print_response(response);
170 }
171 Self::GetInvoice { payment_hash } => {
172 let response = get_invoice(client, GetInvoiceRequest { payment_hash }).await?;
173 print_response(response);
174 }
175 Self::ListTransactions {
176 start_time,
177 end_time,
178 } => {
179 let start_secs = start_time.timestamp().try_into()?;
180 let end_secs = end_time.timestamp().try_into()?;
181 let response = list_transactions(
182 client,
183 ListTransactionsPayload {
184 start_secs,
185 end_secs,
186 },
187 )
188 .await?;
189 print_response(response);
190 }
191 Self::CreateOffer {
192 amount_msat,
193 description,
194 expiry_secs,
195 quantity,
196 } => {
197 let response = create_offer(
198 client,
199 CreateOfferPayload {
200 amount: amount_msat.map(Amount::from_msats),
201 description,
202 expiry_secs,
203 quantity,
204 },
205 )
206 .await?;
207 print_response(response);
208 }
209 Self::PayOffer {
210 offer,
211 amount_msat,
212 quantity,
213 payer_note,
214 } => {
215 let response = pay_offer(
216 client,
217 PayOfferPayload {
218 offer,
219 amount: amount_msat.map(Amount::from_msats),
220 quantity,
221 payer_note,
222 },
223 )
224 .await?;
225 print_response(response);
226 }
227 }
228
229 Ok(())
230 }
231}