Project · Gold
Crypto Millions
The alchemist's prize — a lottery that proves it cannot cheat you.
Essence
For the people who play it
Crypto Millions is a decentralized lottery on Ethereum. Pick six unique numbers from one to thirty-nine. Buy a ticket with stablecoin USDC. Each ticket you buy is an NFT — a proper ERC-721 token, transferable, inspectable, yours on chain. Every seven days, Chainlink VRF draws six winning numbers and a Chainlink Keeper triggers the draw automatically. Winners claim their prize themselves; unclaimed prizes roll into the next jackpot.
The point is to remove trust. A traditional lottery asks you to take the operator's word that the draw was honest, the books are right and the prize will be paid. Crypto Millions removes the operator: the contract is the operator, the randomness is provably fair (Chainlink VRF publishes the proof), the prize pool is visible on chain, and the rules cannot be quietly changed behind a closed door.
Three prize tiers reward six, five and four matches. The jackpot grows from 95% of ticket revenue. Tickets are NFTs you actually own — keep them, transfer them, sell them, prove them.
- Pick 6 numbers from 1–39, buy a ticket with stablecoin USDC.
- NFT tickets — every ticket is an ERC-721 with deterministic metadata.
- Chainlink VRF for cryptographically verifiable random draws.
- Chainlink Automation triggers draws every seven days, no admin in the loop.
- Three prize tiers — 6, 5 and 4 matches, with rollover when unclaimed.
- Pull-based prize claims — winners call
claimRewardthemselves. - Promotional "golden tickets" for beta testers, with extended expiry.
Construction
For the engineers
Stack
- Smart contracts
- Solidity ^0.8.4 · Hardhat · viaIR + 200 optimization runs
- Token standards
- ERC-20 (USDC stablecoin) · ERC-721 + ERC-721URIStorage (tickets)
- Randomness
- Chainlink VRF v2.5 via
VRFConsumerBaseV2Upgradeable - Automation
- Chainlink Automation via
AutomationCompatibleInterface - Upgrades
- OpenZeppelin UUPS proxy pattern across all four contracts
- Security
- ReentrancyGuard · SafeERC20 for non-standard token transfer returns
- Frontend
- React 18 · TypeScript · ethers.js v5 · web3-react · Tailwind CSS
- Error tracking
- Sentry with sourcemap injection in production
- Network
- Ethereum Sepolia testnet (chain id 11155111)
Architecture
Three contracts separated by concern. CryptoMillions.sol holds the
core lottery logic — ticket purchase, draw initiation, prize claim.
CryptoMillionsDrawManager.sol isolates draw state, history and VRF request tracking
so the main contract stays focused on user flow. CryptoMillionsNFT.sol mints the
ERC-721 tickets with deterministic URI metadata. A fourth CryptoMillionsUtils
contract holds the pure functions — number generation, match counting, sorting — so they can be
tested in isolation and reused.
UUPS upgradeable everywhere. All four contracts use OpenZeppelin's UUPS proxy pattern. Storage layout follows the compatibility guidelines so upgrades do not corrupt existing tickets or prize pools. The proxy admin is the only point that can authorise an upgrade.
Async randomness with a graceful state machine. When a draw fires the contract
requests randomness from Chainlink VRF and transitions DrawState to
Drawing. When the VRF callback arrives (fulfillRandomWords), the utils
contract generates six winning numbers, the draw manager records them, and the state returns to
Pending for the next round. Players cannot buy tickets during the Drawing window —
no race between purchase and reveal.
Pull-based payouts. Winners call claimReward(ticketId) themselves
rather than the contract pushing prizes out. This is cheaper and safer: one greedy winner cannot
DOS the payout loop, gas spikes are absorbed by the winner, and unclaimed prizes (30-day window)
roll into the next jackpot automatically.
Provider-aware frontend. React contexts split concerns — WalletContext
for connection state, DrawContext for draw history, TicketContext for
the user's tickets, TransactionContext as a transaction-state machine.
ethers.js v5 talks to the contracts, web3-react manages the MetaMask
connector, and Sentry catches any frontend error with sourcemaps injected at build time.
Notable details
- 95 / 5 split — 95% of ticket revenue to the prize pool, 5% to stakeholders.
- Prize tiers — 88% to first prize, 5% to second, 2% to third (configurable in the constructor).
- Gas-optimised mint —
_mint()over_safeMint(), URI generated once before mint. - Configurable batch size (default 100) for processing winners in chunks during draw completion.
- Comprehensive event logging —
TicketPurchased,NumbersDrawn,PrizeClaimed,DrawStateChanged— friendly to off-chain indexing. - Hardhat mocks for local dev —
VRFCoordinatorV2_5Mock,MockERC20,KeeperCompatibleMock. - Promotional / golden tickets capped at 1000 for the beta phase.
- USDC test token deployed on Sepolia at
0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238.