A full rewrite of the original faucet into a modern, abuse-resistant architecture:
Legacy faucet implementations (single function + static frontend) are easy to drain with scripted wallets and botnets. Modern public faucets now combine:
This repository now follows that model.
The legacy GitHub Pages frontend deployment was removed. This project is now run as a full-stack application (apps/api + apps/web) and deployed via your own hosting/runtime.
apps/web (React + viem)
|
| 1) POST /challenge
| 2) sign challenge with wallet
| 3) POST /ticket
v
apps/api (Fastify + viem signer)
|
| validates: signature, captcha (optional), ip/address cooldown,
| on-chain claim eligibility, faucet balance
| issues EIP-712 ticket (nonce + deadline + signature)
v
contracts/contracts/ModernEthFaucet.sol
|
| verifies ticket signer + nonce uniqueness + cooldown + wallet balance cap
| transfers drip amount and records next claim window
v
recipient wallet
.
├── apps/
│ ├── api/ # Fastify backend issuing claim tickets
│ └── web/ # React frontend
├── contracts/
│ ├── contracts/ # Solidity contracts
│ ├── scripts/ # Deployment scripts
│ └── test/ # Hardhat tests
├── package.json # Workspace scripts
└── tsconfig.base.json
contracts/contracts/ModernEthFaucet.sol
Key protections:
claim(bytes32 nonce, uint256 deadline, bytes signature)
claimSignerusedNonces)nextEligibleAt)maxRecipientBalanceWei)call + nonReentrant for payout safetysetClaimSignersetFaucetConfigpause / unpausewithdrawreceive() and donate() emit DonationReceivedapps/api/src/index.ts
GET /api/v1/healthGET /api/v1/configPOST /api/v1/challenge
POST /api/v1/ticket
nonce, deadline, signature)verifyMessage)nextEligibleAt)apps/web/src/App.tsx
Features:
claim executionnpm install
cp contracts/.env.example contracts/.env
cp apps/api/.env.example apps/api/.env
cp apps/web/.env.example apps/web/.env
Fill all required variables, especially:
contracts/.env
SEPOLIA_RPC_URLDEPLOYER_PRIVATE_KEYFAUCET_OWNERTICKET_SIGNER_ADDRESSapps/api/.env
RPC_URLFAUCET_CONTRACT_ADDRESSTICKET_SIGNER_PRIVATE_KEYapps/web/.env
VITE_API_BASE_URLVITE_FAUCET_ADDRESSnpm run contracts:test
npm run contracts:deploy
The deployment script can seed initial faucet balance via INITIAL_FUNDING_WEI.
npm run dev
contracts/.env| Variable | Description |
|---|---|
SEPOLIA_RPC_URL |
RPC endpoint used for deployment/verification. |
HOLESKY_RPC_URL |
Optional alternate network RPC endpoint. |
DEPLOYER_PRIVATE_KEY |
Deployer wallet private key. |
ETHERSCAN_API_KEY |
Optional contract verification key. |
FAUCET_OWNER |
Owner address for admin controls. |
TICKET_SIGNER_ADDRESS |
Address allowed to sign claim tickets. |
DRIP_AMOUNT_WEI |
Drip amount paid per successful claim. |
COOLDOWN_SECONDS |
Minimum wait between claims per wallet. |
MAX_RECIPIENT_BALANCE_WEI |
Wallet balance ceiling for eligibility. |
INITIAL_FUNDING_WEI |
Optional deploy-time faucet seed amount. |
apps/api/.env| Variable | Description |
|---|---|
HOST / PORT |
API bind address/port. |
WEB_ORIGIN |
Allowed CORS origin for frontend. |
RPC_URL |
RPC endpoint for on-chain checks. |
FAUCET_CHAIN_ID |
Chain ID used in typed-data domain. |
FAUCET_CONTRACT_ADDRESS |
Deployed faucet contract address. |
FAUCET_DOMAIN_NAME / FAUCET_DOMAIN_VERSION |
EIP-712 domain values (must match contract). |
FAUCET_DRIP_WEI |
Expected on-chain drip amount. |
FAUCET_COOLDOWN_SECONDS |
Expected on-chain cooldown. |
FAUCET_MAX_RECIPIENT_BALANCE_WEI |
Eligibility balance cap. |
TICKET_SIGNER_PRIVATE_KEY |
Private key used to sign claim tickets. |
CHALLENGE_TTL_SECONDS |
Signature challenge expiry window. |
TICKET_TTL_SECONDS |
Claim ticket expiry window. |
ADDRESS_COOLDOWN_SECONDS / IP_COOLDOWN_SECONDS |
Local anti-abuse cooldown windows. |
TURNSTILE_SECRET_KEY |
Optional Cloudflare Turnstile secret. |
TURNSTILE_VERIFY_URL |
Turnstile verification endpoint. |
apps/web/.env| Variable | Description |
|---|---|
VITE_API_BASE_URL |
Base URL of faucet API. |
VITE_CHAIN_ID |
Target chain ID for wallet switching. |
VITE_RPC_URL |
Public RPC used for read operations. |
VITE_FAUCET_ADDRESS |
Faucet contract address. |
VITE_EXPLORER_BASE_URL |
Explorer base URL for tx/address links. |
VITE_TURNSTILE_SITE_KEY |
Optional key for frontend Turnstile integration. |
npm run dev
npm run build
npm run test
npm run lint
npm run format
Primary defense layers:
This rewrite follows patterns used by current public faucets and standards docs:
MIT