Skip to content

Instantly share code, notes, and snippets.

@jozanza
Last active January 22, 2024 12:48
Show Gist options
  • Save jozanza/1bcc7f853236dcaca9e960eec0ab09cf to your computer and use it in GitHub Desktop.
Save jozanza/1bcc7f853236dcaca9e960eec0ab09cf to your computer and use it in GitHub Desktop.
Use Cloudflare Workers to make a Solana Devnet Faucet.

Solana Devnet Faucet

Here's a quick walkthrough on how to use Cloudflare Workers to make a Solana Devnet Faucet.

Get started by setting up your workspace with a few commands

cargo install wrangler # or npm install -g @cloudflare/wrangler
wrangler generate sol-devnet-faucet https://github.com/cloudflare/rustwasm-worker-template/
cd sol-devnet-faucet

Modify src/lib.rs to handle a POST /airdrop/:pubkey request.

use worker::*;
mod utils;
#[event(fetch)]
pub async fn main(req: Request, env: Env) -> Result<Response> {
  utils::set_panic_hook();
  Router::new().post_async("/airdrop/:pubkey", |_req, ctx| async move {
    if let Some(pubkey) = ctx.param("pubkey") {
      let json = json!({
          "jsonrpc": "2.0",
          "id": 1,
          "method": "requestAirdrop",
          "params": [pubkey, 1000000000]
      });
      let body = wasm_bindgen::JsValue::from_str(&json.to_string());
      let req = Request::new_with_init(
        "https://api.devnet.solana.com",
        RequestInit::default()
          .with_method(Method::Post)
          .with_headers({
            let mut headers = Headers::new();
            headers.set("Content-Type", "application/json; charset=utf-8")?;
            headers
          })
          .with_body(Some(body)),
      )?;
      let res = Fetch::Request(req).send().await?;
      return Ok(res);
    }
    Response::error("Bad Request", 400)
  })
  .run(req, env)
  .await
}

If you want to serve a test HTML page, you can add another route handler:

static HTML: &str = r#"..."#
Router::new()
  .post_async("/airdrop/:pubkey", |_req, ctx| async move { ... })
  .get("/", |_, _| {
    Response::from_html(HTML)
  })
  .run(req, env)
  .await  

You might consider defining HTML as something along these lines

<!DOCTYPE html>
<html>
<head>
    <title>Solana Devnet Faucet</title>
    <style>
    :root {
       --body-bg: #14F195;
       --body-color: #1c1e21;
    }
    @media (prefers-color-scheme: dark) {
       :root {
           --body-bg: #1c1e21;
           --body-color: #14F195;
       }
    }
    body {
        background: var(--body-bg);
        color: var(--body-color);
        font-family: sans-serif;
    }
    fieldset {
        transition: all .3s linear;
    }
    fieldset:disabled {
        opacity: 0.5;
    }
    </style>
</head>
<body>
    <h1>Solana Devnet Faucet</h1>
    <form>
        <fieldset>
            <legend>Enter Your Account Address</legend>
            <input name="addr" type="text" placeholder="Solana Account Address" />
            <input type="submit" value="Request 1 SOL" />
        </fieldset>
        <div><pre><code></code></pre></div>
    </form>
    <script>
        const form = document.querySelector("form");
        const fieldset = form.querySelector("fieldset");
        const result = form.querySelector("code");
        form.addEventListener("submit", async e => {
            try {
                e.preventDefault();
                const data = new FormData(e.target);
                result.innerText = "Submitting request...";
                fieldset.disabled = true;
                const res = await fetch(`/airdrop/${data.get("addr")}`, {
                    method: "POST",
                    headers: { "Content-Type": "application/json" }
                });
                result.innerText = await res.text();
            } catch(err) {
                result.innerText = err.message ?? "Request failed. Try again later.";
            } finally {
                fieldset.disabled = false;
            }
        });
    </script>
</body>
</html>

Solana Devnet Faucet Screenshot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment