- Node.js (v18+)
- Foundry — Install from book.getfoundry.sh:
curl -L https://foundry.paradigm.xyz | bash foundryup - MetaMask — Browser wallet extension
- Sepolia ETH — Get testnet ETH from a faucet (e.g., sepoliafaucet.com, Alchemy, or Infura faucets)
npm installFoundry dependencies (forge-std, openzeppelin-contracts) are already in lib/. If missing, run:
forge install OpenZeppelin/openzeppelin-contracts --no-git
forge install foundry-rs/forge-std --no-gitCopy the example and fill in your keys:
cp .env.example .env# Sepolia testnet RPC
SEPOLIA_RPC_URL=https://rpc.sepolia.org
# Private keys (must have Sepolia ETH)
DEPLOYER_PRIVATE_KEY=0xYOUR_PRIVATE_KEY
RELAYER_PRIVATE_KEY=0xYOUR_PRIVATE_KEY
# Auto-populated after deployment
BATCH_EXECUTOR_ADDRESS=
SAMPLE_TOKEN_ADDRESS=
GAS_SPONSOR_ADDRESS=
RELAYER_ADDRESS=- Go to a Sepolia faucet and request testnet ETH
- You need ETH in the deployer wallet to pay for contract deployment gas
forge buildThis will:
- Compile all contracts in
contracts/with Solidity 0.8.24 - Use optimizer with 1000 runs + viaIR for maximum gas savings
- Target
cancunEVM version (enables transient storage) - Generate artifacts in
out/
npm run deploy:sepoliaOr manually:
forge script script/Deploy.s.sol:DeployScript \
--rpc-url $SEPOLIA_RPC_URL \
--private-key $DEPLOYER_PRIVATE_KEY \
--broadcast --verify
node script/post-deploy.jsNote: The
--verifyflag requires anETHERSCAN_API_KEYenvironment variable. Set it in your.envfile or omit--verifyif not needed.
The deployment will:
- Deploy BatchExecutor contract (minBatchSize = 1)
- Deploy SampleToken with BatchExecutor as the trusted forwarder
- Deploy GasSponsor with Sepolia-appropriate limits
- Whitelist the deployer as relayer in GasSponsor
- Write
deployment.jsonwith all contract addresses - Update
.envwith deployed contract addresses (viapost-deploy.js)
Send ETH to the GasSponsor contract address for gas reimbursement:
cast send $GAS_SPONSOR_ADDRESS --value 0.05ether \
--rpc-url $SEPOLIA_RPC_URL \
--private-key $DEPLOYER_PRIVATE_KEYnpm startThe server runs on http://localhost:3000
- Push your repository to GitHub
- On Render.com, create a new Web Service and connect your repo
- Render auto-detects
render.yamland configures the build/start commands - Set these environment variables in the Render dashboard:
SEPOLIA_RPC_URL— Your Sepolia RPC endpointDEPLOYER_PRIVATE_KEY— Deployer wallet key (needed for the token faucet)RELAYER_PRIVATE_KEY— Relayer wallet private keyBATCH_EXECUTOR_ADDRESS— Fromdeployment.jsonSAMPLE_TOKEN_ADDRESS— Fromdeployment.jsonGAS_SPONSOR_ADDRESS— Fromdeployment.jsonRELAYER_ADDRESS— Relayer wallet address
- Set
CORS_ORIGINto your Render URL (e.g.,https://gas-optimizer.onrender.com)
.
+-- contracts/ # Solidity contracts
| +-- BatchExecutor.sol # Main batch execution contract
| +-- GasSponsor.sol # Gas sponsorship pool
| +-- SampleToken.sol # ERC-20 token for testing
+-- test/ # Foundry test suite
| +-- GasBenchmark.t.sol # 27 tests across 8 categories
+-- script/ # Deployment scripts
| +-- Deploy.s.sol # Foundry Solidity deploy script
| +-- post-deploy.js # Updates .env after deployment
+-- lib/ # Foundry dependencies
| +-- forge-std/ # Foundry standard library
| +-- openzeppelin-contracts/# OpenZeppelin contracts
+-- out/ # Compiled artifacts (generated by forge)
+-- index.html # Frontend dApp interface
+-- server.js # Express server
+-- relayer.js # Relayer logic
+-- signer.js # Offline signer utility
+-- foundry.toml # Foundry configuration
+-- remappings.txt # Import remappings
+-- render.yaml # Render.com deployment config
+-- package.json # Node.js dependencies
+-- deployment.json # Deployed contract addresses (auto-generated)
+-- .env.example # Environment variable template
+-- .env # Environment variables (create this)
After deployment, check deployment.json for:
BatchExecutoraddressSampleTokenaddressGasSponsoraddress
The frontend automatically fetches these addresses from the server via GET /api/config, so no manual editing of index.html is needed.
To verify the config endpoint:
curl http://localhost:3000/api/configDefault limits on Sepolia (set in script/Deploy.s.sol):
- Max per claim: 0.005 ETH
- Daily relayer limit: 0.1 ETH
- Daily user limit: 0.002 ETH per address
- Global daily limit: 0.5 ETH total
For local development (Anvil), limits are 10x higher.
HTML frontend interface
Check server and relayer status
{
"status": "ok",
"relayer": "initialized|not configured",
"timestamp": "2026-02-22T..."
}Get deployed contract addresses and network config
{
"batchExecutorAddress": "0x...",
"sampleTokenAddress": "0x...",
"gasSponsorAddress": "0x...",
"rpcUrl": "https://rpc.sepolia.org",
"chainId": 11155111,
"chainName": "Sepolia",
"blockExplorer": "https://sepolia.etherscan.io"
}Get current batch queue status
Force flush the current queue (admin endpoint)
Get gas usage analytics and batch history
Get on-chain nonce for a user address
Submit a signed request
{
"request": {
"from": "0x...",
"to": "0x...",
"value": "0",
"gas": "100000",
"nonce": "0",
"deadline": "0",
"data": "0x..."
},
"signature": "0x..."
}forge test -vvforge test --gas-report- Open the app (localhost:3000 or your Render URL)
- Connect MetaMask to Sepolia
- Select recipients and set amount
- Sign and submit
# Clear cache and recompile
forge clean
forge build- Verify you have Sepolia ETH in the deployer wallet
- Check
SEPOLIA_RPC_URLis valid and reachable - Ensure
DEPLOYER_PRIVATE_KEYis correct
# Check if port 3000 is in use
netstat -ano | findstr :3000 # Windows
lsof -i :3000 # macOS/Linux- Verify all environment variables in
.envare set - Check contract addresses match deployed contracts
- Ensure
SEPOLIA_RPC_URLis working
IMPORTANT: Never use mainnet private keys with this project.
- Use Sepolia testnet accounts only
- Never commit
.envto git (it's in.gitignore) - Use environment variables on Render.com (marked as secrets)
- The GasSponsor contract owns the sponsorship pool
- Owner can pause claims and withdraw funds
- Foundry Book: https://book.getfoundry.sh/
- Ethers.js Docs: https://docs.ethers.org/v6
- EIP-712 Spec: https://eips.ethereum.org/EIPS/eip-712
- Sepolia Faucet: https://sepoliafaucet.com/
- Render.com Docs: https://docs.render.com/