Skip to main content

fedimint_gateway_client/
lib.rs

1use std::collections::BTreeMap;
2
3use bitcoin::address::NetworkUnchecked;
4use bitcoin::{Address, Txid};
5use fedimint_connectors::ServerResult;
6use fedimint_connectors::error::ServerError;
7use fedimint_core::PeerId;
8use fedimint_core::config::FederationId;
9use fedimint_core::invite_code::InviteCode;
10use fedimint_core::util::SafeUrl;
11use fedimint_gateway_common::{
12    ADDRESS_ENDPOINT, ADDRESS_RECHECK_ENDPOINT, BACKUP_ENDPOINT, BackupPayload,
13    CLOSE_CHANNELS_WITH_PEER_ENDPOINT, CONFIGURATION_ENDPOINT, CONNECT_FED_ENDPOINT,
14    CONNECT_PEER_ENDPOINT, CREATE_BOLT11_INVOICE_FOR_OPERATOR_ENDPOINT,
15    CREATE_BOLT12_OFFER_FOR_OPERATOR_ENDPOINT, ChannelInfo, CloseChannelsWithPeerRequest,
16    CloseChannelsWithPeerResponse, ConfigPayload, ConnectFedPayload, ConnectPeerRequest,
17    CreateInvoiceForOperatorPayload, CreateOfferPayload, CreateOfferResponse,
18    DepositAddressPayload, DepositAddressRecheckPayload, FEDERATION_STATUS_ENDPOINT,
19    FederationInfo, FederationStatusRequest, FederationStatusResponse, GATEWAY_INFO_ENDPOINT,
20    GET_BALANCES_ENDPOINT, GET_INVOICE_ENDPOINT, GET_LN_ONCHAIN_ADDRESS_ENDPOINT, GatewayBalances,
21    GatewayFedConfig, GatewayInfo, GetInvoiceRequest, GetInvoiceResponse, INVITE_CODES_ENDPOINT,
22    LEAVE_FED_ENDPOINT, LIST_CHANNELS_ENDPOINT, LIST_TRANSACTIONS_ENDPOINT, LeaveFedPayload,
23    ListTransactionsPayload, ListTransactionsResponse, MNEMONIC_ENDPOINT, MnemonicResponse,
24    OPEN_CHANNEL_ENDPOINT, OPEN_CHANNEL_WITH_PUSH_ENDPOINT, OpenChannelRequest,
25    PAY_INVOICE_FOR_OPERATOR_ENDPOINT, PAY_OFFER_FOR_OPERATOR_ENDPOINT, PAYMENT_LOG_ENDPOINT,
26    PAYMENT_SUMMARY_ENDPOINT, PEGIN_FROM_ONCHAIN_ENDPOINT, PayInvoiceForOperatorPayload,
27    PayOfferPayload, PayOfferResponse, PaymentLogPayload, PaymentLogResponse,
28    PaymentSummaryPayload, PaymentSummaryResponse, PeginFromOnchainPayload, RECEIVE_ECASH_ENDPOINT,
29    ReceiveEcashPayload, ReceiveEcashResponse, SEND_ONCHAIN_ENDPOINT, SET_CHANNEL_FEES_ENDPOINT,
30    SET_FEES_ENDPOINT, SET_PAYMENT_POLICY_ENDPOINT, SPEND_ECASH_ENDPOINT, STOP_ENDPOINT,
31    SendOnchainRequest, SetChannelFeesRequest, SetFeesPayload, SetMnemonicPayload,
32    SetPaymentPolicyPayload, SpendEcashPayload, SpendEcashResponse, WITHDRAW_ENDPOINT,
33    WITHDRAW_TO_ONCHAIN_ENDPOINT, WithdrawPayload, WithdrawResponse, WithdrawToOnchainPayload,
34};
35use fedimint_ln_common::Method;
36use fedimint_ln_common::client::GatewayApi;
37use lightning_invoice::Bolt11Invoice;
38
39pub async fn get_info(client: &GatewayApi, base_url: &SafeUrl) -> ServerResult<GatewayInfo> {
40    client
41        .request::<(), GatewayInfo>(base_url, Method::GET, GATEWAY_INFO_ENDPOINT, None)
42        .await
43}
44
45/// Queries one federation's public, sanitized gateway capability and
46/// registration status.
47///
48/// Gateway UI, CLI, and server use one release, so the request does not
49/// negotiate endpoint versions. Transport failures, malformed responses, and
50/// non-success statuses remain errors.
51pub async fn get_federation_status(
52    client: &GatewayApi,
53    base_url: &SafeUrl,
54    federation_id: FederationId,
55) -> ServerResult<FederationStatusResponse> {
56    let status = client
57        .request::<_, FederationStatusResponse>(
58            base_url,
59            Method::POST,
60            FEDERATION_STATUS_ENDPOINT,
61            Some(FederationStatusRequest { federation_id }),
62        )
63        .await?;
64    validate_federation_status(status, federation_id)
65}
66
67fn validate_federation_status(
68    status: FederationStatusResponse,
69    federation_id: FederationId,
70) -> ServerResult<FederationStatusResponse> {
71    if status.federation_id() != federation_id {
72        return Err(ServerError::InvalidResponse(
73            "Gateway federation status response names a different federation".to_string(),
74        ));
75    }
76    Ok(status)
77}
78
79pub async fn get_config(
80    client: &GatewayApi,
81    base_url: &SafeUrl,
82    payload: ConfigPayload,
83) -> ServerResult<GatewayFedConfig> {
84    client
85        .request(
86            base_url,
87            Method::POST,
88            CONFIGURATION_ENDPOINT,
89            Some(payload),
90        )
91        .await
92}
93
94pub async fn get_deposit_address(
95    client: &GatewayApi,
96    base_url: &SafeUrl,
97    payload: DepositAddressPayload,
98) -> ServerResult<Address<NetworkUnchecked>> {
99    client
100        .request(base_url, Method::POST, ADDRESS_ENDPOINT, Some(payload))
101        .await
102}
103
104pub async fn pegin_from_onchain(
105    client: &GatewayApi,
106    base_url: &SafeUrl,
107    payload: PeginFromOnchainPayload,
108) -> ServerResult<Txid> {
109    client
110        .request(
111            base_url,
112            Method::POST,
113            PEGIN_FROM_ONCHAIN_ENDPOINT,
114            Some(payload),
115        )
116        .await
117}
118
119pub async fn withdraw(
120    client: &GatewayApi,
121    base_url: &SafeUrl,
122    payload: WithdrawPayload,
123) -> ServerResult<WithdrawResponse> {
124    client
125        .request(base_url, Method::POST, WITHDRAW_ENDPOINT, Some(payload))
126        .await
127}
128
129pub async fn withdraw_to_onchain(
130    client: &GatewayApi,
131    base_url: &SafeUrl,
132    payload: WithdrawToOnchainPayload,
133) -> ServerResult<WithdrawResponse> {
134    client
135        .request(
136            base_url,
137            Method::POST,
138            WITHDRAW_TO_ONCHAIN_ENDPOINT,
139            Some(payload),
140        )
141        .await
142}
143
144pub async fn connect_federation(
145    client: &GatewayApi,
146    base_url: &SafeUrl,
147    payload: ConnectFedPayload,
148) -> ServerResult<FederationInfo> {
149    client
150        .request(base_url, Method::POST, CONNECT_FED_ENDPOINT, Some(payload))
151        .await
152}
153
154pub async fn leave_federation(
155    client: &GatewayApi,
156    base_url: &SafeUrl,
157    payload: LeaveFedPayload,
158) -> ServerResult<FederationInfo> {
159    client
160        .request(base_url, Method::POST, LEAVE_FED_ENDPOINT, Some(payload))
161        .await
162}
163
164pub async fn backup(
165    client: &GatewayApi,
166    base_url: &SafeUrl,
167    payload: BackupPayload,
168) -> ServerResult<()> {
169    client
170        .request(base_url, Method::POST, BACKUP_ENDPOINT, Some(payload))
171        .await
172}
173
174pub async fn set_fees(
175    client: &GatewayApi,
176    base_url: &SafeUrl,
177    payload: SetFeesPayload,
178) -> ServerResult<()> {
179    client
180        .request(base_url, Method::POST, SET_FEES_ENDPOINT, Some(payload))
181        .await
182}
183
184pub async fn set_payment_policy(
185    client: &GatewayApi,
186    base_url: &SafeUrl,
187    payload: SetPaymentPolicyPayload,
188) -> ServerResult<()> {
189    client
190        .request(
191            base_url,
192            Method::POST,
193            SET_PAYMENT_POLICY_ENDPOINT,
194            Some(payload),
195        )
196        .await
197}
198
199pub async fn create_invoice_for_self(
200    client: &GatewayApi,
201    base_url: &SafeUrl,
202    payload: CreateInvoiceForOperatorPayload,
203) -> ServerResult<Bolt11Invoice> {
204    client
205        .request(
206            base_url,
207            Method::POST,
208            CREATE_BOLT11_INVOICE_FOR_OPERATOR_ENDPOINT,
209            Some(payload),
210        )
211        .await
212}
213
214pub async fn pay_invoice(
215    client: &GatewayApi,
216    base_url: &SafeUrl,
217    payload: PayInvoiceForOperatorPayload,
218) -> ServerResult<String> {
219    client
220        .request(
221            base_url,
222            Method::POST,
223            PAY_INVOICE_FOR_OPERATOR_ENDPOINT,
224            Some(payload),
225        )
226        .await
227}
228
229pub async fn get_ln_onchain_address(
230    client: &GatewayApi,
231    base_url: &SafeUrl,
232) -> ServerResult<Address<NetworkUnchecked>> {
233    client
234        .request::<(), Address<NetworkUnchecked>>(
235            base_url,
236            Method::GET,
237            GET_LN_ONCHAIN_ADDRESS_ENDPOINT,
238            None,
239        )
240        .await
241}
242
243pub async fn open_channel(
244    client: &GatewayApi,
245    base_url: &SafeUrl,
246    payload: OpenChannelRequest,
247) -> ServerResult<Txid> {
248    client
249        .request(base_url, Method::POST, OPEN_CHANNEL_ENDPOINT, Some(payload))
250        .await
251}
252
253pub async fn connect_peer(
254    client: &GatewayApi,
255    base_url: &SafeUrl,
256    payload: ConnectPeerRequest,
257) -> ServerResult<()> {
258    client
259        .request(base_url, Method::POST, CONNECT_PEER_ENDPOINT, Some(payload))
260        .await
261}
262
263pub async fn open_channel_with_push(
264    client: &GatewayApi,
265    base_url: &SafeUrl,
266    payload: OpenChannelRequest,
267) -> ServerResult<Txid> {
268    client
269        .request(
270            base_url,
271            Method::POST,
272            OPEN_CHANNEL_WITH_PUSH_ENDPOINT,
273            Some(payload),
274        )
275        .await
276}
277
278pub async fn close_channels_with_peer(
279    client: &GatewayApi,
280    base_url: &SafeUrl,
281    payload: CloseChannelsWithPeerRequest,
282) -> ServerResult<CloseChannelsWithPeerResponse> {
283    client
284        .request(
285            base_url,
286            Method::POST,
287            CLOSE_CHANNELS_WITH_PEER_ENDPOINT,
288            Some(payload),
289        )
290        .await
291}
292
293pub async fn list_channels(
294    client: &GatewayApi,
295    base_url: &SafeUrl,
296) -> ServerResult<Vec<ChannelInfo>> {
297    client
298        .request::<(), Vec<ChannelInfo>>(base_url, Method::GET, LIST_CHANNELS_ENDPOINT, None)
299        .await
300}
301
302pub async fn set_channel_fees(
303    client: &GatewayApi,
304    base_url: &SafeUrl,
305    payload: SetChannelFeesRequest,
306) -> ServerResult<()> {
307    client
308        .request(
309            base_url,
310            Method::POST,
311            SET_CHANNEL_FEES_ENDPOINT,
312            Some(payload),
313        )
314        .await
315}
316
317pub async fn send_onchain(
318    client: &GatewayApi,
319    base_url: &SafeUrl,
320    payload: SendOnchainRequest,
321) -> ServerResult<Txid> {
322    client
323        .request(base_url, Method::POST, SEND_ONCHAIN_ENDPOINT, Some(payload))
324        .await
325}
326
327pub async fn recheck_address(
328    client: &GatewayApi,
329    base_url: &SafeUrl,
330    payload: DepositAddressRecheckPayload,
331) -> ServerResult<serde_json::Value> {
332    client
333        .request(
334            base_url,
335            Method::POST,
336            ADDRESS_RECHECK_ENDPOINT,
337            Some(payload),
338        )
339        .await
340}
341
342pub async fn spend_ecash(
343    client: &GatewayApi,
344    base_url: &SafeUrl,
345    payload: SpendEcashPayload,
346) -> ServerResult<SpendEcashResponse> {
347    client
348        .request(base_url, Method::POST, SPEND_ECASH_ENDPOINT, Some(payload))
349        .await
350}
351
352pub async fn receive_ecash(
353    client: &GatewayApi,
354    base_url: &SafeUrl,
355    payload: ReceiveEcashPayload,
356) -> ServerResult<ReceiveEcashResponse> {
357    client
358        .request(
359            base_url,
360            Method::POST,
361            RECEIVE_ECASH_ENDPOINT,
362            Some(payload),
363        )
364        .await
365}
366
367pub async fn get_balances(
368    client: &GatewayApi,
369    base_url: &SafeUrl,
370) -> ServerResult<GatewayBalances> {
371    client
372        .request::<(), GatewayBalances>(base_url, Method::GET, GET_BALANCES_ENDPOINT, None)
373        .await
374}
375
376pub async fn get_mnemonic(
377    client: &GatewayApi,
378    base_url: &SafeUrl,
379) -> ServerResult<MnemonicResponse> {
380    client
381        .request::<(), MnemonicResponse>(base_url, Method::GET, MNEMONIC_ENDPOINT, None)
382        .await
383}
384
385pub async fn stop(client: &GatewayApi, base_url: &SafeUrl) -> ServerResult<()> {
386    client
387        .request::<(), ()>(base_url, Method::GET, STOP_ENDPOINT, None)
388        .await
389}
390
391pub async fn payment_log(
392    client: &GatewayApi,
393    base_url: &SafeUrl,
394    payload: PaymentLogPayload,
395) -> ServerResult<PaymentLogResponse> {
396    client
397        .request(base_url, Method::POST, PAYMENT_LOG_ENDPOINT, Some(payload))
398        .await
399}
400
401pub async fn payment_summary(
402    client: &GatewayApi,
403    base_url: &SafeUrl,
404    payload: PaymentSummaryPayload,
405) -> ServerResult<PaymentSummaryResponse> {
406    client
407        .request(
408            base_url,
409            Method::POST,
410            PAYMENT_SUMMARY_ENDPOINT,
411            Some(payload),
412        )
413        .await
414}
415
416pub async fn get_invoice(
417    client: &GatewayApi,
418    base_url: &SafeUrl,
419    payload: GetInvoiceRequest,
420) -> ServerResult<Option<GetInvoiceResponse>> {
421    client
422        .request(base_url, Method::POST, GET_INVOICE_ENDPOINT, Some(payload))
423        .await
424}
425
426pub async fn list_transactions(
427    client: &GatewayApi,
428    base_url: &SafeUrl,
429    payload: ListTransactionsPayload,
430) -> ServerResult<ListTransactionsResponse> {
431    client
432        .request(
433            base_url,
434            Method::POST,
435            LIST_TRANSACTIONS_ENDPOINT,
436            Some(payload),
437        )
438        .await
439}
440
441pub async fn create_offer(
442    client: &GatewayApi,
443    base_url: &SafeUrl,
444    payload: CreateOfferPayload,
445) -> ServerResult<CreateOfferResponse> {
446    client
447        .request(
448            base_url,
449            Method::POST,
450            CREATE_BOLT12_OFFER_FOR_OPERATOR_ENDPOINT,
451            Some(payload),
452        )
453        .await
454}
455
456pub async fn pay_offer(
457    client: &GatewayApi,
458    base_url: &SafeUrl,
459    payload: PayOfferPayload,
460) -> ServerResult<PayOfferResponse> {
461    client
462        .request(
463            base_url,
464            Method::POST,
465            PAY_OFFER_FOR_OPERATOR_ENDPOINT,
466            Some(payload),
467        )
468        .await
469}
470
471pub async fn set_mnemonic(
472    client: &GatewayApi,
473    base_url: &SafeUrl,
474    payload: SetMnemonicPayload,
475) -> ServerResult<()> {
476    client
477        .request(base_url, Method::POST, MNEMONIC_ENDPOINT, Some(payload))
478        .await
479}
480
481pub async fn get_invite_codes(
482    client: &GatewayApi,
483    base_url: &SafeUrl,
484) -> ServerResult<BTreeMap<FederationId, BTreeMap<PeerId, (String, InviteCode)>>> {
485    client
486        .request::<(), BTreeMap<FederationId, BTreeMap<PeerId, (String, InviteCode)>>>(
487            base_url,
488            Method::GET,
489            INVITE_CODES_ENDPOINT,
490            None,
491        )
492        .await
493}
494
495#[cfg(test)]
496mod tests;