A decentralized raffle application built with Solidity and Foundry, utilizing Chainlink VRF for provably fair randomness and automated winner selection. Contract for Creating and managing a raffle where participants enter by sending ETH, and a winner is randomly selected.
The project is a decentralized Raffle/Lottery system which :
- Allows Users to enter a raffle , after paying an entrance fee.
- Winners are selected at random using Chainlink VRF (Verifiable Random Function).
- A Winner selection is automated using Chainlink Automation (which automatically triggers the draw after a set time or a certain number of participants).
- The entire prize pool is automatically sent to the winner address, immediately upon drawing the winner, ensuring guaranteed payouts.
- Decentralized Entry Mechanism: Allows participants to buy tickets securely by sending the correct native cryptocurrency (e.g., ETH) or tokens to the contract, eliminating third-party processors.
- Provably Fair Randomness (Chainlink VRF): Utilizes Verifiable Random Functions (VRF) to generate a secure, transparent, and unpredictable random number to select winners, ensuring the drawing is not manipulated.
- Automated Execution (Chainlink Automation): Chainlink Automation triggers winner selection based on specific triggers at predetermined intervals. Uses automated, time-based triggers to close the raffle and initiate the winner selection process without requiring human intervention.
- Automated Prize Distribution: Automatically sends the accumulated prize pool to the winning address immediately upon drawing the winner, ensuring guaranteed payouts.
- Raffle State Management: Define distinct states, such as
OPEN,CALCULATING(drawing), andCLOSED, to prevent entries after the deadline and manage the lifecycle of the raffle. - Custom Solidity errors: gas-efficient custom errors replace
requirestring messages . - Programmatic subscription management: Foundry interaction scripts automate creating, funding, and registering the VRF subscription, so the full setup is reproducible in one command.
- Gas Optimization: Developed with smart contract best practices for gas efficiency, to reduce cost.
- Transparency & Auditability: All participants, ticket purchases, and winner selections are recorded on a public ledger, allowing anyone to verify the results.
- Event Logging: Emits events for key actions (e.g., WinnerPicked) that allow interfaces to display real-time updates to users on the blockchain.
- Intensive Testing: Includes several testing mechanism. They include: unit tests, integration tests, and fork tests.
- Blockchain Transparency: Every transaction and role assignment is recorded on the Ethereum blockchain for transparency.
- Multi-Network Support: Deployable on localhost (Anvil) and Sepolia Eth testnet.
- Time-Based Draws: Uses a configurable time interval between raffles.
- Security Checks: Validates conditions before performing sensitive operations.
- Solidity : The programming language for writing the Smart contracts.
- Foundry : Development framework and testing suite.
- Chainlink Automation : Decentralized, secure, and cost-efficient Web3 services network.
- Chainlink VRF V2.5 : Tamper-proof random number generator (RNG).
- Clone the repository:
git clone https://github.com/legendarycode3/raffle-smart-contract
cd raffle-smart-contract - Install dependencies:
make install
- Build the project:
make build # or forge build
- Configure your
.envfile:SEPOLIA_RPC_URL=your_sepolia_rpc_url ETHERSCAN_SEPOLIA_API_KEY=your_etherscan_api_key
- Get testnet ETH & LINK:
- ETH Sepolia Faucet: Eth Sepolia Faucet .
- Link testnet faucets: Link Testnet Faucet
- Fund Chainlink VRF Subscription:
- Visit VRF Chain Link with your SepoliaEth obtained (always use burner wallet).
- Create a subscription and note the Subscription ID.
- Fund it with testnet LINK tokens.
- Configure Makefile:
- Change account name in Makefile to the name of your desired encrypted key.
- change "--account defaultKey" to "--account <YOUR_ENCRYPTED_KEY_NAME>" (if tryintg to deploy on
sepolia testnet networkrather thananvil) - check encrypted key names stored locally with:
- change "--account defaultKey" to "--account <YOUR_ENCRYPTED_KEY_NAME>" (if tryintg to deploy on
cast wallet list- If no encrypted keys found
- Encrypt private key to be used securely within foundry:
cast wallet import <account_name> --interactive- Never commit your
.envfile. - Never use your mainnet private key for testing.
- Use a separate wallet with only testnet funds.
Compile the smart contracts:
make buildDeploy to Sepolia testnet:
make deploy-sepoliaRun all tests:
make testOr
forge testRun tests with verbosity:
make test -vvvRun specific test:
forge test --mt testFunctionNameTest coverage:
make coverage forge fmtYou can estimate how much gas things cost by running:
forge snapshot cast <subcommand> forge --help
anvil --help
cast --helpExample interactions using cast (Foundry's CLI tool):
- Enter the raffle by sending entrance fee:
cast send <RAFFLE_ADDRESS> "enterRaffle()" --value 0.01ether --rpc-url $SEPOLIA_RPC_URL --account yourEncryptedAccount
- Check raffle state (0=OPEN, 1=CALCULATING):
cast call <RAFFLE_ADDRESS> "getRaffleState()" --rpc-url $SEPOLIA_RPC_URL
- Check raffle entrance fee:
cast call <RAFFLE_ADDRESS> "getEntranceFee()" --rpc-url $SEPOLIA_RPC_URL
- Get recent winner:
cast call <RAFFLE_ADDRESS> "getRecentWinner()" --rpc-url $SEPOLIA_RPC_URL
- Check if automation should trigger:
cast call <RAFFLE_ADDRESS> "checkUpkeep(0x)" --rpc-url $SEPOLIA_RPC_URL
- Get any player on a specific index:
cast call <RAFFLE_ADDRESS> "getPlayer(0)" --rpc-url $SEPOLIA_RPC_URL
Or Interact with Raffle contract deployed on sepolia testnet:
Directly (from Sepolia Etherscan
- Entry Raffle(Phase): Players can send the required entrance fee to the contract to join the current raffle round, by calling
enterRaffle(). The players addresses are stored in an array. What happens here:- Checks if a player entry fee is enough.
- Stores player address.
- Adds ETH to contract balance.
- Upkeep Check(Automated): Chainlink Automation detects that if the raffle is ready to pick a winner or not, based on the following checks:
- Raffle entry is in
OPENstate. - Enough time has passed.
- Contract holds sufficient ETH(atleast 1 to 2 players).
- Subscription funded(LINK deposit successful).
- Raffle entry is in
- Winner Selection: When conditions are satisfied, the function
performUpkeep()is called:- Raffle status updated to
CALCULATING. - Random number request initiated (from Chainlink VRF).
- Raffle status updated to
- Fulfillment: The Chainlink VRF automatically calls the
fulfillRandomWords():- Random number is received
- Random winner calculated via modulo.
- Prize awarded to winner and transfered.
- Raffle Resets for Next Round:
After payout:- Players array is cleared.
- Timestamp is reset.
- State goes back to OPEN.
Constructor Parameters:
| Parameter | Description |
|---|---|
_subscriptionId |
Chainlink VRF v2.5 subscription ID |
gasLane |
KeyHash to define the max gas price for VRF |
interval |
Time (in seconds) between raffle draws |
enteranceFee |
Amount of ETH required to enter the raffle |
callbackGasLimit |
Max gas to use for fulfillRandomWords() callback |
vrfCoordinator |
Address of the Chainlink VRF coordinator for your network |
constructor(...): Initializes the raffle configuration and Chainlink VRF setup. Sets theentrance fee,raffle interval,VRF coordinator details,subscription ID,callback gas limit,initializes the timestamp, and opens the raffle.enterRaffle(): Enter the raffle after paying the entrance feecheckUpkeep(): Ensures to Check if raffle conditions are met for winner selectionperformUpkeep(): Initiate / Triggers the winner selection processfulfillRandomWords(): Randomness request fulfillment function. Callback function for Chainlink VRF
getEntranceFee(): Returns the minimum ETH required to enter the raffle.getRaffleState(): Returns the current state of the raffle (e.g., OPEN, CALCULATING).getPlayer(uint256): Returns the address of a player given their index.getLastTimeStamp(): Returns the timestamp of the last recorded action.getRecentWinner(): Returns the address of the winner from the previous round.
REQUEST_CONFIRMATIONS: How many confirmations the Chainlink node should wait before responding.NUM_WORDS: The number of words/slots to request from the oracle.i_entranceFee: The minimum amount of ETH required to enter the raffle.i_interval: Time interval set at deployment (e.g., in seconds).i_keyHash: The key hash for the Chainlink VRF Coordinator, identifying the gas lane.i_subscriptionId: Subscription ID assigned by Chainlink/external service at deploymen.i_callbackGasLimit: The maximum gas allowed for the callback function (e.g., fulfillRandomWords).s_players: Array of players allowed to receive ETH, managed privately.
Create a .env file with the following variables:
SEPOLIA_RPC_URL=your_sepolia_rpc_url
ETHERSCAN_SEPOLIA_API_KEY=your_etherscan_api_key├── script # deployment, configuration, and interaction scripts directory
│ ├── DeployRaffle.s.sol # Main deployment script
│ ├── HelperConfig.s.sol # Network configuration script
│ └── Interaction.s.sol # Chainlink interaction scripts (VRF subscription management)
├── src # Smart contracts directory
│ └── Raffle.sol # Smart contracts main source code(raffle contract logic)
└── test # Test files directory
│ ├── integration
│ │ └── IntegrationTest.t.sol # Integration tests
│ ├── mocks # development/testing contract directory
│ │ └── LinkToken.sol # dummy testing contract designed to simulate the real Chainlink LINK token on a local blockchain when test with mock
│ └── unit
│ └── RaffleTest.t.sol # Unit tests
├── lib
│ ├── forge-std/ # Foundry standard library
│ ├── chainlink-brownie-contracts/ # Chainlink contracts
│ ├── foundry-devops/ # DevOps utilities
│ └── solmate/ # Optimized utilities
├── foundry.toml # Foundry configuration
├── Makefile # Build commands
└── README.md # This entire text documents file
The main raffle contract implementation:
- Entry raffle fee payment.
- Player registration for the raffle commencement.
- Integration with Chainlink VRF to activate random winner selection.
- Integration with Chainlink Automation for automated, decentralized , periodic draws.
- Raffle Winner Prize distribution.
- VRF (Verifiable Random Function): Provides cryptographically secure randomness.
- Automation: Enables trustless execution of raffle draws based on time intervals.
The project includes intensive tests covering:
- Contract deployment.
- Raffle entry flow mechanics.
- Chainlink VRF integration (for randomness).
- Automation functionality.
- Edge cases and error handling.
Run tests with different verbosity levels:
forge test # Standard output
forge test -v # Verbose
forge test -vv # More verbose
forge test -vvv # Very verbose
forge test -vvvv # Maximum verbosity- RPC URL:
http://localhost:8545 - Chain ID: 31337
- Default funded accounts available already
- Requires SEPOLIA_RPC_URL in
.env - Requires a testnet sepoliaETH for deployment
- Contracts are verified on Etherscan automatically (E.g sepolia.ethscan.io)
- The contract securely transfers Ether to the winner only if it ensures the contract has sufficient balance before processing the transfer.
- Uses Chainlink VRF for cryptographically secure randomness.
- Executes checks-effects-interactions smart contract pattern.
- Fulfils State changes before external calls to prevent reentrancy.
- Intensive test coverage covers edge cases.
- All Raffle randomness is generated using Chainlink VRF service.
- Consider professional audit before mainnet deployment.
- Professional security audit.
- Bug bounty program.
- Optimized iteration and data structure operations.
- Implemented custom errors for gas efficiency.
- Efficient storage patterns.
A Makefile is included to streamline commands for cleaning, building, testing, updating, formatting, deployment, and more. You can use it to execute tasks without needing to remember specific commands. Just run the command you need like this:
make <command>- LinkedIn: @legendarycode3
- Twitter: @legendary_code_
- Github: @legendarycode3 Feel free to explore and improve the project. Contributions, issues, and feature requests are welcome! ❤️
This project demonstrates how smart contracts on the Ethereum blockchain can enable decentralized and trustless raffle systems. The transparency and immutability of the blockchain ensure fairness through the blockchain technology.
Built with ❤️ using Foundry and Chainlink