From 5127e850e3f2760a7cd73049e5429c515b2b2c56 Mon Sep 17 00:00:00 2001 From: owen05 Date: Tue, 13 Apr 2021 11:12:13 +0800 Subject: [PATCH] dodoMysteryBox finish --- README.md | 4 +- contracts/DODOToken/DODOMysteryBox.sol | 80 ++++++++++++++----- .../proxies/DODOMysteryBoxProxy.sol | 78 ++++++++++++++++++ contracts/lib/RandomGenerator.sol | 8 +- deploy-nft.txt | 5 ++ test/DODONFT/nftMainFlow.test.ts | 28 +++---- 6 files changed, 166 insertions(+), 37 deletions(-) create mode 100644 contracts/SmartRoute/proxies/DODOMysteryBoxProxy.sol diff --git a/README.md b/README.md index 1049818..03dc7da 100644 --- a/README.md +++ b/README.md @@ -20,4 +20,6 @@ - contracts/external/ERC1155/ -- contracts/lib/RandomGenerator.sol \ No newline at end of file +- contracts/lib/RandomGenerator.sol + +- contracts/SmartRoute/proxies/DODOMysteryBoxProxy.sol \ No newline at end of file diff --git a/contracts/DODOToken/DODOMysteryBox.sol b/contracts/DODOToken/DODOMysteryBox.sol index c03deac..8a2e083 100644 --- a/contracts/DODOToken/DODOMysteryBox.sol +++ b/contracts/DODOToken/DODOMysteryBox.sol @@ -10,9 +10,10 @@ import {SafeERC20} from "../lib/SafeERC20.sol"; import {SafeMath} from "../lib/SafeMath.sol"; import {IRandomGenerator} from "../lib/RandomGenerator.sol"; import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; +import {ReentrancyGuard} from "../lib/ReentrancyGuard.sol"; import {ERC1155} from "../external/ERC1155/ERC1155.sol"; -contract DODOMysteryBox is ERC1155, InitializableOwnable { +contract DODOMysteryBox is ERC1155, InitializableOwnable, ReentrancyGuard { using SafeMath for uint256; using SafeERC20 for IERC20; @@ -27,13 +28,15 @@ contract DODOMysteryBox is ERC1155, InitializableOwnable { uint256[][] public _PRIZE_SET_; // Interval index => tokenIds mapping(uint256 => bool) _TOKEN_ID_FLAG_; - uint256 constant totalInterval = 1000; - // ============ Event ============= event ChangeRandomGenerator(address randomGenerator); event ChangeTicketUnit(uint256 newTicketUnit); event RetriveTicket(address to, uint256 amount); event BurnTicket(uint256 amount); + event RedeemPrize(address to, uint256 ticketInput, uint256 ticketNum); + event SetProbInterval(); + event SetPrizeSet(); + event SetPrizeSetByIndex(uint256 index); function init( address owner, @@ -44,8 +47,10 @@ contract DODOMysteryBox is ERC1155, InitializableOwnable { uint256[] memory probIntervals, uint256[][] memory prizeSet ) public { - require(probIntervals.length == prizeSet.length, "DODOMysteryBox:PARAM_NOT_MATCH"); - + require( + probIntervals.length == prizeSet.length && probIntervals.length > 0, + "DODOMysteryBox: PARAM_NOT_INVALID" + ); initOwner(owner); _setURI(baseUri); @@ -57,7 +62,7 @@ contract DODOMysteryBox is ERC1155, InitializableOwnable { _setPrizeSet(prizeSet); } - function redeemPrize(address to) external { + function redeemPrize(address to) preventReentrant external { uint256 ticketBalance = IERC20(_TICKET_).balanceOf(address(this)); uint256 ticketInput = ticketBalance.sub(_TICKET_RESERVE_); uint256 ticketNum = ticketInput.div(_TICKET_UNIT_); @@ -65,13 +70,28 @@ contract DODOMysteryBox is ERC1155, InitializableOwnable { for (uint256 i = 0; i < ticketNum; i++) { _redeemSinglePrize(to); } - _TICKET_RESERVE_ = _TICKET_RESERVE_.add(ticketBalance); + _TICKET_RESERVE_ = _TICKET_RESERVE_.add(ticketInput); + emit RedeemPrize(to, ticketInput, ticketNum); + } + + // =============== View ================ + function getRarityByTokenId(uint256 tokenId) external view returns (uint256) { + require(_TOKEN_ID_FLAG_[tokenId], "DODOMysteryBox: TOKEN_ID_NOT_FOUND"); + for (uint256 i = 0; i < _PRIZE_SET_.length; i++) { + uint256[] memory curPrizes = _PRIZE_SET_[i]; + for (uint256 j = 0; j < curPrizes.length; j++) { + if(tokenId == curPrizes[j]) { + return i; + } + } + } } // ============ Internal ============ function _redeemSinglePrize(address to) internal { - uint256 random = IRandomGenerator(_RANDOM_GENERATOR_).random() % totalInterval; + uint256 range = _PROB_INTERVAL_[_PROB_INTERVAL_.length - 1]; + uint256 random = IRandomGenerator(_RANDOM_GENERATOR_).random(gasleft()) % range; uint256 i; for (i = 0; i < _PROB_INTERVAL_.length; i++) { if (random <= _PROB_INTERVAL_[i]) { @@ -84,34 +104,58 @@ contract DODOMysteryBox is ERC1155, InitializableOwnable { } function _setProbInterval(uint256[] memory probIntervals) internal { - uint256 sum; - for (uint256 i = 0; i < probIntervals.length; i++) { - require(probIntervals[i] > 0, "DODOMysteryBox: INTERVAL_INVALID"); - sum += probIntervals[i]; - _PROB_INTERVAL_.push(probIntervals[i]); + for (uint256 i = 1; i < probIntervals.length; i++) { + require(probIntervals[i] > probIntervals[i - 1], "DODOMysteryBox: INTERVAL_INVALID"); } - require(sum == totalInterval, "DODOMysteryBox: TOTAL_INTERVAL_INVALID"); + _PROB_INTERVAL_ = probIntervals; + emit SetProbInterval(); } function _setPrizeSet(uint256[][] memory prizeSet) internal { for (uint256 i = 0; i < prizeSet.length; i++) { uint256[] memory curPrizes = prizeSet[i]; require(curPrizes.length > 0, "DODOMysteryBox: PRIZES_INVALID"); - _PRIZE_SET_.push(); for (uint256 j = 0; j < curPrizes.length; j++) { uint256 curTokenId = prizeSet[i][j]; - if(_TOKEN_ID_FLAG_[curTokenId]){ + if (_TOKEN_ID_FLAG_[curTokenId]) { require(false, "DODOMysteryBox: TOKEN_ID_INVALID"); - }else { - _PRIZE_SET_[i].push(curTokenId); + } else { _TOKEN_ID_FLAG_[curTokenId] = true; } } } + _PRIZE_SET_ = prizeSet; + emit SetPrizeSet(); } // ================= Owner =================== + function setProbInterval(uint256[] memory probIntervals) external onlyOwner { + require(probIntervals.length > 0, "DODOMysteryBox: PARAM_NOT_INVALID"); + _setProbInterval(probIntervals); + } + + function setPrzieSet(uint256[][] memory prizeSet) external onlyOwner { + require(prizeSet.length == _PROB_INTERVAL_.length, "DODOMysteryBox: PARAM_NOT_INVALID"); + _setPrizeSet(prizeSet); + } + + function setPrizeSetByIndex(uint256 index, uint256[] memory prizes) external onlyOwner { + require( + prizes.length > 0 && index < _PRIZE_SET_.length, + "DODOMysteryBox: PARAM_NOT_INVALID" + ); + for (uint256 i = 0; i < prizes.length; i++) { + if (_TOKEN_ID_FLAG_[prizes[i]]) { + require(false, "DODOMysteryBox: TOKEN_ID_INVALID"); + } else { + _TOKEN_ID_FLAG_[prizes[i]] = true; + } + } + _PRIZE_SET_[index] = prizes; + emit SetPrizeSetByIndex(index); + } + function updateRandomGenerator(address newRandomGenerator) external onlyOwner { require(newRandomGenerator != address(0)); _RANDOM_GENERATOR_ = newRandomGenerator; diff --git a/contracts/SmartRoute/proxies/DODOMysteryBoxProxy.sol b/contracts/SmartRoute/proxies/DODOMysteryBoxProxy.sol new file mode 100644 index 0000000..d51338d --- /dev/null +++ b/contracts/SmartRoute/proxies/DODOMysteryBoxProxy.sol @@ -0,0 +1,78 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +import {IDODOApproveProxy} from "../DODOApproveProxy.sol"; +import {IERC20} from "../../intf/IERC20.sol"; +import {IWETH} from "../../intf/IWETH.sol"; +import {SafeMath} from "../../lib/SafeMath.sol"; +import {SafeERC20} from "../../lib/SafeERC20.sol"; +import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; + +interface IDODOMysteryBox { + function _TICKET_() external view returns (address); + function redeemPrize(address to) external; +} + +/** + * @title DODO MysteryBoxProxy + * @author DODO Breeder + * + * @notice Entrance of MysteryBox in DODO platform + */ +contract DODOMysteryBoxProxy is ReentrancyGuard { + using SafeMath for uint256; + + // ============ Storage ============ + + address constant _ETH_ADDRESS_ = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address public immutable _WETH_; + address public immutable _DODO_APPROVE_PROXY_; + + // ============ Events ============ + event RedeemPrize(address indexed account, address indexed mysteryBox, uint256 ticketAmount); + + fallback() external payable {} + + receive() external payable {} + + constructor( + address payable weth, + address dodoApproveProxy + ) public { + _WETH_ = weth; + _DODO_APPROVE_PROXY_ = dodoApproveProxy; + } + + function redeemPrize( + address dodoMysteryBox, + uint256 ticketAmount, + uint8 flag // 0 - ERC20, 1 - quoteInETH + ) external payable preventReentrant { + _deposit(msg.sender, dodoMysteryBox, IDODOMysteryBox(dodoMysteryBox)._TICKET_(), ticketAmount, flag == 1); + IDODOMysteryBox(dodoMysteryBox).redeemPrize(msg.sender); + emit RedeemPrize(msg.sender, dodoMysteryBox, ticketAmount); + } + + function _deposit( + address from, + address to, + address token, + uint256 amount, + bool isETH + ) internal { + if (isETH) { + if (amount > 0) { + IWETH(_WETH_).deposit{value: amount}(); + if (to != address(this)) SafeERC20.safeTransfer(IERC20(_WETH_), to, amount); + } + } else { + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(token, from, to, amount); + } + } +} diff --git a/contracts/lib/RandomGenerator.sol b/contracts/lib/RandomGenerator.sol index 0fb5e89..d85e36a 100644 --- a/contracts/lib/RandomGenerator.sol +++ b/contracts/lib/RandomGenerator.sol @@ -8,7 +8,7 @@ pragma solidity 0.6.9; interface IRandomGenerator { - function random() external view returns (uint256); + function random(uint256 seed) external view returns (uint256); } interface IDODOMidPrice { @@ -16,7 +16,7 @@ interface IDODOMidPrice { } contract RandomGenerator { - address[] internal pools; + address[] public pools; constructor(address[] memory _pools) public { for (uint256 i = 0; i < pools.length; i++) { @@ -24,11 +24,11 @@ contract RandomGenerator { } } - function random() external view returns (uint256) { + function random(uint256 seed) external view returns (uint256) { uint256 priceSum; for (uint256 i = 0; i < pools.length; i++) { priceSum += IDODOMidPrice(pools[i]).getMidPrice(); } - return uint256(keccak256(abi.encodePacked(blockhash(block.number-1), priceSum))); + return uint256(keccak256(abi.encodePacked(blockhash(block.number-1), priceSum, seed))); } } diff --git a/deploy-nft.txt b/deploy-nft.txt index 10d1341..8a4425f 100644 --- a/deploy-nft.txt +++ b/deploy-nft.txt @@ -131,3 +131,8 @@ Init DODONFTProxyAddress Tx: 0x1ea94a3bedab5e35fec95071738066762a63253a68bc535ac DODOApproveProxy unlockAddProxy tx: 0xb8472dd2df9baeeff89d982299cbfc2d90e18c20baff97577fba959541f2514f DODOApproveProxy addDODOProxy tx: 0x8aa82620b6735ddfad0d55d3e9a752c3056e7d83664077e0240c0215a2e0017d Add AdminList on DODONFTRegistry Tx: 0x20f41302401122d950049e66458f307257743c0d395e06af607215b0f2532643 +==================================================== +network type: development +Deploy time: 2021/4/12 下午9:22:30 +Deploy type: NFT +multiSigAddress: undefined diff --git a/test/DODONFT/nftMainFlow.test.ts b/test/DODONFT/nftMainFlow.test.ts index 7d8dd32..897cb1d 100644 --- a/test/DODONFT/nftMainFlow.test.ts +++ b/test/DODONFT/nftMainFlow.test.ts @@ -138,11 +138,11 @@ describe("DODONFT", () => { var erc721Instance = contracts.getContractWithAddress(contracts.ERC721, erc721Address); await erc721Instance.methods.safeTransferFrom(author, vaultAddress, 0).send(ctx.sendParam(author)); - // var quoteToken = "0x156595bAF85D5C29E91d959889B022d952190A64"; - // var vaultPreOwner = "0xaac153c1344cA14497A5dd22b1F70C28793625aa"; + var quoteToken = "0x156595bAF85D5C29E91d959889B022d952190A64"; + var vaultPreOwner = "0x7e83d9d94837eE82F0cc18a691da6f42F03F1d86"; // var stakeToken = "0x854b0f89BAa9101e49Bfb357A38071C9db5d0DFa"; - var quoteToken = ctx.USDT.options.address; - var vaultPreOwner = author; + // var quoteToken = ctx.USDT.options.address; + // var vaultPreOwner = author; var stakeToken = "0x0000000000000000000000000000000000000000"; var dvmParams = [ @@ -168,18 +168,18 @@ describe("DODONFT", () => { ).encodeABI(); console.log("data:", callData); - await logGas(await nftVaultInstance.methods.createFragment( - ctx.NFTProxy.options.address, - callData - ), ctx.sendParam(author), "createFragment"); + // await logGas(await nftVaultInstance.methods.createFragment( + // ctx.NFTProxy.options.address, + // callData + // ), ctx.sendParam(author), "createFragment"); - let [fragAddress, , dvmAddress] = await ctx.getRegistry(ctx, vaultAddress); + // let [fragAddress, , dvmAddress] = await ctx.getRegistry(ctx, vaultAddress); - var dvmInstance = contracts.getContractWithAddress(contracts.DVM_NAME, dvmAddress); - var midPrice = await dvmInstance.methods.getMidPrice().call(); - assert(midPrice, mweiStr("1")); - let newVaultOwner = await nftVaultInstance.methods._OWNER_().call(); - assert(fragAddress, newVaultOwner); + // var dvmInstance = contracts.getContractWithAddress(contracts.DVM_NAME, dvmAddress); + // var midPrice = await dvmInstance.methods.getMidPrice().call(); + // assert(midPrice, mweiStr("1")); + // let newVaultOwner = await nftVaultInstance.methods._OWNER_().call(); + // assert(fragAddress, newVaultOwner); }); it.only("stakeToFeeDistributor", async () => {