├── src ├── interfaces │ ├── ILotteryV1.sol │ ├── ILotteryToken.sol │ ├── IVRFConsumer.sol │ └── IWrappedLotteryToken.sol ├── LotteryV2.sol ├── WrappedLotteryToken.sol ├── LotteryToken.sol ├── VRFConsumer.sol └── LotteryV1.sol ├── .gitignore ├── foundry.toml ├── test ├── utils │ ├── UUPSProxy.sol │ ├── Math.sol │ └── Util.sol ├── scripts │ ├── package.json │ ├── merkle_tree.js │ ├── data │ │ ├── wallets.dat │ │ └── merkletree.dat │ └── package-lock.json ├── BaseSetup.sol └── Lottery.t.sol ├── remappings.txt ├── .gitmodules ├── .github └── workflows │ └── test.yml ├── script ├── deploy.js └── Lottery.s.sol └── README.md /src/interfaces/ILotteryV1.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | interface ILotteryV1 { 5 | function fulfillRandomWords(uint[] memory) external; 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiler files 2 | cache/ 3 | out/ 4 | test/scripts/node_modules/ 5 | node_modules/ 6 | 7 | # Ignores development broadcast logs 8 | !/broadcast 9 | /broadcast/*/31337/ 10 | /broadcast/**/dry-run/ 11 | 12 | # Docs 13 | docs/ 14 | 15 | # Dotenv file 16 | .env 17 | -------------------------------------------------------------------------------- /src/LotteryV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.20; 3 | 4 | import "./LotteryV1.sol"; 5 | 6 | contract LotteryV2 is LotteryV1 { 7 | uint256 public prizeAmount; 8 | 9 | function setPrizeAmount(uint256 _prizeAmount) public { 10 | prizeAmount = _prizeAmount; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = "src" 3 | out = "out" 4 | libs = ["lib"] 5 | solc = "0.8.20" 6 | remappings = ["@openzeppelin-upgrade/=lib/openzeppelin-contracts-upgradeable", "@openzeppelin/=lib/openzeppelin-contracts", "@chainlink/=lib/chainlink"] 7 | 8 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config -------------------------------------------------------------------------------- /test/utils/UUPSProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.20; 3 | 4 | import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 5 | 6 | contract UUPSProxy is ERC1967Proxy { 7 | constructor( 8 | address _implementation, 9 | bytes memory _data 10 | ) ERC1967Proxy(_implementation, _data) {} 11 | } 12 | -------------------------------------------------------------------------------- /src/interfaces/ILotteryToken.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | interface ILotteryToken { 5 | function mintToken(address _to) external returns (uint256); 6 | 7 | function tokenIdOf(address _to) external view returns (uint256); 8 | 9 | function ownerOf(uint256 tokenId) external view returns (address); 10 | 11 | function changeManagerAddress(address _address) external; 12 | } 13 | -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- 1 | @chainlink/=lib/chainlink/ 2 | @openzeppelin-upgrade/=lib/openzeppelin-contracts-upgradeable/ 3 | @openzeppelin/=lib/openzeppelin-contracts/ 4 | chainlink/=lib/chainlink/ 5 | ds-test/=lib/chainlink/contracts/foundry-lib/forge-std/lib/ds-test/src/ 6 | erc4626-tests/=lib/chainlink/contracts/foundry-lib/openzeppelin-contracts/lib/erc4626-tests/ 7 | forge-std/=lib/chainlink/contracts/foundry-lib/openzeppelin-contracts/lib/forge-std/src/ 8 | -------------------------------------------------------------------------------- /src/interfaces/IVRFConsumer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | interface IVRFConsumer { 5 | // Assumes the subscription is funded sufficiently. 6 | function requestRandomWords() external; 7 | 8 | //function to change the number of requested words per VRF request. 9 | function setNumWords(uint32 _numWords) external; 10 | 11 | function setLotteryAddress(address _address) external; 12 | 13 | function changeOwnerAddress(address _owner) external; 14 | } 15 | -------------------------------------------------------------------------------- /src/interfaces/IWrappedLotteryToken.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | interface IWrappedLotteryToken { 5 | function mintToken(address _owner, address _to) external returns (uint256); 6 | 7 | function burnToken(address _to) external; 8 | 9 | function tokenIdOf(address _to) external view returns (uint256); 10 | 11 | function originOwnerOf(uint256 _tokenId) external view returns (address); 12 | 13 | function ownerOf(uint256 tokenId) external view returns (address); 14 | } 15 | -------------------------------------------------------------------------------- /test/utils/Math.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.20; 3 | 4 | /** 5 | * @title Math library 6 | * @notice The math library. 7 | * @author Alpha 8 | **/ 9 | 10 | library MathCeil { 11 | /** 12 | * @notice a ceiling division 13 | * @return the ceiling result of division 14 | */ 15 | function divCeil(uint256 a, uint256 b) internal pure returns (uint256) { 16 | require(b > 0, "divider must more than 0"); 17 | uint256 c = a / b; 18 | if (a % b != 0) { 19 | c = c + 1; 20 | } 21 | return c; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "merkle-tree", 3 | "version": "1.0.0", 4 | "description": "Scripts to perform solidity/js merkle tree testing", 5 | "main": "generate.js", 6 | "author": "", 7 | "license": "MIT", 8 | "dependencies": { 9 | "ethereumjs-util": "^7.1.4", 10 | "ethers": "^5.6.4", 11 | "keccak256": "^1.0.6", 12 | "merkletreejs": "^0.3.10", 13 | "rlp": "^3.0.0" 14 | }, 15 | "devDependencies": { 16 | "typescript": "^4.6.3" 17 | }, 18 | "scripts": { 19 | "compile": "npx tsc --esModuleInterop ./*.ts", 20 | "start": "node merkle_tree.js" 21 | } 22 | } -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | [submodule "lib/openzeppelin-contracts"] 5 | path = lib/openzeppelin-contracts 6 | url = https://github.com/Openzeppelin/openzeppelin-contracts 7 | [submodule "lib/openzeppelin-contracts-upgradeable"] 8 | path = lib/openzeppelin-contracts-upgradeable 9 | url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable 10 | [submodule "lib/chainlink-brownie-contracts"] 11 | path = lib/chainlink-brownie-contracts 12 | url = https://github.com/smartcontractkit/chainlink-brownie-contracts 13 | [submodule "lib/chainlink"] 14 | path = lib/chainlink 15 | url = https://github.com/smartcontractkit/chainlink 16 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: workflow_dispatch 4 | 5 | env: 6 | FOUNDRY_PROFILE: ci 7 | 8 | jobs: 9 | check: 10 | strategy: 11 | fail-fast: true 12 | 13 | name: Foundry project 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | with: 18 | submodules: recursive 19 | 20 | - name: Install Foundry 21 | uses: foundry-rs/foundry-toolchain@v1 22 | with: 23 | version: nightly 24 | 25 | - name: Run Forge build 26 | run: | 27 | forge --version 28 | forge build --sizes 29 | id: build 30 | 31 | - name: Run Forge tests 32 | run: | 33 | forge test -vvv 34 | id: test 35 | -------------------------------------------------------------------------------- /script/deploy.js: -------------------------------------------------------------------------------- 1 | const { ethers, upgrades } = require("hardhat"); 2 | 3 | async function main() { 4 | 5 | //deploy lotteryV1 6 | const LotteryV1Factory = await ethers.getContractFactory("LotteryV1"); 7 | lotteryV1 = await upgrades.deployProxy( 8 | LotteryV1Factory, 9 | [10, 80, 3, 10, 0xEE86283a2DFCc1f52E86790e275e5b07b44A50E5], 10 | { initializer: "initialize", kind: "uups" } 11 | ); 12 | await lotteryV1.deployed(); 13 | 14 | //upgrade to lottery 15 | const LotteryV2Factory = await ethers.getContractFactory("LotteryV2"); 16 | lottery = await upgrades.upgradeProxy(lotteryV1, LotteryV2Factory, { 17 | gasLimit: 30000000, 18 | }); 19 | } 20 | 21 | // We recommend this pattern to be able to use async/await everywhere 22 | // and properly handle errors. 23 | main().catch((error) => { 24 | console.error(error); 25 | process.exitCode = 1; 26 | }); -------------------------------------------------------------------------------- /src/WrappedLotteryToken.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | import "./LotteryToken.sol"; 5 | 6 | contract WrappedLotteryToken is LotteryToken { 7 | mapping(uint256 => address) internal originTokenOwners; // wrapped Token Id and NFT ticket owner 8 | 9 | constructor( 10 | string memory _name, 11 | string memory _symbol 12 | ) LotteryToken(_name, _symbol) {} 13 | 14 | function mintToken( 15 | address _owner, 16 | address _to 17 | ) external onlyManager returns (uint256) { 18 | uint256 newTokenId = super._mintToken(_to); 19 | 20 | originTokenOwners[newTokenId] = _owner; 21 | 22 | return newTokenId; 23 | } 24 | 25 | function burnToken(address _to) external onlyManager { 26 | uint256 tokenId = tokenIds[_to]; 27 | 28 | delete originTokenOwners[tokenId]; 29 | 30 | _burn(tokenId); 31 | } 32 | 33 | function originOwnerOf(uint256 _tokenId) external view returns (address) { 34 | return originTokenOwners[_tokenId]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/utils/Util.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity 0.8.20; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {console} from "forge-std/console.sol"; 6 | 7 | contract Utils is Test { 8 | bytes32 internal nextUser = keccak256(abi.encodePacked("user address")); 9 | 10 | function getNextUserAddress() external returns (address payable) { 11 | address payable user = payable(address(uint160(uint256(nextUser)))); 12 | nextUser = keccak256(abi.encodePacked(nextUser)); 13 | return user; 14 | } 15 | 16 | // create users with 100 ETH balance each 17 | function createUsers( 18 | uint256 userNum 19 | ) external returns (address payable[] memory) { 20 | address payable[] memory users = new address payable[](userNum); 21 | for (uint256 i = 0; i < userNum; i++) { 22 | address payable user = this.getNextUserAddress(); 23 | vm.deal(user, 100 ether); 24 | users[i] = user; 25 | } 26 | 27 | return users; 28 | } 29 | 30 | // move block.number forward by a given number of blocks 31 | function mineBlocks(uint256 numBlocks) external { 32 | uint256 targetBlock = block.number + numBlocks; 33 | vm.roll(targetBlock); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/LotteryToken.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | import "@openzeppelin/contracts/utils/Counters.sol"; 5 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 6 | import "forge-std/console.sol"; 7 | 8 | contract LotteryToken is ERC721 { 9 | using Counters for Counters.Counter; 10 | 11 | Counters.Counter private tokenIdCounter; 12 | 13 | mapping(address => uint256) public tokenIds; 14 | 15 | // address 16 | address internal managerAddress; 17 | 18 | error NotAvailableForUser(); 19 | 20 | modifier onlyManager() { 21 | checkIsManager(); 22 | _; 23 | } 24 | 25 | constructor( 26 | string memory _name, 27 | string memory _symbol 28 | ) ERC721(_name, _symbol) { 29 | managerAddress = msg.sender; 30 | } 31 | 32 | function changeManagerAddress(address _manager) external onlyManager { 33 | managerAddress = _manager; 34 | } 35 | 36 | function mintToken(address _to) external onlyManager returns (uint256) { 37 | return _mintToken(_to); 38 | } 39 | 40 | function tokenIdOf(address _to) external view returns (uint256) { 41 | return tokenIds[_to]; 42 | } 43 | 44 | function _mintToken(address _to) internal returns (uint256) { 45 | tokenIdCounter.increment(); 46 | 47 | uint256 newTokenId = tokenIdCounter.current(); 48 | _mint(_to, newTokenId); 49 | 50 | tokenIds[_to] = newTokenId; 51 | 52 | return newTokenId; 53 | } 54 | 55 | function checkIsManager() internal view virtual { 56 | if (managerAddress != _msgSender()) revert NotAvailableForUser(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /test/scripts/merkle_tree.js: -------------------------------------------------------------------------------- 1 | const { MerkleTree } = require("merkletreejs"); 2 | const { keccak256 } = require("web3-utils"); 3 | const ethers = require('ethers'); 4 | 5 | var fs = require("fs"); 6 | var path = require("path"); 7 | const WALLET_FILE_PATH = "./data/wallets.dat"; 8 | const MERKLE_TREE_FILE_PATH = "./data/merkletree.dat" 9 | 10 | const wallets = fs 11 | .readFileSync(WALLET_FILE_PATH, "utf8") 12 | .split("\n"); 13 | 14 | const leaves = wallets.map(wallet => 15 | ethers.utils.keccak256(wallet.replace("\r", "")) 16 | ); 17 | console.log(leaves); 18 | const tree = new MerkleTree(leaves, ethers.utils.keccak256, { sortPairs: true }); 19 | const root = tree.getHexRoot(); 20 | const rootHash = tree.getRoot().toString('hex'); 21 | console.log(root); 22 | console.log(rootHash); 23 | 24 | let data = ""; 25 | 26 | for (let i = 0; i < leaves.length; i++) { 27 | const leaf = leaves[i]; 28 | 29 | const proof = tree.getHexProof(leaf); 30 | console.log("leaf = ", leaf); 31 | 32 | data += "Wallet Address : " + wallets[i] + "\n"; 33 | data += "Proof : " + proof + "\n"; 34 | data += "Verify result : " + tree.verify(proof, leaf, root) + "\n\n"; 35 | } 36 | 37 | writeToFile(MERKLE_TREE_FILE_PATH, "Root = " + root + "\n\n" + data, (err) => { 38 | if (err) { 39 | console.error('There was an error writing the file.', err); 40 | } else { 41 | console.log('File has been written successfully.'); 42 | } 43 | }); 44 | 45 | 46 | function writeToFile(filePath, data, callback) { 47 | fs.appendFile(filePath, data, (err) => { 48 | if (err) { 49 | callback(err); 50 | } else { 51 | callback(null); 52 | } 53 | }); 54 | } 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /script/Lottery.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.20; 3 | 4 | import "forge-std/Script.sol"; 5 | import "../src/LotteryV1.sol"; 6 | import "../src/LotteryV2.sol"; 7 | import "../src/LotteryToken.sol"; 8 | import "../src/WrappedLotteryToken.sol"; 9 | import "../src/VRFConsumer.sol"; 10 | 11 | import {UUPSProxy} from "../test/utils/UUPSProxy.sol"; 12 | 13 | contract Lottery is Script { 14 | uint8 internal constant PROTOCOL_FEE = 10; 15 | uint8 internal constant RENTTOKEN_FEE = 80; 16 | uint32 internal constant WINNER_COUNT = 10; 17 | uint64 internal constant SUBSCRIPTION_ID = 5534; 18 | uint256 internal constant RENT_AMOUNT = 3; 19 | 20 | LotteryToken token; 21 | WrappedLotteryToken wrappedToken; 22 | VRFConsumer vrfConsumer; 23 | LotteryV1 wrappedProxy1; 24 | LotteryV2 wrappedProxy2; 25 | UUPSProxy proxy; 26 | 27 | function run() external { 28 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 29 | vm.startBroadcast(deployerPrivateKey); 30 | 31 | token = new LotteryToken("Nft token", "NFT_TOKEN"); 32 | wrappedToken = new WrappedLotteryToken( 33 | "Wrapped Nft token", 34 | "WRAPPED_NFT_TOKEN" 35 | ); 36 | 37 | vrfConsumer = new VRFConsumer(WINNER_COUNT, SUBSCRIPTION_ID); 38 | 39 | LotteryV1 lottery1 = new LotteryV1(); 40 | proxy = new UUPSProxy(address(lottery1), ""); 41 | 42 | wrappedProxy1 = LotteryV1(address(proxy)); 43 | 44 | wrappedProxy1.initialize( 45 | PROTOCOL_FEE, 46 | RENTTOKEN_FEE, 47 | WINNER_COUNT, 48 | RENT_AMOUNT, 49 | 0xEE86283a2DFCc1f52E86790e275e5b07b44A50E5, 50 | address(token), 51 | address(wrappedToken), 52 | address(vrfConsumer) 53 | ); 54 | 55 | LotteryV2 lottery2 = new LotteryV2(); 56 | wrappedProxy1.upgradeTo(address(lottery2)); 57 | 58 | // re-wrap the proxy 59 | wrappedProxy2 = LotteryV2(address(proxy)); 60 | 61 | // Once upgrade is finished, check if the new function is working fine. 62 | wrappedProxy2.setPrizeAmount(200); 63 | 64 | vm.stopBroadcast(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test/scripts/data/wallets.dat: -------------------------------------------------------------------------------- 1 | 0x9aF2E2B7e57c1CD7C68C5C3796d8ea67e0018dB7 2 | 0x2f66c75A001Ba71ccb135934F48d844b46454543 3 | 0x8CE502537D13f249834eAa02DDe4781EBFe0d40f 4 | 0x075edF3ae919FBef9933f666bb0A95C6b80b04ed 5 | 0x974D20930c6Be64Fa09d53FaE28ef7256A39B47F 6 | 0xba62f7a9a1A2f759746F578031968B4CD0Cb6C18 7 | 0x1243FC793B09D6bd0f5940638F4c68Fe99B44F75 8 | 0x35030aad9d382507Ab852DCb6b167A199F5F8b3C 9 | 0x0eA44112778666706279113e8fFdD61AE43b228b 10 | 0x75Bd02633f173f3e74f6d5071E9294e44A8fDfA3 11 | 0xC4A756F649d290a60f582bA4dff8c0Bab190B834 12 | 0x69956f305fc58E20e0eB055d819bAAf0bB4f00E7 13 | 0xC9299b46FC45931007c5EE7CE30935116D72f5b7 14 | 0x2ae1d869d6D86aDadf787195cB33525305e76E48 15 | 0x6263972A92D383CAcba91Ab1734ceF0D64d5BB08 16 | 0x27F1d0Ac6c72a43D5b1f384A84CC6Aa6E752AE00 17 | 0x8904Ff7C89C5581ac7F5fe1c732698018543973f 18 | 0x74AF06f5Af063d7F9F300C2CFe0D80A1d334D06A 19 | 0x07A9054513cb5fF8692853D07d529c4d6A09A435 20 | 0x04aAAdfF8514322915352761cc906ef8a021Eb9B 21 | 0x0726849Cf48E4387e9d656547124Fb2163Fe010B 22 | 0xc47E1c2B96e3fdbD949c680dD3d9002b7C5d1fb7 23 | 0x047a13987d3EC9f4b94ED8b87Abc052AeBa15Ebb 24 | 0x19a92bbEE10848d80B13160c1501398884927Aa7 25 | 0x8995a1b642806A5999c6d602bC8A5B661d6D6682 26 | 0xd47358Ab58ecf9cf22B07F00F712C4D2e216A16E 27 | 0xCE2F2E13a2A280508A060eD8a18d5450F3f43B8d 28 | 0x8AA140D74216bC31B5D8F1b27BE1b291AB41C044 29 | 0xD0AE4aC60f1e7A632b7C9Aa5bCE529A637a1C0C7 30 | 0xEBa2DbdBb35D4D8b21C3D6BCEcc63f4791EafBa8 31 | 0x9437e0f81D377229A4Ec2f41396e72a5206F8706 32 | 0xA43f98837Ab45a360184E2105158F857F0cE3929 33 | 0x171Ef98689d74745085D994188f5F8831f96940F 34 | 0xa73895FC281d27fd452bF00Ce3D16739CCaAcb63 35 | 0xc037dA5967bfDBf841BE5aaD83F4d261c463Ee98 36 | 0x3EfDdDE33a6d135A2219C60F6feb85552302C426 37 | 0xB50328b7498dE5DE820F9C840142CF53A5e94AdB 38 | 0x6F1a852Af244eE5D8bECbe4c78512FB873D150E5 39 | 0x6757Ec5AA352da9FE6bBf27FDEE5624c8a11E3ff 40 | 0xa47a8B20bDdBea889bbC9900d4B57Fe2f1A504cB 41 | 0xb755C996591eA1B6E97a65B373A051cA2648021b 42 | 0x5D04AD29E9A9BA33494a06D9d6EE4f562bdf0Fbc 43 | 0xA208A43f4dEFacB392fcb8fC4719AfeF4f7f14C4 44 | 0x78Cf1f7afeeBc804f36ea2d35d139c6a1B7291bf 45 | 0x9337a746594b190D468cBAD0a5F016fb2D5FbddD 46 | 0x151fcBc48298DB429F069035C7F80BaF0E21eb82 47 | 0x1fBEa008CBa0D1d0CE1724b9F6a1Bb6542b09C52 48 | 0x53e5B89d32D5d716F0d9DD676293fEbC179051D9 49 | 0x81D9c5c1840a28f095ffd3866b78dd7F6C17d99c 50 | 0xFf03dCFFb9A5823970bf85EC2637cF4AB75A1F76 51 | 0xF62849F9A0B5Bf2913b396098F7c7019b51A820a 52 | 0xF62849F9A0B5Bf2913b396098F7c7019b51A820a -------------------------------------------------------------------------------- /src/VRFConsumer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol"; 5 | import "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol"; 6 | import "./interfaces/ILotteryV1.sol"; 7 | import "forge-std/console.sol"; 8 | 9 | contract VRFConsumer is VRFConsumerBaseV2 { 10 | address internal constant VRF_COORDINATOR_ADDRESS = 11 | 0x7a1BaC17Ccc5b313516C5E16fb24f7659aA5ebed; 12 | bytes32 internal constant KEY_HASH = 13 | 0x4b09e658ed251bcafeebbc69400383d49f344ace09b9576fe248bb02c003fe9f; 14 | 15 | uint16 internal constant REQUEST_CONFIRMATION = 5; 16 | uint32 internal constant CALL_BACK_GAS_LIMIT = 2500000; 17 | uint32 public numWords; 18 | uint64 internal subscriptionId; 19 | uint256 public requestId; 20 | 21 | address internal ownerAddress; 22 | 23 | VRFCoordinatorV2Interface internal immutable coordinator; 24 | ILotteryV1 internal lottery; 25 | 26 | modifier onlyOwner() { 27 | require(msg.sender == ownerAddress); 28 | _; 29 | } 30 | 31 | constructor( 32 | uint32 _numWords, 33 | uint64 _subscriptionId 34 | ) VRFConsumerBaseV2(VRF_COORDINATOR_ADDRESS) { 35 | coordinator = VRFCoordinatorV2Interface(VRF_COORDINATOR_ADDRESS); 36 | ownerAddress = msg.sender; 37 | numWords = _numWords; 38 | subscriptionId = _subscriptionId; 39 | } 40 | 41 | function changeOwnerAddress(address _owner) external onlyOwner { 42 | ownerAddress = _owner; 43 | } 44 | 45 | // Assumes the subscription is funded sufficiently. 46 | function requestRandomWords() external onlyOwner { 47 | // Will revert if subscription is not set and funded. 48 | requestId = coordinator.requestRandomWords( 49 | KEY_HASH, 50 | subscriptionId, 51 | REQUEST_CONFIRMATION, 52 | CALL_BACK_GAS_LIMIT, 53 | numWords 54 | ); 55 | } 56 | 57 | //function to change the number of requested words per VRF request. 58 | function setNumWords(uint32 _numWords) external onlyOwner { 59 | numWords = _numWords; 60 | } 61 | 62 | function setLotteryAddress(address _lotteryAddress) external onlyOwner { 63 | lottery = ILotteryV1(_lotteryAddress); 64 | } 65 | 66 | function fulfillRandomWords( 67 | uint256 /*_requestId*/, 68 | uint256[] memory _randomWords 69 | ) internal override { 70 | lottery.fulfillRandomWords(_randomWords); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ## Lottery Smart Contract 5 | 6 | This smart contract is designed to implement a lottery system where users can participate by depositing ETH. The lottery consists of two periods: the DEPOSIT period and the BREAK period. During the DEPOSIT period, users can join the lottery by paying ETH, while in the BREAK period, no new participants are allowed. At the end of each DEPOSIT period, N random winners are determined, and they can claim their winnings. Users who are not winners cannot withdraw their deposits unless they are whitelisted or have rented Ticket NFTs. 7 | 8 | #### Requirements 9 | + Node.js 10 | + Foundry 11 | + Forge 12 | 13 | #### Installation 14 | 15 | 16 | 1. Install Repository. 17 | ```shell 18 | git clone https://github.com/peterch0829/Lottery.git 19 | ``` 20 | 21 | 2. Install dependencies. 22 | ```shell 23 | forge install 24 | ``` 25 | 26 | 3. Compile the contract. 27 | ```shell 28 | forge build 29 | ``` 30 | 31 | 4. Generate the merkle tree using the provided script: 32 | ```shell 33 | cd test/scripts 34 | npm install 35 | node test/scripts/merkle_tree.js 36 | ``` 37 | 38 | 5. Test smart contract. 39 | ```shell 40 | forge test --fork-url -vvv --fork-block-number 41 | ``` 42 | 43 | 6. Deploy the smart contract to your desired Ethereum network using Forge or Hardhat or any other deployment tool of your choice. 44 | ```shell 45 | forge script script/Lottery.s.sol --rpc-url --chain-id 80001 --etherscan-api-key --broadcast --verify -vvvv --legacy 46 | ``` 47 | 48 | 7. Create and deploy the subgraph to fetch the list of winners. Refer to the documentation of the subgraph framework you are using for detailed instructions. 49 | 50 | - Proxy 51 | https://mumbai.polygonscan.com/address/0xCCF6a20fd003C44cB48c3cF6A211659262d70F23 52 | 53 | - Lottery 54 | https://mumbai.polygonscan.com/address/0x51dc3395a4f1985Ca9dd622e6627096726a859E5 55 | 56 | - Subgraph 57 | https://thegraph.com/hosted-service/subgraph/0x00dev/lottery 58 | 59 | #### Usage 60 | 61 | Once the smart contract is deployed and the merkle tree is generated, users can interact with the lottery system using an Ethereum wallet or DApp. 62 | 63 | ##### Joining the Lottery: 64 | 65 | During the DEPOSIT period, users can join the lottery by sending a deposit of ETH to the smart contract. 66 | Whitelisted users can participate for free. 67 | 68 | ##### Renting Ticket NFTs: 69 | 70 | Users who own Ticket NFTs can rent them to other users in exchange for a fixed amount of ETH. 71 | This action mints a new Wrapped NFT for the borrower. 72 | 73 | ##### Claiming Rewards: 74 | 75 | If a Ticket NFT is selected as a winner, the borrower should pay a certain percentage of the reward to the Ticket NFT owner as a fee. 76 | The borrower can claim the remaining amount to their address. 77 | Once claimed, the Wrapped NFT is burnt. 78 | 79 | ##### Handling Pending Rewards: 80 | 81 | If the pending rewards for a Wrapped NFT are not used to claim during the BREAK period, lenders can burn the Wrapped NFT and claim the entire pending reward. 82 | 83 | #### License 84 | 85 | This project is licensed under the [MIT License](https://opensource.org/license/mit/ "MIT License link") 86 | -------------------------------------------------------------------------------- /test/BaseSetup.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import {console} from "forge-std/console.sol"; 5 | import {stdStorage, StdStorage, Test} from "forge-std/Test.sol"; 6 | 7 | import {Utils} from "./utils/Util.sol"; 8 | 9 | contract BaseSetup is Test { 10 | // Skip forward block.timestamp for 3 days. 11 | uint256 internal constant SKIP_FORWARD_PERIOD = 3600 * 24 * 3; 12 | uint256 internal constant USDC_DECIMAL = 1e6; 13 | uint256 internal constant ETHER_DECIMAL = 1e18; 14 | 15 | Utils internal utils; 16 | 17 | address payable[] internal users; 18 | address internal alice; 19 | address internal bob; 20 | address internal carol; 21 | address internal david; 22 | address internal edward; 23 | address internal fraig; 24 | 25 | function setUp() public virtual { 26 | utils = new Utils(); 27 | users = utils.createUsers(50); 28 | 29 | alice = users[0]; 30 | vm.label(alice, "Alice"); 31 | 32 | bob = users[1]; 33 | vm.label(bob, "Bob"); 34 | 35 | carol = users[2]; 36 | vm.label(carol, "Carol"); 37 | 38 | david = users[3]; 39 | vm.label(david, "David"); 40 | 41 | edward = users[4]; 42 | vm.label(edward, "Edward"); 43 | 44 | fraig = users[5]; 45 | vm.label(fraig, "Fraig"); 46 | } 47 | 48 | function getCarolProof() internal pure virtual returns (bytes32[] memory) { 49 | bytes32[] memory carolProof = new bytes32[](6); 50 | carolProof[ 51 | 0 52 | ] = 0x411e41cf526f9b4828f6f061c006384b3f94d8fce89a584920eea74822442900; 53 | carolProof[ 54 | 1 55 | ] = 0x28ada27893d936924fa9e8cfebced2bf6e7a798d7ac2e83bcccf21529736d043; 56 | carolProof[ 57 | 2 58 | ] = 0x9a2e42a5ebbb8ba90ea2dccb4a5b0760b70531b2b96b04398a80e777cc714cab; 59 | carolProof[ 60 | 3 61 | ] = 0x060fc39ac014e73e7231eaa79053ef49eec49e3a554d1f68aa0f384cdfcb5599; 62 | carolProof[ 63 | 4 64 | ] = 0x568e566600331f0f34d18cb63c8d22c4878d32d5dedcc12330980f0b98742cdc; 65 | carolProof[ 66 | 5 67 | ] = 0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f; 68 | 69 | return carolProof; 70 | } 71 | 72 | function getFraigProof() internal pure virtual returns (bytes32[] memory) { 73 | bytes32[] memory fraigProof = new bytes32[](6); 74 | fraigProof[ 75 | 0 76 | ] = 0x5c5566ed9ca278962336d8318093681848d3b15d943f9984481273cd7a0bcdfe; 77 | fraigProof[ 78 | 1 79 | ] = 0x08e9d837d1874b31c14ba278d75a503e6e5a31653e74203255973ff6a0957581; 80 | fraigProof[ 81 | 2 82 | ] = 0x9fd6d6c685b39b8973429ce39ea7d8499ce3d7c1113b27a87d6bf18ff217e0fe; 83 | fraigProof[ 84 | 3 85 | ] = 0x060fc39ac014e73e7231eaa79053ef49eec49e3a554d1f68aa0f384cdfcb5599; 86 | fraigProof[ 87 | 4 88 | ] = 0x568e566600331f0f34d18cb63c8d22c4878d32d5dedcc12330980f0b98742cdc; 89 | fraigProof[ 90 | 5 91 | ] = 0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f; 92 | 93 | return fraigProof; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /test/Lottery.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.20; 3 | 4 | import "@openzeppelin-upgrade/contracts/proxy/ClonesUpgradeable.sol"; 5 | import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; 6 | 7 | import "../src/LotteryV1.sol"; 8 | import "../src/LotteryV2.sol"; 9 | import "../src/LotteryToken.sol"; 10 | import "../src/WrappedLotteryToken.sol"; 11 | import "../src/VRFConsumer.sol"; 12 | 13 | import "./utils/Math.sol"; 14 | import {BaseSetup} from "./BaseSetup.sol"; 15 | import {UUPSProxy} from "./utils/UUPSProxy.sol"; 16 | 17 | contract LotteryTest is BaseSetup { 18 | using ClonesUpgradeable for address; 19 | using MathCeil for uint256; 20 | 21 | LotteryToken public token; 22 | WrappedLotteryToken public wrappedToken; 23 | 24 | LotteryV1 public lottery1; 25 | LotteryV2 public lottery2; 26 | VRFConsumer public vrfConsumer; 27 | 28 | LotteryV1 public wrappedProxy1; 29 | LotteryV2 public wrappedProxy2; 30 | 31 | UUPSProxy public proxy; 32 | 33 | uint8 internal constant PROTOCOL_FEE = 10; 34 | uint8 internal constant RENTTOKEN_FEE = 80; 35 | uint32 internal constant WINNER_COUNT = 10; 36 | uint64 internal constant SUBSCRIPTION_ID = 5534; 37 | uint256 internal constant RENT_AMOUNT = 3; 38 | 39 | bytes32 internal constant ROOT_HASH = 40 | 0x3f00740cab856a945742c57efce40128df32394d0d7e8732fe46a36da2e40d1a; 41 | 42 | function setUp() public virtual override { 43 | BaseSetup.setUp(); 44 | 45 | token = new LotteryToken("Nft token", "NFT_TOKEN"); 46 | wrappedToken = new WrappedLotteryToken( 47 | "Wrapped Nft token", 48 | "WRAPPED_NFT_TOKEN" 49 | ); 50 | 51 | vrfConsumer = new VRFConsumer(WINNER_COUNT, SUBSCRIPTION_ID); 52 | 53 | lottery1 = new LotteryV1(); 54 | proxy = new UUPSProxy(address(lottery1), ""); 55 | 56 | wrappedProxy1 = LotteryV1(address(proxy)); 57 | 58 | wrappedProxy1.initialize( 59 | PROTOCOL_FEE, 60 | RENTTOKEN_FEE, 61 | WINNER_COUNT, 62 | RENT_AMOUNT, 63 | address(david), 64 | address(token), 65 | address(wrappedToken), 66 | address(vrfConsumer) 67 | ); 68 | 69 | token.changeManagerAddress(address(wrappedProxy1)); 70 | wrappedToken.changeManagerAddress(address(wrappedProxy1)); 71 | vrfConsumer.setLotteryAddress(address(wrappedProxy1)); 72 | vrfConsumer.changeOwnerAddress(address(wrappedProxy1)); 73 | } 74 | 75 | function testCanInitialize() public { 76 | assertEq(wrappedProxy1.rentAmount(), RENT_AMOUNT); 77 | assertEq(wrappedProxy1.rentTokenFee(), RENTTOKEN_FEE); 78 | assertEq(wrappedProxy1.protocolFee(), PROTOCOL_FEE); 79 | assertEq(wrappedProxy1.numberOfWinners(), WINNER_COUNT); 80 | } 81 | 82 | function testCanUpgrade() public { 83 | // Create Lottery V2 contract for upgrade. 84 | lottery2 = new LotteryV2(); 85 | wrappedProxy1.upgradeTo(address(lottery2)); 86 | 87 | // re-wrap the proxy 88 | wrappedProxy2 = LotteryV2(address(proxy)); 89 | 90 | // Once upgrade is finished, check Lottery V1's variable is still alive 91 | assertEq(wrappedProxy2.rentAmount(), RENT_AMOUNT); 92 | 93 | // Once upgrade is finished, check if the new function is working fine. 94 | wrappedProxy2.setPrizeAmount(200); 95 | assertEq(wrappedProxy2.prizeAmount(), 200); 96 | assertEq(wrappedProxy2.rentAmount(), RENT_AMOUNT); 97 | assertEq(wrappedProxy2.rentTokenFee(), RENTTOKEN_FEE); 98 | assertEq(wrappedProxy2.protocolFee(), PROTOCOL_FEE); 99 | assertEq(wrappedProxy2.numberOfWinners(), WINNER_COUNT); 100 | } 101 | 102 | function testStartLottery() public { 103 | // Alice is not onwer and tries to start lottery, should revert 104 | vm.startPrank(alice); 105 | vm.expectRevert("Ownable: caller is not the owner"); 106 | wrappedProxy1.startLottery(ROOT_HASH); 107 | vm.stopPrank(); 108 | 109 | // Onwer tries to start lottery with invalid root hash address 110 | vm.expectRevert(LotteryV1.NotValidRootHash.selector); 111 | wrappedProxy1.startLottery(bytes32(0)); 112 | 113 | wrappedProxy1.startLottery(ROOT_HASH); 114 | } 115 | 116 | function testDecideWinner() public { 117 | // Alice is not onwer and tries to start lottery, should revert 118 | vm.startPrank(alice); 119 | vm.expectRevert("Ownable: caller is not the owner"); 120 | wrappedProxy1.decideWinner(); 121 | vm.stopPrank(); 122 | 123 | // Onwer tries to decide winner, but lottery is not started yet, should revert 124 | vm.expectRevert(LotteryV1.LotteryNotInDepositPeriod.selector); 125 | wrappedProxy1.decideWinner(); 126 | 127 | // Onwer sets number of winner 128 | wrappedProxy1.setNumberOfWinners(WINNER_COUNT); 129 | 130 | // Onwer start lottery 131 | wrappedProxy1.startLottery(ROOT_HASH); 132 | 133 | // Onwer tries to decide winner, but no participants in the lottery, should revert 134 | vm.expectRevert(LotteryV1.NoParticipantsInLottery.selector); 135 | wrappedProxy1.decideWinner(); 136 | 137 | bytes32[] memory proof = new bytes32[](0); 138 | 139 | // Alice participate in the current lottery with 20 Ether 140 | vm.prank(alice); 141 | wrappedProxy1.joinLottery{value: 20 ether}(proof); 142 | 143 | // Bob participate in the current lottery with 10 Ether 144 | vm.prank(bob); 145 | wrappedProxy1.joinLottery{value: 10 ether}(proof); 146 | 147 | // Owner sets number of winner 10, but the number of participator is 2 now, should revert 148 | vm.expectRevert(LotteryV1.InvalidNumberOfWinners.selector); 149 | wrappedProxy1.decideWinner(); 150 | 151 | // Frag joins to lottery and he is a whitelist user, so that he doesn't send Ether. 152 | vm.prank(fraig); 153 | wrappedProxy1.joinLottery(getFraigProof()); 154 | 155 | uint256 depositAmount; 156 | 157 | // N members participate in the current lottery 158 | for (uint256 i = 5; i != users.length; ++i) { 159 | uint256 randomNumber = getRandom(); 160 | depositAmount = randomNumber % (1 * ETHER_DECIMAL); 161 | vm.prank(users[i]); 162 | wrappedProxy1.joinLottery{value: depositAmount}(proof); 163 | 164 | skip(3600); 165 | } 166 | 167 | wrappedProxy1.decideWinner(); 168 | } 169 | 170 | function testClaimReward() public { 171 | vm.startPrank(alice); 172 | // Alice tries to enter the current lottery, but lottery is not started yet, therfore should revert 173 | vm.expectRevert(LotteryV1.NotSelectedWinners.selector); 174 | wrappedProxy1.claimReward(); 175 | vm.stopPrank(); 176 | 177 | initLotteryConf(); 178 | 179 | // Bob borrows alice's NFT ticket 180 | vm.prank(bob); 181 | wrappedProxy1.rentTicket{value: RENT_AMOUNT}(address(alice)); 182 | 183 | // Owner decided winner 184 | wrappedProxy1.decideWinner(); 185 | 186 | uint256 balanceBefore = address(bob).balance; 187 | 188 | // Bob claims reward 189 | vm.prank(bob); 190 | wrappedProxy1.claimReward(); 191 | 192 | uint256 bobReward = address(bob).balance - balanceBefore; 193 | 194 | // Bob's lender is Alice and He claims reward 195 | balanceBefore = address(alice).balance; 196 | vm.prank(alice); 197 | wrappedProxy1.claimReward(); 198 | 199 | uint256 aliceReward = address(alice).balance - balanceBefore; 200 | 201 | uint256 totalReward = aliceReward + bobReward; 202 | 203 | // check if Bobs claimed 20% NFT ticket's reward and Alice claimed 80% of it. 204 | assertEq(bobReward, (totalReward * 20) / 100); 205 | assertEq(aliceReward, (totalReward * 80).divCeil(100)); 206 | 207 | /// warning claim test in not break period 208 | } 209 | 210 | function testJoinLottery() public { 211 | bytes32[] memory proof = new bytes32[](0); 212 | 213 | // Alice tries to enter the current lottery, but lottery is not started yet, therfore should revert 214 | vm.startPrank(alice); 215 | vm.expectRevert(LotteryV1.LotteryNotInDepositPeriod.selector); 216 | wrappedProxy1.joinLottery{value: 10}(proof); 217 | vm.stopPrank(); 218 | 219 | // Onwer set number of winners as 2 220 | wrappedProxy1.setNumberOfWinners(2); 221 | 222 | // Owner start the new lottery 223 | wrappedProxy1.startLottery(ROOT_HASH); 224 | 225 | // Alice is not a whitelist user,but tries to participate in the lottery without deposited amount and proof. 226 | vm.startPrank(alice); 227 | vm.expectRevert(LotteryV1.NotWhitelistedUser.selector); 228 | wrappedProxy1.joinLottery(proof); 229 | 230 | // Alice participate in the current lottery 231 | wrappedProxy1.joinLottery{value: 10}(proof); 232 | 233 | skip(1000); 234 | 235 | // Alice deposit amount in the current lottery again 236 | wrappedProxy1.joinLottery{value: 20}(proof); 237 | vm.stopPrank(); 238 | 239 | // Bob participate in the current lottery again 240 | vm.prank(bob); 241 | wrappedProxy1.joinLottery{value: 30}(proof); 242 | 243 | // Carol participate in the current lottery again 244 | vm.prank(carol); 245 | wrappedProxy1.joinLottery{value: 15}(proof); 246 | 247 | // Carol participate in the current lottery again 248 | vm.prank(edward); 249 | wrappedProxy1.joinLottery{value: 5}(proof); 250 | 251 | // Owner decides winner 252 | wrappedProxy1.decideWinner(); 253 | 254 | // Carol tries to participate in the current lottery, but deposit amount is already ended, should revert 255 | vm.expectRevert(LotteryV1.LotteryNotInDepositPeriod.selector); 256 | vm.prank(carol); 257 | wrappedProxy1.joinLottery{value: 10}(proof); 258 | } 259 | 260 | function testRentTicket() public { 261 | bytes32[] memory proof = new bytes32[](0); 262 | 263 | // Alice tries to enter the current lottery, but lottery is not started yet, therfore should revert 264 | vm.startPrank(alice); 265 | vm.expectRevert(LotteryV1.LotteryNotInDepositPeriod.selector); 266 | wrappedProxy1.rentTicket{value: 10}(address(alice)); 267 | vm.stopPrank(); 268 | 269 | // Owner start the new lottery 270 | wrappedProxy1.startLottery(ROOT_HASH); 271 | 272 | // Alice participate in the current lottery 273 | vm.startPrank(alice); 274 | wrappedProxy1.joinLottery{value: 10}(proof); 275 | 276 | // Alice tries to borrow NFT ticket with 2 Ether, but rent amount is 3, should revert 277 | vm.expectRevert(LotteryV1.InsufficientRentAmount.selector); 278 | wrappedProxy1.rentTicket{value: 2}(address(alice)); 279 | 280 | // Alice tries to borrow his NFT ticket with 3 Ether, but can't borrow his own ticket, should revert 281 | vm.expectRevert(LotteryV1.NotRentableForOwner.selector); 282 | wrappedProxy1.rentTicket{value: RENT_AMOUNT}(address(alice)); 283 | vm.stopPrank(); 284 | 285 | vm.startPrank(bob); 286 | // Bob borrows carol's NFT ticket with 3 Ether, but Carol hasn't a NFT ticket for him, so should revert 287 | vm.expectRevert(LotteryV1.InvalidTicketForOwner.selector); 288 | wrappedProxy1.rentTicket{value: RENT_AMOUNT}(address(carol)); 289 | 290 | // Bob borrows alice's NFT ticket 291 | wrappedProxy1.rentTicket{value: RENT_AMOUNT}(address(alice)); 292 | vm.stopPrank(); 293 | } 294 | 295 | function testWithdrawProtocolReward() public { 296 | initLotteryConf(); 297 | 298 | // Owner decides lottery's winner. 299 | wrappedProxy1.decideWinner(); 300 | 301 | // Alice tries to withdraw the protocol reward and he is not a developer of contract, should revert 302 | vm.expectRevert(LotteryV1.InvalidDevAddress.selector); 303 | vm.prank(alice); 304 | wrappedProxy1.withdrawProtocolReward(); 305 | 306 | uint256 balanceBefore = address(david).balance; 307 | 308 | // David is a developer, possible to withdraw protocol reward 309 | vm.prank(david); 310 | wrappedProxy1.withdrawProtocolReward(); 311 | 312 | assertGe(address(david).balance, balanceBefore); 313 | } 314 | 315 | function testWithdrawProtocolRewardWithAmount() public { 316 | initLotteryConf(); 317 | 318 | // Owner decides lottery's winner. 319 | wrappedProxy1.decideWinner(); 320 | 321 | // Alice tries to withdraw the protocol reward and he is not a developer of contract, should revert 322 | vm.expectRevert(LotteryV1.InvalidDevAddress.selector); 323 | vm.prank(alice); 324 | wrappedProxy1.withdrawProtocolReward(10 ether); 325 | 326 | uint256 balanceBefore = address(david).balance; 327 | 328 | // David is a developer, possible to withdraw protocol reward 329 | vm.prank(david); 330 | wrappedProxy1.withdrawProtocolReward(10 ether); 331 | 332 | assertEq(address(david).balance, balanceBefore + 10 ether); 333 | } 334 | 335 | function testSetRentTokenFee() public { 336 | // Alice tries to set rent token fee, but he is not a owner, should rever 337 | vm.expectRevert("Ownable: caller is not the owner"); 338 | vm.prank(alice); 339 | wrappedProxy1.setRentTokenFee(RENTTOKEN_FEE); 340 | 341 | // Lottery is already started, hence should revert 342 | wrappedProxy1.setRentTokenFee(RENTTOKEN_FEE); 343 | 344 | // Owner starts new lottery. 345 | wrappedProxy1.startLottery(ROOT_HASH); 346 | 347 | // Lottery is already started, hence should revert 348 | vm.expectRevert(LotteryV1.LotteryNotEnded.selector); 349 | wrappedProxy1.setRentTokenFee(RENTTOKEN_FEE); 350 | } 351 | 352 | function initLotteryConf() private { 353 | bytes32[] memory aliceProof = new bytes32[](0); 354 | 355 | // Owner starts new lottery. 356 | wrappedProxy1.startLottery(ROOT_HASH); 357 | 358 | // Alice joins to lottery with 10 Ether. 359 | vm.prank(alice); 360 | wrappedProxy1.joinLottery{value: 10 ether}(aliceProof); 361 | 362 | // Carol joins to lottery and he is a whitelist user, so that he doesn't send Ether. 363 | vm.prank(carol); 364 | wrappedProxy1.joinLottery(getCarolProof()); 365 | 366 | // Alice deposit 20 Ether again 367 | vm.prank(alice); 368 | wrappedProxy1.joinLottery{value: 20 ether}(aliceProof); 369 | 370 | // Frag joins to lottery and he is a whitelist user, so that he doesn't send Ether. 371 | vm.prank(fraig); 372 | wrappedProxy1.joinLottery(getFraigProof()); 373 | 374 | uint256 depositAmount; 375 | 376 | for (uint256 i = 10; i != users.length; ++i) { 377 | uint256 randomNumber = getRandom(); 378 | depositAmount = randomNumber % (1 * ETHER_DECIMAL); 379 | vm.prank(users[i]); 380 | wrappedProxy1.joinLottery{value: depositAmount}(aliceProof); 381 | 382 | skip(360); 383 | } 384 | } 385 | 386 | function testIntegration() public { 387 | initLotteryConf(); 388 | 389 | // Owner decides lottery's winner. 390 | wrappedProxy1.decideWinner(); 391 | 392 | // Alice claims his reward 393 | vm.prank(alice); 394 | wrappedProxy1.claimReward(); 395 | } 396 | 397 | function getRandom() private view returns (uint256) { 398 | return 399 | uint256( 400 | keccak256( 401 | abi.encodePacked( 402 | ROOT_HASH, 403 | block.timestamp, 404 | block.difficulty 405 | ) 406 | ) 407 | ); 408 | } 409 | } 410 | -------------------------------------------------------------------------------- /test/scripts/data/merkletree.dat: -------------------------------------------------------------------------------- 1 | Root = 0x3f00740cab856a945742c57efce40128df32394d0d7e8732fe46a36da2e40d1a 2 | 3 | Wallet Address : 0x9aF2E2B7e57c1CD7C68C5C3796d8ea67e0018dB7 4 | Proof : 0x6336b8bb274032aa3be701ac6a1d53b59751cb189032350fca009329bdacf405,0x0986c6f4bf8c4477d8577f391abb444f3d21608b6b68be5ae05ed26cd8dcf054,0x9a2e42a5ebbb8ba90ea2dccb4a5b0760b70531b2b96b04398a80e777cc714cab,0x060fc39ac014e73e7231eaa79053ef49eec49e3a554d1f68aa0f384cdfcb5599,0x568e566600331f0f34d18cb63c8d22c4878d32d5dedcc12330980f0b98742cdc,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 5 | Verify result : true 6 | 7 | Wallet Address : 0x2f66c75A001Ba71ccb135934F48d844b46454543 8 | Proof : 0x972a69aadb9fb2dd5e3d4936ac6c01ebf152fc475a5f13a2ba0c5cf039d11064,0x0986c6f4bf8c4477d8577f391abb444f3d21608b6b68be5ae05ed26cd8dcf054,0x9a2e42a5ebbb8ba90ea2dccb4a5b0760b70531b2b96b04398a80e777cc714cab,0x060fc39ac014e73e7231eaa79053ef49eec49e3a554d1f68aa0f384cdfcb5599,0x568e566600331f0f34d18cb63c8d22c4878d32d5dedcc12330980f0b98742cdc,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 9 | Verify result : true 10 | 11 | Wallet Address : 0x8CE502537D13f249834eAa02DDe4781EBFe0d40f 12 | Proof : 0x411e41cf526f9b4828f6f061c006384b3f94d8fce89a584920eea74822442900,0x28ada27893d936924fa9e8cfebced2bf6e7a798d7ac2e83bcccf21529736d043,0x9a2e42a5ebbb8ba90ea2dccb4a5b0760b70531b2b96b04398a80e777cc714cab,0x060fc39ac014e73e7231eaa79053ef49eec49e3a554d1f68aa0f384cdfcb5599,0x568e566600331f0f34d18cb63c8d22c4878d32d5dedcc12330980f0b98742cdc,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 13 | Verify result : true 14 | 15 | Wallet Address : 0x075edF3ae919FBef9933f666bb0A95C6b80b04ed 16 | Proof : 0x6f27acbb13347a0359ee7a8619cbc89319e9ae4034a33a3e0f5fddd5d2ff468b,0x28ada27893d936924fa9e8cfebced2bf6e7a798d7ac2e83bcccf21529736d043,0x9a2e42a5ebbb8ba90ea2dccb4a5b0760b70531b2b96b04398a80e777cc714cab,0x060fc39ac014e73e7231eaa79053ef49eec49e3a554d1f68aa0f384cdfcb5599,0x568e566600331f0f34d18cb63c8d22c4878d32d5dedcc12330980f0b98742cdc,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 17 | Verify result : true 18 | 19 | Wallet Address : 0x974D20930c6Be64Fa09d53FaE28ef7256A39B47F 20 | Proof : 0x6983349ff3d99186dbf811e90cbbc810784cc7d9d855199e459cc6f64e3a401e,0x08e9d837d1874b31c14ba278d75a503e6e5a31653e74203255973ff6a0957581,0x9fd6d6c685b39b8973429ce39ea7d8499ce3d7c1113b27a87d6bf18ff217e0fe,0x060fc39ac014e73e7231eaa79053ef49eec49e3a554d1f68aa0f384cdfcb5599,0x568e566600331f0f34d18cb63c8d22c4878d32d5dedcc12330980f0b98742cdc,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 21 | Verify result : true 22 | 23 | Wallet Address : 0xba62f7a9a1A2f759746F578031968B4CD0Cb6C18 24 | Proof : 0x5c5566ed9ca278962336d8318093681848d3b15d943f9984481273cd7a0bcdfe,0x08e9d837d1874b31c14ba278d75a503e6e5a31653e74203255973ff6a0957581,0x9fd6d6c685b39b8973429ce39ea7d8499ce3d7c1113b27a87d6bf18ff217e0fe,0x060fc39ac014e73e7231eaa79053ef49eec49e3a554d1f68aa0f384cdfcb5599,0x568e566600331f0f34d18cb63c8d22c4878d32d5dedcc12330980f0b98742cdc,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 25 | Verify result : true 26 | 27 | Wallet Address : 0x1243FC793B09D6bd0f5940638F4c68Fe99B44F75 28 | Proof : 0x2a43ee8ca7eca84d0a2062505c0547ce4ac261033cd5f0b947115bf54c96128b,0x95a05404cc97c044a8de24224ac63846c316796de10f431c7df6f1a7d2877274,0x9fd6d6c685b39b8973429ce39ea7d8499ce3d7c1113b27a87d6bf18ff217e0fe,0x060fc39ac014e73e7231eaa79053ef49eec49e3a554d1f68aa0f384cdfcb5599,0x568e566600331f0f34d18cb63c8d22c4878d32d5dedcc12330980f0b98742cdc,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 29 | Verify result : true 30 | 31 | Wallet Address : 0x35030aad9d382507Ab852DCb6b167A199F5F8b3C 32 | Proof : 0xfde9449d45a6a0421e34e54aae26655d3e28cff5f7480c50553230f3cb4eb4cd,0x95a05404cc97c044a8de24224ac63846c316796de10f431c7df6f1a7d2877274,0x9fd6d6c685b39b8973429ce39ea7d8499ce3d7c1113b27a87d6bf18ff217e0fe,0x060fc39ac014e73e7231eaa79053ef49eec49e3a554d1f68aa0f384cdfcb5599,0x568e566600331f0f34d18cb63c8d22c4878d32d5dedcc12330980f0b98742cdc,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 33 | Verify result : true 34 | 35 | Wallet Address : 0x0eA44112778666706279113e8fFdD61AE43b228b 36 | Proof : 0xc649f826b4be94cd6e9719630720bd4a09f7a90de7ec6d985edeb50f1b3dd765,0x36a85d58b69fe1dc3c32f9668b00ae17e5aafcc23b979215aa445ae8e4514877,0x7654b06ae9237df694259eac9f8d7edfd53e002d24b7efe74f8520327360eda8,0xa10fc32f79c4fa6370517fe72ce7e491da0aa83ab648ac9b80927b7a884967d9,0x568e566600331f0f34d18cb63c8d22c4878d32d5dedcc12330980f0b98742cdc,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 37 | Verify result : true 38 | 39 | Wallet Address : 0x75Bd02633f173f3e74f6d5071E9294e44A8fDfA3 40 | Proof : 0x6d37959719f00b7715c05470e76c12efcebc00fc0bbcea7d2ce9ab5f2af0bdd9,0x36a85d58b69fe1dc3c32f9668b00ae17e5aafcc23b979215aa445ae8e4514877,0x7654b06ae9237df694259eac9f8d7edfd53e002d24b7efe74f8520327360eda8,0xa10fc32f79c4fa6370517fe72ce7e491da0aa83ab648ac9b80927b7a884967d9,0x568e566600331f0f34d18cb63c8d22c4878d32d5dedcc12330980f0b98742cdc,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 41 | Verify result : true 42 | 43 | Wallet Address : 0xC4A756F649d290a60f582bA4dff8c0Bab190B834 44 | Proof : 0xa0024c7389e34f3fbb99c9d8c7029a6f03ca37e43bcb0c89ff34ff4a94184953,0x19005616bb25aef2f8c41603a0c662a6fb9cf1400dca34bcd65b76aa00ad44be,0x7654b06ae9237df694259eac9f8d7edfd53e002d24b7efe74f8520327360eda8,0xa10fc32f79c4fa6370517fe72ce7e491da0aa83ab648ac9b80927b7a884967d9,0x568e566600331f0f34d18cb63c8d22c4878d32d5dedcc12330980f0b98742cdc,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 45 | Verify result : true 46 | 47 | Wallet Address : 0x69956f305fc58E20e0eB055d819bAAf0bB4f00E7 48 | Proof : 0xe357e2bdd5dc4ab62e09d1be875f51009f15986bb919c9fefb743f591ca1a754,0x19005616bb25aef2f8c41603a0c662a6fb9cf1400dca34bcd65b76aa00ad44be,0x7654b06ae9237df694259eac9f8d7edfd53e002d24b7efe74f8520327360eda8,0xa10fc32f79c4fa6370517fe72ce7e491da0aa83ab648ac9b80927b7a884967d9,0x568e566600331f0f34d18cb63c8d22c4878d32d5dedcc12330980f0b98742cdc,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 49 | Verify result : true 50 | 51 | Wallet Address : 0xC9299b46FC45931007c5EE7CE30935116D72f5b7 52 | Proof : 0xc6f61c80155fcaef6c184ea579281e43a9b75836e39bd65b8a62654cb678fbbc,0x808e089e696a2f429f24ee692b57836ef2bf1529fb2cd549fd0fb0adfe6b972a,0x8affa644446076c7c6af95f12f048c4e4231a0630f04f0a9a0a9387b2f7f875b,0xa10fc32f79c4fa6370517fe72ce7e491da0aa83ab648ac9b80927b7a884967d9,0x568e566600331f0f34d18cb63c8d22c4878d32d5dedcc12330980f0b98742cdc,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 53 | Verify result : true 54 | 55 | Wallet Address : 0x2ae1d869d6D86aDadf787195cB33525305e76E48 56 | Proof : 0xda53dbba60bd8a13bdc856be2d06580bf481497d9b090b6cfeb73bc0c1905ed1,0x808e089e696a2f429f24ee692b57836ef2bf1529fb2cd549fd0fb0adfe6b972a,0x8affa644446076c7c6af95f12f048c4e4231a0630f04f0a9a0a9387b2f7f875b,0xa10fc32f79c4fa6370517fe72ce7e491da0aa83ab648ac9b80927b7a884967d9,0x568e566600331f0f34d18cb63c8d22c4878d32d5dedcc12330980f0b98742cdc,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 57 | Verify result : true 58 | 59 | Wallet Address : 0x6263972A92D383CAcba91Ab1734ceF0D64d5BB08 60 | Proof : 0x5ea486b64e79f7e1567c98ec89f87894f4a1739ab9b7d0c12ae5a564be222050,0x429fb6ea9edfa9748920a8985d289341834f4e386e7f3faf25e55c89cd26a9cc,0x8affa644446076c7c6af95f12f048c4e4231a0630f04f0a9a0a9387b2f7f875b,0xa10fc32f79c4fa6370517fe72ce7e491da0aa83ab648ac9b80927b7a884967d9,0x568e566600331f0f34d18cb63c8d22c4878d32d5dedcc12330980f0b98742cdc,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 61 | Verify result : true 62 | 63 | Wallet Address : 0x27F1d0Ac6c72a43D5b1f384A84CC6Aa6E752AE00 64 | Proof : 0xa75b4126028dd4e9f8f1e010342087958470f5923ef3a8e10dca622b94a43655,0x429fb6ea9edfa9748920a8985d289341834f4e386e7f3faf25e55c89cd26a9cc,0x8affa644446076c7c6af95f12f048c4e4231a0630f04f0a9a0a9387b2f7f875b,0xa10fc32f79c4fa6370517fe72ce7e491da0aa83ab648ac9b80927b7a884967d9,0x568e566600331f0f34d18cb63c8d22c4878d32d5dedcc12330980f0b98742cdc,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 65 | Verify result : true 66 | 67 | Wallet Address : 0x8904Ff7C89C5581ac7F5fe1c732698018543973f 68 | Proof : 0x2f59b198c7ac69c8f8618773d12d4e70d51c2893f79d4fa307020fadf403c538,0x02e3aeecdfa0307b31fa26991af554e606873f3507b00c1d8d24fdce72adfee2,0xe90909431063412e88adf9cf0e9587a2c3fa556411e8226c2e2ba9b4b4aab9cd,0x52928c6b38d4dba06b3100212dc0911da4f25327df239708232061a357857a37,0xec4c7b6988aba0a5dcac4103fdbec92e2b5443a91fe4785b792f38807c145050,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 69 | Verify result : true 70 | 71 | Wallet Address : 0x74AF06f5Af063d7F9F300C2CFe0D80A1d334D06A 72 | Proof : 0xf6f10d42776a2b4ed727006030716bd8b0b92dfe8a671297153e51ccc3ca3a90,0x02e3aeecdfa0307b31fa26991af554e606873f3507b00c1d8d24fdce72adfee2,0xe90909431063412e88adf9cf0e9587a2c3fa556411e8226c2e2ba9b4b4aab9cd,0x52928c6b38d4dba06b3100212dc0911da4f25327df239708232061a357857a37,0xec4c7b6988aba0a5dcac4103fdbec92e2b5443a91fe4785b792f38807c145050,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 73 | Verify result : true 74 | 75 | Wallet Address : 0x07A9054513cb5fF8692853D07d529c4d6A09A435 76 | Proof : 0x2d7b716628b832673ea6eedd1b85fcbc290d07afb76c23a75e0323a2c8b3ee5d,0xba43bc461aa6a495b837367ac984dc9d30fc0262187af3b1ca5880cd8897656c,0xe90909431063412e88adf9cf0e9587a2c3fa556411e8226c2e2ba9b4b4aab9cd,0x52928c6b38d4dba06b3100212dc0911da4f25327df239708232061a357857a37,0xec4c7b6988aba0a5dcac4103fdbec92e2b5443a91fe4785b792f38807c145050,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 77 | Verify result : true 78 | 79 | Wallet Address : 0x04aAAdfF8514322915352761cc906ef8a021Eb9B 80 | Proof : 0x78c488d7fa97df319753d93fc187df9451ca86dc3b50304946c1ea99c1a2e243,0xba43bc461aa6a495b837367ac984dc9d30fc0262187af3b1ca5880cd8897656c,0xe90909431063412e88adf9cf0e9587a2c3fa556411e8226c2e2ba9b4b4aab9cd,0x52928c6b38d4dba06b3100212dc0911da4f25327df239708232061a357857a37,0xec4c7b6988aba0a5dcac4103fdbec92e2b5443a91fe4785b792f38807c145050,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 81 | Verify result : true 82 | 83 | Wallet Address : 0x0726849Cf48E4387e9d656547124Fb2163Fe010B 84 | Proof : 0x997efe4272441d67c66fc2fb846d90f986a4005550f8c80c662dcce40a384a12,0x58cda278837c07fdc0678e7bd830b89e96e0df4fed8901902ec9d1bdab6a4f33,0x15f65f5e818e1a04f442b11ded0bbcb22b89e1f0fb5cc73acb864e8b059e7c10,0x52928c6b38d4dba06b3100212dc0911da4f25327df239708232061a357857a37,0xec4c7b6988aba0a5dcac4103fdbec92e2b5443a91fe4785b792f38807c145050,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 85 | Verify result : true 86 | 87 | Wallet Address : 0xc47E1c2B96e3fdbD949c680dD3d9002b7C5d1fb7 88 | Proof : 0xc5640e35531ad9b42487ca7c451ae003456e8f8942090e5461d16410178c06bb,0x58cda278837c07fdc0678e7bd830b89e96e0df4fed8901902ec9d1bdab6a4f33,0x15f65f5e818e1a04f442b11ded0bbcb22b89e1f0fb5cc73acb864e8b059e7c10,0x52928c6b38d4dba06b3100212dc0911da4f25327df239708232061a357857a37,0xec4c7b6988aba0a5dcac4103fdbec92e2b5443a91fe4785b792f38807c145050,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 89 | Verify result : true 90 | 91 | Wallet Address : 0x047a13987d3EC9f4b94ED8b87Abc052AeBa15Ebb 92 | Proof : 0xf938376baff550bb19ad629410d498a24b4f57605c9188fcf0cf78a9977dd2f6,0xf6f4d7db77251aa44a06ef3079cf22584fcb8ed043563aaaab9b1e4e9d6fb171,0x15f65f5e818e1a04f442b11ded0bbcb22b89e1f0fb5cc73acb864e8b059e7c10,0x52928c6b38d4dba06b3100212dc0911da4f25327df239708232061a357857a37,0xec4c7b6988aba0a5dcac4103fdbec92e2b5443a91fe4785b792f38807c145050,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 93 | Verify result : true 94 | 95 | Wallet Address : 0x19a92bbEE10848d80B13160c1501398884927Aa7 96 | Proof : 0x70e49c5459ab7a074fced0bc28e3a6189ca4dc6e904e30d7b47809d77a4e388e,0xf6f4d7db77251aa44a06ef3079cf22584fcb8ed043563aaaab9b1e4e9d6fb171,0x15f65f5e818e1a04f442b11ded0bbcb22b89e1f0fb5cc73acb864e8b059e7c10,0x52928c6b38d4dba06b3100212dc0911da4f25327df239708232061a357857a37,0xec4c7b6988aba0a5dcac4103fdbec92e2b5443a91fe4785b792f38807c145050,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 97 | Verify result : true 98 | 99 | Wallet Address : 0x8995a1b642806A5999c6d602bC8A5B661d6D6682 100 | Proof : 0xec70c40cc317cc760d578eb1d7de0e742a64543db995d799f915db437779c383,0x7b79debe8729efddbb6fc7afa862327e2f3a854101ee958e49de993faa62a97e,0x81fb6aa589fa606178e32ffeca7fee5c3a948cca64440d4afa5fb3bb5151466b,0xcd872e6647de23006d536ed6dcd3a62498fe87bc80a1fb9b75cf24ddc112ab92,0xec4c7b6988aba0a5dcac4103fdbec92e2b5443a91fe4785b792f38807c145050,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 101 | Verify result : true 102 | 103 | Wallet Address : 0xd47358Ab58ecf9cf22B07F00F712C4D2e216A16E 104 | Proof : 0xdeb5072dd6d9a54d01b15f84605a8e3c05df07113330aeff5ec3444dd9897a9f,0x7b79debe8729efddbb6fc7afa862327e2f3a854101ee958e49de993faa62a97e,0x81fb6aa589fa606178e32ffeca7fee5c3a948cca64440d4afa5fb3bb5151466b,0xcd872e6647de23006d536ed6dcd3a62498fe87bc80a1fb9b75cf24ddc112ab92,0xec4c7b6988aba0a5dcac4103fdbec92e2b5443a91fe4785b792f38807c145050,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 105 | Verify result : true 106 | 107 | Wallet Address : 0xCE2F2E13a2A280508A060eD8a18d5450F3f43B8d 108 | Proof : 0xfeb51f1f284d39e3edebe998d5ea022f35c341c0e3b7d7a7992ec56b87fb80e9,0x648fe965298bc7281058b2d5422404970b00aec2274471f37fca365dbcc70104,0x81fb6aa589fa606178e32ffeca7fee5c3a948cca64440d4afa5fb3bb5151466b,0xcd872e6647de23006d536ed6dcd3a62498fe87bc80a1fb9b75cf24ddc112ab92,0xec4c7b6988aba0a5dcac4103fdbec92e2b5443a91fe4785b792f38807c145050,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 109 | Verify result : true 110 | 111 | Wallet Address : 0x8AA140D74216bC31B5D8F1b27BE1b291AB41C044 112 | Proof : 0x33e94770d55fed9889e830316ef0cb2d80c2bd78692c836fa8c81bd8735bcc38,0x648fe965298bc7281058b2d5422404970b00aec2274471f37fca365dbcc70104,0x81fb6aa589fa606178e32ffeca7fee5c3a948cca64440d4afa5fb3bb5151466b,0xcd872e6647de23006d536ed6dcd3a62498fe87bc80a1fb9b75cf24ddc112ab92,0xec4c7b6988aba0a5dcac4103fdbec92e2b5443a91fe4785b792f38807c145050,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 113 | Verify result : true 114 | 115 | Wallet Address : 0xD0AE4aC60f1e7A632b7C9Aa5bCE529A637a1C0C7 116 | Proof : 0x2f73ed12642cd6308124c08e68c2f08123c763cc1d8ecd4fae0cd2a407156bf1,0xfc7ccee089ab4c994ae7cc778859389400af8930953a36518395e47b9695dffc,0x969e120aa0aa63b6f0a3607543b4c1840a11a65c6c0ae744eea7b3396bffb7ea,0xcd872e6647de23006d536ed6dcd3a62498fe87bc80a1fb9b75cf24ddc112ab92,0xec4c7b6988aba0a5dcac4103fdbec92e2b5443a91fe4785b792f38807c145050,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 117 | Verify result : true 118 | 119 | Wallet Address : 0xEBa2DbdBb35D4D8b21C3D6BCEcc63f4791EafBa8 120 | Proof : 0x2481ce851389b37aa7fdce67daa9487dca7d55d43acb6305137831758124c5f6,0xfc7ccee089ab4c994ae7cc778859389400af8930953a36518395e47b9695dffc,0x969e120aa0aa63b6f0a3607543b4c1840a11a65c6c0ae744eea7b3396bffb7ea,0xcd872e6647de23006d536ed6dcd3a62498fe87bc80a1fb9b75cf24ddc112ab92,0xec4c7b6988aba0a5dcac4103fdbec92e2b5443a91fe4785b792f38807c145050,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 121 | Verify result : true 122 | 123 | Wallet Address : 0x9437e0f81D377229A4Ec2f41396e72a5206F8706 124 | Proof : 0x5d8f74ad556866889e010a8fcacded17053d803129fa5266305395d8bf1d6200,0x9c2ece764f23edbec3585d2a5dffa537bb21c388227945e04295e1f3c16fd1a8,0x969e120aa0aa63b6f0a3607543b4c1840a11a65c6c0ae744eea7b3396bffb7ea,0xcd872e6647de23006d536ed6dcd3a62498fe87bc80a1fb9b75cf24ddc112ab92,0xec4c7b6988aba0a5dcac4103fdbec92e2b5443a91fe4785b792f38807c145050,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 125 | Verify result : true 126 | 127 | Wallet Address : 0xA43f98837Ab45a360184E2105158F857F0cE3929 128 | Proof : 0xccd3b5eae93c7c12633253452a64ed4e5598110038d210cc936042fc3cc38b94,0x9c2ece764f23edbec3585d2a5dffa537bb21c388227945e04295e1f3c16fd1a8,0x969e120aa0aa63b6f0a3607543b4c1840a11a65c6c0ae744eea7b3396bffb7ea,0xcd872e6647de23006d536ed6dcd3a62498fe87bc80a1fb9b75cf24ddc112ab92,0xec4c7b6988aba0a5dcac4103fdbec92e2b5443a91fe4785b792f38807c145050,0x09ec4188996816741bb7669dc0236b4364c2aa3f50e355553036883b1e33615f 129 | Verify result : true 130 | 131 | Wallet Address : 0x171Ef98689d74745085D994188f5F8831f96940F 132 | Proof : 0xadbe42a593d54dc102103d319e23acf9cf077baa47661abd6c8ed09a707310c7,0x3efecf53241e5887a50529f822239ae064399166f226c019b49a6fcb9ece36cb,0x98a253f8f9439ca74c3450e5624ea1e984c183301c0e1679082b580a1e231a39,0x7d9709d7d58199415b89ed39d0b00d0743406aa7e357af403883275833cf60f4,0x49ba93b68ce783baf20d22bf29aaedc849b9585aae1d4cc8c1daa1a2f3308e5e,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 133 | Verify result : true 134 | 135 | Wallet Address : 0xa73895FC281d27fd452bF00Ce3D16739CCaAcb63 136 | Proof : 0xdf8a8a3617031c3bc5acecf8005c3b0ea33dfbf87551ec31e08a13b998b7dd0e,0x3efecf53241e5887a50529f822239ae064399166f226c019b49a6fcb9ece36cb,0x98a253f8f9439ca74c3450e5624ea1e984c183301c0e1679082b580a1e231a39,0x7d9709d7d58199415b89ed39d0b00d0743406aa7e357af403883275833cf60f4,0x49ba93b68ce783baf20d22bf29aaedc849b9585aae1d4cc8c1daa1a2f3308e5e,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 137 | Verify result : true 138 | 139 | Wallet Address : 0xc037dA5967bfDBf841BE5aaD83F4d261c463Ee98 140 | Proof : 0xbbcd5b176b765937f104b816cac040b4a51f046d16f0f91f540e4c8f4e09fec9,0x30ea4c9fd3a3f9bd442d435cc93907b085f8061493850fb14db53945f88ece31,0x98a253f8f9439ca74c3450e5624ea1e984c183301c0e1679082b580a1e231a39,0x7d9709d7d58199415b89ed39d0b00d0743406aa7e357af403883275833cf60f4,0x49ba93b68ce783baf20d22bf29aaedc849b9585aae1d4cc8c1daa1a2f3308e5e,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 141 | Verify result : true 142 | 143 | Wallet Address : 0x3EfDdDE33a6d135A2219C60F6feb85552302C426 144 | Proof : 0xe6c6fac08ecdc0ec04524348e58a14552fcae3150765252d8a1f169dd10c17e5,0x30ea4c9fd3a3f9bd442d435cc93907b085f8061493850fb14db53945f88ece31,0x98a253f8f9439ca74c3450e5624ea1e984c183301c0e1679082b580a1e231a39,0x7d9709d7d58199415b89ed39d0b00d0743406aa7e357af403883275833cf60f4,0x49ba93b68ce783baf20d22bf29aaedc849b9585aae1d4cc8c1daa1a2f3308e5e,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 145 | Verify result : true 146 | 147 | Wallet Address : 0xB50328b7498dE5DE820F9C840142CF53A5e94AdB 148 | Proof : 0xbc119bcc5134a65c321c8b05f084cedd0c0b7fd5bdf1c0244cb486e2860a05b3,0x0245e327a5ac40ee02c263c87a2413bd6e14abe2ba9990ac208a9d4b6a20fa82,0xf0365eb8bca5f3cc08bbc97be119b21e1ee1eefdfe4d23276a9592c201ddb777,0x7d9709d7d58199415b89ed39d0b00d0743406aa7e357af403883275833cf60f4,0x49ba93b68ce783baf20d22bf29aaedc849b9585aae1d4cc8c1daa1a2f3308e5e,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 149 | Verify result : true 150 | 151 | Wallet Address : 0x6F1a852Af244eE5D8bECbe4c78512FB873D150E5 152 | Proof : 0xed7f6c06bc4cb2deddaf4359e913601018fd20fb4ee3b588d49cfe01b55aa95b,0x0245e327a5ac40ee02c263c87a2413bd6e14abe2ba9990ac208a9d4b6a20fa82,0xf0365eb8bca5f3cc08bbc97be119b21e1ee1eefdfe4d23276a9592c201ddb777,0x7d9709d7d58199415b89ed39d0b00d0743406aa7e357af403883275833cf60f4,0x49ba93b68ce783baf20d22bf29aaedc849b9585aae1d4cc8c1daa1a2f3308e5e,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 153 | Verify result : true 154 | 155 | Wallet Address : 0x6757Ec5AA352da9FE6bBf27FDEE5624c8a11E3ff 156 | Proof : 0x6da719a32d94c914b2be2852c8e38de8fbdbbd10e7fef2267c4f6bf545669afa,0x68535a415f281549fc882f327608e165e34b51345525a35165129f0abb94bc57,0xf0365eb8bca5f3cc08bbc97be119b21e1ee1eefdfe4d23276a9592c201ddb777,0x7d9709d7d58199415b89ed39d0b00d0743406aa7e357af403883275833cf60f4,0x49ba93b68ce783baf20d22bf29aaedc849b9585aae1d4cc8c1daa1a2f3308e5e,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 157 | Verify result : true 158 | 159 | Wallet Address : 0xa47a8B20bDdBea889bbC9900d4B57Fe2f1A504cB 160 | Proof : 0xd884c4292bb85967ea226dd41330efe080a26f3d9f75ee97894f3318b414130b,0x68535a415f281549fc882f327608e165e34b51345525a35165129f0abb94bc57,0xf0365eb8bca5f3cc08bbc97be119b21e1ee1eefdfe4d23276a9592c201ddb777,0x7d9709d7d58199415b89ed39d0b00d0743406aa7e357af403883275833cf60f4,0x49ba93b68ce783baf20d22bf29aaedc849b9585aae1d4cc8c1daa1a2f3308e5e,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 161 | Verify result : true 162 | 163 | Wallet Address : 0xb755C996591eA1B6E97a65B373A051cA2648021b 164 | Proof : 0x805193d944020a9988d2e41926e67ebf1022a5f3dc2d6ecbc7eda3a871bfa0dd,0x2b31eb3223c67f829f429db1db6e5f3b6e50f511f8a17713305fde1dddce4438,0x0ef0553a28cf4ad88f6df60fb19e49ecc2288ad752e906d2acd9d87cf217e982,0x41a949ca2fec874064b9a6d0ed6a60a9af9873a7e56c372b09852d651fc1742c,0x49ba93b68ce783baf20d22bf29aaedc849b9585aae1d4cc8c1daa1a2f3308e5e,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 165 | Verify result : true 166 | 167 | Wallet Address : 0x5D04AD29E9A9BA33494a06D9d6EE4f562bdf0Fbc 168 | Proof : 0xfa020e301c2779cee122e50d19449d47ab3b50810ff57f18f99fcde028e3bc4b,0x2b31eb3223c67f829f429db1db6e5f3b6e50f511f8a17713305fde1dddce4438,0x0ef0553a28cf4ad88f6df60fb19e49ecc2288ad752e906d2acd9d87cf217e982,0x41a949ca2fec874064b9a6d0ed6a60a9af9873a7e56c372b09852d651fc1742c,0x49ba93b68ce783baf20d22bf29aaedc849b9585aae1d4cc8c1daa1a2f3308e5e,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 169 | Verify result : true 170 | 171 | Wallet Address : 0xA208A43f4dEFacB392fcb8fC4719AfeF4f7f14C4 172 | Proof : 0x74f9b3b3557c199d98cb55f8dd8633a0999ced8f0f44f9a2efe441723358cddb,0x140c6ce9e891b7c9a120c0270e2bc755dfc8b8401aa2ab3d50165141d25c04fe,0x0ef0553a28cf4ad88f6df60fb19e49ecc2288ad752e906d2acd9d87cf217e982,0x41a949ca2fec874064b9a6d0ed6a60a9af9873a7e56c372b09852d651fc1742c,0x49ba93b68ce783baf20d22bf29aaedc849b9585aae1d4cc8c1daa1a2f3308e5e,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 173 | Verify result : true 174 | 175 | Wallet Address : 0x78Cf1f7afeeBc804f36ea2d35d139c6a1B7291bf 176 | Proof : 0x54eef0c728f8918259b55c86b79776faa72a3da60467ae067e42455c3135ca2d,0x140c6ce9e891b7c9a120c0270e2bc755dfc8b8401aa2ab3d50165141d25c04fe,0x0ef0553a28cf4ad88f6df60fb19e49ecc2288ad752e906d2acd9d87cf217e982,0x41a949ca2fec874064b9a6d0ed6a60a9af9873a7e56c372b09852d651fc1742c,0x49ba93b68ce783baf20d22bf29aaedc849b9585aae1d4cc8c1daa1a2f3308e5e,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 177 | Verify result : true 178 | 179 | Wallet Address : 0x9337a746594b190D468cBAD0a5F016fb2D5FbddD 180 | Proof : 0x549c715dfb05eacc43b77d2a8229b44f4d6c6108132ef30b4b1ef388891c4a58,0xb50272828eeb201cabf88ec32619d389adc99041005c3b7844f7533a91f7f80b,0x2917180f4dfd171f7aef115de83956273f46b9df653ff2aa8b7b0b672c24fae3,0x41a949ca2fec874064b9a6d0ed6a60a9af9873a7e56c372b09852d651fc1742c,0x49ba93b68ce783baf20d22bf29aaedc849b9585aae1d4cc8c1daa1a2f3308e5e,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 181 | Verify result : true 182 | 183 | Wallet Address : 0x151fcBc48298DB429F069035C7F80BaF0E21eb82 184 | Proof : 0x0794844e6b20a6173a1f78c99260dd8cdc292562d68c9acd4b45ee6fd8e8f624,0xb50272828eeb201cabf88ec32619d389adc99041005c3b7844f7533a91f7f80b,0x2917180f4dfd171f7aef115de83956273f46b9df653ff2aa8b7b0b672c24fae3,0x41a949ca2fec874064b9a6d0ed6a60a9af9873a7e56c372b09852d651fc1742c,0x49ba93b68ce783baf20d22bf29aaedc849b9585aae1d4cc8c1daa1a2f3308e5e,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 185 | Verify result : true 186 | 187 | Wallet Address : 0x1fBEa008CBa0D1d0CE1724b9F6a1Bb6542b09C52 188 | Proof : 0x3a123c5e716dcaa6b35e2cd90914cee13bbd2f3c660cc4b5ff12319339da4347,0x3d11bf6dfc096385bd33c99ee793872a9941c9ec7b50b5217c2a0ab2fc3eaff9,0x2917180f4dfd171f7aef115de83956273f46b9df653ff2aa8b7b0b672c24fae3,0x41a949ca2fec874064b9a6d0ed6a60a9af9873a7e56c372b09852d651fc1742c,0x49ba93b68ce783baf20d22bf29aaedc849b9585aae1d4cc8c1daa1a2f3308e5e,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 189 | Verify result : true 190 | 191 | Wallet Address : 0x53e5B89d32D5d716F0d9DD676293fEbC179051D9 192 | Proof : 0x8fe06b7192f02099f9f418443bbb5c8a78dd52534f1c8300eb362f71040b6e4d,0x3d11bf6dfc096385bd33c99ee793872a9941c9ec7b50b5217c2a0ab2fc3eaff9,0x2917180f4dfd171f7aef115de83956273f46b9df653ff2aa8b7b0b672c24fae3,0x41a949ca2fec874064b9a6d0ed6a60a9af9873a7e56c372b09852d651fc1742c,0x49ba93b68ce783baf20d22bf29aaedc849b9585aae1d4cc8c1daa1a2f3308e5e,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 193 | Verify result : true 194 | 195 | Wallet Address : 0x81D9c5c1840a28f095ffd3866b78dd7F6C17d99c 196 | Proof : 0xf48e58a90d5fda7ca3a3a6e014a6998f372c55c596c3cbf994cc44d354e2a407,0xaed267d524f1888884a06bf20fac9af822779841797a84e8fd52a7ab56fef9e4,0x87e873c207d6905def81a67a103823ac5480e39426b4dc44978eb6dc642f3c53,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 197 | Verify result : true 198 | 199 | Wallet Address : 0xFf03dCFFb9A5823970bf85EC2637cF4AB75A1F76 200 | Proof : 0xac3f5320da8a78302397dacd46fc660c67d057bf263954338a264c9c6e1c2a17,0xaed267d524f1888884a06bf20fac9af822779841797a84e8fd52a7ab56fef9e4,0x87e873c207d6905def81a67a103823ac5480e39426b4dc44978eb6dc642f3c53,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 201 | Verify result : true 202 | 203 | Wallet Address : 0xF62849F9A0B5Bf2913b396098F7c7019b51A820a 204 | Proof : 0x5aba6a75f0abf94b86704bf62023ef10cde987cb02506d36581bab140dc8ee5d,0x701c9d0229d7c156d9f1b64eda7dd3801c2884240a54be833468f05120d324a4,0x87e873c207d6905def81a67a103823ac5480e39426b4dc44978eb6dc642f3c53,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 205 | Verify result : true 206 | 207 | Wallet Address : 0xF62849F9A0B5Bf2913b396098F7c7019b51A820a 208 | Proof : 0x5aba6a75f0abf94b86704bf62023ef10cde987cb02506d36581bab140dc8ee5d,0x701c9d0229d7c156d9f1b64eda7dd3801c2884240a54be833468f05120d324a4,0x87e873c207d6905def81a67a103823ac5480e39426b4dc44978eb6dc642f3c53,0xacb5c9e13625f79e0beb2c04e9b83fcdb909c779d4c6d1c68f282dd835be3aad 209 | Verify result : true 210 | 211 | -------------------------------------------------------------------------------- /src/LotteryV1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.20; 3 | 4 | import "@openzeppelin-upgrade/contracts/token/ERC721/IERC721Upgradeable.sol"; 5 | import "@openzeppelin-upgrade/contracts/access/OwnableUpgradeable.sol"; 6 | import "@openzeppelin-upgrade/contracts/proxy/utils/UUPSUpgradeable.sol"; 7 | import "@openzeppelin-upgrade/contracts/token/ERC721/utils/ERC721HolderUpgradeable.sol"; 8 | import "@openzeppelin-upgrade/contracts/utils/math/SafeMathUpgradeable.sol"; 9 | import "@openzeppelin-upgrade/contracts/security/ReentrancyGuardUpgradeable.sol"; 10 | import "@openzeppelin-upgrade/contracts/utils/cryptography/MerkleProofUpgradeable.sol"; 11 | 12 | import "./interfaces/ILotteryToken.sol"; 13 | import "./interfaces/IWrappedLotteryToken.sol"; 14 | import "./interfaces/IVRFConsumer.sol"; 15 | 16 | contract LotteryV1 is 17 | Initializable, 18 | UUPSUpgradeable, 19 | OwnableUpgradeable, 20 | ReentrancyGuardUpgradeable 21 | { 22 | using SafeMathUpgradeable for uint256; 23 | 24 | struct Ticket { 25 | uint256 tokenId; 26 | uint256 depositorId; 27 | uint256 wrappedTokenId; 28 | uint256 rewardAmount; 29 | bool ownerClaimed; 30 | } 31 | 32 | struct Depositor { 33 | address user; 34 | uint256 amount; 35 | bool isWhitelistUser; 36 | } 37 | 38 | enum LOTTERY_STATE { 39 | DEPOSIT, 40 | BREAK, 41 | ENDED 42 | } 43 | 44 | uint256 internal constant DEPOSIT_PERIOD = 7 * 86400; // Duration of the deposit period in seconds 45 | uint256 internal constant BREAK_PERIOD = 7 * 86400; // Duration of the break period in seconds 46 | uint64 internal constant SUBSCRIPTION_ID = 5534; 47 | 48 | mapping(address => Ticket) public tickets; // Ticket for users 49 | Depositor[] public depositors; // Depositor list 50 | 51 | bytes32 public rootHash; // root hash for whitelist 52 | uint8 public rentTokenFee; // Rent fee for a NFT token owner 53 | uint8 public protocolFee; // Lottery fee to be rewarded to Lottery contract onwer 54 | uint32 public numberOfWinners; // Number of winners in each period 55 | uint256 public rentAmount; // Rent amount for NFT ticket 56 | uint256 public lotteryUpdatedTime; // start timestamp for the current lottery 57 | uint256 public averageWeight; 58 | 59 | ILotteryToken private token; // NFT token for owner 60 | IWrappedLotteryToken private wrappedToken; // Wrapped token for borrower 61 | IVRFConsumer private vrfConsumer; 62 | 63 | uint256 internal totalDepositAmount; // Total deposit amount in the current lottery 64 | uint256 internal accumulatedProtocolReward; // Protocol fee Reward 65 | bool internal winnersSelected; 66 | address internal devAddress; 67 | LOTTERY_STATE public lotteryState; 68 | 69 | event JoinedLottery( 70 | uint256 indexed tokenId, 71 | address indexed buyer, 72 | uint256 indexed amount 73 | ); 74 | event StartedLottery(uint256 indexed timestamp); 75 | event WinnerSelected(address[] winners); 76 | event BorrowedTicket( 77 | address indexed borrower, 78 | uint256 indexed tokenId, 79 | uint256 indexed wrappedTokenId 80 | ); 81 | event ClaimedReward(uint256 indexed amount); 82 | 83 | error NotWhitelistedUser(); 84 | error LotteryNotEnded(); 85 | error InvalidDevAddress(); 86 | error LotteryNotInDepositPeriod(); 87 | error LotteryNotInBreakPeriod(); 88 | error InsufficientRentAmount(); 89 | error NotRentableForOwner(); 90 | error AlreadyRentTicket(); 91 | error InvalidTicketForOwner(); 92 | error NotWinner(); 93 | error NotAvailableReward(); 94 | error NoReward(); 95 | error AlreadyWinnerSelected(); 96 | error NoParticipantsInLottery(); 97 | error InvalidNumberOfWinners(); 98 | error NotValidRootHash(); 99 | error NotSelectedWinners(); 100 | 101 | /** 102 | * @dev Modifier to check that the current block timestamp is within the deposit period. 103 | * @notice If the current block timestamp is outside the deposit period, the function call will revert. 104 | */ 105 | modifier onlyDuringDepositPeriod() { 106 | checkInDepsoitPeriod(); 107 | _; 108 | } 109 | 110 | /** 111 | * @dev Modifier to check that the current block timestamp is within the break period. 112 | * @notice If the current block timestamp is outside the break period, the function call will revert. 113 | */ 114 | modifier onlyDuringBreakPeriod() { 115 | checkInBreakPeriod(); 116 | _; 117 | } 118 | 119 | /** 120 | * @dev Modifier to check that the lottery has ended. 121 | * @notice This modifier can be used to restrict the execution of a function to after the lottery has ended. 122 | * @notice If the current block timestamp is before the lottery end time, the function call will revert. 123 | */ 124 | modifier onlyLotteryEnded() { 125 | checkLotteryEnded(); 126 | _; 127 | } 128 | 129 | modifier onlyDevAddress() { 130 | checkDevAddress(); 131 | _; 132 | } 133 | 134 | /** 135 | * @dev Initializes the contract with the specified parameters. 136 | * @param _protocolFee The percentage of the total reward that will be charged as protocol fee. 137 | * @param _rentTokenFee The percentage of the total pot that will be charged as rent token fee. 138 | * @param _rentAmount The amount of rent tokens that each participant must hold to participate in the lottery. 139 | * @param _numberOfWinners The number of winners in the lottery draw. 140 | * @notice This function can only be called once after contract deployment. 141 | * @notice The contract owner will be set to the sender of the `initialize` transaction. 142 | */ 143 | function initialize( 144 | uint8 _protocolFee, 145 | uint8 _rentTokenFee, 146 | uint32 _numberOfWinners, 147 | uint256 _rentAmount, 148 | address _devAddress, 149 | address _lotteryTokenAddress, 150 | address _wrappedTokenAddress, 151 | address _consumerAddress 152 | ) external initializer { 153 | __Ownable_init(); 154 | __UUPSUpgradeable_init(); 155 | 156 | numberOfWinners = _numberOfWinners; 157 | protocolFee = _protocolFee; 158 | rentTokenFee = _rentTokenFee; 159 | rentAmount = _rentAmount; 160 | devAddress = _devAddress; 161 | lotteryState = LOTTERY_STATE.ENDED; 162 | 163 | token = ILotteryToken(_lotteryTokenAddress); 164 | wrappedToken = IWrappedLotteryToken(_wrappedTokenAddress); 165 | vrfConsumer = IVRFConsumer(_consumerAddress); 166 | // vrfConsumer.setLotteryAddress(address(this)); 167 | } 168 | 169 | /** 170 | * @dev Starts a new lottery by clearing the depositor list, updating the root hash for whitelist users, 171 | setting the lottery start time, clearing the total deposit amount. 172 | * @param _rootHash The new root hash for whitelist users. 173 | */ 174 | function startLottery( 175 | bytes32 _rootHash 176 | ) external onlyOwner onlyLotteryEnded { 177 | // check if rootHash is valid 178 | if (_rootHash == bytes32(0)) revert NotValidRootHash(); 179 | 180 | // clear depositor list 181 | delete depositors; 182 | 183 | // update rootHash for whitelist users 184 | rootHash = _rootHash; 185 | 186 | // clear totalDeposit amount for new lottery 187 | totalDepositAmount = 0; 188 | 189 | // set winner flag as false and available to decide winner for the current lottery 190 | winnersSelected = false; 191 | 192 | // lottery state as DEPOSIT period 193 | lotteryState = LOTTERY_STATE.DEPOSIT; 194 | 195 | // start lottery and set start time as now 196 | lotteryUpdatedTime = block.timestamp; 197 | 198 | emit StartedLottery(lotteryUpdatedTime); 199 | } 200 | 201 | /** 202 | * @dev Selects the winners of the current lottery and distributes the reward among them. 203 | * 204 | * Requirements: 205 | * - Only the contract owner can call this function. 206 | * - The function can only be called during the deposit period. 207 | * - The function can only be called once per lottery. 208 | * - There must be at least one depositor in the current lottery. 209 | * - The number of winners must be less than or equal to the number of depositors. 210 | */ 211 | function decideWinner() external onlyOwner onlyDuringDepositPeriod { 212 | // if already selected winner, should revert 213 | if (winnersSelected) revert AlreadyWinnerSelected(); 214 | 215 | uint256 depositorCount = depositors.length; 216 | 217 | // if there is no depositors in the current lottery, should revert 218 | if (depositorCount == 0) revert NoParticipantsInLottery(); 219 | 220 | // if depositors are less than winners, should revert 221 | if (numberOfWinners > depositorCount) revert InvalidNumberOfWinners(); 222 | 223 | vrfConsumer.requestRandomWords(); 224 | } 225 | 226 | function removeDeposit(uint256 index) internal { 227 | depositors[index] = depositors[depositors.length - 1]; 228 | depositors.pop(); 229 | } 230 | 231 | /** 232 | * @dev Allows a user to join the current lottery by depositing ETH and receiving a ticket. 233 | * 234 | * If the user is not already a depositor, a new ticket will be minted and added to the user's account. 235 | * If the user has already deposited in a previous lottery, their existing ticket will be updated with the new deposit amount. 236 | * If the user is a whitelisted user, they can join the lottery without depositing any ETH. 237 | * 238 | * Requirements: 239 | * - The function can only be called during the deposit period. 240 | */ 241 | function joinLottery( 242 | bytes32[] calldata _data 243 | ) external payable nonReentrant onlyDuringDepositPeriod { 244 | uint256 depositAmount = msg.value; 245 | 246 | // check if user transfer the valid ETH's amount 247 | if (msg.value == 0) { 248 | bytes32[] memory proof = _data; 249 | // whitelist user doesn't need to send ETH 250 | if (!verifyWhitelistUser(proof, msg.sender)) { 251 | revert NotWhitelistedUser(); 252 | } 253 | 254 | if (averageWeight == 0) depositAmount = 1 * 1e18; 255 | else depositAmount = averageWeight; 256 | } 257 | 258 | // get ticket for user 259 | Ticket storage ticket = tickets[msg.sender]; 260 | 261 | // check if user already joined to the lottery portal once 262 | if (ticket.tokenId == 0) { 263 | // add new depositor in the depositor list 264 | depositors.push( 265 | Depositor( 266 | msg.sender, 267 | depositAmount, 268 | msg.value == 0 ? true : false 269 | ) 270 | ); 271 | 272 | // mint new NFT token for user 273 | uint256 tokenId = token.mintToken(msg.sender); 274 | 275 | ticket.tokenId = tokenId; 276 | ticket.depositorId = depositors.length; 277 | } else { 278 | // check if user already has joined to the previous lottery and get reward, and 279 | if (ticket.rewardAmount > 0) _claimReward(msg.sender, false); 280 | 281 | ticket.ownerClaimed = false; 282 | 283 | uint256 depositorIndex = ticket.depositorId - 1; 284 | 285 | // check if ticket is already created and owner didn't deposit yet in the current lottery draw. 286 | if (depositorIndex >= 0 && depositorIndex < depositors.length) { 287 | Depositor storage depositor = depositors[depositorIndex]; 288 | 289 | if (depositor.user == msg.sender) { 290 | // increase the deposited amount for user 291 | depositor.amount += depositAmount; 292 | } else { 293 | depositors.push( 294 | Depositor( 295 | msg.sender, 296 | depositAmount, 297 | depositAmount == 0 ? true : false 298 | ) 299 | ); 300 | 301 | // update token's deposit id 302 | ticket.depositorId = depositors.length; 303 | } 304 | } 305 | } 306 | 307 | // increase the deposited amount for current lottery 308 | totalDepositAmount += depositAmount; 309 | 310 | emit JoinedLottery(ticket.tokenId, msg.sender, depositAmount); 311 | } 312 | 313 | /** 314 | * @dev Allows a user to rent a ticket from another user by paying the rent amount in ETH. 315 | * 316 | * Requirements: 317 | * - The function can only be called during the deposit period. 318 | * - The user must pay an amount of ETH equal or greater than the rent amount. 319 | * - The user cannot rent their own ticket. 320 | * - The user cannot rent a ticket if they already have a rented ticket. 321 | * - The owner of the ticket must have a valid NFT token. 322 | */ 323 | function rentTicket( 324 | address _owner 325 | ) external payable nonReentrant onlyDuringDepositPeriod { 326 | // check if paying ETH is bigger than rent amount 327 | if (msg.value < rentAmount) revert InsufficientRentAmount(); 328 | 329 | // check if user has already participated in lottery 330 | if (_owner == msg.sender) revert NotRentableForOwner(); 331 | 332 | // check if user has already rent 333 | if (wrappedToken.tokenIdOf(msg.sender) > 0) revert AlreadyRentTicket(); 334 | 335 | // check if owner has his ticket 336 | if (token.tokenIdOf(_owner) == 0) revert InvalidTicketForOwner(); 337 | 338 | // mint wrapped nft token for borrower 339 | uint256 wrappedTokenId = wrappedToken.mintToken(_owner, msg.sender); 340 | 341 | // get the nft ticket and update wrapped token id 342 | Ticket storage ticket = tickets[_owner]; 343 | ticket.wrappedTokenId = wrappedTokenId; 344 | 345 | emit BorrowedTicket(msg.sender, ticket.tokenId, wrappedTokenId); 346 | } 347 | 348 | function claimReward() external nonReentrant { 349 | _claimReward(msg.sender, true); 350 | } 351 | 352 | /** 353 | * @dev Allows the contract owner to withdraw the accumulated protocol reward in ETH. 354 | * 355 | * Requirements: 356 | * - The function can only be called by the contract owner. 357 | */ 358 | function withdrawProtocolReward() 359 | external 360 | onlyDevAddress 361 | nonReentrant 362 | returns (uint256) 363 | { 364 | payable(devAddress).transfer(accumulatedProtocolReward); 365 | 366 | return accumulatedProtocolReward; 367 | } 368 | 369 | /** 370 | * @dev Allows the contract owner to withdraw a specified amount of the accumulated protocol reward in ETH. 371 | * 372 | * Requirements: 373 | * - The function can only be called by the contract owner. 374 | * 375 | * @param _amount The amount of ETH to withdraw from the accumulated protocol reward. 376 | */ 377 | function withdrawProtocolReward( 378 | uint256 _amount 379 | ) external onlyDevAddress nonReentrant returns (uint256) { 380 | uint256 balance = address(devAddress).balance; 381 | if (_amount > balance) { 382 | _amount = balance; 383 | accumulatedProtocolReward -= _amount; 384 | } 385 | 386 | payable(devAddress).transfer(_amount); 387 | 388 | return accumulatedProtocolReward; 389 | } 390 | 391 | /** 392 | * @dev Allows the contract owner to set the rent token fee percentage. 393 | * 394 | * Requirements: 395 | * - The function can only be called by the contract owner. 396 | * - The lottery must have ended. 397 | * 398 | * @param _rentTokenFee The new rent token fee percentage. 399 | */ 400 | function setRentTokenFee( 401 | uint8 _rentTokenFee 402 | ) external onlyOwner onlyLotteryEnded { 403 | rentTokenFee = _rentTokenFee; 404 | } 405 | 406 | /** 407 | * @dev Allows the contract owner to set the protocol fee percentage. 408 | * 409 | * Requirements: 410 | * - The function can only be called by the contract owner. 411 | * - The lottery must have ended. 412 | * 413 | * @param _protocolFee The new protocol fee percentage. 414 | */ 415 | function setProtocolFee( 416 | uint8 _protocolFee 417 | ) external onlyOwner onlyLotteryEnded { 418 | protocolFee = _protocolFee; 419 | } 420 | 421 | /** 422 | * @dev Allows the contract owner to set the number of winners for the lottery. 423 | * 424 | * Requirements: 425 | * - The function can only be called by the contract owner. 426 | * - The lottery must have ended. 427 | * 428 | * @param _numberOfWinners The new number of winners. 429 | */ 430 | function setNumberOfWinners( 431 | uint32 _numberOfWinners 432 | ) external onlyOwner onlyLotteryEnded { 433 | numberOfWinners = _numberOfWinners; 434 | vrfConsumer.setNumWords(_numberOfWinners); 435 | } 436 | 437 | /** 438 | * @dev Allows a user to claim their reward for a winning ticket and/or for borrowing a ticket during the lottery deposit period. 439 | * 440 | * If the user has rented a ticket, the borrower's reward is subtracted from the owner's reward, and the borrower receives their reward amount in ETH. If the user has not rented a ticket, the owner receives their full reward amount in ETH. 441 | * 442 | * Requirements: 443 | * - The function can only be called once per ticket. 444 | * - The function can only be called during the claim period. 445 | * - The user must have a valid NFT token linked to the wrapped token. 446 | * - The user must have a reward amount greater than 0. 447 | * @param _user The address of the user claiming the reward. 448 | * @param _needRevert The flag decide to revert, or not. 449 | */ 450 | function _claimReward(address _user, bool _needRevert) internal { 451 | // check if winners are selected in the current lottery draw 452 | if (winnersSelected == false) { 453 | if (_needRevert) revert NotSelectedWinners(); 454 | else return; 455 | } 456 | 457 | // get user wrapped token id 458 | uint256 borrowTokenId = wrappedToken.tokenIdOf(_user); 459 | 460 | // get owner's ticket 461 | Ticket storage ticket; 462 | 463 | // check if user has a borrow token 464 | if (borrowTokenId > 0) { 465 | // get owner of nft token linked to the wrapped token 466 | address owner = wrappedToken.originOwnerOf(borrowTokenId); 467 | ticket = tickets[owner]; 468 | 469 | // check if period is in break for getting reward 470 | checkInBreakPeriod(); 471 | 472 | // check if user has reward 473 | if (ticket.rewardAmount == 0) { 474 | if (_needRevert) revert NotAvailableReward(); 475 | else return; 476 | } 477 | 478 | // calculate borrower's reward 479 | uint256 borrowerReward; 480 | 481 | if (ticket.ownerClaimed) { 482 | borrowerReward = ticket.rewardAmount; 483 | } else { 484 | borrowerReward = ticket 485 | .rewardAmount 486 | .mul(100 - rentTokenFee) 487 | .div(100); 488 | } 489 | 490 | ticket.rewardAmount -= borrowerReward; 491 | ticket.wrappedTokenId = 0; 492 | 493 | // burn the borrower's wrapped token 494 | wrappedToken.burnToken(_user); 495 | 496 | // transfer reward to borrower 497 | payable(_user).transfer(borrowerReward); 498 | 499 | uint256 tokenId = token.tokenIdOf(_user); 500 | 501 | if (tokenId == 0) return; 502 | 503 | ticket = tickets[_user]; 504 | } else { 505 | ticket = tickets[msg.sender]; 506 | } 507 | 508 | // check if ticket is win or has reward 509 | if (ticket.rewardAmount == 0) { 510 | if (_needRevert) revert NotAvailableReward(); 511 | else return; 512 | } 513 | 514 | uint256 rewardAmount; 515 | 516 | // check if someone has borrown the ticket 517 | if (ticket.wrappedTokenId > 0) { 518 | // check if lottery is still in break period 519 | if (isLotteryEnded()) { 520 | // owner burn the borrower's wrapped token and get all the reward 521 | rewardAmount = ticket.rewardAmount; 522 | 523 | // update reward amount 524 | ticket.rewardAmount = 0; 525 | 526 | // wrapped token id should be 0, because burning borrower's wrapped token 527 | ticket.wrappedTokenId = 0; 528 | 529 | // burn borrower's wrapped token 530 | wrappedToken.burnToken( 531 | wrappedToken.ownerOf(ticket.wrappedTokenId) 532 | ); 533 | } else { 534 | // owner get his reward except the borrower reward 535 | rewardAmount = ticket.rewardAmount.mul(rentTokenFee).div(100); 536 | 537 | // calculates reward amount by substracting owner's lending fee. 538 | ticket.rewardAmount -= rewardAmount; 539 | } 540 | } else { 541 | // owner hasn't borrower and get his reward 542 | rewardAmount = ticket.rewardAmount; 543 | // clear reward amount 544 | ticket.rewardAmount = 0; 545 | } 546 | 547 | // transfer reward to user 548 | if (rewardAmount > 0) payable(_user).transfer(rewardAmount); 549 | 550 | emit ClaimedReward(rewardAmount); 551 | } 552 | 553 | function fulfillRandomWords( 554 | uint256[] memory _randomWords 555 | ) external virtual { 556 | uint256 winnerCount = _randomWords.length; 557 | 558 | address[] memory selectedWinners = new address[](winnerCount); 559 | 560 | uint256 totalAmount = totalDepositAmount; 561 | averageWeight = totalAmount.div(depositors.length); 562 | 563 | // calculates the reward amount for the winners by subtracting the protocol fee from the total deposit amount. 564 | uint256 rewardAmount = totalAmount.mul(100 - protocolFee).div(100); 565 | 566 | // calculates the reward amount per winner. 567 | uint256 rewardAmountPerUser = rewardAmount.div(winnerCount); 568 | 569 | // calculates the accumulated protocol reward by subtracting the reward amount from the total deposit amount. 570 | accumulatedProtocolReward += totalAmount.sub(rewardAmount); 571 | 572 | // Choose winner using QuickSelect algorithm 573 | for (uint256 i; i != winnerCount; ++i) { 574 | uint256 winningNumber; 575 | 576 | if (totalDepositAmount > 0) 577 | winningNumber = _randomWords[i] % totalDepositAmount; 578 | else { 579 | continue; 580 | } 581 | 582 | uint256 accumulated = 0; 583 | uint256 depositorCount = depositors.length; 584 | 585 | for (uint256 j; j != depositorCount; j++) { 586 | Depositor memory depositor = depositors[j]; 587 | 588 | accumulated += depositor.amount; 589 | 590 | if (accumulated >= winningNumber) { 591 | totalDepositAmount -= depositor.amount; 592 | selectedWinners[i] = depositor.user; 593 | 594 | tickets[selectedWinners[i]] 595 | .rewardAmount += rewardAmountPerUser; 596 | removeDeposit(j); 597 | break; 598 | } 599 | } 600 | } 601 | 602 | // sets the winnersSelected flag to true to indicate that the winners have been selected. 603 | winnersSelected = true; 604 | 605 | // set lottery state as BREAK 606 | lotteryState = LOTTERY_STATE.BREAK; 607 | 608 | emit WinnerSelected(selectedWinners); 609 | } 610 | 611 | /** 612 | * @dev Checks whether the lottery has ended. 613 | * 614 | * Requirements: 615 | * - The current time must be after the end of the deposit period and the break period. 616 | * 617 | * Throws: 618 | * - `LotteryNotEnded` if the lottery has not ended yet. 619 | */ 620 | function checkLotteryEnded() internal view virtual { 621 | if (block.timestamp < lotteryUpdatedTime + BREAK_PERIOD) 622 | revert LotteryNotEnded(); 623 | } 624 | 625 | function checkDevAddress() internal view virtual { 626 | if (devAddress != msg.sender) revert InvalidDevAddress(); 627 | } 628 | 629 | function isLotteryEnded() internal view returns (bool) { 630 | if (block.timestamp < lotteryUpdatedTime + BREAK_PERIOD) return false; 631 | 632 | return true; 633 | } 634 | 635 | /** 636 | * @dev Checks whether the current time is within the deposit period. 637 | * 638 | * Requirements: 639 | * - The current time must be between the start of the deposit period and the end of the deposit period. 640 | * 641 | * Throws: 642 | * - `LotteryNotInDepositPeriod` if the current time is not within the deposit period. 643 | */ 644 | function checkInDepsoitPeriod() internal view virtual { 645 | if (lotteryState != LOTTERY_STATE.DEPOSIT) 646 | revert LotteryNotInDepositPeriod(); 647 | } 648 | 649 | /** 650 | * @dev Checks whether the current time is within the break period. 651 | * 652 | * Requirements: 653 | * - The current time must be between the end of the deposit period and the end of the break period. 654 | * 655 | * Throws: 656 | * - `LotteryNotInBreakPeriod` if the current time is not within the break period. 657 | */ 658 | function checkInBreakPeriod() internal view virtual { 659 | if (lotteryState != LOTTERY_STATE.BREAK) 660 | revert LotteryNotInBreakPeriod(); 661 | } 662 | 663 | /** 664 | * @dev Verifies whether a user is on the whitelist. 665 | * 666 | * @param _proof The Merkle proof for the user. 667 | * @param _user The address of the user. 668 | * @return `true` if the user is on the whitelist, `false` otherwise. 669 | */ 670 | function verifyWhitelistUser( 671 | bytes32[] memory _proof, 672 | address _user 673 | ) internal view returns (bool) { 674 | bytes32 leaf = keccak256(abi.encodePacked(_user)); 675 | return MerkleProofUpgradeable.verify(_proof, rootHash, leaf); 676 | } 677 | 678 | function _authorizeUpgrade( 679 | address newImplementation 680 | ) internal override onlyOwner {} 681 | } 682 | -------------------------------------------------------------------------------- /test/scripts/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "merkle-tree", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "merkle-tree", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "ethereumjs-util": "^7.1.4", 13 | "ethers": "^5.6.4", 14 | "keccak256": "^1.0.6", 15 | "merkletreejs": "^0.3.10", 16 | "rlp": "^3.0.0" 17 | }, 18 | "devDependencies": { 19 | "typescript": "^4.6.3" 20 | } 21 | }, 22 | "node_modules/@ethersproject/abi": { 23 | "version": "5.7.0", 24 | "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", 25 | "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", 26 | "funding": [ 27 | { 28 | "type": "individual", 29 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 30 | }, 31 | { 32 | "type": "individual", 33 | "url": "https://www.buymeacoffee.com/ricmoo" 34 | } 35 | ], 36 | "dependencies": { 37 | "@ethersproject/address": "^5.7.0", 38 | "@ethersproject/bignumber": "^5.7.0", 39 | "@ethersproject/bytes": "^5.7.0", 40 | "@ethersproject/constants": "^5.7.0", 41 | "@ethersproject/hash": "^5.7.0", 42 | "@ethersproject/keccak256": "^5.7.0", 43 | "@ethersproject/logger": "^5.7.0", 44 | "@ethersproject/properties": "^5.7.0", 45 | "@ethersproject/strings": "^5.7.0" 46 | } 47 | }, 48 | "node_modules/@ethersproject/abstract-provider": { 49 | "version": "5.7.0", 50 | "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", 51 | "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", 52 | "funding": [ 53 | { 54 | "type": "individual", 55 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 56 | }, 57 | { 58 | "type": "individual", 59 | "url": "https://www.buymeacoffee.com/ricmoo" 60 | } 61 | ], 62 | "dependencies": { 63 | "@ethersproject/bignumber": "^5.7.0", 64 | "@ethersproject/bytes": "^5.7.0", 65 | "@ethersproject/logger": "^5.7.0", 66 | "@ethersproject/networks": "^5.7.0", 67 | "@ethersproject/properties": "^5.7.0", 68 | "@ethersproject/transactions": "^5.7.0", 69 | "@ethersproject/web": "^5.7.0" 70 | } 71 | }, 72 | "node_modules/@ethersproject/abstract-signer": { 73 | "version": "5.7.0", 74 | "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", 75 | "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", 76 | "funding": [ 77 | { 78 | "type": "individual", 79 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 80 | }, 81 | { 82 | "type": "individual", 83 | "url": "https://www.buymeacoffee.com/ricmoo" 84 | } 85 | ], 86 | "dependencies": { 87 | "@ethersproject/abstract-provider": "^5.7.0", 88 | "@ethersproject/bignumber": "^5.7.0", 89 | "@ethersproject/bytes": "^5.7.0", 90 | "@ethersproject/logger": "^5.7.0", 91 | "@ethersproject/properties": "^5.7.0" 92 | } 93 | }, 94 | "node_modules/@ethersproject/address": { 95 | "version": "5.7.0", 96 | "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", 97 | "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", 98 | "funding": [ 99 | { 100 | "type": "individual", 101 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 102 | }, 103 | { 104 | "type": "individual", 105 | "url": "https://www.buymeacoffee.com/ricmoo" 106 | } 107 | ], 108 | "dependencies": { 109 | "@ethersproject/bignumber": "^5.7.0", 110 | "@ethersproject/bytes": "^5.7.0", 111 | "@ethersproject/keccak256": "^5.7.0", 112 | "@ethersproject/logger": "^5.7.0", 113 | "@ethersproject/rlp": "^5.7.0" 114 | } 115 | }, 116 | "node_modules/@ethersproject/base64": { 117 | "version": "5.7.0", 118 | "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", 119 | "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", 120 | "funding": [ 121 | { 122 | "type": "individual", 123 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 124 | }, 125 | { 126 | "type": "individual", 127 | "url": "https://www.buymeacoffee.com/ricmoo" 128 | } 129 | ], 130 | "dependencies": { 131 | "@ethersproject/bytes": "^5.7.0" 132 | } 133 | }, 134 | "node_modules/@ethersproject/basex": { 135 | "version": "5.7.0", 136 | "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", 137 | "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", 138 | "funding": [ 139 | { 140 | "type": "individual", 141 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 142 | }, 143 | { 144 | "type": "individual", 145 | "url": "https://www.buymeacoffee.com/ricmoo" 146 | } 147 | ], 148 | "dependencies": { 149 | "@ethersproject/bytes": "^5.7.0", 150 | "@ethersproject/properties": "^5.7.0" 151 | } 152 | }, 153 | "node_modules/@ethersproject/bignumber": { 154 | "version": "5.7.0", 155 | "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", 156 | "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", 157 | "funding": [ 158 | { 159 | "type": "individual", 160 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 161 | }, 162 | { 163 | "type": "individual", 164 | "url": "https://www.buymeacoffee.com/ricmoo" 165 | } 166 | ], 167 | "dependencies": { 168 | "@ethersproject/bytes": "^5.7.0", 169 | "@ethersproject/logger": "^5.7.0", 170 | "bn.js": "^5.2.1" 171 | } 172 | }, 173 | "node_modules/@ethersproject/bytes": { 174 | "version": "5.7.0", 175 | "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", 176 | "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", 177 | "funding": [ 178 | { 179 | "type": "individual", 180 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 181 | }, 182 | { 183 | "type": "individual", 184 | "url": "https://www.buymeacoffee.com/ricmoo" 185 | } 186 | ], 187 | "dependencies": { 188 | "@ethersproject/logger": "^5.7.0" 189 | } 190 | }, 191 | "node_modules/@ethersproject/constants": { 192 | "version": "5.7.0", 193 | "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", 194 | "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", 195 | "funding": [ 196 | { 197 | "type": "individual", 198 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 199 | }, 200 | { 201 | "type": "individual", 202 | "url": "https://www.buymeacoffee.com/ricmoo" 203 | } 204 | ], 205 | "dependencies": { 206 | "@ethersproject/bignumber": "^5.7.0" 207 | } 208 | }, 209 | "node_modules/@ethersproject/contracts": { 210 | "version": "5.7.0", 211 | "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", 212 | "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", 213 | "funding": [ 214 | { 215 | "type": "individual", 216 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 217 | }, 218 | { 219 | "type": "individual", 220 | "url": "https://www.buymeacoffee.com/ricmoo" 221 | } 222 | ], 223 | "dependencies": { 224 | "@ethersproject/abi": "^5.7.0", 225 | "@ethersproject/abstract-provider": "^5.7.0", 226 | "@ethersproject/abstract-signer": "^5.7.0", 227 | "@ethersproject/address": "^5.7.0", 228 | "@ethersproject/bignumber": "^5.7.0", 229 | "@ethersproject/bytes": "^5.7.0", 230 | "@ethersproject/constants": "^5.7.0", 231 | "@ethersproject/logger": "^5.7.0", 232 | "@ethersproject/properties": "^5.7.0", 233 | "@ethersproject/transactions": "^5.7.0" 234 | } 235 | }, 236 | "node_modules/@ethersproject/hash": { 237 | "version": "5.7.0", 238 | "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", 239 | "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", 240 | "funding": [ 241 | { 242 | "type": "individual", 243 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 244 | }, 245 | { 246 | "type": "individual", 247 | "url": "https://www.buymeacoffee.com/ricmoo" 248 | } 249 | ], 250 | "dependencies": { 251 | "@ethersproject/abstract-signer": "^5.7.0", 252 | "@ethersproject/address": "^5.7.0", 253 | "@ethersproject/base64": "^5.7.0", 254 | "@ethersproject/bignumber": "^5.7.0", 255 | "@ethersproject/bytes": "^5.7.0", 256 | "@ethersproject/keccak256": "^5.7.0", 257 | "@ethersproject/logger": "^5.7.0", 258 | "@ethersproject/properties": "^5.7.0", 259 | "@ethersproject/strings": "^5.7.0" 260 | } 261 | }, 262 | "node_modules/@ethersproject/hdnode": { 263 | "version": "5.7.0", 264 | "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", 265 | "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", 266 | "funding": [ 267 | { 268 | "type": "individual", 269 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 270 | }, 271 | { 272 | "type": "individual", 273 | "url": "https://www.buymeacoffee.com/ricmoo" 274 | } 275 | ], 276 | "dependencies": { 277 | "@ethersproject/abstract-signer": "^5.7.0", 278 | "@ethersproject/basex": "^5.7.0", 279 | "@ethersproject/bignumber": "^5.7.0", 280 | "@ethersproject/bytes": "^5.7.0", 281 | "@ethersproject/logger": "^5.7.0", 282 | "@ethersproject/pbkdf2": "^5.7.0", 283 | "@ethersproject/properties": "^5.7.0", 284 | "@ethersproject/sha2": "^5.7.0", 285 | "@ethersproject/signing-key": "^5.7.0", 286 | "@ethersproject/strings": "^5.7.0", 287 | "@ethersproject/transactions": "^5.7.0", 288 | "@ethersproject/wordlists": "^5.7.0" 289 | } 290 | }, 291 | "node_modules/@ethersproject/json-wallets": { 292 | "version": "5.7.0", 293 | "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", 294 | "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", 295 | "funding": [ 296 | { 297 | "type": "individual", 298 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 299 | }, 300 | { 301 | "type": "individual", 302 | "url": "https://www.buymeacoffee.com/ricmoo" 303 | } 304 | ], 305 | "dependencies": { 306 | "@ethersproject/abstract-signer": "^5.7.0", 307 | "@ethersproject/address": "^5.7.0", 308 | "@ethersproject/bytes": "^5.7.0", 309 | "@ethersproject/hdnode": "^5.7.0", 310 | "@ethersproject/keccak256": "^5.7.0", 311 | "@ethersproject/logger": "^5.7.0", 312 | "@ethersproject/pbkdf2": "^5.7.0", 313 | "@ethersproject/properties": "^5.7.0", 314 | "@ethersproject/random": "^5.7.0", 315 | "@ethersproject/strings": "^5.7.0", 316 | "@ethersproject/transactions": "^5.7.0", 317 | "aes-js": "3.0.0", 318 | "scrypt-js": "3.0.1" 319 | } 320 | }, 321 | "node_modules/@ethersproject/keccak256": { 322 | "version": "5.7.0", 323 | "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", 324 | "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", 325 | "funding": [ 326 | { 327 | "type": "individual", 328 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 329 | }, 330 | { 331 | "type": "individual", 332 | "url": "https://www.buymeacoffee.com/ricmoo" 333 | } 334 | ], 335 | "dependencies": { 336 | "@ethersproject/bytes": "^5.7.0", 337 | "js-sha3": "0.8.0" 338 | } 339 | }, 340 | "node_modules/@ethersproject/logger": { 341 | "version": "5.7.0", 342 | "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", 343 | "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", 344 | "funding": [ 345 | { 346 | "type": "individual", 347 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 348 | }, 349 | { 350 | "type": "individual", 351 | "url": "https://www.buymeacoffee.com/ricmoo" 352 | } 353 | ] 354 | }, 355 | "node_modules/@ethersproject/networks": { 356 | "version": "5.7.1", 357 | "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", 358 | "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", 359 | "funding": [ 360 | { 361 | "type": "individual", 362 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 363 | }, 364 | { 365 | "type": "individual", 366 | "url": "https://www.buymeacoffee.com/ricmoo" 367 | } 368 | ], 369 | "dependencies": { 370 | "@ethersproject/logger": "^5.7.0" 371 | } 372 | }, 373 | "node_modules/@ethersproject/pbkdf2": { 374 | "version": "5.7.0", 375 | "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", 376 | "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", 377 | "funding": [ 378 | { 379 | "type": "individual", 380 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 381 | }, 382 | { 383 | "type": "individual", 384 | "url": "https://www.buymeacoffee.com/ricmoo" 385 | } 386 | ], 387 | "dependencies": { 388 | "@ethersproject/bytes": "^5.7.0", 389 | "@ethersproject/sha2": "^5.7.0" 390 | } 391 | }, 392 | "node_modules/@ethersproject/properties": { 393 | "version": "5.7.0", 394 | "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", 395 | "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", 396 | "funding": [ 397 | { 398 | "type": "individual", 399 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 400 | }, 401 | { 402 | "type": "individual", 403 | "url": "https://www.buymeacoffee.com/ricmoo" 404 | } 405 | ], 406 | "dependencies": { 407 | "@ethersproject/logger": "^5.7.0" 408 | } 409 | }, 410 | "node_modules/@ethersproject/providers": { 411 | "version": "5.7.2", 412 | "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", 413 | "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", 414 | "funding": [ 415 | { 416 | "type": "individual", 417 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 418 | }, 419 | { 420 | "type": "individual", 421 | "url": "https://www.buymeacoffee.com/ricmoo" 422 | } 423 | ], 424 | "dependencies": { 425 | "@ethersproject/abstract-provider": "^5.7.0", 426 | "@ethersproject/abstract-signer": "^5.7.0", 427 | "@ethersproject/address": "^5.7.0", 428 | "@ethersproject/base64": "^5.7.0", 429 | "@ethersproject/basex": "^5.7.0", 430 | "@ethersproject/bignumber": "^5.7.0", 431 | "@ethersproject/bytes": "^5.7.0", 432 | "@ethersproject/constants": "^5.7.0", 433 | "@ethersproject/hash": "^5.7.0", 434 | "@ethersproject/logger": "^5.7.0", 435 | "@ethersproject/networks": "^5.7.0", 436 | "@ethersproject/properties": "^5.7.0", 437 | "@ethersproject/random": "^5.7.0", 438 | "@ethersproject/rlp": "^5.7.0", 439 | "@ethersproject/sha2": "^5.7.0", 440 | "@ethersproject/strings": "^5.7.0", 441 | "@ethersproject/transactions": "^5.7.0", 442 | "@ethersproject/web": "^5.7.0", 443 | "bech32": "1.1.4", 444 | "ws": "7.4.6" 445 | } 446 | }, 447 | "node_modules/@ethersproject/random": { 448 | "version": "5.7.0", 449 | "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", 450 | "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", 451 | "funding": [ 452 | { 453 | "type": "individual", 454 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 455 | }, 456 | { 457 | "type": "individual", 458 | "url": "https://www.buymeacoffee.com/ricmoo" 459 | } 460 | ], 461 | "dependencies": { 462 | "@ethersproject/bytes": "^5.7.0", 463 | "@ethersproject/logger": "^5.7.0" 464 | } 465 | }, 466 | "node_modules/@ethersproject/rlp": { 467 | "version": "5.7.0", 468 | "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", 469 | "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", 470 | "funding": [ 471 | { 472 | "type": "individual", 473 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 474 | }, 475 | { 476 | "type": "individual", 477 | "url": "https://www.buymeacoffee.com/ricmoo" 478 | } 479 | ], 480 | "dependencies": { 481 | "@ethersproject/bytes": "^5.7.0", 482 | "@ethersproject/logger": "^5.7.0" 483 | } 484 | }, 485 | "node_modules/@ethersproject/sha2": { 486 | "version": "5.7.0", 487 | "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", 488 | "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", 489 | "funding": [ 490 | { 491 | "type": "individual", 492 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 493 | }, 494 | { 495 | "type": "individual", 496 | "url": "https://www.buymeacoffee.com/ricmoo" 497 | } 498 | ], 499 | "dependencies": { 500 | "@ethersproject/bytes": "^5.7.0", 501 | "@ethersproject/logger": "^5.7.0", 502 | "hash.js": "1.1.7" 503 | } 504 | }, 505 | "node_modules/@ethersproject/signing-key": { 506 | "version": "5.7.0", 507 | "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", 508 | "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", 509 | "funding": [ 510 | { 511 | "type": "individual", 512 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 513 | }, 514 | { 515 | "type": "individual", 516 | "url": "https://www.buymeacoffee.com/ricmoo" 517 | } 518 | ], 519 | "dependencies": { 520 | "@ethersproject/bytes": "^5.7.0", 521 | "@ethersproject/logger": "^5.7.0", 522 | "@ethersproject/properties": "^5.7.0", 523 | "bn.js": "^5.2.1", 524 | "elliptic": "6.5.4", 525 | "hash.js": "1.1.7" 526 | } 527 | }, 528 | "node_modules/@ethersproject/solidity": { 529 | "version": "5.7.0", 530 | "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", 531 | "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", 532 | "funding": [ 533 | { 534 | "type": "individual", 535 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 536 | }, 537 | { 538 | "type": "individual", 539 | "url": "https://www.buymeacoffee.com/ricmoo" 540 | } 541 | ], 542 | "dependencies": { 543 | "@ethersproject/bignumber": "^5.7.0", 544 | "@ethersproject/bytes": "^5.7.0", 545 | "@ethersproject/keccak256": "^5.7.0", 546 | "@ethersproject/logger": "^5.7.0", 547 | "@ethersproject/sha2": "^5.7.0", 548 | "@ethersproject/strings": "^5.7.0" 549 | } 550 | }, 551 | "node_modules/@ethersproject/strings": { 552 | "version": "5.7.0", 553 | "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", 554 | "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", 555 | "funding": [ 556 | { 557 | "type": "individual", 558 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 559 | }, 560 | { 561 | "type": "individual", 562 | "url": "https://www.buymeacoffee.com/ricmoo" 563 | } 564 | ], 565 | "dependencies": { 566 | "@ethersproject/bytes": "^5.7.0", 567 | "@ethersproject/constants": "^5.7.0", 568 | "@ethersproject/logger": "^5.7.0" 569 | } 570 | }, 571 | "node_modules/@ethersproject/transactions": { 572 | "version": "5.7.0", 573 | "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", 574 | "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", 575 | "funding": [ 576 | { 577 | "type": "individual", 578 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 579 | }, 580 | { 581 | "type": "individual", 582 | "url": "https://www.buymeacoffee.com/ricmoo" 583 | } 584 | ], 585 | "dependencies": { 586 | "@ethersproject/address": "^5.7.0", 587 | "@ethersproject/bignumber": "^5.7.0", 588 | "@ethersproject/bytes": "^5.7.0", 589 | "@ethersproject/constants": "^5.7.0", 590 | "@ethersproject/keccak256": "^5.7.0", 591 | "@ethersproject/logger": "^5.7.0", 592 | "@ethersproject/properties": "^5.7.0", 593 | "@ethersproject/rlp": "^5.7.0", 594 | "@ethersproject/signing-key": "^5.7.0" 595 | } 596 | }, 597 | "node_modules/@ethersproject/units": { 598 | "version": "5.7.0", 599 | "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", 600 | "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", 601 | "funding": [ 602 | { 603 | "type": "individual", 604 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 605 | }, 606 | { 607 | "type": "individual", 608 | "url": "https://www.buymeacoffee.com/ricmoo" 609 | } 610 | ], 611 | "dependencies": { 612 | "@ethersproject/bignumber": "^5.7.0", 613 | "@ethersproject/constants": "^5.7.0", 614 | "@ethersproject/logger": "^5.7.0" 615 | } 616 | }, 617 | "node_modules/@ethersproject/wallet": { 618 | "version": "5.7.0", 619 | "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", 620 | "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", 621 | "funding": [ 622 | { 623 | "type": "individual", 624 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 625 | }, 626 | { 627 | "type": "individual", 628 | "url": "https://www.buymeacoffee.com/ricmoo" 629 | } 630 | ], 631 | "dependencies": { 632 | "@ethersproject/abstract-provider": "^5.7.0", 633 | "@ethersproject/abstract-signer": "^5.7.0", 634 | "@ethersproject/address": "^5.7.0", 635 | "@ethersproject/bignumber": "^5.7.0", 636 | "@ethersproject/bytes": "^5.7.0", 637 | "@ethersproject/hash": "^5.7.0", 638 | "@ethersproject/hdnode": "^5.7.0", 639 | "@ethersproject/json-wallets": "^5.7.0", 640 | "@ethersproject/keccak256": "^5.7.0", 641 | "@ethersproject/logger": "^5.7.0", 642 | "@ethersproject/properties": "^5.7.0", 643 | "@ethersproject/random": "^5.7.0", 644 | "@ethersproject/signing-key": "^5.7.0", 645 | "@ethersproject/transactions": "^5.7.0", 646 | "@ethersproject/wordlists": "^5.7.0" 647 | } 648 | }, 649 | "node_modules/@ethersproject/web": { 650 | "version": "5.7.1", 651 | "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", 652 | "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", 653 | "funding": [ 654 | { 655 | "type": "individual", 656 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 657 | }, 658 | { 659 | "type": "individual", 660 | "url": "https://www.buymeacoffee.com/ricmoo" 661 | } 662 | ], 663 | "dependencies": { 664 | "@ethersproject/base64": "^5.7.0", 665 | "@ethersproject/bytes": "^5.7.0", 666 | "@ethersproject/logger": "^5.7.0", 667 | "@ethersproject/properties": "^5.7.0", 668 | "@ethersproject/strings": "^5.7.0" 669 | } 670 | }, 671 | "node_modules/@ethersproject/wordlists": { 672 | "version": "5.7.0", 673 | "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", 674 | "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", 675 | "funding": [ 676 | { 677 | "type": "individual", 678 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 679 | }, 680 | { 681 | "type": "individual", 682 | "url": "https://www.buymeacoffee.com/ricmoo" 683 | } 684 | ], 685 | "dependencies": { 686 | "@ethersproject/bytes": "^5.7.0", 687 | "@ethersproject/hash": "^5.7.0", 688 | "@ethersproject/logger": "^5.7.0", 689 | "@ethersproject/properties": "^5.7.0", 690 | "@ethersproject/strings": "^5.7.0" 691 | } 692 | }, 693 | "node_modules/@types/bn.js": { 694 | "version": "5.1.1", 695 | "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", 696 | "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", 697 | "dependencies": { 698 | "@types/node": "*" 699 | } 700 | }, 701 | "node_modules/@types/node": { 702 | "version": "20.4.2", 703 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.2.tgz", 704 | "integrity": "sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==" 705 | }, 706 | "node_modules/@types/pbkdf2": { 707 | "version": "3.1.0", 708 | "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", 709 | "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==", 710 | "dependencies": { 711 | "@types/node": "*" 712 | } 713 | }, 714 | "node_modules/@types/secp256k1": { 715 | "version": "4.0.3", 716 | "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", 717 | "integrity": "sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==", 718 | "dependencies": { 719 | "@types/node": "*" 720 | } 721 | }, 722 | "node_modules/aes-js": { 723 | "version": "3.0.0", 724 | "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", 725 | "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==" 726 | }, 727 | "node_modules/base-x": { 728 | "version": "3.0.9", 729 | "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", 730 | "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", 731 | "dependencies": { 732 | "safe-buffer": "^5.0.1" 733 | } 734 | }, 735 | "node_modules/base64-js": { 736 | "version": "1.5.1", 737 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 738 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 739 | "funding": [ 740 | { 741 | "type": "github", 742 | "url": "https://github.com/sponsors/feross" 743 | }, 744 | { 745 | "type": "patreon", 746 | "url": "https://www.patreon.com/feross" 747 | }, 748 | { 749 | "type": "consulting", 750 | "url": "https://feross.org/support" 751 | } 752 | ] 753 | }, 754 | "node_modules/bech32": { 755 | "version": "1.1.4", 756 | "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", 757 | "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" 758 | }, 759 | "node_modules/bignumber.js": { 760 | "version": "9.1.1", 761 | "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", 762 | "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==", 763 | "engines": { 764 | "node": "*" 765 | } 766 | }, 767 | "node_modules/blakejs": { 768 | "version": "1.2.1", 769 | "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", 770 | "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==" 771 | }, 772 | "node_modules/bn.js": { 773 | "version": "5.2.1", 774 | "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", 775 | "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" 776 | }, 777 | "node_modules/brorand": { 778 | "version": "1.1.0", 779 | "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", 780 | "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" 781 | }, 782 | "node_modules/browserify-aes": { 783 | "version": "1.2.0", 784 | "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", 785 | "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", 786 | "dependencies": { 787 | "buffer-xor": "^1.0.3", 788 | "cipher-base": "^1.0.0", 789 | "create-hash": "^1.1.0", 790 | "evp_bytestokey": "^1.0.3", 791 | "inherits": "^2.0.1", 792 | "safe-buffer": "^5.0.1" 793 | } 794 | }, 795 | "node_modules/bs58": { 796 | "version": "4.0.1", 797 | "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", 798 | "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", 799 | "dependencies": { 800 | "base-x": "^3.0.2" 801 | } 802 | }, 803 | "node_modules/bs58check": { 804 | "version": "2.1.2", 805 | "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", 806 | "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", 807 | "dependencies": { 808 | "bs58": "^4.0.0", 809 | "create-hash": "^1.1.0", 810 | "safe-buffer": "^5.1.2" 811 | } 812 | }, 813 | "node_modules/buffer": { 814 | "version": "6.0.3", 815 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", 816 | "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", 817 | "funding": [ 818 | { 819 | "type": "github", 820 | "url": "https://github.com/sponsors/feross" 821 | }, 822 | { 823 | "type": "patreon", 824 | "url": "https://www.patreon.com/feross" 825 | }, 826 | { 827 | "type": "consulting", 828 | "url": "https://feross.org/support" 829 | } 830 | ], 831 | "dependencies": { 832 | "base64-js": "^1.3.1", 833 | "ieee754": "^1.2.1" 834 | } 835 | }, 836 | "node_modules/buffer-reverse": { 837 | "version": "1.0.1", 838 | "resolved": "https://registry.npmjs.org/buffer-reverse/-/buffer-reverse-1.0.1.tgz", 839 | "integrity": "sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg==" 840 | }, 841 | "node_modules/buffer-xor": { 842 | "version": "1.0.3", 843 | "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", 844 | "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" 845 | }, 846 | "node_modules/cipher-base": { 847 | "version": "1.0.4", 848 | "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", 849 | "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", 850 | "dependencies": { 851 | "inherits": "^2.0.1", 852 | "safe-buffer": "^5.0.1" 853 | } 854 | }, 855 | "node_modules/create-hash": { 856 | "version": "1.2.0", 857 | "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", 858 | "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", 859 | "dependencies": { 860 | "cipher-base": "^1.0.1", 861 | "inherits": "^2.0.1", 862 | "md5.js": "^1.3.4", 863 | "ripemd160": "^2.0.1", 864 | "sha.js": "^2.4.0" 865 | } 866 | }, 867 | "node_modules/create-hmac": { 868 | "version": "1.1.7", 869 | "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", 870 | "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", 871 | "dependencies": { 872 | "cipher-base": "^1.0.3", 873 | "create-hash": "^1.1.0", 874 | "inherits": "^2.0.1", 875 | "ripemd160": "^2.0.0", 876 | "safe-buffer": "^5.0.1", 877 | "sha.js": "^2.4.8" 878 | } 879 | }, 880 | "node_modules/crypto-js": { 881 | "version": "3.3.0", 882 | "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz", 883 | "integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==" 884 | }, 885 | "node_modules/elliptic": { 886 | "version": "6.5.4", 887 | "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", 888 | "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", 889 | "dependencies": { 890 | "bn.js": "^4.11.9", 891 | "brorand": "^1.1.0", 892 | "hash.js": "^1.0.0", 893 | "hmac-drbg": "^1.0.1", 894 | "inherits": "^2.0.4", 895 | "minimalistic-assert": "^1.0.1", 896 | "minimalistic-crypto-utils": "^1.0.1" 897 | } 898 | }, 899 | "node_modules/elliptic/node_modules/bn.js": { 900 | "version": "4.12.0", 901 | "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", 902 | "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" 903 | }, 904 | "node_modules/ethereum-bloom-filters": { 905 | "version": "1.0.10", 906 | "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz", 907 | "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==", 908 | "dependencies": { 909 | "js-sha3": "^0.8.0" 910 | } 911 | }, 912 | "node_modules/ethereum-cryptography": { 913 | "version": "0.1.3", 914 | "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", 915 | "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", 916 | "dependencies": { 917 | "@types/pbkdf2": "^3.0.0", 918 | "@types/secp256k1": "^4.0.1", 919 | "blakejs": "^1.1.0", 920 | "browserify-aes": "^1.2.0", 921 | "bs58check": "^2.1.2", 922 | "create-hash": "^1.2.0", 923 | "create-hmac": "^1.1.7", 924 | "hash.js": "^1.1.7", 925 | "keccak": "^3.0.0", 926 | "pbkdf2": "^3.0.17", 927 | "randombytes": "^2.1.0", 928 | "safe-buffer": "^5.1.2", 929 | "scrypt-js": "^3.0.0", 930 | "secp256k1": "^4.0.1", 931 | "setimmediate": "^1.0.5" 932 | } 933 | }, 934 | "node_modules/ethereumjs-util": { 935 | "version": "7.1.5", 936 | "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", 937 | "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", 938 | "dependencies": { 939 | "@types/bn.js": "^5.1.0", 940 | "bn.js": "^5.1.2", 941 | "create-hash": "^1.1.2", 942 | "ethereum-cryptography": "^0.1.3", 943 | "rlp": "^2.2.4" 944 | }, 945 | "engines": { 946 | "node": ">=10.0.0" 947 | } 948 | }, 949 | "node_modules/ethereumjs-util/node_modules/rlp": { 950 | "version": "2.2.7", 951 | "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", 952 | "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", 953 | "dependencies": { 954 | "bn.js": "^5.2.0" 955 | }, 956 | "bin": { 957 | "rlp": "bin/rlp" 958 | } 959 | }, 960 | "node_modules/ethers": { 961 | "version": "5.7.2", 962 | "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", 963 | "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", 964 | "funding": [ 965 | { 966 | "type": "individual", 967 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 968 | }, 969 | { 970 | "type": "individual", 971 | "url": "https://www.buymeacoffee.com/ricmoo" 972 | } 973 | ], 974 | "dependencies": { 975 | "@ethersproject/abi": "5.7.0", 976 | "@ethersproject/abstract-provider": "5.7.0", 977 | "@ethersproject/abstract-signer": "5.7.0", 978 | "@ethersproject/address": "5.7.0", 979 | "@ethersproject/base64": "5.7.0", 980 | "@ethersproject/basex": "5.7.0", 981 | "@ethersproject/bignumber": "5.7.0", 982 | "@ethersproject/bytes": "5.7.0", 983 | "@ethersproject/constants": "5.7.0", 984 | "@ethersproject/contracts": "5.7.0", 985 | "@ethersproject/hash": "5.7.0", 986 | "@ethersproject/hdnode": "5.7.0", 987 | "@ethersproject/json-wallets": "5.7.0", 988 | "@ethersproject/keccak256": "5.7.0", 989 | "@ethersproject/logger": "5.7.0", 990 | "@ethersproject/networks": "5.7.1", 991 | "@ethersproject/pbkdf2": "5.7.0", 992 | "@ethersproject/properties": "5.7.0", 993 | "@ethersproject/providers": "5.7.2", 994 | "@ethersproject/random": "5.7.0", 995 | "@ethersproject/rlp": "5.7.0", 996 | "@ethersproject/sha2": "5.7.0", 997 | "@ethersproject/signing-key": "5.7.0", 998 | "@ethersproject/solidity": "5.7.0", 999 | "@ethersproject/strings": "5.7.0", 1000 | "@ethersproject/transactions": "5.7.0", 1001 | "@ethersproject/units": "5.7.0", 1002 | "@ethersproject/wallet": "5.7.0", 1003 | "@ethersproject/web": "5.7.1", 1004 | "@ethersproject/wordlists": "5.7.0" 1005 | } 1006 | }, 1007 | "node_modules/ethjs-unit": { 1008 | "version": "0.1.6", 1009 | "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", 1010 | "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", 1011 | "dependencies": { 1012 | "bn.js": "4.11.6", 1013 | "number-to-bn": "1.7.0" 1014 | }, 1015 | "engines": { 1016 | "node": ">=6.5.0", 1017 | "npm": ">=3" 1018 | } 1019 | }, 1020 | "node_modules/ethjs-unit/node_modules/bn.js": { 1021 | "version": "4.11.6", 1022 | "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", 1023 | "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==" 1024 | }, 1025 | "node_modules/evp_bytestokey": { 1026 | "version": "1.0.3", 1027 | "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", 1028 | "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", 1029 | "dependencies": { 1030 | "md5.js": "^1.3.4", 1031 | "safe-buffer": "^5.1.1" 1032 | } 1033 | }, 1034 | "node_modules/hash-base": { 1035 | "version": "3.1.0", 1036 | "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", 1037 | "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", 1038 | "dependencies": { 1039 | "inherits": "^2.0.4", 1040 | "readable-stream": "^3.6.0", 1041 | "safe-buffer": "^5.2.0" 1042 | }, 1043 | "engines": { 1044 | "node": ">=4" 1045 | } 1046 | }, 1047 | "node_modules/hash.js": { 1048 | "version": "1.1.7", 1049 | "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", 1050 | "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", 1051 | "dependencies": { 1052 | "inherits": "^2.0.3", 1053 | "minimalistic-assert": "^1.0.1" 1054 | } 1055 | }, 1056 | "node_modules/hmac-drbg": { 1057 | "version": "1.0.1", 1058 | "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", 1059 | "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", 1060 | "dependencies": { 1061 | "hash.js": "^1.0.3", 1062 | "minimalistic-assert": "^1.0.0", 1063 | "minimalistic-crypto-utils": "^1.0.1" 1064 | } 1065 | }, 1066 | "node_modules/ieee754": { 1067 | "version": "1.2.1", 1068 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 1069 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 1070 | "funding": [ 1071 | { 1072 | "type": "github", 1073 | "url": "https://github.com/sponsors/feross" 1074 | }, 1075 | { 1076 | "type": "patreon", 1077 | "url": "https://www.patreon.com/feross" 1078 | }, 1079 | { 1080 | "type": "consulting", 1081 | "url": "https://feross.org/support" 1082 | } 1083 | ] 1084 | }, 1085 | "node_modules/inherits": { 1086 | "version": "2.0.4", 1087 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1088 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 1089 | }, 1090 | "node_modules/is-hex-prefixed": { 1091 | "version": "1.0.0", 1092 | "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", 1093 | "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", 1094 | "engines": { 1095 | "node": ">=6.5.0", 1096 | "npm": ">=3" 1097 | } 1098 | }, 1099 | "node_modules/js-sha3": { 1100 | "version": "0.8.0", 1101 | "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", 1102 | "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" 1103 | }, 1104 | "node_modules/keccak": { 1105 | "version": "3.0.3", 1106 | "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.3.tgz", 1107 | "integrity": "sha512-JZrLIAJWuZxKbCilMpNz5Vj7Vtb4scDG3dMXLOsbzBmQGyjwE61BbW7bJkfKKCShXiQZt3T6sBgALRtmd+nZaQ==", 1108 | "hasInstallScript": true, 1109 | "dependencies": { 1110 | "node-addon-api": "^2.0.0", 1111 | "node-gyp-build": "^4.2.0", 1112 | "readable-stream": "^3.6.0" 1113 | }, 1114 | "engines": { 1115 | "node": ">=10.0.0" 1116 | } 1117 | }, 1118 | "node_modules/keccak256": { 1119 | "version": "1.0.6", 1120 | "resolved": "https://registry.npmjs.org/keccak256/-/keccak256-1.0.6.tgz", 1121 | "integrity": "sha512-8GLiM01PkdJVGUhR1e6M/AvWnSqYS0HaERI+K/QtStGDGlSTx2B1zTqZk4Zlqu5TxHJNTxWAdP9Y+WI50OApUw==", 1122 | "dependencies": { 1123 | "bn.js": "^5.2.0", 1124 | "buffer": "^6.0.3", 1125 | "keccak": "^3.0.2" 1126 | } 1127 | }, 1128 | "node_modules/md5.js": { 1129 | "version": "1.3.5", 1130 | "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", 1131 | "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", 1132 | "dependencies": { 1133 | "hash-base": "^3.0.0", 1134 | "inherits": "^2.0.1", 1135 | "safe-buffer": "^5.1.2" 1136 | } 1137 | }, 1138 | "node_modules/merkletreejs": { 1139 | "version": "0.3.10", 1140 | "resolved": "https://registry.npmjs.org/merkletreejs/-/merkletreejs-0.3.10.tgz", 1141 | "integrity": "sha512-lin42tKfRdkW+6iE5pjtQ9BnH+1Hk3sJ5Fn9hUUSjcXRcJbSISHgPCfYvMNEXiNqZPhz/TyRPEV30qgnujsQ7A==", 1142 | "dependencies": { 1143 | "bignumber.js": "^9.0.1", 1144 | "buffer-reverse": "^1.0.1", 1145 | "crypto-js": "^3.1.9-1", 1146 | "treeify": "^1.1.0", 1147 | "web3-utils": "^1.3.4" 1148 | }, 1149 | "engines": { 1150 | "node": ">= 7.6.0" 1151 | } 1152 | }, 1153 | "node_modules/minimalistic-assert": { 1154 | "version": "1.0.1", 1155 | "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", 1156 | "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" 1157 | }, 1158 | "node_modules/minimalistic-crypto-utils": { 1159 | "version": "1.0.1", 1160 | "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", 1161 | "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" 1162 | }, 1163 | "node_modules/node-addon-api": { 1164 | "version": "2.0.2", 1165 | "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", 1166 | "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" 1167 | }, 1168 | "node_modules/node-gyp-build": { 1169 | "version": "4.6.0", 1170 | "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", 1171 | "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", 1172 | "bin": { 1173 | "node-gyp-build": "bin.js", 1174 | "node-gyp-build-optional": "optional.js", 1175 | "node-gyp-build-test": "build-test.js" 1176 | } 1177 | }, 1178 | "node_modules/number-to-bn": { 1179 | "version": "1.7.0", 1180 | "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", 1181 | "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", 1182 | "dependencies": { 1183 | "bn.js": "4.11.6", 1184 | "strip-hex-prefix": "1.0.0" 1185 | }, 1186 | "engines": { 1187 | "node": ">=6.5.0", 1188 | "npm": ">=3" 1189 | } 1190 | }, 1191 | "node_modules/number-to-bn/node_modules/bn.js": { 1192 | "version": "4.11.6", 1193 | "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", 1194 | "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==" 1195 | }, 1196 | "node_modules/pbkdf2": { 1197 | "version": "3.1.2", 1198 | "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", 1199 | "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", 1200 | "dependencies": { 1201 | "create-hash": "^1.1.2", 1202 | "create-hmac": "^1.1.4", 1203 | "ripemd160": "^2.0.1", 1204 | "safe-buffer": "^5.0.1", 1205 | "sha.js": "^2.4.8" 1206 | }, 1207 | "engines": { 1208 | "node": ">=0.12" 1209 | } 1210 | }, 1211 | "node_modules/randombytes": { 1212 | "version": "2.1.0", 1213 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 1214 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 1215 | "dependencies": { 1216 | "safe-buffer": "^5.1.0" 1217 | } 1218 | }, 1219 | "node_modules/readable-stream": { 1220 | "version": "3.6.2", 1221 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 1222 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 1223 | "dependencies": { 1224 | "inherits": "^2.0.3", 1225 | "string_decoder": "^1.1.1", 1226 | "util-deprecate": "^1.0.1" 1227 | }, 1228 | "engines": { 1229 | "node": ">= 6" 1230 | } 1231 | }, 1232 | "node_modules/ripemd160": { 1233 | "version": "2.0.2", 1234 | "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", 1235 | "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", 1236 | "dependencies": { 1237 | "hash-base": "^3.0.0", 1238 | "inherits": "^2.0.1" 1239 | } 1240 | }, 1241 | "node_modules/rlp": { 1242 | "version": "3.0.0", 1243 | "resolved": "https://registry.npmjs.org/rlp/-/rlp-3.0.0.tgz", 1244 | "integrity": "sha512-PD6U2PGk6Vq2spfgiWZdomLvRGDreBLxi5jv5M8EpRo3pU6VEm31KO+HFxE18Q3vgqfDrQ9pZA3FP95rkijNKw==", 1245 | "bin": { 1246 | "rlp": "bin/rlp" 1247 | } 1248 | }, 1249 | "node_modules/safe-buffer": { 1250 | "version": "5.2.1", 1251 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1252 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1253 | "funding": [ 1254 | { 1255 | "type": "github", 1256 | "url": "https://github.com/sponsors/feross" 1257 | }, 1258 | { 1259 | "type": "patreon", 1260 | "url": "https://www.patreon.com/feross" 1261 | }, 1262 | { 1263 | "type": "consulting", 1264 | "url": "https://feross.org/support" 1265 | } 1266 | ] 1267 | }, 1268 | "node_modules/scrypt-js": { 1269 | "version": "3.0.1", 1270 | "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", 1271 | "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" 1272 | }, 1273 | "node_modules/secp256k1": { 1274 | "version": "4.0.3", 1275 | "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", 1276 | "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", 1277 | "hasInstallScript": true, 1278 | "dependencies": { 1279 | "elliptic": "^6.5.4", 1280 | "node-addon-api": "^2.0.0", 1281 | "node-gyp-build": "^4.2.0" 1282 | }, 1283 | "engines": { 1284 | "node": ">=10.0.0" 1285 | } 1286 | }, 1287 | "node_modules/setimmediate": { 1288 | "version": "1.0.5", 1289 | "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", 1290 | "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" 1291 | }, 1292 | "node_modules/sha.js": { 1293 | "version": "2.4.11", 1294 | "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", 1295 | "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", 1296 | "dependencies": { 1297 | "inherits": "^2.0.1", 1298 | "safe-buffer": "^5.0.1" 1299 | }, 1300 | "bin": { 1301 | "sha.js": "bin.js" 1302 | } 1303 | }, 1304 | "node_modules/string_decoder": { 1305 | "version": "1.3.0", 1306 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 1307 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 1308 | "dependencies": { 1309 | "safe-buffer": "~5.2.0" 1310 | } 1311 | }, 1312 | "node_modules/strip-hex-prefix": { 1313 | "version": "1.0.0", 1314 | "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", 1315 | "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", 1316 | "dependencies": { 1317 | "is-hex-prefixed": "1.0.0" 1318 | }, 1319 | "engines": { 1320 | "node": ">=6.5.0", 1321 | "npm": ">=3" 1322 | } 1323 | }, 1324 | "node_modules/treeify": { 1325 | "version": "1.1.0", 1326 | "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", 1327 | "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", 1328 | "engines": { 1329 | "node": ">=0.6" 1330 | } 1331 | }, 1332 | "node_modules/typescript": { 1333 | "version": "4.9.5", 1334 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", 1335 | "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", 1336 | "dev": true, 1337 | "bin": { 1338 | "tsc": "bin/tsc", 1339 | "tsserver": "bin/tsserver" 1340 | }, 1341 | "engines": { 1342 | "node": ">=4.2.0" 1343 | } 1344 | }, 1345 | "node_modules/utf8": { 1346 | "version": "3.0.0", 1347 | "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", 1348 | "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" 1349 | }, 1350 | "node_modules/util-deprecate": { 1351 | "version": "1.0.2", 1352 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1353 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 1354 | }, 1355 | "node_modules/web3-utils": { 1356 | "version": "1.10.0", 1357 | "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.10.0.tgz", 1358 | "integrity": "sha512-kSaCM0uMcZTNUSmn5vMEhlo02RObGNRRCkdX0V9UTAU0+lrvn0HSaudyCo6CQzuXUsnuY2ERJGCGPfeWmv19Rg==", 1359 | "dependencies": { 1360 | "bn.js": "^5.2.1", 1361 | "ethereum-bloom-filters": "^1.0.6", 1362 | "ethereumjs-util": "^7.1.0", 1363 | "ethjs-unit": "0.1.6", 1364 | "number-to-bn": "1.7.0", 1365 | "randombytes": "^2.1.0", 1366 | "utf8": "3.0.0" 1367 | }, 1368 | "engines": { 1369 | "node": ">=8.0.0" 1370 | } 1371 | }, 1372 | "node_modules/ws": { 1373 | "version": "7.4.6", 1374 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", 1375 | "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", 1376 | "engines": { 1377 | "node": ">=8.3.0" 1378 | }, 1379 | "peerDependencies": { 1380 | "bufferutil": "^4.0.1", 1381 | "utf-8-validate": "^5.0.2" 1382 | }, 1383 | "peerDependenciesMeta": { 1384 | "bufferutil": { 1385 | "optional": true 1386 | }, 1387 | "utf-8-validate": { 1388 | "optional": true 1389 | } 1390 | } 1391 | } 1392 | } 1393 | } 1394 | --------------------------------------------------------------------------------