A decentralized raffle system with verifiable randomness via Chainlink VRF. Fully transparent lifecycle and automatic on-chain payouts.
Live Frontend: https://raffle-dapp-five.vercel.app/
- Solidity ^0.8.24
- React 18
- TypeScript 5
- Wagmi/Viem
- Tailwind
- Foundry
- Network: Ethereum Sepolia ·
- Chainlink VRF v2 (Subscription 12478)
- Anyone can create a public raffle by setting a ticket price.
- Users buy tickets while the raffle is open.
- The creator closes the raffle to trigger a provably fair winner selection via VRF.
- The winner is paid automatically on-chain.
├── LotteryFactoryV7.sol # Deploys raffles and orchestrates VRF
├── LotteryOpenV4.sol # Individual raffle instances
├── VRFManager.sol # Chainlink VRF subscription/requests
└── EmergencyWithdraw.sol # Controlled recovery escape hatches
-
Wagmi/Viem for on-chain calls
-
Custom hooks for raffle state and events
-
Tailwind UI with state filters and pagination
-
Chainlink VRF v2 with ~100k gas callback budget
-
Factory pattern for consistent, cheaper deployments
-
Addresses & Deployment
-
Network: Sepolia
-
Factory: 0x9dc217a2b06d55e1E3C913D0597bE3847Ab373CE
-
VRF Subscription: 12478
-
VRF Coordinator / Key Hash: configured in VRFManager.sol per network
Acceptance Criteria (Given/When/Then):
Given my wallet is connected, When I set a valid ticket price and confirm, Then the Factory deploys a new raffle in Open state.
Given a raffle is Open, When I close it, Then it moves to Drawing and requests randomness from VRF.
Given a raffle is Open, When I call buyTicket() with msg.value == ticketPrice, Then I’m registered as a participant and the transaction is visible on the explorer.
Given I send an incorrect amount, When I try to buy, Then the transaction reverts with a clear error.
Given a raffle in Drawing, When the VRF callback arrives, Then the contract deterministically picks one winner on-chain and sets the raffle to Completed.
Given I’m selected, When the callback executes, Then the pot is transferred to me in the same transaction (no manual steps).
Given an external issue (e.g., unfunded VRF subscription), When I use an allowed emergency function, Then I can recover stuck funds under strict access control.
Given any raffle, When I inspect events and state, Then I can verify participants, state transitions, VRF requestId, and the final winner.
-
Create Raffle → Connect wallet → Set ticket price → Factory.createLottery() → new LotteryOpenV4 instance.
-
Participate Player calls buyTicket() with exact ticketPrice.
-
Close & Draw Creator calls close() → VRFManager.requestRandomWords() → state Drawing.
-
VRF Callback Coordinator calls fulfillRandomWords() → compute winning index → auto-payout → state Completed.
-
Key Features
-
Provably Fair Randomness: Chainlink VRF v2 with cryptographic proofs.
-
Automatic Payouts: Winner receives the pot in the VRF callback.
-
Factory Pattern: Lower deployment cost for new raffles.
-
Productive UI: Filters by state (All/Open/Drawing/Completed/Cancelled), pagination, recent-first ordering.
-
Wallet Compatibility: MetaMask and WalletConnect via Wagmi.
-
Node.js 18+
-
Foundry (forge + cast)
-
Wallet (MetaMask or compatible)
cd loteria-frontend
npm install
npm run dev
cd contracts
forge install
forge build
forge test
forge script script/DeployV5.s.sol --rpc-url $SEPOLIA_RPC --broadcast --verify
Create an .env for Foundry and the frontend:
PRIVATE_KEY=0x... # test-only deploy key
SEPOLIA_RPC=https://...
ETHERSCAN_API_KEY=...
VITE_FACTORY_ADDRESS=0x9dc217a2b06d55e1E3C913D0597bE3847Ab373CE
VITE_NETWORK_ID=11155111
VITE_VRF_SUBSCRIPTION_ID=12478
-
Access Control: only the raffle creator can close/cancel that raffle.
-
Strict State Guards: transitions enforced (Open → Drawing → Completed/Cancelled).
-
Reentrancy Protection: applied to payment-sensitive paths.
-
VRF Isolation: VRFManager separates subscription + callbacks.
-
Emergency Functions: constrained and event-logged for auditability.
-
Threat Model (high-level):
-
Malicious inputs → explicit validation with informative require messages.
-
VRF liveness → monitor LINK funding and operational alerts.
-
Fund lockups → defined recovery paths gated by roles (optional timelocks).
-
Unit Tests (Foundry):
-
Factory deploy + raffle creation
-
Ticket purchase + validation
-
VRF integration (mock) + winner selection
-
Emergency paths + invalid states
-
Gas checks for callback budget
forge test -vvv
-
Single winner per raffle
-
Manual closing by the creator
-
Requires funded VRF subscription (LINK)
-
Private raffles with whitelists
-
Multiple winners / prize tiers
-
Time-boxed raffles (auto-close by deadline)
-
Multi-chain support (Base, Abstract, Ronin EVM)
-
Analytics dashboard (history, participation KPIs)
- License: MIT
- Author: @pimmpi_ — GitHub
-
Chainlink VRF v2: https://docs.chain.link/vrf/v2/introduction
-
Foundry Book: https://book.getfoundry.sh/
-
Wagmi: https://wagmi.sh/
-
Tailwind CSS: https://tailwindcss.com/
-
Extras (optional but recruiter-friendly)
-
.env.example
PRIVATE_KEY=
SEPOLIA_RPC=
ETHERSCAN_API_KEY=
VITE_FACTORY_ADDRESS=
VITE_NETWORK_ID=11155111
VITE_VRF_SUBSCRIPTION_ID=
- Fork the repo and create a feature branch.
- Keep PRs small and focused (tests required for contracts).
- Explain the rationale, gas impact, and UX impact.
- Avoid committing secrets; use
.envand.env.example.
Built for the web3 community