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>