Skip to main content

devimint/
faucet.rs

1use std::str::FromStr;
2
3use axum::Router;
4use axum::extract::State;
5use axum::http::StatusCode;
6use axum::routing::{get, post};
7use fedimint_gateway_common::V1_API_ENDPOINT;
8use fedimint_ln_server::common::lightning_invoice::Bolt11Invoice;
9use tokio::net::TcpListener;
10use tower_http::cors::CorsLayer;
11
12use crate::federation::Federation;
13use crate::{DevFed, Gatewayd};
14
15#[derive(Clone)]
16pub struct Faucet {
17    gw_ldk: Gatewayd,
18    fed: Federation,
19}
20
21impl Faucet {
22    pub fn new(dev_fed: &DevFed) -> Self {
23        let gw_ldk = dev_fed.gw_ldk.clone();
24        let fed = dev_fed.fed.clone();
25        Faucet { gw_ldk, fed }
26    }
27
28    async fn pay_invoice(&self, invoice: String) -> anyhow::Result<()> {
29        self.gw_ldk
30            .client()
31            .pay_invoice(Bolt11Invoice::from_str(&invoice).expect("Could not parse invoice"))
32            .await?;
33        Ok(())
34    }
35
36    async fn generate_invoice(&self, amount: u64) -> anyhow::Result<String> {
37        Ok(self
38            .gw_ldk
39            .client()
40            .create_invoice(amount)
41            .await?
42            .to_string())
43    }
44
45    fn get_invite_code(&self) -> anyhow::Result<String> {
46        self.fed.invite_code()
47    }
48}
49
50pub async fn run(
51    dev_fed: &DevFed,
52    fauct_bind_addr: String,
53    gw_lnd_port: u16,
54) -> anyhow::Result<()> {
55    let faucet = Faucet::new(dev_fed);
56    let router = Router::new()
57        .route(
58            "/connect-string",
59            get(|State(faucet): State<Faucet>| async move {
60                faucet
61                    .get_invite_code()
62                    .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("{e:?}")))
63            }),
64        )
65        .route(
66            "/pay",
67            post(|State(faucet): State<Faucet>, invoice: String| async move {
68                faucet
69                    .pay_invoice(invoice)
70                    .await
71                    .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("{e:?}")))
72            }),
73        )
74        .route(
75            "/invoice",
76            post(|State(faucet): State<Faucet>, amt: String| async move {
77                let amt = amt
78                    .parse::<u64>()
79                    .map_err(|e| (StatusCode::BAD_REQUEST, format!("{e:?}")))?;
80                faucet
81                    .generate_invoice(amt)
82                    .await
83                    .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("{e:?}")))
84            }),
85        )
86        .route(
87            "/gateway-api",
88            get(move || async move { format!("http://127.0.0.1:{gw_lnd_port}/{V1_API_ENDPOINT}") }),
89        )
90        .layer(CorsLayer::permissive())
91        .with_state(faucet);
92
93    let listener = TcpListener::bind(fauct_bind_addr).await?;
94    axum::serve(listener, router.into_make_service()).await?;
95    Ok(())
96}