From b043b965b87b05fbd9ff84e70c378980a1eb7da6 Mon Sep 17 00:00:00 2001 From: pancake Date: Sat, 3 Oct 2020 18:10:08 +0800 Subject: [PATCH 01/11] add smartchef --- contracts/Lottery.sol | 2 +- contracts/SmartChef.sol | 181 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 contracts/SmartChef.sol diff --git a/contracts/Lottery.sol b/contracts/Lottery.sol index 0639b7c..6d396c6 100644 --- a/contracts/Lottery.sol +++ b/contracts/Lottery.sol @@ -133,7 +133,7 @@ contract Lottery is Ownable { emit Buy(msg.sender, _amount, _numbers); } - function drawing() external returns (uint256){ + function drawing() public { require(msg.sender == devaddr, "dev: wut?"); diff --git a/contracts/SmartChef.sol b/contracts/SmartChef.sol new file mode 100644 index 0000000..7d6ee36 --- /dev/null +++ b/contracts/SmartChef.sol @@ -0,0 +1,181 @@ +pragma solidity 0.6.12; + +import '@pancakeswap/pancake-swap-lib/contracts/math/SafeMath.sol'; +import '@pancakeswap/pancake-swap-lib/contracts/token/BEP20/IBEP20.sol'; +import '@pancakeswap/pancake-swap-lib/contracts/token/BEP20/SafeBEP20.sol'; +import '@pancakeswap/pancake-swap-lib/contracts/access/Ownable.sol'; + +// import "@nomiclabs/buidler/console.sol"; + +contract SmartChef is Ownable { + using SafeMath for uint256; + using SafeBEP20 for IBEP20; + + // Info of each user. + struct UserInfo { + uint256 amount; // How many LP tokens the user has provided. + uint256 rewardDebt; // Reward debt. See explanation below. + } + + // Info of each pool. + struct PoolInfo { + IBEP20 lpToken; // Address of LP token contract. + uint256 allocPoint; // How many allocation points assigned to this pool. CAKEs to distribute per block. + uint256 lastRewardBlock; // Last block number that CAKEs distribution occurs. + uint256 accCakePerShare; // Accumulated CAKEs per share, times 1e12. See below. + } + + // The CAKE TOKEN! + IBEP20 public syrup; + IBEP20 public rewardToken; + + // CAKE tokens created per block. + uint256 public rewardPerBlock; + + // Info of each pool. + PoolInfo[] public poolInfo; + // Info of each user that stakes LP tokens. + mapping (address => UserInfo) public userInfo; + // Total allocation poitns. Must be the sum of all allocation points in all pools. + uint256 public totalAllocPoint = 0; + // The block number when CAKE mining starts. + uint256 public startBlock; + // The block number when CAKE mining ends. + uint256 public bonusEndBlock; + + event Deposit(address indexed user, uint256 amount); + event Withdraw(address indexed user, uint256 amount); + event EmergencyWithdraw(address indexed user, uint256 amount); + + constructor( + IBEP20 _syrup, + IBEP20 _rewardToken, + uint256 _rewardPerBlock, + uint256 _startBlock, + uint256 _bonusEndBlock + ) public { + syrup = _syrup; + rewardToken = _rewardToken; + rewardPerBlock = _rewardPerBlock; + startBlock = _startBlock; + bonusEndBlock = _bonusEndBlock; + + // staking pool + poolInfo.push(PoolInfo({ + lpToken: _syrup, + allocPoint: 1000, + lastRewardBlock: startBlock, + accCakePerShare: 0 + })); + + totalAllocPoint = 1000; + + } + + // Return reward multiplier over the given _from to _to block. + function getMultiplier(uint256 _from, uint256 _to) public view returns (uint256) { + if (_to <= bonusEndBlock) { + return _to.sub(_from); + } else if (_from >= bonusEndBlock) { + return 0; + } else { + return bonusEndBlock.sub(_from); + } + } + + // View function to see pending Reward on frontend. + function pendingReward(address _user) external view returns (uint256) { + PoolInfo storage pool = poolInfo[0]; + UserInfo storage user = userInfo[_user]; + uint256 accCakePerShare = pool.accCakePerShare; + uint256 lpSupply = pool.lpToken.balanceOf(address(this)); + if (block.number > pool.lastRewardBlock && lpSupply != 0) { + uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number); + uint256 cakeReward = multiplier.mul(rewardPerBlock).mul(pool.allocPoint).div(totalAllocPoint); + accCakePerShare = accCakePerShare.add(cakeReward.mul(1e12).div(lpSupply)); + } + return user.amount.mul(accCakePerShare).div(1e12).sub(user.rewardDebt); + } + + // Update reward variables of the given pool to be up-to-date. + function updatePool(uint256 _pid) public { + PoolInfo storage pool = poolInfo[_pid]; + if (block.number <= pool.lastRewardBlock) { + return; + } + uint256 lpSupply = pool.lpToken.balanceOf(address(this)); + if (lpSupply == 0) { + pool.lastRewardBlock = block.number; + return; + } + uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number); + uint256 cakeReward = multiplier.mul(rewardPerBlock).mul(pool.allocPoint).div(totalAllocPoint); + pool.accCakePerShare = pool.accCakePerShare.add(cakeReward.mul(1e12).div(lpSupply)); + pool.lastRewardBlock = block.number; + } + + // Update reward variables for all pools. Be careful of gas spending! + function massUpdatePools() public { + uint256 length = poolInfo.length; + for (uint256 pid = 0; pid < length; ++pid) { + updatePool(pid); + } + } + + + // Stake SYRUP tokens to SmartChef + function deposit(uint256 _amount) public { + PoolInfo storage pool = poolInfo[0]; + UserInfo storage user = userInfo[msg.sender]; + updatePool(0); + if (user.amount > 0) { + uint256 pending = user.amount.mul(pool.accCakePerShare).div(1e12).sub(user.rewardDebt); + if(pending > 0) { + rewardToken.safeTransfer(address(msg.sender), pending); + } + } + if(_amount > 0) { + pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount); + user.amount = user.amount.add(_amount); + } + user.rewardDebt = user.amount.mul(pool.accCakePerShare).div(1e12); + + emit Deposit(msg.sender, _amount); + } + + // Withdraw SYRUP tokens from STAKING. + function withdraw(uint256 _amount) public { + PoolInfo storage pool = poolInfo[0]; + UserInfo storage user = userInfo[msg.sender]; + require(user.amount >= _amount, "withdraw: not good"); + updatePool(0); + uint256 pending = user.amount.mul(pool.accCakePerShare).div(1e12).sub(user.rewardDebt); + if(pending > 0) { + rewardToken.safeTransfer(address(msg.sender), pending); + } + if(_amount > 0) { + user.amount = user.amount.sub(_amount); + pool.lpToken.safeTransfer(address(msg.sender), _amount); + } + user.rewardDebt = user.amount.mul(pool.accCakePerShare).div(1e12); + + emit Withdraw(msg.sender, _amount); + } + + // Withdraw without caring about rewards. EMERGENCY ONLY. + function emergencyWithdraw() public { + PoolInfo storage pool = poolInfo[0]; + UserInfo storage user = userInfo[msg.sender]; + pool.lpToken.safeTransfer(address(msg.sender), user.amount); + emit EmergencyWithdraw(msg.sender, user.amount); + user.amount = 0; + user.rewardDebt = 0; + } + + // Withdraw reward. EMERGENCY ONLY. + function emergencyRewardWithdraw(uint256 _amount) public onlyOwner { + require (_amount < rewardToken.balanceOf(address(this)), 'not enough token'); + rewardToken.safeTransfer(address(msg.sender), _amount); + } + +} From 1912df5a4c18598e48ee08245cb3298c8261546d Mon Sep 17 00:00:00 2001 From: pancake Date: Mon, 5 Oct 2020 12:23:27 +0800 Subject: [PATCH 02/11] update lottery --- contracts/Lottery.sol | 277 ++++++++++++++++++++------------------- contracts/LotteryNFT.sol | 21 ++- contracts/SmartChef.sol | 4 +- 3 files changed, 156 insertions(+), 146 deletions(-) diff --git a/contracts/Lottery.sol b/contracts/Lottery.sol index 6d396c6..be9999b 100644 --- a/contracts/Lottery.sol +++ b/contracts/Lottery.sol @@ -8,144 +8,112 @@ import "@openzeppelin/contracts/access/Ownable.sol"; import "./LotteryNFT.sol"; -import "@nomiclabs/buidler/console.sol"; +// import "@nomiclabs/buidler/console.sol"; +// 4 numbers contract Lottery is Ownable { using SafeMath for uint256; using SafeERC20 for IERC20; - address public devaddr; + // Allocation for first/sencond/third reward + uint256[] private allocation = [60, 20, 10]; + // The TOKEN to buy lottery + IERC20 public cake; + // The Lottery NFT for tickets + LotteryNFT public lotteryNtf; + // adminAddress + address public adminAddress; + // maxNumber + uint256 public maxNumber = 5; + + // ================================= - uint256 public issueIndex; + // issueId => winningNumbers[numbers] + mapping (uint256 => uint256[]) public historyNumbers; + // issueId => [tokenId] + mapping (uint256 => uint256[]) public lotteryInfo; + // address => [tokenId] + mapping (address => uint256[]) public userInfo; + uint256 public issueIndex = 0; uint256 public totalAddresses = 0; uint256 public totalAmount = 0; uint256 public lastTimestamp; - uint256 public maxNumber = 5; - - uint256 private firstAllocation = 60; - uint256 private sencondAllocation = 20; - uint256 private thirdAllocation = 10; - uint256 private devAllocation = 10; - - // Info of each lottery. - // TODO: ERC721 - struct Lottery { - uint256 amount; - address owner; - uint256 lotteryNumber1; - uint256 lotteryNumber2; - uint256 lotteryNumber3; - uint256 lotteryNumber4; - } - // Reward uint256[] public winningNumbers; - Lottery[] public firstPrize; - Lottery[] public sencondPrize; - Lottery[] public thirdPrize; - uint256 public firstPrizeAmount; - uint256 public sencondPrizeAmount; - uint256 public thirdPrizeAmount; + // ================================= - uint256[][] public historyNumbers; - - // The SYRUP TOKEN! - IERC20 public cake; - - LotteryNFT public lotteryNtf; - - mapping (address => Lottery[]) public userInfo; - Lottery[] public lotteryInfo; - - event Buy(address indexed user, uint256 _amount, uint256[] numbers); - event Drawing(uint256[] winningNumbers, uint256 firstPrizeNum); - event Claim(address indexed user, uint256 indexed tokenid, uint256 amount); + event Buy(address indexed user, uint256 tokenId); + event Drawing(uint256 indexed issueIndex, uint256[] winningNumbers); + event Claim(address indexed user, uint256 tokenid, uint256 amount); + event DevWithdraw(address indexed user, uint256 amount); + event Reset(uint256 indexed issueIndex); constructor( IERC20 _cake, LotteryNFT _lottery, uint256 _maxNumber, - address _devaddr + address _adminAddress ) public { cake = _cake; lotteryNtf = _lottery; maxNumber = _maxNumber; - devaddr = _devaddr; + adminAddress = _adminAddress; lastTimestamp = block.timestamp; - issueIndex = 0; } + uint256[] public nullTicket = [0,0,0,0]; + function reset() external { + require(msg.sender == adminAddress, "admin: wut?"); + require(winningNumbers.length == 4, "drawed?"); + lastTimestamp = block.timestamp; - for (uint i = 0; i < lotteryInfo.length; i++) { - delete userInfo[lotteryInfo[i].owner]; - } - delete lotteryInfo; - delete firstPrize; - delete sencondPrize; - delete thirdPrize; + totalAddresses = 0; + totalAmount = 0; + delete winningNumbers; issueIndex++; - } + if(getMatchingRewardAmount(issueIndex-1, 4) == 0) { + uint256 amount = getTotalRewards(issueIndex-1).mul(allocation[0]).div(100); + buy(amount, nullTicket); + } - function buy(uint256 _amount, uint256[] memory _numbers) public { + emit Reset(issueIndex); + } + function buy(uint256 _amount, uint256[] memory _numbers) public { require (_numbers.length == 4, 'wrong length'); - for (uint i = 0; i < 4; i++) { require (_numbers[i] <= maxNumber, 'exceed the maximum'); } cake.safeTransferFrom(address(msg.sender), address(this), _amount); - - lotteryNtf.newLotteryItem(msg.sender, _numbers, _amount, issueIndex); - - lotteryInfo.push(Lottery({ - amount: _amount, - owner: address(msg.sender), - lotteryNumber1: _numbers[0], - lotteryNumber2: _numbers[1], - lotteryNumber3: _numbers[2], - lotteryNumber4: _numbers[3] - })); - - Lottery[] storage userLotteries = userInfo[msg.sender]; - + uint256 tokenId = lotteryNtf.newLotteryItem(msg.sender, _numbers, _amount, issueIndex); + lotteryInfo[issueIndex].push(tokenId); if (userInfo[msg.sender].length == 0) { totalAddresses = totalAddresses + 1; } - - userLotteries.push(Lottery({ - amount: _amount, - owner: address(msg.sender), - lotteryNumber1: _numbers[0], - lotteryNumber2: _numbers[1], - lotteryNumber3: _numbers[2], - lotteryNumber4: _numbers[3] - })); - + userInfo[msg.sender].push(tokenId); totalAmount = totalAmount + _amount; - lastTimestamp = block.timestamp; - emit Buy(msg.sender, _amount, _numbers); + emit Buy(msg.sender, tokenId); } function drawing() public { - require(msg.sender == devaddr, "dev: wut?"); - + require(msg.sender == adminAddress, "admin: wut?"); bytes32 _structHash; uint256 _randomNumber; uint256 _maxNumber = maxNumber; - bytes32 blockhash = blockhash(block.number-1); + bytes32 _blockhash = blockhash(block.number-1); // 1 _structHash = keccak256( abi.encode( - blockhash, + _blockhash, totalAddresses ) ); @@ -153,11 +121,10 @@ contract Lottery is Ownable { assembly {_randomNumber := add(mod(_randomNumber, _maxNumber),1)} winningNumbers.push(_randomNumber); - // 2 _structHash = keccak256( abi.encode( - blockhash, + _blockhash, totalAmount ) ); @@ -165,11 +132,10 @@ contract Lottery is Ownable { assembly {_randomNumber := add(mod(_randomNumber, _maxNumber),1)} winningNumbers.push(_randomNumber); - // 3 _structHash = keccak256( abi.encode( - blockhash, + _blockhash, lastTimestamp ) ); @@ -180,7 +146,7 @@ contract Lottery is Ownable { // 4 _structHash = keccak256( abi.encode( - blockhash, + _blockhash, block.difficulty ) ); @@ -189,77 +155,112 @@ contract Lottery is Ownable { winningNumbers.push(_randomNumber); - for (uint i = 0; i < lotteryInfo.length; i++) { - uint matchingNumber = 0; - if (lotteryInfo[i].lotteryNumber1 == winningNumbers[0]) { - matchingNumber = matchingNumber + 1; - } - if (lotteryInfo[i].lotteryNumber2 == winningNumbers[1]) { - matchingNumber = matchingNumber + 1; - } - if (lotteryInfo[i].lotteryNumber3 == winningNumbers[2]) { - matchingNumber = matchingNumber + 1; - } - if (lotteryInfo[i].lotteryNumber4 == winningNumbers[3]) { - matchingNumber = matchingNumber + 1; - } + historyNumbers[issueIndex] = winningNumbers; + emit Drawing(issueIndex, winningNumbers); - if(matchingNumber == 4) { - firstPrize.push(lotteryInfo[i]); - firstPrizeAmount = firstPrizeAmount + lotteryInfo[i].amount; - } + } - if(matchingNumber == 3) { - sencondPrize.push(lotteryInfo[i]); - sencondPrizeAmount = sencondPrizeAmount + lotteryInfo[i].amount; + function getMatchingRewardAmount(uint256 _issueIndex, uint256 _matchingNumber) internal view returns (uint256) { + uint256 totalAmout = 0; + for (uint i = 0; i < lotteryInfo[_issueIndex].length; i++) { + uint256 tokenId = lotteryInfo[_issueIndex][i]; + + uint256[] memory lotteryNumbers = lotteryNtf.getLotteryNumbers(tokenId); + uint256[] storage _winningNumbers = historyNumbers[_issueIndex]; + uint256 matchingNumber = 0; + for (uint j = 0; j < _winningNumbers.length; j++) { + if(lotteryNumbers[j] == _winningNumbers[j]) { + matchingNumber++; + } + } + if (matchingNumber == _matchingNumber) { + totalAmout = totalAmout + lotteryNtf.getLotteryAmount(tokenId); } + } + return totalAmout; + } - if(matchingNumber == 2) { - thirdPrize.push(lotteryInfo[i]); - thirdPrizeAmount = thirdPrizeAmount + lotteryInfo[i].amount; + function getMatchingLotteries(uint256 _issueIndex, uint256 _matchingNumber, uint256 _index) external view returns(uint256) { + uint256 index = 0; + for (uint i = 0; i < lotteryInfo[_issueIndex].length; i++) { + uint256 tokenId = lotteryInfo[_issueIndex][i]; + + uint256[] memory lotteryNumbers = lotteryNtf.getLotteryNumbers(tokenId); + uint256[] storage _winningNumbers = historyNumbers[_issueIndex]; + uint256 matchingNumber = 0; + for (uint j = 0; j < _winningNumbers.length; j++) { + if (lotteryNumbers[j] == _winningNumbers[j]) { + matchingNumber++; + } + } + if (matchingNumber == _matchingNumber) { + if (index == _index) { + return tokenId; + } + index++; } } + return index; + } - historyNumbers.push(winningNumbers); + function getTotalRewards(uint256 _issueIndex) public view returns(uint256) { + uint256 total = 0; + for (uint i = 0; i < lotteryInfo[_issueIndex].length; i++) { + uint256 tokenId = lotteryInfo[_issueIndex][i]; + total = total.add(lotteryNtf.getLotteryAmount(tokenId)); + } + return total; + } - emit Drawing(winningNumbers, firstPrize.length); + function getRewardView(uint256 _tokenId) public view returns(uint256) { - } + uint256 _issueIndex = lotteryNtf.getLotteryIssueIndex(_tokenId); + uint256[] memory lotteryNumbers = lotteryNtf.getLotteryNumbers(_tokenId); + uint256[] storage _winningNumbers = historyNumbers[_issueIndex]; - function claimReward(uint256 tokenId) public { - require(msg.sender == lotteryNtf.ownerOf(tokenId), "not from owner"); - require(issueIndex == lotteryNtf.getLotteryIndex(tokenId), "issue index wrong"); - uint256[] memory lotteryNumber = lotteryNtf.getLotteryNumber(tokenId); - uint256 amount = lotteryNtf.getLotteryAmount(tokenId); uint256 matchingNumber = 0; - for (uint i = 0; i < lotteryNumber.length; i++) { - if(winningNumbers[i] == lotteryNumber[i]) { + for (uint i = 0; i < lotteryNumbers.length; i++) { + if (_winningNumbers[i] == lotteryNumbers[i]) { matchingNumber= matchingNumber +1; } } - uint256 reward; - if(matchingNumber==4) { - reward = amount.div(firstPrizeAmount).mul(totalAmount).mul(firstAllocation).div(100); - cake.safeTransferFrom(address(this), address(msg.sender), reward); - } - if(matchingNumber==3) { - reward = amount.div(firstPrizeAmount).mul(totalAmount).mul(firstAllocation).div(100); - cake.safeTransferFrom(address(this), address(msg.sender), reward); + + uint256 reward = 0; + if (matchingNumber > 1) { + uint256 amount = lotteryNtf.getLotteryAmount(_tokenId); + uint256 poolAmount = getTotalRewards(_issueIndex).mul(allocation[4-matchingNumber]).div(100); + reward = amount.mul(1e12).div(getMatchingRewardAmount(_issueIndex, matchingNumber)).mul(poolAmount); } - if(matchingNumber==2) { - reward = amount.div(firstPrizeAmount).mul(totalAmount).mul(firstAllocation).div(100); - cake.safeTransferFrom(address(this), address(msg.sender), reward); + + return reward.div(1e12); + } + + + function claimReward(uint256 _tokenId) public { + require(msg.sender == lotteryNtf.ownerOf(_tokenId), "not from owner"); + require (lotteryNtf.getClaimStatus(_tokenId) == false, "claimed"); + + uint256 reward = getRewardView(_tokenId); + + if(reward>0) { + cake.safeTransfer(address(msg.sender), reward); } + lotteryNtf.claimReward(_tokenId); - emit Claim(msg.sender, tokenId, reward); + emit Claim(msg.sender, _tokenId, reward); } - // Update dev address by the previous dev. - function dev(address _devaddr) public { - require(msg.sender == devaddr, "dev: wut?"); - devaddr = _devaddr; + // Update admin address by the previous dev. + function dev(address _adminAddress) public onlyOwner { + adminAddress = _adminAddress; + } + + // Withdraw without caring about rewards. EMERGENCY ONLY. + function devWithdraw(uint256 _amount) public onlyOwner { + cake.safeTransfer(address(msg.sender), _amount); + emit DevWithdraw(msg.sender, _amount); } } diff --git a/contracts/LotteryNFT.sol b/contracts/LotteryNFT.sol index 29b775c..f7650be 100644 --- a/contracts/LotteryNFT.sol +++ b/contracts/LotteryNFT.sol @@ -5,19 +5,21 @@ pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; -contract LotteryNFT is ERC721 { +contract LotteryNFT is ERC721, Ownable { using Counters for Counters.Counter; Counters.Counter private _tokenIds; mapping (uint256 => uint256[]) public lotteryInfo; mapping (uint256 => uint256) public lotteryAmount; mapping (uint256 => uint256) public issueIndex; + mapping (uint256 => bool) public claimInfo; - constructor() public ERC721("Pancake Lottery", "Lottery") {} + constructor() public ERC721("Pancake Lottery NFT", "ticket") {} function newLotteryItem(address player, uint256[] memory _lotteryNumbers, uint256 _amount, uint256 _issueIndex) - public + public onlyOwner returns (uint256) { _tokenIds.increment(); @@ -27,18 +29,25 @@ contract LotteryNFT is ERC721 { lotteryInfo[newItemId] = _lotteryNumbers; lotteryAmount[newItemId] = _amount; issueIndex[newItemId] = _issueIndex; + claimInfo[newItemId] = false; // _setTokenURI(newItemId, tokenURI); return newItemId; } - function getLotteryNumber(uint256 tokenId) public returns (uint256[] memory) { + function getLotteryNumbers(uint256 tokenId) external view returns (uint256[] memory) { return lotteryInfo[tokenId]; } - function getLotteryAmount(uint256 tokenId) public returns (uint256) { + function getLotteryAmount(uint256 tokenId) external view returns (uint256) { return lotteryAmount[tokenId]; } - function getLotteryIndex(uint256 tokenId) public returns (uint256) { + function getLotteryIssueIndex(uint256 tokenId) external view returns (uint256) { return issueIndex[tokenId]; } + function claimReward(uint256 tokenId) external onlyOwner { + claimInfo[tokenId] = true; + } + function getClaimStatus(uint256 tokenId) external view returns (bool) { + return claimInfo[tokenId]; + } } diff --git a/contracts/SmartChef.sol b/contracts/SmartChef.sol index 7d6ee36..3b90a1e 100644 --- a/contracts/SmartChef.sol +++ b/contracts/SmartChef.sol @@ -167,14 +167,14 @@ contract SmartChef is Ownable { PoolInfo storage pool = poolInfo[0]; UserInfo storage user = userInfo[msg.sender]; pool.lpToken.safeTransfer(address(msg.sender), user.amount); - emit EmergencyWithdraw(msg.sender, user.amount); user.amount = 0; user.rewardDebt = 0; + emit EmergencyWithdraw(msg.sender, user.amount); } // Withdraw reward. EMERGENCY ONLY. function emergencyRewardWithdraw(uint256 _amount) public onlyOwner { - require (_amount < rewardToken.balanceOf(address(this)), 'not enough token'); + require(_amount < rewardToken.balanceOf(address(this)), 'not enough token'); rewardToken.safeTransfer(address(msg.sender), _amount); } From c933e36e7e53559305e8882f58bc2990c459ab5f Mon Sep 17 00:00:00 2001 From: pancake Date: Mon, 5 Oct 2020 12:26:07 +0800 Subject: [PATCH 03/11] feat: update lottery --- contracts/Lottery.sol | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/contracts/Lottery.sol b/contracts/Lottery.sol index be9999b..bf5a384 100644 --- a/contracts/Lottery.sol +++ b/contracts/Lottery.sol @@ -63,23 +63,20 @@ contract Lottery is Ownable { lastTimestamp = block.timestamp; } - uint256[] public nullTicket = [0,0,0,0]; + uint256[] private nullTicket = [0,0,0,0]; function reset() external { require(msg.sender == adminAddress, "admin: wut?"); require(winningNumbers.length == 4, "drawed?"); - lastTimestamp = block.timestamp; totalAddresses = 0; totalAmount = 0; delete winningNumbers; issueIndex++; - if(getMatchingRewardAmount(issueIndex-1, 4) == 0) { uint256 amount = getTotalRewards(issueIndex-1).mul(allocation[0]).div(100); buy(amount, nullTicket); } - emit Reset(issueIndex); } @@ -88,7 +85,6 @@ contract Lottery is Ownable { for (uint i = 0; i < 4; i++) { require (_numbers[i] <= maxNumber, 'exceed the maximum'); } - cake.safeTransferFrom(address(msg.sender), address(this), _amount); uint256 tokenId = lotteryNtf.newLotteryItem(msg.sender, _numbers, _amount, issueIndex); lotteryInfo[issueIndex].push(tokenId); @@ -98,13 +94,11 @@ contract Lottery is Ownable { userInfo[msg.sender].push(tokenId); totalAmount = totalAmount + _amount; lastTimestamp = block.timestamp; - emit Buy(msg.sender, tokenId); } function drawing() public { require(msg.sender == adminAddress, "admin: wut?"); - bytes32 _structHash; uint256 _randomNumber; uint256 _maxNumber = maxNumber; @@ -153,12 +147,8 @@ contract Lottery is Ownable { _randomNumber = uint256(_structHash); assembly {_randomNumber := add(mod(_randomNumber, _maxNumber),1)} winningNumbers.push(_randomNumber); - - historyNumbers[issueIndex] = winningNumbers; - emit Drawing(issueIndex, winningNumbers); - } function getMatchingRewardAmount(uint256 _issueIndex, uint256 _matchingNumber) internal view returns (uint256) { @@ -214,25 +204,21 @@ contract Lottery is Ownable { } function getRewardView(uint256 _tokenId) public view returns(uint256) { - uint256 _issueIndex = lotteryNtf.getLotteryIssueIndex(_tokenId); uint256[] memory lotteryNumbers = lotteryNtf.getLotteryNumbers(_tokenId); uint256[] storage _winningNumbers = historyNumbers[_issueIndex]; - uint256 matchingNumber = 0; for (uint i = 0; i < lotteryNumbers.length; i++) { if (_winningNumbers[i] == lotteryNumbers[i]) { matchingNumber= matchingNumber +1; } } - uint256 reward = 0; if (matchingNumber > 1) { uint256 amount = lotteryNtf.getLotteryAmount(_tokenId); uint256 poolAmount = getTotalRewards(_issueIndex).mul(allocation[4-matchingNumber]).div(100); reward = amount.mul(1e12).div(getMatchingRewardAmount(_issueIndex, matchingNumber)).mul(poolAmount); } - return reward.div(1e12); } @@ -240,26 +226,22 @@ contract Lottery is Ownable { function claimReward(uint256 _tokenId) public { require(msg.sender == lotteryNtf.ownerOf(_tokenId), "not from owner"); require (lotteryNtf.getClaimStatus(_tokenId) == false, "claimed"); - uint256 reward = getRewardView(_tokenId); - if(reward>0) { cake.safeTransfer(address(msg.sender), reward); } lotteryNtf.claimReward(_tokenId); - emit Claim(msg.sender, _tokenId, reward); - } // Update admin address by the previous dev. - function dev(address _adminAddress) public onlyOwner { + function setAdmin(address _adminAddress) public onlyOwner { adminAddress = _adminAddress; } // Withdraw without caring about rewards. EMERGENCY ONLY. - function devWithdraw(uint256 _amount) public onlyOwner { + function adminWithdraw(uint256 _amount) public onlyOwner { cake.safeTransfer(address(msg.sender), _amount); emit DevWithdraw(msg.sender, _amount); } From 977323f84d9a7e0909d15f22de6f8a62f04c3832 Mon Sep 17 00:00:00 2001 From: pancake Date: Mon, 5 Oct 2020 17:16:35 +0800 Subject: [PATCH 04/11] update lottery --- contracts/Lottery.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/contracts/Lottery.sol b/contracts/Lottery.sol index bf5a384..5afed2f 100644 --- a/contracts/Lottery.sol +++ b/contracts/Lottery.sol @@ -155,7 +155,6 @@ contract Lottery is Ownable { uint256 totalAmout = 0; for (uint i = 0; i < lotteryInfo[_issueIndex].length; i++) { uint256 tokenId = lotteryInfo[_issueIndex][i]; - uint256[] memory lotteryNumbers = lotteryNtf.getLotteryNumbers(tokenId); uint256[] storage _winningNumbers = historyNumbers[_issueIndex]; uint256 matchingNumber = 0; @@ -175,7 +174,6 @@ contract Lottery is Ownable { uint256 index = 0; for (uint i = 0; i < lotteryInfo[_issueIndex].length; i++) { uint256 tokenId = lotteryInfo[_issueIndex][i]; - uint256[] memory lotteryNumbers = lotteryNtf.getLotteryNumbers(tokenId); uint256[] storage _winningNumbers = historyNumbers[_issueIndex]; uint256 matchingNumber = 0; From f90ee9536b222370e638c40ddb54dc282fd99207 Mon Sep 17 00:00:00 2001 From: Chi Date: Mon, 5 Oct 2020 22:23:12 +0200 Subject: [PATCH 05/11] Fix typo in Lottery.sol lotteryNtf -> lotteryNFT --- contracts/Lottery.sol | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/contracts/Lottery.sol b/contracts/Lottery.sol index 5afed2f..2a6fb80 100644 --- a/contracts/Lottery.sol +++ b/contracts/Lottery.sol @@ -20,7 +20,7 @@ contract Lottery is Ownable { // The TOKEN to buy lottery IERC20 public cake; // The Lottery NFT for tickets - LotteryNFT public lotteryNtf; + LotteryNFT public lotteryNFT; // adminAddress address public adminAddress; // maxNumber @@ -57,7 +57,7 @@ contract Lottery is Ownable { address _adminAddress ) public { cake = _cake; - lotteryNtf = _lottery; + lotteryNFT = _lottery; maxNumber = _maxNumber; adminAddress = _adminAddress; lastTimestamp = block.timestamp; @@ -86,7 +86,7 @@ contract Lottery is Ownable { require (_numbers[i] <= maxNumber, 'exceed the maximum'); } cake.safeTransferFrom(address(msg.sender), address(this), _amount); - uint256 tokenId = lotteryNtf.newLotteryItem(msg.sender, _numbers, _amount, issueIndex); + uint256 tokenId = lotteryNFT.newLotteryItem(msg.sender, _numbers, _amount, issueIndex); lotteryInfo[issueIndex].push(tokenId); if (userInfo[msg.sender].length == 0) { totalAddresses = totalAddresses + 1; @@ -155,7 +155,7 @@ contract Lottery is Ownable { uint256 totalAmout = 0; for (uint i = 0; i < lotteryInfo[_issueIndex].length; i++) { uint256 tokenId = lotteryInfo[_issueIndex][i]; - uint256[] memory lotteryNumbers = lotteryNtf.getLotteryNumbers(tokenId); + uint256[] memory lotteryNumbers = lotteryNFT.getLotteryNumbers(tokenId); uint256[] storage _winningNumbers = historyNumbers[_issueIndex]; uint256 matchingNumber = 0; for (uint j = 0; j < _winningNumbers.length; j++) { @@ -164,7 +164,7 @@ contract Lottery is Ownable { } } if (matchingNumber == _matchingNumber) { - totalAmout = totalAmout + lotteryNtf.getLotteryAmount(tokenId); + totalAmout = totalAmout + lotteryNFT.getLotteryAmount(tokenId); } } return totalAmout; @@ -174,7 +174,7 @@ contract Lottery is Ownable { uint256 index = 0; for (uint i = 0; i < lotteryInfo[_issueIndex].length; i++) { uint256 tokenId = lotteryInfo[_issueIndex][i]; - uint256[] memory lotteryNumbers = lotteryNtf.getLotteryNumbers(tokenId); + uint256[] memory lotteryNumbers = lotteryNFT.getLotteryNumbers(tokenId); uint256[] storage _winningNumbers = historyNumbers[_issueIndex]; uint256 matchingNumber = 0; for (uint j = 0; j < _winningNumbers.length; j++) { @@ -196,14 +196,14 @@ contract Lottery is Ownable { uint256 total = 0; for (uint i = 0; i < lotteryInfo[_issueIndex].length; i++) { uint256 tokenId = lotteryInfo[_issueIndex][i]; - total = total.add(lotteryNtf.getLotteryAmount(tokenId)); + total = total.add(lotteryNFT.getLotteryAmount(tokenId)); } return total; } function getRewardView(uint256 _tokenId) public view returns(uint256) { - uint256 _issueIndex = lotteryNtf.getLotteryIssueIndex(_tokenId); - uint256[] memory lotteryNumbers = lotteryNtf.getLotteryNumbers(_tokenId); + uint256 _issueIndex = lotteryNFT.getLotteryIssueIndex(_tokenId); + uint256[] memory lotteryNumbers = lotteryNFT.getLotteryNumbers(_tokenId); uint256[] storage _winningNumbers = historyNumbers[_issueIndex]; uint256 matchingNumber = 0; for (uint i = 0; i < lotteryNumbers.length; i++) { @@ -213,7 +213,7 @@ contract Lottery is Ownable { } uint256 reward = 0; if (matchingNumber > 1) { - uint256 amount = lotteryNtf.getLotteryAmount(_tokenId); + uint256 amount = lotteryNFT.getLotteryAmount(_tokenId); uint256 poolAmount = getTotalRewards(_issueIndex).mul(allocation[4-matchingNumber]).div(100); reward = amount.mul(1e12).div(getMatchingRewardAmount(_issueIndex, matchingNumber)).mul(poolAmount); } @@ -222,13 +222,13 @@ contract Lottery is Ownable { function claimReward(uint256 _tokenId) public { - require(msg.sender == lotteryNtf.ownerOf(_tokenId), "not from owner"); - require (lotteryNtf.getClaimStatus(_tokenId) == false, "claimed"); + require(msg.sender == lotteryNFT.ownerOf(_tokenId), "not from owner"); + require (lotteryNFT.getClaimStatus(_tokenId) == false, "claimed"); uint256 reward = getRewardView(_tokenId); if(reward>0) { cake.safeTransfer(address(msg.sender), reward); } - lotteryNtf.claimReward(_tokenId); + lotteryNFT.claimReward(_tokenId); emit Claim(msg.sender, _tokenId, reward); } From 38ecd0fb9bcd6907eb7451c89a25a23f7eccb36b Mon Sep 17 00:00:00 2001 From: pancake-swap <71247357+pancake-swap@users.noreply.github.com> Date: Tue, 20 Oct 2020 22:14:54 +0800 Subject: [PATCH 06/11] Update Lottery.sol --- contracts/Lottery.sol | 147 +++++++++++++++++++++++++++++++++++------- 1 file changed, 123 insertions(+), 24 deletions(-) diff --git a/contracts/Lottery.sol b/contracts/Lottery.sol index 2a6fb80..0873de5 100644 --- a/contracts/Lottery.sol +++ b/contracts/Lottery.sol @@ -8,7 +8,7 @@ import "@openzeppelin/contracts/access/Ownable.sol"; import "./LotteryNFT.sol"; -// import "@nomiclabs/buidler/console.sol"; +import "@nomiclabs/buidler/console.sol"; // 4 numbers contract Lottery is Ownable { @@ -25,6 +25,8 @@ contract Lottery is Ownable { address public adminAddress; // maxNumber uint256 public maxNumber = 5; + // claiming + bool public claiming = false; // ================================= @@ -32,6 +34,8 @@ contract Lottery is Ownable { mapping (uint256 => uint256[]) public historyNumbers; // issueId => [tokenId] mapping (uint256 => uint256[]) public lotteryInfo; + // issueId => [totalAmount, firstMatchAmount, secondMatchingAmount, thirdMatchingAmount] + mapping (uint256 => uint256[]) public historyAmount; // address => [tokenId] mapping (address => uint256[]) public userInfo; @@ -49,6 +53,7 @@ contract Lottery is Ownable { event Claim(address indexed user, uint256 tokenid, uint256 amount); event DevWithdraw(address indexed user, uint256 amount); event Reset(uint256 indexed issueIndex); + event MultiClaim(address indexed user, uint256 amount); constructor( IERC20 _cake, @@ -67,38 +72,76 @@ contract Lottery is Ownable { function reset() external { require(msg.sender == adminAddress, "admin: wut?"); - require(winningNumbers.length == 4, "drawed?"); + require(drawed(), "drawed?"); lastTimestamp = block.timestamp; totalAddresses = 0; totalAmount = 0; delete winningNumbers; - issueIndex++; + issueIndex = issueIndex +1; if(getMatchingRewardAmount(issueIndex-1, 4) == 0) { uint256 amount = getTotalRewards(issueIndex-1).mul(allocation[0]).div(100); buy(amount, nullTicket); } + claiming = false; emit Reset(issueIndex); } + function drawed() public view returns(bool res) { + return winningNumbers.length != 0; + } + function buy(uint256 _amount, uint256[] memory _numbers) public { require (_numbers.length == 4, 'wrong length'); + require (!drawed(), 'drawed, can not buy now'); for (uint i = 0; i < 4; i++) { require (_numbers[i] <= maxNumber, 'exceed the maximum'); } - cake.safeTransferFrom(address(msg.sender), address(this), _amount); - uint256 tokenId = lotteryNFT.newLotteryItem(msg.sender, _numbers, _amount, issueIndex); - lotteryInfo[issueIndex].push(tokenId); - if (userInfo[msg.sender].length == 0) { - totalAddresses = totalAddresses + 1; + if(_numbers[0] == 0) { + uint256 tokenId = lotteryNFT.newLotteryItem(address(this), _numbers, _amount, issueIndex); + lotteryInfo[issueIndex].push(tokenId); + totalAmount = totalAmount + _amount; + lastTimestamp = block.timestamp; + emit Buy(address(this), tokenId); + } + else { + cake.safeTransferFrom(address(msg.sender), address(this), _amount); + uint256 tokenId = lotteryNFT.newLotteryItem(msg.sender, _numbers, _amount, issueIndex); + lotteryInfo[issueIndex].push(tokenId); + if (userInfo[msg.sender].length == 0) { + totalAddresses = totalAddresses + 1; + } + userInfo[msg.sender].push(tokenId); + totalAmount = totalAmount + _amount; + lastTimestamp = block.timestamp; + emit Buy(msg.sender, tokenId); } - userInfo[msg.sender].push(tokenId); - totalAmount = totalAmount + _amount; - lastTimestamp = block.timestamp; - emit Buy(msg.sender, tokenId); + } + + function multiBuy(uint256 _price, uint256[][] memory _numbers) public { + require (!drawed(), 'drawed, can not buy now'); + uint256 totalPrice = 0; + for (uint i = 0; i < _numbers.length; i++) { + require (_numbers[i].length == 4, 'wrong length'); + for (uint j = 0; j < 4; j++) { + require (_numbers[i][j] <= maxNumber, 'exceed the maximum'); + } + uint256 tokenId = lotteryNFT.newLotteryItem(msg.sender, _numbers[i], _price, issueIndex); + lotteryInfo[issueIndex].push(tokenId); + if (userInfo[msg.sender].length == 0) { + totalAddresses = totalAddresses + 1; + } + userInfo[msg.sender].push(tokenId); + totalAmount = totalAmount + _price; + lastTimestamp = block.timestamp; + totalPrice = totalPrice + _price; + // buy(_price, _numbers[i]); + } + cake.safeTransferFrom(address(msg.sender), address(this), totalPrice); } function drawing() public { require(msg.sender == adminAddress, "admin: wut?"); + require(!drawed(), "reset?"); bytes32 _structHash; uint256 _randomNumber; uint256 _maxNumber = maxNumber; @@ -151,8 +194,56 @@ contract Lottery is Ownable { emit Drawing(issueIndex, winningNumbers); } + function setClaiming(uint256[4] memory _historyAmount) public { + require(msg.sender == adminAddress, "admin: wut?"); + require(drawed(), "reset?"); + historyAmount[issueIndex] = _historyAmount; + claiming = true; + } + + function calculateMatchingRewardAmount() external view returns (uint256[4] memory) { + uint256 totalAmout1 = 0; + uint256 totalAmout2 = 0; + uint256 totalAmout3 = 0; + for (uint i = 0; i < lotteryInfo[issueIndex].length; i++) { + uint256 tokenId = lotteryInfo[issueIndex][i]; + uint256[] memory lotteryNumbers = lotteryNFT.getLotteryNumbers(tokenId); + uint256[] storage _winningNumbers = historyNumbers[issueIndex]; + uint256 matchingNumber = 0; + for (uint j = 0; j < _winningNumbers.length; j++) { + if(lotteryNumbers[j] == _winningNumbers[j]) { + matchingNumber++; + } + } + if (matchingNumber == 4) { + totalAmout1 = totalAmout1 + lotteryNFT.getLotteryAmount(tokenId);; + } + if (matchingNumber == 3) { + totalAmout2 = totalAmout2 + lotteryNFT.getLotteryAmount(tokenId);; + } + if (matchingNumber == 2) { + totalAmout3 = totalAmout3 + lotteryNFT.getLotteryAmount(tokenId);; + } + } + return [totalAmount, totalAmout1, totalAmout2, totalAmout3]; + } + function getMatchingRewardAmount(uint256 _issueIndex, uint256 _matchingNumber) internal view returns (uint256) { - uint256 totalAmout = 0; + return historyAmount[_issueIndex][5 - _matchingNumber]; + } + + + function getTotalRewards(uint256 _issueIndex) public view returns(uint256) { + require (_issueIndex <= issueIndex, '_issueIndex <= issueIndex'); + + if(!drawed() && _issueIndex == issueIndex) { + return totalAmount; + } + return historyAmount[_issueIndex][0]; + } + + function getMatchingRewardLength(uint256 _issueIndex, uint256 _matchingNumber) external view returns (uint256) { + uint256 length = 0; for (uint i = 0; i < lotteryInfo[_issueIndex].length; i++) { uint256 tokenId = lotteryInfo[_issueIndex][i]; uint256[] memory lotteryNumbers = lotteryNFT.getLotteryNumbers(tokenId); @@ -164,10 +255,10 @@ contract Lottery is Ownable { } } if (matchingNumber == _matchingNumber) { - totalAmout = totalAmout + lotteryNFT.getLotteryAmount(tokenId); + length = length + 1; } } - return totalAmout; + return length; } function getMatchingLotteries(uint256 _issueIndex, uint256 _matchingNumber, uint256 _index) external view returns(uint256) { @@ -192,15 +283,6 @@ contract Lottery is Ownable { return index; } - function getTotalRewards(uint256 _issueIndex) public view returns(uint256) { - uint256 total = 0; - for (uint i = 0; i < lotteryInfo[_issueIndex].length; i++) { - uint256 tokenId = lotteryInfo[_issueIndex][i]; - total = total.add(lotteryNFT.getLotteryAmount(tokenId)); - } - return total; - } - function getRewardView(uint256 _tokenId) public view returns(uint256) { uint256 _issueIndex = lotteryNFT.getLotteryIssueIndex(_tokenId); uint256[] memory lotteryNumbers = lotteryNFT.getLotteryNumbers(_tokenId); @@ -232,6 +314,23 @@ contract Lottery is Ownable { emit Claim(msg.sender, _tokenId, reward); } + function multiClaim(uint256[] memory _tickets) public { + require (drawed(), 'havnt drawed, can not claim'); + uint256 totalReward = 0; + for (uint i = 0; i < _tickets.length; i++) { + require (msg.sender == lotteryNFT.ownerOf(_tickets[i]), "not from owner"); + require (lotteryNFT.getClaimStatus(_tickets[i]) == false, "claimed"); + uint256 reward = getRewardView(_tickets[i]); + if(reward>0) { + totalReward = reward.add(totalReward); + } + } + lotteryNFT.multiClaimReward(_tickets); + if(totalReward>0) { + cake.safeTransfer(address(msg.sender), totalReward); + } + emit MultiClaim(msg.sender, totalReward); + } // Update admin address by the previous dev. function setAdmin(address _adminAddress) public onlyOwner { From 731c1293839ec8f6229af9498552c59b00fbd99b Mon Sep 17 00:00:00 2001 From: pancake-swap <71247357+pancake-swap@users.noreply.github.com> Date: Tue, 20 Oct 2020 22:16:25 +0800 Subject: [PATCH 07/11] Update Lottery.sol --- contracts/Lottery.sol | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/contracts/Lottery.sol b/contracts/Lottery.sol index 0873de5..2d62457 100644 --- a/contracts/Lottery.sol +++ b/contracts/Lottery.sol @@ -25,8 +25,6 @@ contract Lottery is Ownable { address public adminAddress; // maxNumber uint256 public maxNumber = 5; - // claiming - bool public claiming = false; // ================================= @@ -82,7 +80,6 @@ contract Lottery is Ownable { uint256 amount = getTotalRewards(issueIndex-1).mul(allocation[0]).div(100); buy(amount, nullTicket); } - claiming = false; emit Reset(issueIndex); } @@ -191,16 +188,10 @@ contract Lottery is Ownable { assembly {_randomNumber := add(mod(_randomNumber, _maxNumber),1)} winningNumbers.push(_randomNumber); historyNumbers[issueIndex] = winningNumbers; + historyAmount[issueIndex] = calculateMatchingRewardAmount(); emit Drawing(issueIndex, winningNumbers); } - function setClaiming(uint256[4] memory _historyAmount) public { - require(msg.sender == adminAddress, "admin: wut?"); - require(drawed(), "reset?"); - historyAmount[issueIndex] = _historyAmount; - claiming = true; - } - function calculateMatchingRewardAmount() external view returns (uint256[4] memory) { uint256 totalAmout1 = 0; uint256 totalAmout2 = 0; From f5d6ed482538b051fb8a7c09fc43649b9add32c5 Mon Sep 17 00:00:00 2001 From: pancake-swap <71247357+pancake-swap@users.noreply.github.com> Date: Tue, 20 Oct 2020 22:16:54 +0800 Subject: [PATCH 08/11] Update LotteryNFT.sol --- contracts/LotteryNFT.sol | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/contracts/LotteryNFT.sol b/contracts/LotteryNFT.sol index f7650be..ed3513e 100644 --- a/contracts/LotteryNFT.sol +++ b/contracts/LotteryNFT.sol @@ -47,6 +47,14 @@ contract LotteryNFT is ERC721, Ownable { function claimReward(uint256 tokenId) external onlyOwner { claimInfo[tokenId] = true; } + function multiClaimReward(uint256[] memory _tokenIds) external onlyOwner { + for (uint i = 0; i < _tokenIds.length; i++) { + claimInfo[_tokenIds[i]] = true; + } + } + function burn(uint256 tokenId) external onlyOwner { + _burn(tokenId); + } function getClaimStatus(uint256 tokenId) external view returns (bool) { return claimInfo[tokenId]; } From b16b813dae52c84c5c3be4e7b984fb0dcb62840c Mon Sep 17 00:00:00 2001 From: pancake-swap <71247357+pancake-swap@users.noreply.github.com> Date: Tue, 20 Oct 2020 22:43:07 +0800 Subject: [PATCH 09/11] Update Lottery.sol --- contracts/Lottery.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/Lottery.sol b/contracts/Lottery.sol index 2d62457..8a33d55 100644 --- a/contracts/Lottery.sol +++ b/contracts/Lottery.sol @@ -192,7 +192,7 @@ contract Lottery is Ownable { emit Drawing(issueIndex, winningNumbers); } - function calculateMatchingRewardAmount() external view returns (uint256[4] memory) { + function calculateMatchingRewardAmount() public view returns (uint256[4] memory) { uint256 totalAmout1 = 0; uint256 totalAmout2 = 0; uint256 totalAmout3 = 0; @@ -207,13 +207,13 @@ contract Lottery is Ownable { } } if (matchingNumber == 4) { - totalAmout1 = totalAmout1 + lotteryNFT.getLotteryAmount(tokenId);; + totalAmout1 = totalAmout1 + lotteryNFT.getLotteryAmount(tokenId); } if (matchingNumber == 3) { - totalAmout2 = totalAmout2 + lotteryNFT.getLotteryAmount(tokenId);; + totalAmout2 = totalAmout2 + lotteryNFT.getLotteryAmount(tokenId); } if (matchingNumber == 2) { - totalAmout3 = totalAmout3 + lotteryNFT.getLotteryAmount(tokenId);; + totalAmout3 = totalAmout3 + lotteryNFT.getLotteryAmount(tokenId); } } return [totalAmount, totalAmout1, totalAmout2, totalAmout3]; From 6b5d31ed257168e9c90079223731d94d2f586fad Mon Sep 17 00:00:00 2001 From: pancake-swap <71247357+pancake-swap@users.noreply.github.com> Date: Wed, 21 Oct 2020 23:36:27 +0800 Subject: [PATCH 10/11] Update Lottery.sol --- contracts/Lottery.sol | 298 +++++++++++++++++++++--------------------- 1 file changed, 148 insertions(+), 150 deletions(-) diff --git a/contracts/Lottery.sol b/contracts/Lottery.sol index 8a33d55..56d9a0e 100644 --- a/contracts/Lottery.sol +++ b/contracts/Lottery.sol @@ -15,6 +15,7 @@ contract Lottery is Ownable { using SafeMath for uint256; using SafeERC20 for IERC20; + uint8 constant keyLengthForEachBuy = 11; // Allocation for first/sencond/third reward uint256[] private allocation = [60, 20, 10]; // The TOKEN to buy lottery @@ -29,11 +30,13 @@ contract Lottery is Ownable { // ================================= // issueId => winningNumbers[numbers] - mapping (uint256 => uint256[]) public historyNumbers; + mapping (uint256 => uint8[4]) public historyNumbers; // issueId => [tokenId] mapping (uint256 => uint256[]) public lotteryInfo; // issueId => [totalAmount, firstMatchAmount, secondMatchingAmount, thirdMatchingAmount] mapping (uint256 => uint256[]) public historyAmount; + // issueId => buyAmountSum + mapping (uint256 => mapping(uint64 => uint256)) public userBuyAmountSum; // address => [tokenId] mapping (address => uint256[]) public userInfo; @@ -42,16 +45,17 @@ contract Lottery is Ownable { uint256 public totalAmount = 0; uint256 public lastTimestamp; - uint256[] public winningNumbers; + uint8[4] public winningNumbers; // ================================= event Buy(address indexed user, uint256 tokenId); - event Drawing(uint256 indexed issueIndex, uint256[] winningNumbers); + event Drawing(uint256 indexed issueIndex, uint8[4] winningNumbers); event Claim(address indexed user, uint256 tokenid, uint256 amount); event DevWithdraw(address indexed user, uint256 amount); event Reset(uint256 indexed issueIndex); event MultiClaim(address indexed user, uint256 amount); + event MultiBuy(address indexed user, uint256 amount); constructor( IERC20 _cake, @@ -66,7 +70,7 @@ contract Lottery is Ownable { lastTimestamp = block.timestamp; } - uint256[] private nullTicket = [0,0,0,0]; + uint8[4] public nullTicket = [0,0,0,0]; function reset() external { require(msg.sender == adminAddress, "admin: wut?"); @@ -74,7 +78,10 @@ contract Lottery is Ownable { lastTimestamp = block.timestamp; totalAddresses = 0; totalAmount = 0; - delete winningNumbers; + winningNumbers[0]=0; + winningNumbers[1]=0; + winningNumbers[2]=0; + winningNumbers[3]=0; issueIndex = issueIndex +1; if(getMatchingRewardAmount(issueIndex-1, 4) == 0) { uint256 amount = getTotalRewards(issueIndex-1).mul(allocation[0]).div(100); @@ -83,59 +90,6 @@ contract Lottery is Ownable { emit Reset(issueIndex); } - function drawed() public view returns(bool res) { - return winningNumbers.length != 0; - } - - function buy(uint256 _amount, uint256[] memory _numbers) public { - require (_numbers.length == 4, 'wrong length'); - require (!drawed(), 'drawed, can not buy now'); - for (uint i = 0; i < 4; i++) { - require (_numbers[i] <= maxNumber, 'exceed the maximum'); - } - if(_numbers[0] == 0) { - uint256 tokenId = lotteryNFT.newLotteryItem(address(this), _numbers, _amount, issueIndex); - lotteryInfo[issueIndex].push(tokenId); - totalAmount = totalAmount + _amount; - lastTimestamp = block.timestamp; - emit Buy(address(this), tokenId); - } - else { - cake.safeTransferFrom(address(msg.sender), address(this), _amount); - uint256 tokenId = lotteryNFT.newLotteryItem(msg.sender, _numbers, _amount, issueIndex); - lotteryInfo[issueIndex].push(tokenId); - if (userInfo[msg.sender].length == 0) { - totalAddresses = totalAddresses + 1; - } - userInfo[msg.sender].push(tokenId); - totalAmount = totalAmount + _amount; - lastTimestamp = block.timestamp; - emit Buy(msg.sender, tokenId); - } - } - - function multiBuy(uint256 _price, uint256[][] memory _numbers) public { - require (!drawed(), 'drawed, can not buy now'); - uint256 totalPrice = 0; - for (uint i = 0; i < _numbers.length; i++) { - require (_numbers[i].length == 4, 'wrong length'); - for (uint j = 0; j < 4; j++) { - require (_numbers[i][j] <= maxNumber, 'exceed the maximum'); - } - uint256 tokenId = lotteryNFT.newLotteryItem(msg.sender, _numbers[i], _price, issueIndex); - lotteryInfo[issueIndex].push(tokenId); - if (userInfo[msg.sender].length == 0) { - totalAddresses = totalAddresses + 1; - } - userInfo[msg.sender].push(tokenId); - totalAmount = totalAmount + _price; - lastTimestamp = block.timestamp; - totalPrice = totalPrice + _price; - // buy(_price, _numbers[i]); - } - cake.safeTransferFrom(address(msg.sender), address(this), totalPrice); - } - function drawing() public { require(msg.sender == adminAddress, "admin: wut?"); require(!drawed(), "reset?"); @@ -153,7 +107,7 @@ contract Lottery is Ownable { ); _randomNumber = uint256(_structHash); assembly {_randomNumber := add(mod(_randomNumber, _maxNumber),1)} - winningNumbers.push(_randomNumber); + winningNumbers[0]=uint8(_randomNumber); // 2 _structHash = keccak256( @@ -164,7 +118,7 @@ contract Lottery is Ownable { ); _randomNumber = uint256(_structHash); assembly {_randomNumber := add(mod(_randomNumber, _maxNumber),1)} - winningNumbers.push(_randomNumber); + winningNumbers[1]=uint8(_randomNumber); // 3 _structHash = keccak256( @@ -175,7 +129,7 @@ contract Lottery is Ownable { ); _randomNumber = uint256(_structHash); assembly {_randomNumber := add(mod(_randomNumber, _maxNumber),1)} - winningNumbers.push(_randomNumber); + winningNumbers[2]=uint8(_randomNumber); // 4 _structHash = keccak256( @@ -186,114 +140,72 @@ contract Lottery is Ownable { ); _randomNumber = uint256(_structHash); assembly {_randomNumber := add(mod(_randomNumber, _maxNumber),1)} - winningNumbers.push(_randomNumber); + winningNumbers[3]=uint8(_randomNumber); + historyNumbers[issueIndex] = winningNumbers; historyAmount[issueIndex] = calculateMatchingRewardAmount(); emit Drawing(issueIndex, winningNumbers); } - function calculateMatchingRewardAmount() public view returns (uint256[4] memory) { - uint256 totalAmout1 = 0; - uint256 totalAmout2 = 0; - uint256 totalAmout3 = 0; - for (uint i = 0; i < lotteryInfo[issueIndex].length; i++) { - uint256 tokenId = lotteryInfo[issueIndex][i]; - uint256[] memory lotteryNumbers = lotteryNFT.getLotteryNumbers(tokenId); - uint256[] storage _winningNumbers = historyNumbers[issueIndex]; - uint256 matchingNumber = 0; - for (uint j = 0; j < _winningNumbers.length; j++) { - if(lotteryNumbers[j] == _winningNumbers[j]) { - matchingNumber++; - } - } - if (matchingNumber == 4) { - totalAmout1 = totalAmout1 + lotteryNFT.getLotteryAmount(tokenId); - } - if (matchingNumber == 3) { - totalAmout2 = totalAmout2 + lotteryNFT.getLotteryAmount(tokenId); - } - if (matchingNumber == 2) { - totalAmout3 = totalAmout3 + lotteryNFT.getLotteryAmount(tokenId); - } - } - return [totalAmount, totalAmout1, totalAmout2, totalAmout3]; - } - - function getMatchingRewardAmount(uint256 _issueIndex, uint256 _matchingNumber) internal view returns (uint256) { - return historyAmount[_issueIndex][5 - _matchingNumber]; + function drawed() public view returns(bool res) { + return winningNumbers[0] != 0; } - - function getTotalRewards(uint256 _issueIndex) public view returns(uint256) { - require (_issueIndex <= issueIndex, '_issueIndex <= issueIndex'); - - if(!drawed() && _issueIndex == issueIndex) { - return totalAmount; + function buy(uint256 _amount, uint8[4] memory _numbers) public { + require (!drawed(), 'drawed, can not buy now'); + for (uint i = 0; i < 4; i++) { + require (_numbers[i] <= maxNumber, 'exceed the maximum'); } - return historyAmount[_issueIndex][0]; - } - - function getMatchingRewardLength(uint256 _issueIndex, uint256 _matchingNumber) external view returns (uint256) { - uint256 length = 0; - for (uint i = 0; i < lotteryInfo[_issueIndex].length; i++) { - uint256 tokenId = lotteryInfo[_issueIndex][i]; - uint256[] memory lotteryNumbers = lotteryNFT.getLotteryNumbers(tokenId); - uint256[] storage _winningNumbers = historyNumbers[_issueIndex]; - uint256 matchingNumber = 0; - for (uint j = 0; j < _winningNumbers.length; j++) { - if(lotteryNumbers[j] == _winningNumbers[j]) { - matchingNumber++; - } + if(_numbers[0] == 0) { + uint256 tokenId = lotteryNFT.newLotteryItem(address(this), _numbers, _amount, issueIndex); + lotteryInfo[issueIndex].push(tokenId); + totalAmount = totalAmount + _amount; + lastTimestamp = block.timestamp; + emit Buy(address(this), tokenId); + } + else { + uint256 tokenId = lotteryNFT.newLotteryItem(msg.sender, _numbers, _amount, issueIndex); + lotteryInfo[issueIndex].push(tokenId); + if (userInfo[msg.sender].length == 0) { + totalAddresses = totalAddresses + 1; } - if (matchingNumber == _matchingNumber) { - length = length + 1; + userInfo[msg.sender].push(tokenId); + totalAmount = totalAmount + _amount; + lastTimestamp = block.timestamp; + cake.safeTransferFrom(address(msg.sender), address(this), _amount); + uint64[keyLengthForEachBuy] memory userNumberIndex = generateNumberIndexKey(_numbers); + for (uint i = 0; i < keyLengthForEachBuy; i++) { + userBuyAmountSum[issueIndex][userNumberIndex[i]]=userBuyAmountSum[issueIndex][userNumberIndex[i]].add(_amount); } + emit Buy(msg.sender, tokenId); } - return length; } - function getMatchingLotteries(uint256 _issueIndex, uint256 _matchingNumber, uint256 _index) external view returns(uint256) { - uint256 index = 0; - for (uint i = 0; i < lotteryInfo[_issueIndex].length; i++) { - uint256 tokenId = lotteryInfo[_issueIndex][i]; - uint256[] memory lotteryNumbers = lotteryNFT.getLotteryNumbers(tokenId); - uint256[] storage _winningNumbers = historyNumbers[_issueIndex]; - uint256 matchingNumber = 0; - for (uint j = 0; j < _winningNumbers.length; j++) { - if (lotteryNumbers[j] == _winningNumbers[j]) { - matchingNumber++; - } + function multiBuy(uint256 _price, uint8[4][] memory _numbers) public { + require (!drawed(), 'drawed, can not buy now'); + uint256 totalPrice = 0; + for (uint i = 0; i < _numbers.length; i++) { + for (uint j = 0; j < 4; j++) { + require (_numbers[i][j] <= maxNumber, 'exceed the maximum'); } - if (matchingNumber == _matchingNumber) { - if (index == _index) { - return tokenId; - } - index++; + uint256 tokenId = lotteryNFT.newLotteryItem(msg.sender, _numbers[i], _price, issueIndex); + lotteryInfo[issueIndex].push(tokenId); + if (userInfo[msg.sender].length == 0) { + totalAddresses = totalAddresses + 1; } - } - return index; - } - - function getRewardView(uint256 _tokenId) public view returns(uint256) { - uint256 _issueIndex = lotteryNFT.getLotteryIssueIndex(_tokenId); - uint256[] memory lotteryNumbers = lotteryNFT.getLotteryNumbers(_tokenId); - uint256[] storage _winningNumbers = historyNumbers[_issueIndex]; - uint256 matchingNumber = 0; - for (uint i = 0; i < lotteryNumbers.length; i++) { - if (_winningNumbers[i] == lotteryNumbers[i]) { - matchingNumber= matchingNumber +1; + userInfo[msg.sender].push(tokenId); + totalAmount = totalAmount + _price; + lastTimestamp = block.timestamp; + totalPrice = totalPrice + _price; + uint64[keyLengthForEachBuy] memory numberIndexKey = generateNumberIndexKey(_numbers[i]); + for (uint k = 0; k < keyLengthForEachBuy; k++) { + userBuyAmountSum[issueIndex][numberIndexKey[k]]=userBuyAmountSum[issueIndex][numberIndexKey[k]].add(_price); } } - uint256 reward = 0; - if (matchingNumber > 1) { - uint256 amount = lotteryNFT.getLotteryAmount(_tokenId); - uint256 poolAmount = getTotalRewards(_issueIndex).mul(allocation[4-matchingNumber]).div(100); - reward = amount.mul(1e12).div(getMatchingRewardAmount(_issueIndex, matchingNumber)).mul(poolAmount); - } - return reward.div(1e12); + cake.safeTransferFrom(address(msg.sender), address(this), totalPrice); + emit MultiBuy(msg.sender, totalPrice); } - function claimReward(uint256 _tokenId) public { require(msg.sender == lotteryNFT.ownerOf(_tokenId), "not from owner"); require (lotteryNFT.getClaimStatus(_tokenId) == false, "claimed"); @@ -323,6 +235,92 @@ contract Lottery is Ownable { emit MultiClaim(msg.sender, totalReward); } + function generateNumberIndexKey(uint8[4] memory number) public pure returns (uint64[keyLengthForEachBuy] memory) { + uint64[4] memory tempNumber; + tempNumber[0]=uint64(number[0]); + tempNumber[1]=uint64(number[1]); + tempNumber[2]=uint64(number[2]); + tempNumber[3]=uint64(number[3]); + + uint64[keyLengthForEachBuy] memory result; + result[0] = tempNumber[0]*256*256*256*256*256*256 + 1*256*256*256*256*256 + tempNumber[1]*256*256*256*256 + 2*256*256*256+tempNumber[2]*256*256 + 3*256 + tempNumber[3]; + + result[1] = tempNumber[0]*256*256*256*256 + 1*256*256*256 + tempNumber[1]*256*256 + 2*256+ tempNumber[2]; + result[2] = tempNumber[0]*256*256*256*256 + 1*256*256*256 + tempNumber[1]*256*256 + 3*256+ tempNumber[3]; + result[3] = tempNumber[0]*256*256*256*256 + 2*256*256*256 + tempNumber[2]*256*256 + 3*256 + tempNumber[3]; + result[4] = 1*256*256*256*256*256 + tempNumber[1]*256*256*256*256 + 2*256*256*256 + tempNumber[2]*256*256 + 3*256 + tempNumber[3]; + + result[5] = tempNumber[0]*256*256 + 1*256+ tempNumber[1]; + result[6] = tempNumber[0]*256*256 + 2*256+ tempNumber[2]; + result[7] = tempNumber[0]*256*256 + 3*256+ tempNumber[3]; + result[8] = 1*256*256*256+ tempNumber[1]*256*256 + 2*256+ tempNumber[2]; + result[9] = 1*256*256*256+ tempNumber[1]*256*256 + 3*256+ tempNumber[3]; + result[10] = 2*256*256*256+ tempNumber[2]*256*256 + 3*256+ tempNumber[3]; + + return result; + } + + function calculateMatchingRewardAmount() public view returns (uint256[4] memory) { + uint64[keyLengthForEachBuy] memory numberIndexKey = generateNumberIndexKey(winningNumbers); + + uint256 totalAmout1 = userBuyAmountSum[issueIndex][numberIndexKey[0]]; + + uint256 sumForTotalAmout2 = userBuyAmountSum[issueIndex][numberIndexKey[1]]; + sumForTotalAmout2 = sumForTotalAmout2.add(userBuyAmountSum[issueIndex][numberIndexKey[2]]); + sumForTotalAmout2 = sumForTotalAmout2.add(userBuyAmountSum[issueIndex][numberIndexKey[3]]); + sumForTotalAmout2 = sumForTotalAmout2.add(userBuyAmountSum[issueIndex][numberIndexKey[4]]); + + uint256 totalAmout2 = sumForTotalAmout2.sub(totalAmout1.mul(4)); + + uint256 sumForTotalAmout3 = userBuyAmountSum[issueIndex][numberIndexKey[5]]; + sumForTotalAmout3 = sumForTotalAmout3.add(userBuyAmountSum[issueIndex][numberIndexKey[6]]); + sumForTotalAmout3 = sumForTotalAmout3.add(userBuyAmountSum[issueIndex][numberIndexKey[7]]); + sumForTotalAmout3 = sumForTotalAmout3.add(userBuyAmountSum[issueIndex][numberIndexKey[8]]); + sumForTotalAmout3 = sumForTotalAmout3.add(userBuyAmountSum[issueIndex][numberIndexKey[9]]); + sumForTotalAmout3 = sumForTotalAmout3.add(userBuyAmountSum[issueIndex][numberIndexKey[10]]); + + uint256 totalAmout3 = sumForTotalAmout3.add(totalAmout1.mul(6)).sub(sumForTotalAmout2.mul(3)); + + return [totalAmount, totalAmout1, totalAmout2, totalAmout3]; + } + + function getMatchingRewardAmount(uint256 _issueIndex, uint256 _matchingNumber) internal view returns (uint256) { + return historyAmount[_issueIndex][5 - _matchingNumber]; + } + + function getTotalRewards(uint256 _issueIndex) public view returns(uint256) { + require (_issueIndex <= issueIndex, '_issueIndex <= issueIndex'); + + if(!drawed() && _issueIndex == issueIndex) { + return totalAmount; + } + return historyAmount[_issueIndex][0]; + } + + function getMatchingRewardLength(uint256 _issueIndex, uint256 _matchingNumber, uint256 _price) external view returns (uint256) { + return historyAmount[_issueIndex][5 - _matchingNumber].mul(1e12).div(_price).div(1e12); + } + + function getRewardView(uint256 _tokenId) public view returns(uint256) { + uint256 _issueIndex = lotteryNFT.getLotteryIssueIndex(_tokenId); + uint8[4] memory lotteryNumbers = lotteryNFT.getLotteryNumbers(_tokenId); + uint8[4] memory _winningNumbers = historyNumbers[_issueIndex]; + uint256 matchingNumber = 0; + for (uint i = 0; i < lotteryNumbers.length; i++) { + if (_winningNumbers[i] == lotteryNumbers[i]) { + matchingNumber= matchingNumber +1; + } + } + uint256 reward = 0; + if (matchingNumber > 1) { + uint256 amount = lotteryNFT.getLotteryAmount(_tokenId); + uint256 poolAmount = getTotalRewards(_issueIndex).mul(allocation[4-matchingNumber]).div(100); + reward = amount.mul(1e12).div(getMatchingRewardAmount(_issueIndex, matchingNumber)).mul(poolAmount); + } + return reward.div(1e12); + } + + // Update admin address by the previous dev. function setAdmin(address _adminAddress) public onlyOwner { adminAddress = _adminAddress; From 2cb94306cb680559fa5e67b209cc30d7fd094165 Mon Sep 17 00:00:00 2001 From: pancake-swap <71247357+pancake-swap@users.noreply.github.com> Date: Wed, 21 Oct 2020 23:36:41 +0800 Subject: [PATCH 11/11] Update LotteryNFT.sol --- contracts/LotteryNFT.sol | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/contracts/LotteryNFT.sol b/contracts/LotteryNFT.sol index ed3513e..8970197 100644 --- a/contracts/LotteryNFT.sol +++ b/contracts/LotteryNFT.sol @@ -1,5 +1,3 @@ -// contracts/LotteryNF.sol -// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; @@ -11,14 +9,14 @@ contract LotteryNFT is ERC721, Ownable { using Counters for Counters.Counter; Counters.Counter private _tokenIds; - mapping (uint256 => uint256[]) public lotteryInfo; + mapping (uint256 => uint8[4]) public lotteryInfo; mapping (uint256 => uint256) public lotteryAmount; mapping (uint256 => uint256) public issueIndex; mapping (uint256 => bool) public claimInfo; constructor() public ERC721("Pancake Lottery NFT", "ticket") {} - function newLotteryItem(address player, uint256[] memory _lotteryNumbers, uint256 _amount, uint256 _issueIndex) + function newLotteryItem(address player, uint8[4] memory _lotteryNumbers, uint256 _amount, uint256 _issueIndex) public onlyOwner returns (uint256) { @@ -35,7 +33,7 @@ contract LotteryNFT is ERC721, Ownable { return newItemId; } - function getLotteryNumbers(uint256 tokenId) external view returns (uint256[] memory) { + function getLotteryNumbers(uint256 tokenId) external view returns (uint8[4] memory) { return lotteryInfo[tokenId]; } function getLotteryAmount(uint256 tokenId) external view returns (uint256) {