├── README.md ├── Smart-Contract ├── .env.template ├── .gitignore ├── contracts │ ├── DecentralizedLottery.sol │ └── test │ │ └── VRFCoordinatorV2Mock.sol ├── deploy │ ├── 00-deploy-mocks.js │ ├── 01-deploy-lottery.js │ └── 02-deploy-frontend.js ├── hardhat.config.js ├── helper-hardhat-config.js ├── package-lock.json ├── package.json ├── scripts │ └── mockOffChain.js ├── test │ ├── staging │ │ └── DecentralizedLottery.staging.test.js │ └── unit │ │ └── DecentralizedLottery.test.js └── utils │ └── verify.js └── frontend ├── .eslintrc.json ├── .gitignore ├── README.md ├── components ├── EnterLottery.jsx ├── Navbar.jsx └── subComponents │ ├── btns │ ├── Btn.jsx │ └── ConnectBtn.jsx │ └── logo │ └── Logo.jsx ├── configs └── tsConfig.json ├── constants ├── abi.json ├── contractAddresses.json └── index.js ├── next.config.js ├── package-lock.json ├── package.json ├── pages ├── _app.js ├── api │ └── hello.js └── index.js ├── postcss.config.js ├── public ├── favicon.ico ├── images │ ├── award-img.png │ └── dice-img.png └── vercel.svg ├── styles ├── Home.module.css └── globals.css ├── tailwind.config.js └── yarn.lock /README.md: -------------------------------------------------------------------------------- 1 | # FullStack-Decentralized-Lottery-Application 2 | Complete Full Stack DApp (Decentralized Application) using solidity language, hardhat, and next.js framework for frontend. Connect Multiple wallet functionality, Winner is picked automatically after 30 seconds if all conditions are true. 3 | 4 | _Smart Contract Deployed on **Rinkeby Test Network**_ -> 0x47b5A79aE313DB9FdEcb705Eb6FB252A37AdB63 5 | 6 | **_Check It at_** -> https://shiny-dust-3581.on.fleek.co **(Deployed on fleek (IPFS)** 7 | 8 | _Check Smart Contract At_ -> https://rinkeby.etherscan.io/address/0x47b5A79aE313DB9FdEcb705Eb6FB252A37AdB63c#code 9 | 10 | 11 | 12 | 13 | 14 | ## How to Setup in your local enviroment :- 15 | 16 | ### Frontend 17 | 1. cd frontend 18 | 2. npm install 19 | 3. npm run dev 20 | 21 | 22 | ### Blockchain 23 | 1. cd Smart-Contract 24 | 2. npm install 25 | 3. setup env 26 | 4. npx hardhat test 27 | 5. npx hardhat deploy --network localhost 28 | 29 | 30 | 31 | ## Technologies/Frameworks Used :- 32 | 33 | ### Frontend 34 | 1. Next.js 35 | 2. Tailwind CSS (For styling) 36 | 3. ts-particles (for background animations) 37 | 4. Moralis (For web3 integration) 38 | 5. Web3uikit (For wallet connect button and notifications) 39 | 6. Ethers (For converting value into ethers) 40 | 7. Fleek IPFS (For deploying frontend) 41 | 42 | ## Blockchain 43 | 1. Solidity (To develop Smart Contract) 44 | 2. Javascript (For deploying scripts) 45 | 3. Chai (For testing Smart Contract) 46 | 4. Chain-Link (For getting random number and choosing winner after specific time) 47 | 5. Rinkeby (Test network) 48 | 6. Hardhat 49 | 7. ethers 50 | -------------------------------------------------------------------------------- /Smart-Contract/.env.template: -------------------------------------------------------------------------------- 1 | RINKEBY_RPC_URL= Get it from here -> https://www.alchemy.com/overviews/rinkeby-testnet 2 | PRIVATE_KEY= Your Rinkeby private key 3 | ETHERSCAN_API_KEY= Get it from here -> https://etherscan.io/apis 4 | COINMARKETCAP_API_KEY= Get it from here -> https://coinmarketcap.com/api/ 5 | UPDATE_FRONTEND= boolean -------------------------------------------------------------------------------- /Smart-Contract/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | 4 | artifacts 5 | cache 6 | deployments 7 | coverage 8 | coverage.json 9 | typechain -------------------------------------------------------------------------------- /Smart-Contract/contracts/DecentralizedLottery.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.7; 4 | 5 | // Imports 6 | import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol"; 7 | import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol"; 8 | import "@chainlink/contracts/src/v0.8/interfaces/KeeperCompatibleInterface.sol"; 9 | 10 | // Custom errors 11 | error Lottery__NotEnoughEthEntered(); 12 | error Lottery__TransferToWinnerFailed(); 13 | error Lottery__NotOpen(); 14 | error Lottery__UpKeepNotNeeded( 15 | uint256 currentBalance, 16 | uint256 numOfPlayers, 17 | uint256 raffleState 18 | ); 19 | 20 | // Contract 21 | contract DecentralizedLottery is VRFConsumerBaseV2, KeeperCompatibleInterface { 22 | // State Variables 23 | uint256 private immutable entranceFee; // Fee to get one lottery ticket. 24 | uint256 private lastTimeStamp; 25 | uint256 private immutable interval; 26 | 27 | address private recentWinner; // The most recent winner 28 | address payable[] private allPlayers; 29 | 30 | VRFCoordinatorV2Interface private immutable i_vrfCoordinator; 31 | 32 | // Variables for chainlink random number function; 33 | bytes32 private immutable gasLane; 34 | uint64 private immutable subscriptionId; 35 | uint32 private immutable callbackGasLimit; 36 | uint32 private constant NO_OF_WORDS = 1; 37 | uint16 private constant REQUEST_CONFIRMATIONS = 3; 38 | 39 | // Enums 40 | enum LotteryState { 41 | OPEN, 42 | CALCULATING 43 | } 44 | LotteryState private _LotteryState; 45 | 46 | // Events 47 | event lotteryEnter(address indexed player); 48 | event randomNumberPick(uint256 indexed requestId); 49 | event winnerPicked(address indexed recentWinner); 50 | 51 | // Constructor 52 | constructor( 53 | address vrfCoordinatorV2, 54 | uint256 _entranceFee, 55 | bytes32 _gasLane, 56 | uint64 _subscriptionId, 57 | uint32 _callbackGasLimit, 58 | uint256 _interval 59 | ) VRFConsumerBaseV2(vrfCoordinatorV2) { 60 | entranceFee = _entranceFee; 61 | i_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinatorV2); 62 | gasLane = _gasLane; 63 | subscriptionId = _subscriptionId; 64 | callbackGasLimit = _callbackGasLimit; 65 | _LotteryState = LotteryState.OPEN; 66 | lastTimeStamp = block.timestamp; 67 | interval = _interval; 68 | } 69 | 70 | // Modifiers 71 | // The entered value is less then the entrance fee. 72 | modifier notEnoughEthEntered() { 73 | if (msg.value < entranceFee) { 74 | revert Lottery__NotEnoughEthEntered(); 75 | } 76 | _; 77 | } 78 | 79 | // View Functions 80 | 81 | // get entrance fee. 82 | function getEntranceFee() public view returns (uint256) { 83 | return entranceFee; 84 | } 85 | 86 | // Get player 87 | function getPlayer(uint256 index) public view returns (address) { 88 | return allPlayers[index]; 89 | } 90 | 91 | // Get Recent Winner 92 | function getRecentWinner() public view returns (address) { 93 | return recentWinner; 94 | } 95 | 96 | // Get Lottery State 97 | function getLotteryState() public view returns (LotteryState) { 98 | return _LotteryState; 99 | } 100 | 101 | // Get Numbers of players 102 | function getNumbersOfPlayers() public view returns (uint256) { 103 | return allPlayers.length; 104 | } 105 | 106 | // Get last block timestamp 107 | function getLastTimeStamp() public view returns (uint256) { 108 | return lastTimeStamp; 109 | } 110 | 111 | // Get Num Words 112 | function getNumWords() public pure returns (uint256) { 113 | return NO_OF_WORDS; 114 | } 115 | 116 | // Get Interval 117 | function getInterval() public view returns (uint256) { 118 | return interval; 119 | } 120 | 121 | // Get Request confirmations 122 | function getRequestConfirmations() public pure returns (uint256) { 123 | return REQUEST_CONFIRMATIONS; 124 | } 125 | 126 | // Functions 127 | 128 | // Enter the lottery ticket. 129 | function enterLottery() public payable notEnoughEthEntered { 130 | if (_LotteryState != LotteryState.OPEN) { 131 | revert Lottery__NotOpen(); 132 | } 133 | allPlayers.push(payable(msg.sender)); 134 | 135 | // Emitting events 136 | emit lotteryEnter(msg.sender); 137 | } 138 | 139 | function checkUpkeep( 140 | bytes memory /*checkData*/ 141 | ) 142 | public 143 | view 144 | override 145 | returns ( 146 | bool upkeepNeeded, 147 | bytes memory /* performData */ 148 | ) 149 | { 150 | bool isOpen = (_LotteryState == LotteryState.OPEN); 151 | bool timePassed = ((block.timestamp - lastTimeStamp) > interval); 152 | bool hasPlayers = (allPlayers.length > 0); 153 | bool hasBalance = address(this).balance > 0; 154 | upkeepNeeded = (isOpen && timePassed && hasPlayers && hasBalance); 155 | return (upkeepNeeded, "0x0"); 156 | } 157 | 158 | // Pick a random number; 159 | function performUpkeep( 160 | bytes calldata /* performData */ 161 | ) external override { 162 | (bool upkeepNeeded, ) = checkUpkeep(""); 163 | if (!upkeepNeeded) { 164 | revert Lottery__UpKeepNotNeeded( 165 | address(this).balance, 166 | allPlayers.length, 167 | uint256(_LotteryState) 168 | ); 169 | } 170 | 171 | _LotteryState = LotteryState.CALCULATING; 172 | uint256 requestId = i_vrfCoordinator.requestRandomWords( 173 | gasLane, 174 | subscriptionId, 175 | REQUEST_CONFIRMATIONS, 176 | callbackGasLimit, 177 | NO_OF_WORDS 178 | ); 179 | 180 | // Emitting event 181 | emit randomNumberPick(requestId); 182 | } 183 | 184 | function fulfillRandomWords( 185 | uint256, /*requestId*/ 186 | uint256[] memory randomWords 187 | ) internal override { 188 | uint256 index = randomWords[0] % allPlayers.length; 189 | address payable _recentWinner = allPlayers[index]; 190 | recentWinner = _recentWinner; 191 | (bool success, ) = recentWinner.call{value: address(this).balance}(""); 192 | if (!success) { 193 | revert Lottery__TransferToWinnerFailed(); 194 | } 195 | emit winnerPicked(recentWinner); 196 | _LotteryState = LotteryState.OPEN; 197 | allPlayers = new address payable[](0); 198 | lastTimeStamp = block.timestamp; 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /Smart-Contract/contracts/test/VRFCoordinatorV2Mock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.7; 3 | 4 | import "@chainlink/contracts/src/v0.8/mocks/VRFCoordinatorV2Mock.sol"; -------------------------------------------------------------------------------- /Smart-Contract/deploy/00-deploy-mocks.js: -------------------------------------------------------------------------------- 1 | const { network } = require("hardhat"); 2 | const {developmentChains} = require('../helper-hardhat-config') 3 | 4 | const BASE_FEE = ethers.utils.parseEther("0.25"); 5 | const GAS_PRICE_LINKS = 1e9; 6 | const args = [BASE_FEE, GAS_PRICE_LINKS] 7 | module.exports = async function ({getNamedAccounts, deployments}){ 8 | const {deploy, log} = deployments; 9 | const {deployer} = await getNamedAccounts(); 10 | const chainId = network.config.chainId 11 | 12 | if(developmentChains.includes(network.name)) { 13 | log("local network detected.. deploying") 14 | await deploy("VRFCoordinatorV2Mock", { 15 | from: deployer, 16 | log: true, 17 | args: args 18 | }) 19 | log("Mocks Deployed") 20 | log("---------------------------------------") 21 | } 22 | } 23 | 24 | module.exports.tags = ['all', ['mocks']] 25 | -------------------------------------------------------------------------------- /Smart-Contract/deploy/01-deploy-lottery.js: -------------------------------------------------------------------------------- 1 | const { network, ethers } = require("hardhat"); 2 | const { 3 | developmentChains, 4 | networkConfig, 5 | } = require("../helper-hardhat-config"); 6 | const {verify} = require('../utils/verify') 7 | 8 | const VRF_SUB_FUND_AMOUNT = ethers.utils.parseEther("30"); 9 | 10 | module.exports = async function ({ getNamedAccounts, deployments }) { 11 | const { deploy, log } = deployments; 12 | const { deployer } = await getNamedAccounts(); 13 | const chainId = network.config.chainId; 14 | let vrfCoordinatorV2Address, subscriptionId; 15 | 16 | if (developmentChains.includes(network.name)) { 17 | const vrfCoordinatorV2Mock = await ethers.getContract( 18 | "VRFCoordinatorV2Mock" 19 | ); 20 | vrfCoordinatorV2Address = vrfCoordinatorV2Mock.address; 21 | const transactionResponse = await vrfCoordinatorV2Mock.createSubscription(); 22 | const transactionReceipt = await transactionResponse.wait(1) 23 | subscriptionId = transactionReceipt.events[0].args.subId 24 | 25 | await vrfCoordinatorV2Mock.fundSubscription(subscriptionId, VRF_SUB_FUND_AMOUNT) 26 | } else { 27 | vrfCoordinatorV2Address = networkConfig[chainId]["vrfCoordinatorV2"]; 28 | subscriptionId = networkConfig[chainId]["subscriptionId"]; 29 | } 30 | 31 | const entranceFee = networkConfig[chainId]["entranceFee"]; 32 | const gasLane = networkConfig[chainId]["gasLane"]; 33 | const callbackGasLimit = networkConfig[chainId]["callbackGasLimit"]; 34 | const interval = networkConfig[chainId]["interval"]; 35 | 36 | const args = [vrfCoordinatorV2Address, entranceFee, gasLane, subscriptionId, callbackGasLimit, interval]; 37 | const DecentralizedLottery = await deploy("DecentralizedLottery", { 38 | from: deployer, 39 | args: args, 40 | log: true, 41 | waitConfirmations: network.config.blockConfirmations || 1, 42 | }); 43 | 44 | if (!developmentChains.includes(network.name) && process.env.ETHERSCAN_API_KEY) { 45 | log("verifying..."); 46 | await verify(DecentralizedLottery.address, args) 47 | 48 | 49 | } 50 | log("---------------------------------------------------------") 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | }; 59 | module.exports.tags = ["all", "DecentralizedLottery"] -------------------------------------------------------------------------------- /Smart-Contract/deploy/02-deploy-frontend.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const { ethers, network } = require("hardhat"); 3 | const FRONTEND_ADDRESSES_FILE = "../frontend/constants/contractAddresses.json"; 4 | const FRONTEND_ABI_FILE = "../frontend/constants/abi.json"; 5 | 6 | const updateContractAddress = async () => { 7 | const chainId = network.config.chainId.toString(); 8 | const DecentralizedLottery = await ethers.getContract("DecentralizedLottery"); 9 | const currentAddress = JSON.parse( 10 | fs.readFileSync(FRONTEND_ADDRESSES_FILE, "utf-8") 11 | ); 12 | if (chainId in currentAddress) { 13 | if (!currentAddress[chainId].includes(DecentralizedLottery.address)) { 14 | } 15 | } else { 16 | currentAddress[chainId] = [DecentralizedLottery.address]; 17 | } 18 | fs.writeFileSync(FRONTEND_ADDRESSES_FILE, JSON.stringify(currentAddress)); 19 | }; 20 | 21 | const updateAbi = async () => { 22 | const DecentralizedLottery = await ethers.getContract("DecentralizedLottery"); 23 | fs.writeFileSync( 24 | FRONTEND_ABI_FILE, 25 | DecentralizedLottery.interface.format(ethers.utils.FormatTypes.json) 26 | ); 27 | }; 28 | 29 | module.exports = async () => { 30 | if (process.env.UPDATE_FRONTEND) { 31 | updateContractAddress(); 32 | updateAbi(); 33 | console.log("updating frontend......"); 34 | } 35 | }; 36 | 37 | module.exports.tags = ["all", "frontend"]; 38 | -------------------------------------------------------------------------------- /Smart-Contract/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-waffle") 2 | require("@nomiclabs/hardhat-etherscan") 3 | require("hardhat-deploy") 4 | require("solidity-coverage") 5 | require("hardhat-gas-reporter") 6 | require("hardhat-contract-sizer") 7 | require("dotenv").config() 8 | 9 | const RINKEBY_RPC_URL = process.env.RINKEBY_RPC_URL; 10 | const PRIVATE_KEY = process.env.PRIVATE_KEY; 11 | const COINMARKETCAP_API_KEY = process.env.COINMARKETCAP_API_KEY; 12 | const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY; 13 | 14 | module.exports = { 15 | defaultNetwork: "hardhat", 16 | networks: { 17 | hardhat: { 18 | chainId: 31337, 19 | blockConfirmations: 1 20 | }, 21 | rinkeby: { 22 | url: RINKEBY_RPC_URL, 23 | accounts: [PRIVATE_KEY], 24 | chainId: 4, 25 | blockConfirmations: 6, 26 | } 27 | }, 28 | etherscan: { 29 | apiKey: { 30 | rinkeby: ETHERSCAN_API_KEY, 31 | }, 32 | }, 33 | gasReporter: { 34 | enabled: false, 35 | currency: "USD", 36 | outputFile: "gas-report.txt", 37 | noColors: true 38 | }, 39 | solidity: "0.8.7", 40 | namedAccounts: { 41 | deployer: { 42 | default: 0, 43 | }, 44 | player: { 45 | default: 1, 46 | }, 47 | }, 48 | mocha: { 49 | timeout: 40000 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /Smart-Contract/helper-hardhat-config.js: -------------------------------------------------------------------------------- 1 | const { ethers } = require("hardhat"); 2 | 3 | const networkConfig = { 4 | 4: { 5 | name: "rinkeby", 6 | vrfCoordinatorV2: "0x6168499c0cFfCaCD319c818142124B7A15E857ab", 7 | entranceFee: ethers.utils.parseEther("0.01"), 8 | gasLane: "0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc", 9 | subscriptionId: "8114", 10 | callbackGasLimit: "500000", 11 | interval: '30' 12 | }, 13 | 31337: { 14 | name: "hardhat", 15 | entranceFee: ethers.utils.parseEther("0.01"), 16 | gasLane: "0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc", 17 | callbackGasLimit: "500000", 18 | interval: '30' 19 | 20 | }, 21 | }; 22 | 23 | const developmentChains = ["hardhat", "localhost"]; 24 | module.exports = { networkConfig, developmentChains }; 25 | -------------------------------------------------------------------------------- /Smart-Contract/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@chainlink/contracts": "^0.4.1", 4 | "@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers", 5 | "@nomiclabs/hardhat-etherscan": "^3.1.0", 6 | "@nomiclabs/hardhat-waffle": "^2.0.3", 7 | "chai": "^4.3.6", 8 | "ethereum-waffle": "^3.4.4", 9 | "ethers": "^5.6.9", 10 | "hardhat": "^2.10.0", 11 | "hardhat-contract-sizer": "^2.6.1", 12 | "hardhat-deploy": "^0.11.11", 13 | "hardhat-gas-reporter": "^1.0.8", 14 | "prettier": "^2.7.1", 15 | "prettier-plugin-solidity": "^1.0.0-beta.19", 16 | "solhint": "^3.3.7", 17 | "solidity-coverage": "^0.7.21" 18 | }, 19 | "name": "decentralized-lottery", 20 | "version": "1.0.0", 21 | "main": "index.js", 22 | "license": "MIT", 23 | "dependencies": { 24 | "dotenv": "^16.0.1" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Smart-Contract/scripts/mockOffChain.js: -------------------------------------------------------------------------------- 1 | const { ethers, network } = require("hardhat") 2 | 3 | async function mockKeepers() { 4 | const dlottery = await ethers.getContract("DecentralizedLottery") 5 | const checkData = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("")) 6 | const { upkeepNeeded } = await dlottery.callStatic.checkUpkeep(checkData) 7 | if (upkeepNeeded) { 8 | const tx = await dlottery.performUpkeep(checkData) 9 | const txReceipt = await tx.wait(1) 10 | const requestId = txReceipt.events[1].args.requestId 11 | console.log(`Performed upkeep with RequestId: ${requestId}`) 12 | // if (network.config.chainId == 31337) { 13 | await mockVrf(requestId, dlottery) 14 | // } 15 | } else { 16 | console.log("No upkeep needed!") 17 | } 18 | } 19 | 20 | async function mockVrf(requestId, dlottery) { 21 | console.log("We on a local network? Ok let's pretend...") 22 | const vrfCoordinatorV2Mock = await ethers.getContract("VRFCoordinatorV2Mock") 23 | await vrfCoordinatorV2Mock.fulfillRandomWords(requestId, dlottery.address) 24 | console.log("Responded!") 25 | const recentWinner = await dlottery.getRecentWinner() 26 | console.log(`The winner is: ${recentWinner}`) 27 | } 28 | 29 | mockKeepers() 30 | .then(() => process.exit(0)) 31 | .catch((error) => { 32 | console.error(error) 33 | process.exit(1) 34 | }) -------------------------------------------------------------------------------- /Smart-Contract/test/staging/DecentralizedLottery.staging.test.js: -------------------------------------------------------------------------------- 1 | const { assert, expect } = require("chai"); 2 | const { network, getNamedAccounts, deployments, ethers } = require("hardhat"); 3 | const { 4 | developmentChains, 5 | networkConfig, 6 | } = require("../../helper-hardhat-config"); 7 | 8 | if (!developmentChains.includes(network.name)) { 9 | describe.skip; 10 | } else { 11 | describe("DecentralizedLottery", () => { 12 | let decentralizedLottery, 13 | vrfCoordinatorV2Mock, 14 | entranceFee, 15 | interval, 16 | deployer; 17 | const chainId = network.config.chainId; 18 | 19 | beforeEach(async () => { 20 | deployer = (await getNamedAccounts()).deployer; 21 | await deployments.fixture(["all"]); 22 | decentralizedLottery = await ethers.getContract( 23 | "DecentralizedLottery", 24 | deployer 25 | ); 26 | }); 27 | 28 | describe("fulfillRandomWords", function () { 29 | it("works with live Chainlink Keepers and Chainlink VRF, we get a random winner", async function () { 30 | console.log("Setting up test..."); 31 | const startingTimeStamp = await decentralizedLottery.getLastTimeStamp(); 32 | const accounts = await ethers.getSigners(); 33 | 34 | console.log("Setting up Listener..."); 35 | await new Promise(async (resolve, reject) => { 36 | 37 | 38 | decentralizedLottery.once("WinnerPicked", async () => { 39 | console.log("WinnerPicked event fired!"); 40 | try { 41 | // add our asserts here 42 | const recentWinner = await decentralizedLottery.getRecentWinner(); 43 | const lotteryState = await decentralizedLottery.getLotteryState(); 44 | const winnerEndingBalance = await accounts[0].getBalance(); 45 | const endingTimeStamp = await decentralizedLottery.getLastTimeStamp(); 46 | 47 | await expect(decentralizedLottery.getPlayer(0)).to.be.reverted; 48 | assert.equal(recentWinner.toString(), accounts[0].address); 49 | assert.equal(lotteryState, 0); 50 | assert.equal( 51 | winnerEndingBalance.toString(), 52 | winnerStartingBalance.add(entranceFee).toString() 53 | ); 54 | assert(endingTimeStamp > startingTimeStamp); 55 | resolve(); 56 | } catch (error) { 57 | console.log(error); 58 | reject(error); 59 | } 60 | }); 61 | // Then entering the lottery 62 | console.log("Entering decentralizedLottery..."); 63 | const tx = await decentralizedLottery.enterLottery({ value: entranceFee }); 64 | await tx.wait(1); 65 | console.log("Ok, time to wait..."); 66 | const winnerStartingBalance = await accounts[0].getBalance(); 67 | 68 | // and this code WONT complete until our listener has finished listening! 69 | }); 70 | }); 71 | }); 72 | }); 73 | } 74 | -------------------------------------------------------------------------------- /Smart-Contract/test/unit/DecentralizedLottery.test.js: -------------------------------------------------------------------------------- 1 | const { assert, expect } = require("chai"); 2 | const { network, getNamedAccounts, deployments, ethers } = require("hardhat"); 3 | const { 4 | developmentChains, 5 | networkConfig, 6 | } = require("../../helper-hardhat-config"); 7 | 8 | if (!developmentChains.includes(network.name)) { 9 | describe.skip; 10 | } else { 11 | describe("DecentralizedLottery", () => { 12 | let decentralizedLottery, 13 | vrfCoordinatorV2Mock, 14 | entranceFee, 15 | interval, 16 | deployer; 17 | const chainId = network.config.chainId; 18 | 19 | beforeEach(async () => { 20 | deployer = (await getNamedAccounts()).deployer; 21 | await deployments.fixture(["all"]); 22 | decentralizedLottery = await ethers.getContract( 23 | "DecentralizedLottery", 24 | deployer 25 | ); 26 | vrfCoordinatorV2Mock = await ethers.getContract( 27 | "VRFCoordinatorV2Mock", 28 | deployer 29 | ); 30 | entranceFee = await decentralizedLottery.getEntranceFee(); 31 | interval = await decentralizedLottery.getInterval(); 32 | }); 33 | 34 | // Checks for the values are assigned correctly in constructor; 35 | describe("constructor", () => { 36 | it("Check lottery state", async () => { 37 | const lotteryState = await decentralizedLottery.getLotteryState(); 38 | assert.equal(lotteryState.toString(), "0"); 39 | }); 40 | it("Check lottery interval", async () => { 41 | assert.equal(interval, networkConfig[chainId]["interval"]); 42 | }); 43 | }); 44 | 45 | describe("enterLottery", () => { 46 | it("Check if it reverts successfully if the amount is less then the entrance fee.", async () => { 47 | await expect(decentralizedLottery.enterLottery()).to.be.revertedWith( 48 | "Lottery__NotEnoughEthEntered" 49 | ); 50 | }); 51 | 52 | it("push the players address if they enter lottery with entrance fee", async () => { 53 | await decentralizedLottery.enterLottery({ value: entranceFee }); 54 | const playerFromContract = await decentralizedLottery.getPlayer(0); 55 | assert.equal(playerFromContract, deployer); 56 | }); 57 | 58 | it("emits the event when entering lottery", async () => { 59 | await expect( 60 | decentralizedLottery.enterLottery({ value: entranceFee }) 61 | ).to.emit(decentralizedLottery, "lotteryEnter"); 62 | }); 63 | 64 | it("dosen't allow the lottery to enter when in calculating state", async () => { 65 | await decentralizedLottery.enterLottery({ value: entranceFee }); 66 | await network.provider.send("evm_increaseTime", [ 67 | interval.toNumber() + 1, 68 | ]); 69 | await network.provider.send("evm_mine", []); 70 | // await network.provider.request({method: "evm_mine", params: []}) 71 | await decentralizedLottery.performUpkeep([]); 72 | await expect( 73 | decentralizedLottery.enterLottery({ value: entranceFee }) 74 | ).to.be.revertedWith("Lottery__NotOpen"); 75 | }); 76 | }); 77 | 78 | describe("checkUpKeep", () => { 79 | it("return false if there is no users who have paid.", async () => { 80 | await network.provider.send("evm_increaseTime", [ 81 | interval.toNumber() + 1, 82 | ]); 83 | await network.provider.send("evm_mine", []); 84 | const { upkeepNeeded } = 85 | await decentralizedLottery.callStatic.checkUpkeep([]); 86 | assert(!upkeepNeeded); 87 | }); 88 | 89 | it("returns false if raffle is not open", async () => { 90 | await decentralizedLottery.enterLottery({ value: entranceFee }); 91 | await network.provider.send("evm_increaseTime", [ 92 | interval.toNumber() + 1, 93 | ]); 94 | await network.provider.send("evm_mine", []); 95 | await decentralizedLottery.performUpkeep([]); 96 | const lotteryState = await decentralizedLottery.getLotteryState(); 97 | const { upkeepNeeded } = 98 | await decentralizedLottery.callStatic.checkUpkeep([]); 99 | assert.equal(lotteryState.toString(), "1"); 100 | assert.equal(upkeepNeeded, false); 101 | }); 102 | 103 | it("returns false if enough time has not passed", async () => { 104 | await decentralizedLottery.enterLottery({ value: entranceFee }); 105 | 106 | // await network.provider.send("evm_increaseTime", [ 107 | // interval.toNumber() - 1, 108 | // ]); 109 | await network.provider.send("evm_mine", []); 110 | const { upkeepNeeded } = 111 | await decentralizedLottery.callStatic.checkUpkeep("0x"); 112 | assert(!upkeepNeeded); 113 | }); 114 | 115 | it("returns true if interval and all necessary things are true and has passed", async () => { 116 | await decentralizedLottery.enterLottery({ value: entranceFee }); 117 | 118 | await network.provider.send("evm_increaseTime", [ 119 | interval.toNumber() + 1, 120 | ]); 121 | await network.provider.send("evm_mine", []); 122 | const { upkeepNeeded } = 123 | await decentralizedLottery.callStatic.checkUpkeep("0x"); 124 | assert(upkeepNeeded); 125 | }); 126 | }); 127 | 128 | describe("performUpkeep", () => { 129 | it("It only runs if checkupkeep function returns true", async () => { 130 | await decentralizedLottery.enterLottery({ value: entranceFee }); 131 | await network.provider.send("evm_increaseTime", [ 132 | interval.toNumber() + 1, 133 | ]); 134 | await network.provider.send("evm_mine", []); 135 | const tx = await decentralizedLottery.performUpkeep([]); 136 | assert(tx); 137 | }); 138 | it("revert if the checkupkeep is false", async () => { 139 | await expect(decentralizedLottery.performUpkeep([])).to.be.revertedWith( 140 | "Lottery__UpKeepNotNeeded" 141 | ); 142 | }); 143 | 144 | it("updates the lottery state and calls the performUpkeep", async () => { 145 | await decentralizedLottery.enterLottery({ value: entranceFee }); 146 | await network.provider.send("evm_increaseTime", [ 147 | interval.toNumber() + 1, 148 | ]); 149 | await network.provider.send("evm_mine", []); 150 | const txResponse = await decentralizedLottery.performUpkeep([]); 151 | const txReceipt = await txResponse.wait(1); 152 | const requestId = txReceipt.events[1].args.requestId; 153 | const lotteryState = await decentralizedLottery.getLotteryState(); 154 | assert(requestId.toNumber() > 0); 155 | assert(lotteryState.toString() == "1"); 156 | }); 157 | }); 158 | 159 | describe("fulfillRandomWords", () => { 160 | beforeEach(async () => { 161 | await decentralizedLottery.enterLottery({ value: entranceFee }); 162 | await network.provider.send("evm_increaseTime", [ 163 | interval.toNumber() + 1, 164 | ]); 165 | await network.provider.send("evm_mine", []); 166 | }); 167 | 168 | it("Should be called only after performUpkeep", async () => { 169 | await expect( 170 | vrfCoordinatorV2Mock.fulfillRandomWords( 171 | 0, 172 | decentralizedLottery.address 173 | ) 174 | ).to.be.revertedWith("nonexistent request"); 175 | await expect( 176 | vrfCoordinatorV2Mock.fulfillRandomWords( 177 | 1, 178 | decentralizedLottery.address 179 | ) 180 | ).to.be.revertedWith("nonexistent request"); 181 | }); 182 | 183 | it("picks a winner, resets the lottery, and sends money", async () => { 184 | const extraUsers = 3; 185 | const startingUser = 1; 186 | const accounts = await ethers.getSigners(); 187 | for (let i = startingUser; i < startingUser + extraUsers; i++) { 188 | const accountConnectedUsers = await decentralizedLottery.connect( 189 | accounts[i] 190 | ); 191 | await accountConnectedUsers.enterLottery({ value: entranceFee }); 192 | } 193 | const startingTimeStamp = await decentralizedLottery.getLastTimeStamp(); 194 | 195 | await new Promise(async (resolve, reject) => { 196 | decentralizedLottery.once("winnerPicked", async () => { 197 | resolve(); 198 | try { 199 | console.log("This is account 1", accounts[0].address); 200 | console.log("This is account 1", accounts[1].address); 201 | console.log("This is account 2", accounts[2].address); 202 | console.log("This is account 3", accounts[3].address); 203 | const recentWinner = await decentralizedLottery.getRecentWinner(); 204 | console.log(recentWinner); 205 | const lotteryState = await decentralizedLottery.getLotteryState(); 206 | const endingTimeStamp = 207 | await decentralizedLottery.getLastTimeStamp(); 208 | const numPlayers = 209 | await decentralizedLottery.getNumbersOfPlayers(); 210 | const winnerEndingBalance = await accounts[1].getBalance(); 211 | 212 | assert.equal(numPlayers.toString(), "0"); 213 | assert.equal(lotteryState.toString(), "0"); 214 | assert(endingTimeStamp > startingTimeStamp); 215 | assert.equal( 216 | winnerEndingBalance.toString(), 217 | winnerStartingBalance.add( 218 | entranceFee.mul(extraUsers).add(entranceFee) 219 | ) 220 | ); 221 | } catch (error) { 222 | reject(error); 223 | } 224 | }); 225 | 226 | const tx = await decentralizedLottery.performUpkeep([]); 227 | const txReceipt = await tx.wait(1); 228 | const winnerStartingBalance = await accounts[1].getBalance(); 229 | await vrfCoordinatorV2Mock.fulfillRandomWords( 230 | txReceipt.events[1].args.requestId, 231 | decentralizedLottery.address 232 | ); 233 | }); 234 | }); 235 | }); 236 | }); 237 | } 238 | -------------------------------------------------------------------------------- /Smart-Contract/utils/verify.js: -------------------------------------------------------------------------------- 1 | // we can't have these functions in our `helper-hardhat-config` 2 | // since these use the hardhat library 3 | // and it would be a circular dependency 4 | const { run } = require("hardhat") 5 | 6 | const verify = async (contractAddress, args) => { 7 | console.log("Verifying contract...") 8 | try { 9 | await run("verify:verify", { 10 | address: contractAddress, 11 | constructorArguments: args, 12 | }) 13 | } catch (e) { 14 | if (e.message.toLowerCase().includes("already verified")) { 15 | console.log("Already verified!") 16 | } else { 17 | console.log("You are in else part of verify", e) 18 | } 19 | } 20 | } 21 | 22 | module.exports = { 23 | verify, 24 | } -------------------------------------------------------------------------------- /frontend/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 16 | 17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. 18 | 19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /frontend/components/EnterLottery.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { useWeb3Contract, useMoralis } from "react-moralis"; 3 | import { contractAddresses, abi } from "../constants"; 4 | import { ethers } from "ethers"; 5 | import { Loading, useNotification } from "web3uikit"; 6 | 7 | export default function EnterLottery() { 8 | const [entranceFee, setEntranceFee] = useState(); 9 | const [recentWinner, setRecentWinner] = useState(); 10 | const [allPlayers, setAllPlayers] = useState(); 11 | const [loading, setLoading] = useState(false); 12 | const [btnLoading, setBtnLoading] = useState(false); 13 | const [showFullAddress, setShowFullAddress] = useState(true) 14 | 15 | const { chainId: chainIdHex, isWeb3Enabled } = useMoralis(); 16 | const dispatch = useNotification(); 17 | 18 | const chainId = parseInt(chainIdHex); 19 | const lotteryAddress = 20 | chainId in contractAddresses ? contractAddresses[chainId][0] : null; 21 | 22 | const { runContractFunction: enterLottery } = useWeb3Contract({ 23 | abi, 24 | contractAddress: lotteryAddress, 25 | functionName: "enterLottery", 26 | params: {}, 27 | msgValue: entranceFee, 28 | }); 29 | 30 | const { 31 | runContractFunction: getEntranceFee, 32 | isLoading, 33 | isFetching, 34 | } = useWeb3Contract({ 35 | abi, 36 | contractAddress: lotteryAddress, 37 | functionName: "getEntranceFee", 38 | params: {}, 39 | }); 40 | 41 | const { runContractFunction: getNumbersOfPlayers } = useWeb3Contract({ 42 | abi, 43 | contractAddress: lotteryAddress, 44 | functionName: "getNumbersOfPlayers", 45 | params: {}, 46 | }); 47 | 48 | const { runContractFunction: getRecentWinner } = useWeb3Contract({ 49 | abi, 50 | contractAddress: lotteryAddress, 51 | functionName: "getRecentWinner", 52 | params: {}, 53 | }); 54 | 55 | const handleClick = async () => { 56 | setBtnLoading(true); 57 | await enterLottery({ 58 | onSuccess: handleSuccess, 59 | onError: (error) => console.log(error), 60 | }); 61 | }; 62 | 63 | // Notifications 64 | const handleSuccess = async (tx) => { 65 | await tx.wait(1); 66 | handleNewNotification(tx); 67 | setBtnLoading(false); 68 | // getAll() 69 | }; 70 | 71 | const handleNewNotification = () => { 72 | dispatch({ 73 | type: "info", 74 | message: "Transaction Completed Successfully", 75 | title: "Transaction Notification", 76 | position: "topR", 77 | icon: "bell", 78 | }); 79 | }; 80 | 81 | useEffect(() => { 82 | if (isWeb3Enabled) { 83 | const getAll = async () => { 84 | const getFee = (await getEntranceFee()).toString(); 85 | const getNumOfPlayers = (await getNumbersOfPlayers()).toString(); 86 | const getWinner = await getRecentWinner(); 87 | setEntranceFee(getFee); 88 | setAllPlayers(getNumOfPlayers); 89 | setRecentWinner(getWinner); 90 | }; 91 | getAll(); 92 | } 93 | }, [isWeb3Enabled]); 94 | 95 | return ( 96 |
97 | {lotteryAddress ? ( 98 |
99 |

100 | Entrance Fee = 101 | 102 | {entranceFee && ethers.utils.formatUnits(entranceFee, "ether")} Ether 103 | 104 |

105 |

Players = 106 | {allPlayers && allPlayers} 107 |

108 |

Winner Recent Winner: {recentWinner && !showFullAddress ? recentWinner : recentWinner?.slice(0,6) + "..." + recentWinner?.slice(recentWinner?.length-6)} 109 | 110 | 111 | 112 |

113 |
114 | 127 |
128 |
129 | ) : ( 130 |
Wallet Not Connected (Connect Using Connect wallet Button in the top right)
131 | )} 132 |
133 | ); 134 | } 135 | -------------------------------------------------------------------------------- /frontend/components/Navbar.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import EnterLottery from "./EnterLottery"; 3 | import ConnectBtn from "./subComponents/btns/ConnectBtn"; 4 | import Logo from "./subComponents/logo/Logo"; 5 | // import { useMoralis } from "react-moralis"; 6 | 7 | export default function Navbar() { 8 | return ( 9 |
10 | 11 | 12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /frontend/components/subComponents/btns/Btn.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Loading } from "web3uikit"; 3 | 4 | export default function Btn(handleClick, disabled, children, title) { 5 | return ( 6 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /frontend/components/subComponents/btns/ConnectBtn.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ConnectButton } from "web3uikit"; 3 | 4 | export default function ConnectBtn() { 5 | return ( 6 |
7 | 8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /frontend/components/subComponents/logo/Logo.jsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image' 2 | import React from 'react' 3 | 4 | export default function Logo() { 5 | return ( 6 |
7 | award image 8 | {/* award image */} 9 |

Decentralized Lottery

10 |
11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /frontend/configs/tsConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "fullScreen": { 3 | "enable": true, 4 | "zIndex": 1 5 | }, 6 | "particles": { 7 | "number": { 8 | "value": 50, 9 | "density": { 10 | "enable": false, 11 | "value_area": 800 12 | } 13 | }, 14 | "color": { 15 | "value": "#ffffff" 16 | }, 17 | "shape": { 18 | "type": "star", 19 | "options": { 20 | "sides": 5 21 | } 22 | }, 23 | "opacity": { 24 | "value": 0.5, 25 | "random": false, 26 | "anim": { 27 | "enable": false, 28 | "speed": 1, 29 | "opacity_min": 0.1, 30 | "sync": false 31 | } 32 | }, 33 | "size": { 34 | "value": 4, 35 | "random": true, 36 | "anim": { 37 | "enable": false, 38 | "speed": 70, 39 | "size_min": 0.1, 40 | "sync": false 41 | } 42 | }, 43 | "rotate": { 44 | "value": 0, 45 | "random": true, 46 | "direction": "clockwise", 47 | "animation": { 48 | "enable": true, 49 | "speed": 5, 50 | "sync": false 51 | } 52 | }, 53 | "line_linked": { 54 | "enable": false, 55 | "distance": 600, 56 | "color": "#ffffff", 57 | "opacity": 0.4, 58 | "width": 2 59 | }, 60 | "move": { 61 | "enable": true, 62 | "speed": 2, 63 | "direction": "none", 64 | "random": false, 65 | "straight": false, 66 | "out_mode": "out", 67 | "attract": { 68 | "enable": false, 69 | "rotateX": 600, 70 | "rotateY": 1200 71 | } 72 | } 73 | }, 74 | "interactivity": { 75 | "events": { 76 | "onhover": { 77 | "enable": false, 78 | "mode": ["grab"] 79 | }, 80 | "onclick": { 81 | "enable": false, 82 | "mode": "bubble" 83 | }, 84 | "resize": true 85 | }, 86 | "modes": { 87 | "grab": { 88 | "distance": 400, 89 | "line_linked": { 90 | "opacity": 1 91 | } 92 | }, 93 | "bubble": { 94 | "distance": 400, 95 | "size": 40, 96 | "duration": 2, 97 | "opacity": 8, 98 | "speed": 3 99 | }, 100 | "repulse": { 101 | "distance": 200 102 | }, 103 | "push": { 104 | "particles_nb": 4 105 | }, 106 | "remove": { 107 | "particles_nb": 2 108 | } 109 | } 110 | }, 111 | "retina_detect": true, 112 | "background": { 113 | "color": "black", 114 | "image": "", 115 | "position": "50% 50%", 116 | "repeat": "no-repeat", 117 | "size": "cover" 118 | } 119 | } -------------------------------------------------------------------------------- /frontend/constants/abi.json: -------------------------------------------------------------------------------- 1 | [{"type":"constructor","payable":false,"inputs":[{"type":"address","name":"vrfCoordinatorV2"},{"type":"uint256","name":"_entranceFee"},{"type":"bytes32","name":"_gasLane"},{"type":"uint64","name":"_subscriptionId"},{"type":"uint32","name":"_callbackGasLimit"},{"type":"uint256","name":"_interval"}]},{"type":"error","name":"Lottery__NotEnoughEthEntered","inputs":[]},{"type":"error","name":"Lottery__NotOpen","inputs":[]},{"type":"error","name":"Lottery__TransferToWinnerFailed","inputs":[]},{"type":"error","name":"Lottery__UpKeepNotNeeded","inputs":[{"type":"uint256","name":"currentBalance"},{"type":"uint256","name":"numOfPlayers"},{"type":"uint256","name":"raffleState"}]},{"type":"error","name":"OnlyCoordinatorCanFulfill","inputs":[{"type":"address","name":"have"},{"type":"address","name":"want"}]},{"type":"event","anonymous":false,"name":"lotteryEnter","inputs":[{"type":"address","name":"player","indexed":true}]},{"type":"event","anonymous":false,"name":"randomNumberPick","inputs":[{"type":"uint256","name":"requestId","indexed":true}]},{"type":"event","anonymous":false,"name":"winnerPicked","inputs":[{"type":"address","name":"recentWinner","indexed":true}]},{"type":"function","name":"checkUpkeep","constant":true,"stateMutability":"view","payable":false,"gas":29000000,"inputs":[{"type":"bytes"}],"outputs":[{"type":"bool","name":"upkeepNeeded"},{"type":"bytes"}]},{"type":"function","name":"enterLottery","constant":false,"stateMutability":"payable","payable":true,"gas":29000000,"inputs":[],"outputs":[]},{"type":"function","name":"getEntranceFee","constant":true,"stateMutability":"view","payable":false,"gas":29000000,"inputs":[],"outputs":[{"type":"uint256"}]},{"type":"function","name":"getInterval","constant":true,"stateMutability":"view","payable":false,"gas":29000000,"inputs":[],"outputs":[{"type":"uint256"}]},{"type":"function","name":"getLastTimeStamp","constant":true,"stateMutability":"view","payable":false,"gas":29000000,"inputs":[],"outputs":[{"type":"uint256"}]},{"type":"function","name":"getLotteryState","constant":true,"stateMutability":"view","payable":false,"gas":29000000,"inputs":[],"outputs":[{"type":"uint8"}]},{"type":"function","name":"getNumWords","constant":true,"stateMutability":"pure","payable":false,"gas":29000000,"inputs":[],"outputs":[{"type":"uint256"}]},{"type":"function","name":"getNumbersOfPlayers","constant":true,"stateMutability":"view","payable":false,"gas":29000000,"inputs":[],"outputs":[{"type":"uint256"}]},{"type":"function","name":"getPlayer","constant":true,"stateMutability":"view","payable":false,"gas":29000000,"inputs":[{"type":"uint256","name":"index"}],"outputs":[{"type":"address"}]},{"type":"function","name":"getRecentWinner","constant":true,"stateMutability":"view","payable":false,"gas":29000000,"inputs":[],"outputs":[{"type":"address"}]},{"type":"function","name":"getRequestConfirmations","constant":true,"stateMutability":"pure","payable":false,"gas":29000000,"inputs":[],"outputs":[{"type":"uint256"}]},{"type":"function","name":"performUpkeep","constant":false,"payable":false,"gas":29000000,"inputs":[{"type":"bytes"}],"outputs":[]},{"type":"function","name":"rawFulfillRandomWords","constant":false,"payable":false,"gas":29000000,"inputs":[{"type":"uint256","name":"requestId"},{"type":"uint256[]","name":"randomWords"}],"outputs":[]}] -------------------------------------------------------------------------------- /frontend/constants/contractAddresses.json: -------------------------------------------------------------------------------- 1 | {"31337":["0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"]} -------------------------------------------------------------------------------- /frontend/constants/index.js: -------------------------------------------------------------------------------- 1 | const abi = require('./abi.json') 2 | const contractAddresses = require('./contractAddresses.json') 3 | 4 | module.exports = {abi, contractAddresses} -------------------------------------------------------------------------------- /frontend/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | experimental: { 6 | images: { 7 | unoptimized: true, 8 | }, 9 | }, 10 | }; 11 | 12 | module.exports = nextConfig; 13 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "export": "next export", 10 | "lint": "next lint" 11 | }, 12 | "dependencies": { 13 | "ethers": "^5.6.9", 14 | "moralis": "^1.8.1", 15 | "next": "12.2.1", 16 | "react": "18.2.0", 17 | "react-dom": "18.2.0", 18 | "react-moralis": "^1.4.0", 19 | "react-tsparticles": "^2.1.3", 20 | "tsparticles": "^2.1.3", 21 | "tsparticles-updater-twinkle": "^2.1.3", 22 | "web3uikit": "^0.1.166" 23 | }, 24 | "devDependencies": { 25 | "autoprefixer": "^10.4.7", 26 | "eslint": "8.19.0", 27 | "eslint-config-next": "12.2.1", 28 | "postcss": "^8.4.14", 29 | "tailwindcss": "^3.1.5" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /frontend/pages/_app.js: -------------------------------------------------------------------------------- 1 | import "../styles/globals.css"; 2 | import { MoralisProvider } from "react-moralis"; 3 | import {NotificationProvider} from 'web3uikit' 4 | 5 | function MyApp({ Component, pageProps }) { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | 15 | export default MyApp; 16 | -------------------------------------------------------------------------------- /frontend/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function handler(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /frontend/pages/index.js: -------------------------------------------------------------------------------- 1 | import Head from "next/head"; 2 | import Image from "next/image"; 3 | import EnterLottery from "../components/EnterLottery"; 4 | import Navbar from "../components/Navbar"; 5 | import ConnectBtn from "../components/subComponents/btns/ConnectBtn"; 6 | import Particles from "react-tsparticles"; 7 | import tsConfig from '../configs/tsConfig.json' 8 | import { loadFull } from "tsparticles"; 9 | 10 | export default function Home() { 11 | const particlesInit = async (main) => { 12 | console.log(main); 13 | 14 | // you can initialize the tsParticles instance (main) here, adding custom shapes or presets 15 | // this loads the tsparticles package bundle, it's the easiest method for getting everything ready 16 | // starting from v2 you can add only the features you need reducing the bundle size 17 | await loadFull(main); 18 | }; 19 | 20 | return ( 21 |
22 | 23 | Decentralized Lottery 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 32 |
33 |
34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SachinCoder1/FullStack-Decentralized-Lottery-Application/033aa97ba2f10d4d7ebd58a6f0c09b4b81fd0496/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/images/award-img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SachinCoder1/FullStack-Decentralized-Lottery-Application/033aa97ba2f10d4d7ebd58a6f0c09b4b81fd0496/frontend/public/images/award-img.png -------------------------------------------------------------------------------- /frontend/public/images/dice-img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SachinCoder1/FullStack-Decentralized-Lottery-Application/033aa97ba2f10d4d7ebd58a6f0c09b4b81fd0496/frontend/public/images/dice-img.png -------------------------------------------------------------------------------- /frontend/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/styles/Home.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | padding: 0 2rem; 3 | } 4 | 5 | .main { 6 | min-height: 100vh; 7 | padding: 4rem 0; 8 | flex: 1; 9 | display: flex; 10 | flex-direction: column; 11 | justify-content: center; 12 | align-items: center; 13 | } 14 | 15 | .footer { 16 | display: flex; 17 | flex: 1; 18 | padding: 2rem 0; 19 | border-top: 1px solid #eaeaea; 20 | justify-content: center; 21 | align-items: center; 22 | } 23 | 24 | .footer a { 25 | display: flex; 26 | justify-content: center; 27 | align-items: center; 28 | flex-grow: 1; 29 | } 30 | 31 | .title a { 32 | color: #0070f3; 33 | text-decoration: none; 34 | } 35 | 36 | .title a:hover, 37 | .title a:focus, 38 | .title a:active { 39 | text-decoration: underline; 40 | } 41 | 42 | .title { 43 | margin: 0; 44 | line-height: 1.15; 45 | font-size: 4rem; 46 | } 47 | 48 | .title, 49 | .description { 50 | text-align: center; 51 | } 52 | 53 | .description { 54 | margin: 4rem 0; 55 | line-height: 1.5; 56 | font-size: 1.5rem; 57 | } 58 | 59 | .code { 60 | background: #fafafa; 61 | border-radius: 5px; 62 | padding: 0.75rem; 63 | font-size: 1.1rem; 64 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, 65 | Bitstream Vera Sans Mono, Courier New, monospace; 66 | } 67 | 68 | .grid { 69 | display: flex; 70 | align-items: center; 71 | justify-content: center; 72 | flex-wrap: wrap; 73 | max-width: 800px; 74 | } 75 | 76 | .card { 77 | margin: 1rem; 78 | padding: 1.5rem; 79 | text-align: left; 80 | color: inherit; 81 | text-decoration: none; 82 | border: 1px solid #eaeaea; 83 | border-radius: 10px; 84 | transition: color 0.15s ease, border-color 0.15s ease; 85 | max-width: 300px; 86 | } 87 | 88 | .card:hover, 89 | .card:focus, 90 | .card:active { 91 | color: #0070f3; 92 | border-color: #0070f3; 93 | } 94 | 95 | .card h2 { 96 | margin: 0 0 1rem 0; 97 | font-size: 1.5rem; 98 | } 99 | 100 | .card p { 101 | margin: 0; 102 | font-size: 1.25rem; 103 | line-height: 1.5; 104 | } 105 | 106 | .logo { 107 | height: 1em; 108 | margin-left: 0.5rem; 109 | } 110 | 111 | @media (max-width: 600px) { 112 | .grid { 113 | width: 100%; 114 | flex-direction: column; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /frontend/styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /frontend/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | "./pages/**/*.{js,ts,jsx,tsx}", 4 | "./components/**/*.{js,ts,jsx,tsx}", 5 | ], 6 | theme: { 7 | extend: {}, 8 | }, 9 | plugins: [], 10 | } --------------------------------------------------------------------------------