Keyrxng

Web3 Faucet Service

Ubiquity · 2023 · Gasless onboarding solution using OpenZeppelin Defender Relay in Cloudflare Workers with abuse-resistant verification. Fully functional but never deployed to production due to shifting priorities.

So what?
Complete functional but unused development status · ~$1/10000 user subsidies cost efficiency · Never deployed to production deployment status
Role
Product Engineer
Year
2023
Stack
Cloudflare Workers, OpenZeppelin Defender Relay, Supabase, TypeScript, Viem, Custom fetch patches
Read narrative
Complete functional but unused development status~$1/10000 user subsidies cost efficiencyNever deployed to production deployment status

Problem

New bounty hunters completing their first task at Ubiquity DAO encountered “not enough gas to claim” errors when attempting to redeem payment permits on Gnosis Chain. This critical onboarding barrier prevented crypto-inexperienced developers from accessing earned rewards, effectively blocking legitimate contributors at the moment of first success.

Approach

System diagram

flowchart LR
  Client[Client] --> POST[POST /faucet?address]
  POST --> Worker[Cloudflare Worker]
  Worker --> DB[Database Validation]
  DB --> RelayBal[Relay Balance Check]
  RelayBal --> Transfer[Gas Transfer Transaction]
  Transfer --> Response[Response with TX Hash]

Outcome

Constraints

Design choices

Proof

Code excerpt — core transaction flow

const userBal = await relayer.call("eth_getBalance", [ethAddress, "latest"]);
if (userBal.result > env.CLAIM_FEE) {
  return makeRpcResponse({ error: { code: -32000, message: "Hunter has enough gas" } }, 400);
}

const relay_ = await relayer.getRelayer();
const relayBal = await relayer.call("eth_getBalance", [relay_.address, "latest"]);
if (relayBal.result < env.CLAIM_FEE) {
  return makeRpcResponse({ error: { code: -32000, message: "Faucet has no funds" } }, 400);
}

const tx = await relayer.sendTransaction({
  to: ethAddress,
  value: toHex(env.CLAIM_FEE),
  speed: "fast",
  gasLimit: 21000,
});

Code excerpt — database validation

const { data } = await supabase.from("wallets").select("address").eq("address", ethAddress);
if (!data || data.length === 0) {
  return makeRpcResponse({ error: { code: -32000, message: "Address not found" } }, 400);
}

const { data: permits } = await supabase
  .from("permits")
  .select("users(wallets(address))")
  .not("users", "is", null)
  .eq("users.wallets.address", ethAddress);
if (permits && permits.length > 0) {
  return makeRpcResponse({ error: { code: -32000, message: "Has likely been subsidized before." } }, 400);
}

QA evidence — comprehensive scenario testing

# Already subsidized user (has permit)
res { jsonrpc: '2.0', error: { code: -32000, message: 'Has likely been subsidized before.' }, id: null }

# New user eligible for subsidy
res { jsonrpc: '2.0', result: { txHash: '0x3ca25068a7c59a190df4fb270e4e7348e46ec0b9e4b16f22ad19105b15c0dc58' }, id: null }

# User already has sufficient gas
res { jsonrpc: '2.0', error: { code: -32000, message: 'Hunter has enough gas' }, id: null }

# Verified transaction on-chain
https://mumbai.polygonscan.com/tx/0xfd938289d028e65be9ba38d34532d25c68569c7beb87034a3e0708f7f70d7f48

Deployment configuration — Cloudflare Workers

name = "ubq-gas-faucet"
main = "src/index.ts"
compatibility_date = "2023-02-01"
node_compat = true

# Custom patches for OpenZeppelin Defender compatibility
[build]
command = "npm run build"

# Environment variables for relay configuration
[env.production.vars]
CLAIM_FEE = "1000000000000000"  # 0.001 xDAI
SUPABASE_URL = "https://your-project.supabase.co"

Implementation status — Complete but unused

- Development: ✅ Complete
- Testing: ✅ Comprehensive scenario coverage
- Security: ✅ Zero private key custody
- Deployment: ✅ Production-ready
- Production use: ❌ Never deployed
- Reason: Shifting organizational priorities

References