From 4e1466f8f7df31f7fbcda8cbce24e7938fa67ce3 Mon Sep 17 00:00:00 2001 From: owen05 Date: Thu, 20 May 2021 11:09:59 +0800 Subject: [PATCH] update dropsBox --- .../DODOMysteryBox/MysteryBoxV2/BaseDrops.sol | 302 +++++++++++++++++ .../MysteryBoxV2/BaseMysteryBox.sol | 311 ------------------ ...MysteryBoxERC1155.sol => DropsERC1155.sol} | 2 +- .../{MysteryBoxERC721.sol => DropsERC721.sol} | 2 +- ...steryBoxFeeModel.sol => DropsFeeModel.sol} | 34 +- ...MysteryBoxProxy.sol => DODODropsProxy.sol} | 34 +- .../ERC20/InitializableMintableERC20.sol | 10 +- 7 files changed, 347 insertions(+), 348 deletions(-) create mode 100644 contracts/DODOMysteryBox/MysteryBoxV2/BaseDrops.sol delete mode 100644 contracts/DODOMysteryBox/MysteryBoxV2/BaseMysteryBox.sol rename contracts/DODOMysteryBox/MysteryBoxV2/{MysteryBoxERC1155.sol => DropsERC1155.sol} (94%) rename contracts/DODOMysteryBox/MysteryBoxV2/{MysteryBoxERC721.sol => DropsERC721.sol} (94%) rename contracts/DODOMysteryBox/MysteryBoxV2/{MysteryBoxFeeModel.sol => DropsFeeModel.sol} (54%) rename contracts/SmartRoute/proxies/{DODOMysteryBoxProxy.sol => DODODropsProxy.sol} (57%) diff --git a/contracts/DODOMysteryBox/MysteryBoxV2/BaseDrops.sol b/contracts/DODOMysteryBox/MysteryBoxV2/BaseDrops.sol new file mode 100644 index 0000000..bac4a99 --- /dev/null +++ b/contracts/DODOMysteryBox/MysteryBoxV2/BaseDrops.sol @@ -0,0 +1,302 @@ +/* + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 +*/ +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {IERC20} from "../../intf/IERC20.sol"; +import {UniversalERC20} from "../../SmartRoute/lib/UniversalERC20.sol"; +import {SafeMath} from "../../lib/SafeMath.sol"; +import {Address} from "../../external/utils/Address.sol"; +import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; +import {IRandomGenerator} from "../../lib/RandomGenerator.sol"; +import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; +import {InitializableMintableERC20} from "../../external/ERC20/InitializableMintableERC20.sol"; + +interface IDropsFeeModel { + function getPayAmount(address dodoDrops, address user, uint256 originalPrice, uint256 ticketAmount) external view returns (uint256, uint256); +} + +interface IDropsNft { + function mint(address to, uint256 tokenId) external; + function mint(address account, uint256 id, uint256 amount, bytes memory data) external; +} + +contract BaseDrops is InitializableMintableERC20, ReentrancyGuard { + using SafeMath for uint256; + using Address for address; + using UniversalERC20 for IERC20; + + // ============ Storage ============ + address constant _BASE_COIN_ = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + + address public _BUY_TOKEN_; + uint256 public _BUY_TOKEN_RESERVE_; + address public _FEE_MODEL_; + address payable public _MAINTAINER_; + address public _NFT_TOKEN_; + + uint256 public _TICKET_UNIT_ = 1; // ticket consumed in a single lottery + + uint256 [] public _SELLING_TIME_INTERVAL_; + uint256 [] public _SELLING_PRICE_SET_; + uint256 [] public _SELLING_AMOUNT_SET_; + uint256 public _REDEEM_ALLOWED_TIME_; + + uint256[] public _PROB_INTERVAL_; // index => Interval probability (Only For ProbMode) + uint256[][] public _TOKEN_ID_MAP_; // Interval index => tokenIds (Only For ProbMode) + + uint256[] public _TOKEN_ID_LIST_; //index => tokenId (Only For FixedAmount mode) + + bool public _IS_PROB_MODE_; // false = FixedAmount mode, true = ProbMode + bool public _IS_REVEAL_MODE_; + uint256 public _REVEAL_RN_ = 0; + address public _RNG_; + + fallback() external payable {} + + receive() external payable {} + + // ============ Modifiers ============ + + modifier notStart() { + require(block.timestamp < _SELLING_TIME_INTERVAL_[0] || _SELLING_TIME_INTERVAL_[0] == 0, "ALREADY_START"); + _; + } + + // ============ Event ============= + event BuyTicket(address account, uint256 payAmount, uint256 feeAmount, uint256 ticketAmount); + event RedeemPrize(address account, uint256 tokenId, address referer); + + event ChangeRNG(address rng); + event ChangeRedeemTime(uint256 redeemTime); + event ChangeTicketUnit(uint256 newTicketUnit); + event Withdraw(address account, uint256 amount); + + event SetSellingInfo(); + event SetProbInfo(); // only for ProbMode + event SetTokenIdMapByIndex(uint256 index); // only for ProbMode + event SetFixedAmountInfo(); // only for FixedAmount mode + + + function init( + address[] memory addrList, //0 owner, 1 buyToken, 2 feeModel, 3 defaultMaintainer 4 rng 5 nftToken + uint256[][] memory sellingInfoList, //0 sellingTimeInterval, 1 sellingPrice, 2 sellingAmount + uint256[] memory probInterval, + uint256[][] memory tokenIdMap, + uint256[] memory tokenIdList, + uint256 redeemAllowedTime, + bool isRevealMode, + bool isProbMode + ) public { + initOwner(addrList[0]); + _BUY_TOKEN_ = addrList[1]; + _FEE_MODEL_ = addrList[2]; + _MAINTAINER_ = payable(addrList[3]); + _RNG_ = addrList[4]; + _NFT_TOKEN_ = addrList[5]; + + _IS_REVEAL_MODE_ = isRevealMode; + _IS_PROB_MODE_ = isProbMode; + _REDEEM_ALLOWED_TIME_ = redeemAllowedTime; + + if(sellingInfoList.length > 0) _setSellingInfo(sellingInfoList[0], sellingInfoList[1], sellingInfoList[2]); + + if(isProbMode) { + if(probInterval.length > 0) _setProbInfo(probInterval, tokenIdMap); + }else { + if(tokenIdList.length > 0) _setFixedAmountInfo(tokenIdList); + } + + string memory prefix = "DROPS_"; + name = string(abi.encodePacked(prefix, addressToShortString(address(this)))); + symbol = name; + decimals = 0; + super.init(addrList[0], 0, name, symbol, decimals); + } + + function buyTickets(address ticketTo, uint256 ticketAmount) payable external preventReentrant { + (uint256 curPrice, uint256 sellAmount, uint256 index) = getSellingInfo(); + require(curPrice > 0 && sellAmount > 0, "CAN_NOT_BUY"); + require(ticketAmount <= sellAmount, "TICKETS_NOT_ENOUGH"); + (uint256 payAmount, uint256 feeAmount) = IDropsFeeModel(_FEE_MODEL_).getPayAmount(address(this), ticketTo, curPrice, ticketAmount); + require(payAmount > 0, "UnQualified"); + + uint256 baseBalance = IERC20(_BUY_TOKEN_).universalBalanceOf(address(this)); + uint256 buyInput = baseBalance.sub(_BUY_TOKEN_RESERVE_); + + require(payAmount <= buyInput, "PAY_AMOUNT_NOT_ENOUGH"); + + _SELLING_AMOUNT_SET_[index] = sellAmount.sub(ticketAmount); + _BUY_TOKEN_RESERVE_ = baseBalance.sub(feeAmount); + + IERC20(_BUY_TOKEN_).universalTransfer(_MAINTAINER_,feeAmount); + _mint(ticketTo, ticketAmount); + emit BuyTicket(ticketTo, payAmount, feeAmount, ticketAmount); + } + + function redeemTicket(uint256 ticketNum, address referer) external { + require(!address(msg.sender).isContract(), "ONLY_ALLOW_EOA"); + require(ticketNum >= 1 && ticketNum <= balanceOf(msg.sender), "TICKET_NUM_INVALID"); + _burn(msg.sender,ticketNum); + for (uint256 i = 0; i < ticketNum; i++) { + _redeemSinglePrize(msg.sender, i, referer); + } + } + + // ============ Internal ============ + + function _redeemSinglePrize(address to, uint256 curNo, address referer) internal { + require(block.timestamp >= _REDEEM_ALLOWED_TIME_ && _REDEEM_ALLOWED_TIME_ != 0, "REDEEM_CLOSE"); + uint256 range; + if(_IS_PROB_MODE_) { + range = _PROB_INTERVAL_[_PROB_INTERVAL_.length - 1]; + }else { + range = _TOKEN_ID_LIST_.length; + } + uint256 random; + if(_IS_REVEAL_MODE_) { + require(_REVEAL_RN_ != 0, "REVEAL_NOT_SET"); + random = uint256(keccak256(abi.encodePacked(_REVEAL_RN_, msg.sender, balanceOf(msg.sender).add(curNo + 1)))) % range; + }else { + random = IRandomGenerator(_RNG_).random(gasleft() + block.number) % range; + } + uint256 tokenId; + if(_IS_PROB_MODE_) { + uint256 i; + for (i = 0; i < _PROB_INTERVAL_.length; i++) { + if (random <= _PROB_INTERVAL_[i]) { + break; + } + } + require(_TOKEN_ID_MAP_[i].length > 0, "EMPTY_TOKEN_ID_MAP"); + tokenId = _TOKEN_ID_MAP_[i][random % _TOKEN_ID_MAP_[i].length]; + IDropsNft(_NFT_TOKEN_).mint(to, tokenId, 1, ""); + } else { + tokenId = _TOKEN_ID_LIST_[random]; + if(random != range - 1) { + _TOKEN_ID_LIST_[random] = _TOKEN_ID_LIST_[range - 1]; + } + _TOKEN_ID_LIST_.pop(); + IDropsNft(_NFT_TOKEN_).mint(to, tokenId); + } + emit RedeemPrize(to, tokenId, referer); + } + + + function _setSellingInfo(uint256[] memory sellingTimeIntervals, uint256[] memory sellingPrice, uint256[] memory sellingAmount) internal { + require(sellingTimeIntervals.length > 0, "PARAM_NOT_INVALID"); + require(sellingTimeIntervals.length == sellingPrice.length && sellingPrice.length == sellingAmount.length, "PARAM_NOT_INVALID"); + for (uint256 i = 0; i < sellingTimeIntervals.length - 1; i++) { + require(sellingTimeIntervals[i] < sellingTimeIntervals[i + 1], "INTERVAL_INVALID"); + require(sellingPrice[i] != 0, "PRICE_INVALID"); + } + _SELLING_TIME_INTERVAL_ = sellingTimeIntervals; + _SELLING_PRICE_SET_ = sellingPrice; + _SELLING_AMOUNT_SET_ = sellingAmount; + emit SetSellingInfo(); + } + + + function _setProbInfo(uint256[] memory probIntervals,uint256[][] memory tokenIdMap) internal { + require(_IS_PROB_MODE_, "ONLY_ALLOW_PROB_MODE"); + require(probIntervals.length > 0, "PARAM_NOT_INVALID"); + require(tokenIdMap.length == probIntervals.length, "PARAM_NOT_INVALID"); + + require(tokenIdMap[0].length > 0, "INVALID"); + for (uint256 i = 1; i < probIntervals.length; i++) { + require(probIntervals[i] > probIntervals[i - 1], "INTERVAL_INVALID"); + require(tokenIdMap[i].length > 0, "INVALID"); + } + _PROB_INTERVAL_ = probIntervals; + _TOKEN_ID_MAP_ = tokenIdMap; + emit SetProbInfo(); + } + + function _setFixedAmountInfo(uint256[] memory tokenIdList) internal { + require(!_IS_PROB_MODE_, "ONLY_ALLOW_FIXED_AMOUNT_MODE"); + require(tokenIdList.length > 0, "PARAM_NOT_INVALID"); + _TOKEN_ID_LIST_ = tokenIdList; + emit SetFixedAmountInfo(); + } + + // ================= Owner =================== + + function withdraw() external onlyOwner { + uint256 amount = IERC20(_BUY_TOKEN_).universalBalanceOf(address(this)); + IERC20(_BUY_TOKEN_).universalTransfer(msg.sender ,amount); + emit Withdraw(msg.sender, amount); + } + + function setRevealRn() external onlyOwner { + require(_REVEAL_RN_ == 0, "ALREADY_SET"); + _REVEAL_RN_ = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1)))); + } + + function setSellingInfo(uint256[] memory sellingTimeIntervals, uint256[] memory prices, uint256[] memory amounts) external notStart() onlyOwner { + _setSellingInfo(sellingTimeIntervals, prices, amounts); + } + + function setProbInfo(uint256[] memory probIntervals,uint256[][] memory tokenIdMap) external notStart() onlyOwner { + _setProbInfo(probIntervals, tokenIdMap); + } + + function setFixedAmountInfo(uint256[] memory tokenIdList) external notStart() onlyOwner { + _setFixedAmountInfo(tokenIdList); + } + + function setTokenIdMapByIndex(uint256 index, uint256[] memory tokenIds) external notStart() onlyOwner { + require(_IS_PROB_MODE_, "ONLY_ALLOW_PROB_MODE"); + require(tokenIds.length > 0 && index < _TOKEN_ID_MAP_.length,"PARAM_NOT_INVALID"); + _TOKEN_ID_MAP_[index] = tokenIds; + emit SetTokenIdMapByIndex(index); + } + + function updateRNG(address newRNG) external onlyOwner { + require(newRNG != address(0)); + _RNG_ = newRNG; + emit ChangeRNG(newRNG); + } + + function updateTicketUnit(uint256 newTicketUnit) external onlyOwner { + require(newTicketUnit != 0); + _TICKET_UNIT_ = newTicketUnit; + emit ChangeTicketUnit(newTicketUnit); + } + + function updateRedeemTime(uint256 newRedeemTime) external onlyOwner { + require(newRedeemTime > block.timestamp || newRedeemTime == 0, "PARAM_NOT_INVALID"); + _REDEEM_ALLOWED_TIME_ = newRedeemTime; + emit ChangeRedeemTime(newRedeemTime); + } + + // ================= View =================== + + function getSellingInfo() public view returns (uint256 curPrice, uint256 sellAmount, uint256 index) { + uint256 curBlockTime = block.timestamp; + if(curBlockTime >= _SELLING_TIME_INTERVAL_[0] && _SELLING_TIME_INTERVAL_[0] != 0) { + uint256 i; + for (i = 1; i < _SELLING_TIME_INTERVAL_.length; i++) { + if (curBlockTime <= _SELLING_TIME_INTERVAL_[i]) { + break; + } + } + curPrice = _SELLING_PRICE_SET_[i-1]; + sellAmount = _SELLING_AMOUNT_SET_[i-1]; + index = i - 1; + } + } + + function addressToShortString(address _addr) public pure returns (string memory) { + bytes32 value = bytes32(uint256(_addr)); + bytes memory alphabet = "0123456789abcdef"; + + bytes memory str = new bytes(8); + for (uint256 i = 0; i < 4; i++) { + str[i * 2] = alphabet[uint8(value[i + 12] >> 4)]; + str[1 + i * 2] = alphabet[uint8(value[i + 12] & 0x0f)]; + } + return string(str); + } +} diff --git a/contracts/DODOMysteryBox/MysteryBoxV2/BaseMysteryBox.sol b/contracts/DODOMysteryBox/MysteryBoxV2/BaseMysteryBox.sol deleted file mode 100644 index 06b882e..0000000 --- a/contracts/DODOMysteryBox/MysteryBoxV2/BaseMysteryBox.sol +++ /dev/null @@ -1,311 +0,0 @@ -/* - Copyright 2021 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 -*/ -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -import {IERC20} from "../../intf/IERC20.sol"; -import {UniversalERC20} from "../../SmartRoute/lib/UniversalERC20.sol"; -import {SafeMath} from "../../lib/SafeMath.sol"; -import {Address} from "../../external/utils/Address.sol"; -import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; -import {IRandomGenerator} from "../../lib/RandomGenerator.sol"; -import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; -import {InitializableERC20} from "../../external/ERC20/InitializableERC20.sol"; - -interface IMysteryBoxFeeModel { - function getPayAmount(address mysteryBox, address user, uint256 originalPrice, uint256 ticketAmount) external view returns (uint256, uint256); -} - -interface IMysteryBoxNft { - function mint(address to, uint256 tokenId) external; - function mint(address account, uint256 id, uint256 amount, bytes memory data) external; -} - -contract BaseMysteryBox is InitializableERC20, InitializableOwnable, ReentrancyGuard { - using SafeMath for uint256; - using Address for address; - using UniversalERC20 for IERC20; - - // ============ Storage ============ - address constant _BASE_COIN_ = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - address public _BUY_TOKEN_; - uint256 public _BUY_RESERVE_; - address public _DEFAULT_FEE_MODEL_; - address payable public _DEFAULT_MAINTAINER_; - address public _NFT_TOKEN_; - - uint256 public _TICKET_UNIT_ = 1; // ticket consumed in a single lottery - - uint256 [] public _PRICE_TIME_INTERVAL_; - uint256 [] public _PRICE_SET_; - uint256 [] public _SELLING_AMOUNT_SET_; - uint256 public _REDEEM_ALLOWED_TIME_; - - uint256[] public _BOX_INTERVAL_; // index => Interval probability (For ProbMode) index => tokenId (For FixedAmount mode) - uint256[][] public _TOKEN_ID_SET_; // Interval index => tokenIds (Only For ProbMode) - - bool public _IS_PROB_MODE; // false = FixedAmount mode, true = ProbMode - bool public _IS_REVEAL_MODE_; - uint256 public _REVEAL_RNG_ = 0; - address public _RANDOM_GENERATOR_; - - fallback() external payable {} - - receive() external payable {} - - // ============ Modifiers ============ - - modifier notStart() { - require(block.timestamp < _PRICE_TIME_INTERVAL_[0] || _PRICE_TIME_INTERVAL_[0] == 0, "ALREADY_START"); - _; - } - - // ============ Event ============= - event BuyTicket(address account, uint256 payAmount, uint256 feeAmount, uint256 ticketAmount); - event RedeemPrize(address account, uint256 tokenId, address referer); - - event ChangeRandomGenerator(address randomGenerator); - event ChangeRedeemTime(uint256 redeemTime); - event ChangeTicketUnit(uint256 newTicketUnit); - event Withdraw(address account, uint256 amount); - - event SetPriceInterval(); - event SetBoxInterval(); - event SetTokenIds(); // only for ProbMode - event SetTokenIdByIndex(uint256 index); // only for ProbMode - - - function init( - address[] memory addrList, //0 owner, 1 buyToken, 2 feeModel, 3 defaultMaintainer 4 randomGenerator 5 nftToken - uint256[][] memory priceSetList, //0 priceTimeInterval, 1 priceSet, 2 sellAmount - uint256[] memory boxIntervals, - uint256[][] memory tokenIds, - uint256 redeemAllowedTime, - bool isRevealMode, - bool isProbMode, - uint256 totalTickets - ) public { - initOwner(addrList[0]); - _BUY_TOKEN_ = addrList[1]; - _DEFAULT_FEE_MODEL_ = addrList[2]; - _DEFAULT_MAINTAINER_ = payable(addrList[3]); - _RANDOM_GENERATOR_ = addrList[4]; - _NFT_TOKEN_ = addrList[5]; - - _REDEEM_ALLOWED_TIME_ = redeemAllowedTime; - if(priceSetList.length > 0) _setPrice(priceSetList[0], priceSetList[1], priceSetList[2]); - - _IS_REVEAL_MODE_ = isRevealMode; - _IS_PROB_MODE = isProbMode; - if(boxIntervals.length > 0) _setBoxInterval(boxIntervals); - if(tokenIds.length > 0 && isProbMode) _setTokenIds(tokenIds); - - // init TICKET - string memory prefix = "DROPS_"; - name = string(abi.encodePacked(prefix, addressToShortString(address(this)))); - symbol = name; - decimals = 0; - super.init(address(this), totalTickets, name, symbol, decimals); - } - - function buyTickets(address assetTo, uint256 ticketAmount) payable external preventReentrant { - (uint256 curPrice, uint256 sellAmount, uint256 index) = getPriceAndSellAmount(); - require(curPrice > 0 && sellAmount > 0, "CAN_NOT_BUY"); - require(ticketAmount <= sellAmount, "TICKETS_NOT_ENOUGH"); - (uint256 payAmount, uint256 feeAmount) = IMysteryBoxFeeModel(_DEFAULT_FEE_MODEL_).getPayAmount(address(this), assetTo, curPrice, ticketAmount); - require(payAmount > 0, "UnQualified"); - - uint256 baseBalance = IERC20(_BUY_TOKEN_).universalBalanceOf(address(this)); - uint256 buyInput = baseBalance.sub(_BUY_RESERVE_); - - require(payAmount <= buyInput, "PAY_AMOUNT_NOT_ENOUGH"); - - _SELLING_AMOUNT_SET_[index] = sellAmount.sub(ticketAmount); - _BUY_RESERVE_ = baseBalance.sub(feeAmount); - - IERC20(_BUY_TOKEN_).universalTransfer(_DEFAULT_MAINTAINER_,feeAmount); - _transfer(address(this), assetTo, ticketAmount); - emit BuyTicket(assetTo, payAmount, feeAmount, ticketAmount); - } - - function redeemTicket(uint256 ticketNum, address referer) external { - require(!address(msg.sender).isContract(), "ONLY_ALLOW_EOA"); - require(ticketNum >= 1 && ticketNum <= balanceOf(msg.sender), "TICKET_NUM_INVALID"); - balances[msg.sender] = balances[msg.sender].sub(ticketNum); - balances[address(0)] = balances[address(0)].add(ticketNum); - - emit Transfer(msg.sender, address(0), ticketNum); - - for (uint256 i = 0; i < ticketNum; i++) { - _redeemSinglePrize(msg.sender, i, referer); - } - } - - // ============ Internal ============ - - function _redeemSinglePrize(address to, uint256 curNo, address referer) internal { - require(block.timestamp >= _REDEEM_ALLOWED_TIME_ && _REDEEM_ALLOWED_TIME_ != 0, "REDEEM_CLOSE"); - uint256 range; - if(_IS_PROB_MODE) { - range = _BOX_INTERVAL_[_BOX_INTERVAL_.length - 1]; - }else { - range = _BOX_INTERVAL_.length; - } - uint256 random; - if(_IS_REVEAL_MODE_) { - require(_REVEAL_RNG_ != 0, "REVEAL_NOT_SET"); - random = uint256(keccak256(abi.encodePacked(_REVEAL_RNG_, msg.sender, balanceOf(msg.sender).add(curNo + 1)))) % range; - }else { - random = IRandomGenerator(_RANDOM_GENERATOR_).random(gasleft() + block.number) % range; - } - uint256 tokenId; - if(_IS_PROB_MODE) { - uint256 i; - for (i = 0; i < _BOX_INTERVAL_.length; i++) { - if (random <= _BOX_INTERVAL_[i]) { - break; - } - } - require(_TOKEN_ID_SET_[i].length > 0, "EMPTY_TOKEN_ID_SET"); - tokenId = _TOKEN_ID_SET_[i][random % _TOKEN_ID_SET_[i].length]; - IMysteryBoxNft(_NFT_TOKEN_).mint(to, tokenId, 1, ""); - } else { - tokenId = _BOX_INTERVAL_[random]; - if(random != range - 1) { - _BOX_INTERVAL_[random] = _BOX_INTERVAL_[range - 1]; - } - _BOX_INTERVAL_.pop(); - IMysteryBoxNft(_NFT_TOKEN_).mint(to, tokenId); - } - emit RedeemPrize(to, tokenId, referer); - } - - - function _setPrice(uint256[] memory priceIntervals, uint256[] memory prices, uint256[] memory amounts) internal { - require(priceIntervals.length == prices.length && prices.length == amounts.length, "PARAM_NOT_INVALID"); - for (uint256 i = 0; i < priceIntervals.length - 1; i++) { - require(priceIntervals[i] < priceIntervals[i + 1], "INTERVAL_INVALID"); - require(prices[i] != 0, "PRICE_INVALID"); - require(amounts[i] != 0, "SELL_AMOUNT_INVALID"); - } - _PRICE_TIME_INTERVAL_ = priceIntervals; - _PRICE_SET_ = prices; - _SELLING_AMOUNT_SET_ = amounts; - emit SetPriceInterval(); - } - - function _setBoxInterval(uint256[] memory boxIntervals) internal { - require(boxIntervals.length > 0, "PARAM_NOT_INVALID"); - if(_IS_PROB_MODE) { - for (uint256 i = 1; i < boxIntervals.length; i++) { - require(boxIntervals[i] > boxIntervals[i - 1], "INTERVAL_INVALID"); - } - } - _BOX_INTERVAL_ = boxIntervals; - emit SetBoxInterval(); - } - - function _setTokenIds(uint256[][] memory tokenIds) internal { - require(tokenIds.length == _BOX_INTERVAL_.length, "PARAM_NOT_INVALID"); - require(_IS_PROB_MODE, "ONLY_ALLOW_PROB_MODE"); - for (uint256 i = 0; i < tokenIds.length; i++) { - require(tokenIds[i].length > 0, "INVALID"); - } - _TOKEN_ID_SET_ = tokenIds; - emit SetTokenIds(); - } - - // ================= Owner =================== - - function withdraw() external onlyOwner { - uint256 amount = IERC20(_BUY_TOKEN_).universalBalanceOf(address(this)); - IERC20(_BUY_TOKEN_).universalTransfer(msg.sender ,amount); - emit Withdraw(msg.sender, amount); - } - - function redeemByOwner(uint256 ticketNum, address referer) external onlyOwner { - require(ticketNum >= 1 && ticketNum <= balanceOf(address(this)), "TICKET_NUM_INVALID"); - balances[address(this)] = balances[address(this)].sub(ticketNum); - balances[address(0)] = balances[address(0)].add(ticketNum); - - emit Transfer(address(this), address(0), ticketNum); - - for (uint256 i = 0; i < ticketNum; i++) { - _redeemSinglePrize(msg.sender, i, referer); - } - } - - function setRevealRng() external onlyOwner { - require(_REVEAL_RNG_ == 0, "ALREADY_SET"); - _REVEAL_RNG_ = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1)))); - } - - function setPrice(uint256[] memory priceIntervals, uint256[] memory prices, uint256[] memory amounts) external notStart() onlyOwner { - _setPrice(priceIntervals, prices, amounts); - } - - function setBoxInterval(uint256[] memory boxIntervals) external notStart() onlyOwner { - _setBoxInterval(boxIntervals); - } - - function setTokenIds(uint256[][] memory tokenIds) external notStart() onlyOwner { - _setTokenIds(tokenIds); - } - - function setTokenIdByIndex(uint256 index, uint256[] memory tokenIds) external notStart() onlyOwner { - require(tokenIds.length > 0 && index < _TOKEN_ID_SET_.length,"PARAM_NOT_INVALID"); - require(_IS_PROB_MODE, "ONLY_ALLOW_PROB_MODE"); - _TOKEN_ID_SET_[index] = tokenIds; - emit SetTokenIdByIndex(index); - } - - function updateRandomGenerator(address newRandomGenerator) external onlyOwner { - require(newRandomGenerator != address(0)); - _RANDOM_GENERATOR_ = newRandomGenerator; - emit ChangeRandomGenerator(newRandomGenerator); - } - - function updateTicketUnit(uint256 newTicketUnit) external onlyOwner { - require(newTicketUnit != 0); - _TICKET_UNIT_ = newTicketUnit; - emit ChangeTicketUnit(newTicketUnit); - } - - function updateRedeemTime(uint256 newRedeemTime) external onlyOwner { - require(newRedeemTime > block.timestamp || newRedeemTime == 0, "PARAM_NOT_INVALID"); - _REDEEM_ALLOWED_TIME_ = newRedeemTime; - emit ChangeRedeemTime(newRedeemTime); - } - - // ================= View =================== - - function getPriceAndSellAmount() public view returns (uint256 curPrice, uint256 sellAmount, uint256 index) { - uint256 curBlockTime = block.timestamp; - if(curBlockTime >= _PRICE_TIME_INTERVAL_[0] && _PRICE_TIME_INTERVAL_[0] != 0) { - uint256 i; - for (i = 1; i < _PRICE_TIME_INTERVAL_.length; i++) { - if (curBlockTime <= _PRICE_TIME_INTERVAL_[i]) { - break; - } - } - curPrice = _PRICE_SET_[i-1]; - sellAmount = _SELLING_AMOUNT_SET_[i-1]; - index = i - 1; - } - } - - function addressToShortString(address _addr) public pure returns (string memory) { - bytes32 value = bytes32(uint256(_addr)); - bytes memory alphabet = "0123456789abcdef"; - - bytes memory str = new bytes(8); - for (uint256 i = 0; i < 4; i++) { - str[i * 2] = alphabet[uint8(value[i + 12] >> 4)]; - str[1 + i * 2] = alphabet[uint8(value[i + 12] & 0x0f)]; - } - return string(str); - } -} diff --git a/contracts/DODOMysteryBox/MysteryBoxV2/MysteryBoxERC1155.sol b/contracts/DODOMysteryBox/MysteryBoxV2/DropsERC1155.sol similarity index 94% rename from contracts/DODOMysteryBox/MysteryBoxV2/MysteryBoxERC1155.sol rename to contracts/DODOMysteryBox/MysteryBoxV2/DropsERC1155.sol index cc2ed30..0ab8275 100644 --- a/contracts/DODOMysteryBox/MysteryBoxV2/MysteryBoxERC1155.sol +++ b/contracts/DODOMysteryBox/MysteryBoxV2/DropsERC1155.sol @@ -10,7 +10,7 @@ pragma solidity 0.6.9; import {ERC1155} from "../../external/ERC1155/ERC1155.sol"; import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; -contract MysteryBoxERC1155 is ERC1155, InitializableOwnable { +contract DropsERC1155 is ERC1155, InitializableOwnable { mapping (address => bool) public _IS_ALLOWED_MINT_; // ============ Event ============= diff --git a/contracts/DODOMysteryBox/MysteryBoxV2/MysteryBoxERC721.sol b/contracts/DODOMysteryBox/MysteryBoxV2/DropsERC721.sol similarity index 94% rename from contracts/DODOMysteryBox/MysteryBoxV2/MysteryBoxERC721.sol rename to contracts/DODOMysteryBox/MysteryBoxV2/DropsERC721.sol index d3c197e..c283576 100644 --- a/contracts/DODOMysteryBox/MysteryBoxV2/MysteryBoxERC721.sol +++ b/contracts/DODOMysteryBox/MysteryBoxV2/DropsERC721.sol @@ -10,7 +10,7 @@ pragma solidity 0.6.9; import {ERC721} from "../../external/ERC721/ERC721.sol"; import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; -contract MysteryBoxERC721 is ERC721, InitializableOwnable { +contract DropsERC721 is ERC721, InitializableOwnable { mapping (address => bool) public _IS_ALLOWED_MINT_; // ============ Event ============= diff --git a/contracts/DODOMysteryBox/MysteryBoxV2/MysteryBoxFeeModel.sol b/contracts/DODOMysteryBox/MysteryBoxV2/DropsFeeModel.sol similarity index 54% rename from contracts/DODOMysteryBox/MysteryBoxV2/MysteryBoxFeeModel.sol rename to contracts/DODOMysteryBox/MysteryBoxV2/DropsFeeModel.sol index 25aad85..c3801b1 100644 --- a/contracts/DODOMysteryBox/MysteryBoxV2/MysteryBoxFeeModel.sol +++ b/contracts/DODOMysteryBox/MysteryBoxV2/DropsFeeModel.sol @@ -19,48 +19,48 @@ interface IPrice { function getUserPrice(address user, uint256 originalPrice, uint256 ticketAmount) external view returns (uint256); } -contract MysteryBoxFeeModel is InitializableOwnable { +contract DropsFeeModel is InitializableOwnable { using SafeMath for uint256; - struct MysteryBoxInfo { + struct DropBoxInfo { bool isSet; uint256 globalFee; address feeAddr; address priceAddr; } - mapping(address => MysteryBoxInfo) mysteryBoxes; + mapping(address => DropBoxInfo) dropBoxes; - function addMysteryBoxInfo(address mysteryBox, uint256 globalFee, address feeAddr, address priceAddr) external onlyOwner { - MysteryBoxInfo memory boxInfo = MysteryBoxInfo({ + function addMysteryBoxInfo(address dropBox, uint256 globalFee, address feeAddr, address priceAddr) external onlyOwner { + DropBoxInfo memory dropBoxInfo = DropBoxInfo({ isSet: true, globalFee: globalFee, feeAddr: feeAddr, priceAddr: priceAddr }); - mysteryBoxes[mysteryBox] = boxInfo; + dropBoxes[dropBox] = dropBoxInfo; } - function setMysteryBoxInfo(address mysteryBox, uint256 globalFee, address feeAddr, address priceAddr) external onlyOwner { - require(mysteryBoxes[mysteryBox].isSet, "NOT_FOUND_BOX"); - mysteryBoxes[mysteryBox].globalFee = globalFee; - mysteryBoxes[mysteryBox].feeAddr = feeAddr; - mysteryBoxes[mysteryBox].priceAddr = priceAddr; + function setDropBoxInfo(address dropBox, uint256 globalFee, address feeAddr, address priceAddr) external onlyOwner { + require(dropBoxes[dropBox].isSet, "NOT_FOUND_BOX"); + dropBoxes[dropBox].globalFee = globalFee; + dropBoxes[dropBox].feeAddr = feeAddr; + dropBoxes[dropBox].priceAddr = priceAddr; } - function getPayAmount(address mysteryBox, address user, uint256 originalPrice, uint256 ticketAmount) external view returns (uint256 payAmount, uint256 feeAmount) { - MysteryBoxInfo memory boxInfo = mysteryBoxes[mysteryBox]; - if(!mysteryBoxes[mysteryBox].isSet) { + function getPayAmount(address dropBox, address user, uint256 originalPrice, uint256 ticketAmount) external view returns (uint256 payAmount, uint256 feeAmount) { + DropBoxInfo memory dropBoxInfo = dropBoxes[dropBox]; + if(!dropBoxInfo.isSet) { payAmount = originalPrice.mul(ticketAmount); feeAmount = 0; } else { - uint256 feeRate = boxInfo.globalFee; - address feeAddr = boxInfo.feeAddr; + uint256 feeRate = dropBoxInfo.globalFee; + address feeAddr = dropBoxInfo.feeAddr; if(feeAddr != address(0)) feeRate = IFee(feeAddr).getUserFee(user, ticketAmount); uint256 price = originalPrice; - address priceAddr = boxInfo.priceAddr; + address priceAddr = dropBoxInfo.priceAddr; if(priceAddr != address(0)) price = IPrice(priceAddr).getUserPrice(user, originalPrice, ticketAmount); diff --git a/contracts/SmartRoute/proxies/DODOMysteryBoxProxy.sol b/contracts/SmartRoute/proxies/DODODropsProxy.sol similarity index 57% rename from contracts/SmartRoute/proxies/DODOMysteryBoxProxy.sol rename to contracts/SmartRoute/proxies/DODODropsProxy.sol index 4cf4696..027d3a7 100644 --- a/contracts/SmartRoute/proxies/DODOMysteryBoxProxy.sol +++ b/contracts/SmartRoute/proxies/DODODropsProxy.sol @@ -12,24 +12,24 @@ import {SafeMath} from "../../lib/SafeMath.sol"; import {SafeERC20} from "../../lib/SafeERC20.sol"; import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; -interface IDODOMysteryBox { +interface IDODODrops { function _BUY_TOKEN_() external view returns (address); - function _DEFAULT_FEE_MODEL_() external view returns (address); - function getPriceAndSellAmount() external view returns (uint256, uint256, uint256); + function _FEE_MODEL_() external view returns (address); + function getSellingInfo() external view returns (uint256, uint256, uint256); function buyTickets(address assetTo, uint256 ticketAmount) external; } -interface IMysteryBoxFeeModel { +interface IDropsFeeModel { function getPayAmount(address mysteryBox, address user, uint256 originalPrice, uint256 ticketAmount) external view returns (uint256, uint256); } /** - * @title DODO MysteryBoxProxy + * @title DODO DropsProxy * @author DODO Breeder * - * @notice Entrance of MysteryBox in DODO platform + * @notice Entrance of Drops in DODO platform */ -contract DODOMysteryBoxProxy is ReentrancyGuard { +contract DODODropsProxy is ReentrancyGuard { using SafeMath for uint256; // ============ Storage ============ @@ -48,25 +48,25 @@ contract DODOMysteryBoxProxy is ReentrancyGuard { _DODO_APPROVE_PROXY_ = dodoApproveProxy; } - function buyTickets(address payable dodoMysteryBox, uint256 ticketAmount) external payable preventReentrant { - (uint256 curPrice, uint256 sellAmount,) = IDODOMysteryBox(dodoMysteryBox).getPriceAndSellAmount(); + function buyTickets(address payable dodoDrops, uint256 ticketAmount) external payable preventReentrant { + (uint256 curPrice, uint256 sellAmount,) = IDODODrops(dodoDrops).getSellingInfo(); require(curPrice > 0 && sellAmount > 0, "CAN_NOT_BUY"); require(ticketAmount <= sellAmount, "TICKETS_NOT_ENOUGH"); - address feeModel = IDODOMysteryBox(dodoMysteryBox)._DEFAULT_FEE_MODEL_(); - (uint256 payAmount,) = IMysteryBoxFeeModel(feeModel).getPayAmount(dodoMysteryBox, msg.sender, curPrice, ticketAmount); + address feeModel = IDODODrops(dodoDrops)._FEE_MODEL_(); + (uint256 payAmount,) = IDropsFeeModel(feeModel).getPayAmount(dodoDrops, msg.sender, curPrice, ticketAmount); require(payAmount > 0, "UnQualified"); - address buyToken = IDODOMysteryBox(dodoMysteryBox)._BUY_TOKEN_(); + address buyToken = IDODODrops(dodoDrops)._BUY_TOKEN_(); if(buyToken == _BASE_COIN_) { - require(msg.value >= payAmount, "PAYAMOUNT_NOT_ENOUGH"); - dodoMysteryBox.transfer(payAmount); + require(msg.value == payAmount, "PAYAMOUNT_NOT_ENOUGH"); + dodoDrops.transfer(payAmount); }else { - IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(buyToken, msg.sender, dodoMysteryBox, payAmount); + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(buyToken, msg.sender, dodoDrops, payAmount); } - IDODOMysteryBox(dodoMysteryBox).buyTickets(msg.sender, ticketAmount); + IDODODrops(dodoDrops).buyTickets(msg.sender, ticketAmount); - emit BuyTicket(msg.sender, dodoMysteryBox, ticketAmount); + emit BuyTicket(msg.sender, dodoDrops, ticketAmount); } } \ No newline at end of file diff --git a/contracts/external/ERC20/InitializableMintableERC20.sol b/contracts/external/ERC20/InitializableMintableERC20.sol index afe0364..587c8ad 100644 --- a/contracts/external/ERC20/InitializableMintableERC20.sol +++ b/contracts/external/ERC20/InitializableMintableERC20.sol @@ -83,13 +83,21 @@ contract InitializableMintableERC20 is InitializableOwnable { } function mint(address user, uint256 value) external onlyOwner { + _mint(user, value); + } + + function burn(address user, uint256 value) external onlyOwner { + _burn(user, value); + } + + function _mint(address user, uint256 value) internal { balances[user] = balances[user].add(value); totalSupply = totalSupply.add(value); emit Mint(user, value); emit Transfer(address(0), user, value); } - function burn(address user, uint256 value) external onlyOwner { + function _burn(address user, uint256 value) internal { balances[user] = balances[user].sub(value); totalSupply = totalSupply.sub(value); emit Burn(user, value);