As part of my learning path of smart contract/blockchain/web3 development I took Cyfrin Updraft course. This repository is a result of me learning Foundry fundamentals lesson. Thank you Patrick and the team for putting together such an amazing learning resource!
The core of the application is a Raffle smart contract. The idea of this application is to have an automatic on-chain raffle application that allows users to enter raffle by sending funds to the application. Then after enough time has passed the raffle is supposed to figure figure out who is going to be the winner among users who enetered the raffle since last round was finished.
In this application I needed to generate random numbers to choose the winners, as well as be able to run the raffle automatically on schedule. To achieve this I've made use of Chainlink's VRF capability and Automation service.
As part of lesson I've learned a bunch of things:
- Chainlink VRF service to generate random numbers on-chain.
- Chainlink Automation service to be able to trigger smart contract function calls on schedule.
- Better naming conventions for variable names, events, errors. Better organization of smart conract variables and functions within the file.
- Using enums.
- Emitting and reading from events data. Indexing.
- Reverting errors with parameters.
- Uisng inherited constructors with parameters in the child smart contracts (
VRFConsumerBaseV2Plus). - Using mocks to work with ERC20 token (deploying
LINKtoken locally). - Using
abstract contractto make contracts more modular. - Using
vm.startBroadcast()with different accounts. - Using Chainlink brownie contracts mocks for VRF coordinator.
- Learned
vm.warp()andvm.roll(). - Setting
vm.expectRevert()with specific error codes and parameters passed to the error. - Setting expectations for specific events using
vm.expectEmit(). - Learned how to use
vm.recordLogs()to read from logs data.
- Run
git clone https://github.com/accurec/CyfrinUpdraftCourseFoundryRaffle.git
- Run
make installto install required dependencies. - Run
make buildto build the project. - Add
.envfile with the following variables:SEPOLIA_RPC_URL- could take this from Alchemy;ETHERSCAN_API_KEY- needed for automatic verification, can get it from Etherscan account. - Make sure that in
HelperConfig.s.solthe following values are properly setup for your subscription and account ingetSepoliaEthConfigfunction:subscriptionIdandaccount. - Make sure you have encrypted and saved your Sepolia private key using
cast wallet import --interactive sepoliaKeyfor the account that you have setup in the previous step inHelperConfig.s.solfile. - Make sure you have some testnet ETH and LINK balance on the account that you have specified in step #5 above. These are good faucets to use to get some: ETH Sepolia faucet and LINK and ETH Sepolia faucet.
- Make sure you've created subscription for VRF using this link and funded it with enough LINK. Use the subscription ID that you get from it and replace in
HelperConfig.s.solfile forgetSepoliaEthConfig()function,subscriptionIdfield. - You can now deploy to Sepolia using
make deploy-sepoliacommand. - Alternatively, can deploy locally and run
make deploy-local. Make sure to encrypt Anvil's key for the address0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266usingcast wallet importcommand. After deployment we can wait for30 seconds(interval time) and then runmake local-enter-raffle. Then we can runmake local-perform-upkeepand thenlocal-vrf-coordinator-fulfill-random-wordsto simulate Chainlink's Automation and VRF request fulfillments. After that we can observe that the winner of the raffle has been set by runningmake get-local-recent-winner.
NOTE 1: A little hacky way, but I have not been able to figure out quicker way for now to fix the problem with 0 blocks blockchain when running anvil is to go directly to lib/chainlink-brownie-contracts/contracts/src/v0.8/vrf/dev/SubscriptionAPI.sol and modify the line where the subscription ID is set based on the block - 1 logic by removing the -1 part before running make deploy-local:
keccak256(abi.encodePacked(msg.sender, blockhash(block.number - 1), address(this), currentSubNonce))
NOTE 2: For .env file the local values reference is:
LOCAL_RPC_URL=http://127.0.0.1:8545
LOCAL_RAFFLE_ADDRESS=0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9
LOCAL_VRF_COORDINATOR=0x5FbDB2315678afecb367f032d93F642f64180aa3
LOCAL_SEND_VALUE=50000000000000000 # 0.05 ETH
- After deploying to Sepolia we can look at the contract state variables. Example. We can see latest winner and verify that if raffle has never been run then the address will be a zero address.
- We then can register a new upkeep in Chainlink. Example.
- Once the upkeep has been registered, we can go ahead and write to our
Rafflecontract toenterRafflefunction with any value that is higher than the minimum entry fee. - Once this is done, we can wait an amount of time that has been configured in
HelperConfig.s.solfilegetSepoliaEthConfig()functionintervalparameter, and see that Chainlink calledperformUpkeep()function. - Once the
performUpkeep()is called then the control flow is passed to VRF, which will callfulfillRandomWordsfunction on the contract once the random values are ready. Example can see internal call toFulfill Random Words. - We then can check that the latest winner has been updated in our contract by reading from it and verify that the winner was indeed us.
- Write integration tests.
- Add interaction for registering an upkeep for
Rafflecontract.
