Skip to main content

miden_node_rpc/
funding.rs

1use std::num::NonZeroU64;
2use std::time::Duration;
3
4use anyhow::Context;
5use miden_node_tracing::miden_instrument;
6use miden_protocol::account::AccountId;
7use reqwest::Client;
8use serde::Serialize;
9use url::Url;
10
11use crate::COMPONENT;
12
13/// Requests public P2ID notes from the funding service.
14#[derive(Clone)]
15pub struct FundingClient {
16    http: Client,
17    url: Url,
18    amount: NonZeroU64,
19}
20
21impl FundingClient {
22    pub fn new(mut url: Url, amount: NonZeroU64) -> anyhow::Result<Self> {
23        anyhow::ensure!(
24            matches!(url.scheme(), "http" | "https") && url.host_str().is_some(),
25            "funding service URL must use HTTP or HTTPS"
26        );
27        anyhow::ensure!(
28            url.query().is_none() && url.fragment().is_none(),
29            "funding service URL must not contain a query or fragment"
30        );
31        url.path_segments_mut()
32            .map_err(|()| anyhow::anyhow!("funding service URL must be a base URL"))?
33            .pop_if_empty()
34            .push("request-funds");
35        let http = Client::builder()
36            .connect_timeout(Duration::from_secs(5))
37            .timeout(Duration::from_secs(120))
38            .build()
39            .context("failed to create funding service client")?;
40        Ok(Self { http, url, amount })
41    }
42
43    /// Requests the configured amount and waits for the funding service to queue the note. This
44    /// method does not retry failed requests.
45    #[miden_instrument(
46        target = COMPONENT,
47        name = "account.funding",
48        fields(account.id = account_id, asset.amount = self.amount.get()),
49        err,
50    )]
51    pub async fn fund(&self, account_id: AccountId) -> anyhow::Result<()> {
52        self.http
53            .post(self.url.clone())
54            .json(&FundingRequest {
55                account_id: account_id.to_hex(),
56                amount: self.amount.get(),
57            })
58            .send()
59            .await
60            .context("failed to send account funding request")?
61            .error_for_status()
62            .context("funding service rejected account funding request")?
63            // Clients retrieve the public note through the account's note tag.
64            .bytes()
65            .await
66            .context("failed to read account funding response")?;
67        Ok(())
68    }
69}
70
71#[derive(Serialize)]
72struct FundingRequest {
73    account_id: String,
74    amount: u64,
75}