From e617e61fe6a67c4218a243b4f92b06de2a6cdda4 Mon Sep 17 00:00:00 2001 From: owen05 Date: Thu, 9 Sep 2021 22:38:19 +0800 Subject: [PATCH] dev --- contracts/NFTPool/impl/ControllerModel.sol | 39 +--- contracts/NFTPool/impl/FilterAdmin.sol | 66 +++--- .../{FilterModel01.sol => FilterERC721V1.sol} | 218 ++++++++++-------- contracts/NFTPool/intf/IControllerModel.sol | 8 +- contracts/NFTPool/intf/IFilterAdmin.sol | 22 +- contracts/NFTPool/intf/IFilterModel.sol | 34 +-- contracts/SmartRoute/DODONFTApprove.sol | 103 +++++++++ .../SmartRoute/proxies/DODONFTPoolProxy.sol | 98 ++++++-- contracts/intf/IDODONFTApprove.sol | 18 ++ 9 files changed, 379 insertions(+), 227 deletions(-) rename contracts/NFTPool/impl/{FilterModel01.sol => FilterERC721V1.sol} (55%) create mode 100644 contracts/SmartRoute/DODONFTApprove.sol create mode 100644 contracts/intf/IDODONFTApprove.sol diff --git a/contracts/NFTPool/impl/ControllerModel.sol b/contracts/NFTPool/impl/ControllerModel.sol index 1bded27..1ef135e 100644 --- a/contracts/NFTPool/impl/ControllerModel.sol +++ b/contracts/NFTPool/impl/ControllerModel.sol @@ -16,13 +16,11 @@ contract ControllerModel is InitializableOwnable { using SafeMath for uint256; uint256 public _GLOBAL_NFT_IN_FEE_ = 0; - uint256 public _GLOBAL_NFT_RANDOM_OUT_FEE_ = 0; - uint256 public _GLOBAL_NFT_TARGET_OUT_FEE_ = 50000000000000000;//0.05 + uint256 public _GLOBAL_NFT_OUT_FEE_ = 0; struct FilterAdminFeeInfo { uint256 nftInFee; - uint256 nftRandomOutFee; - uint256 nftTargetOutFee; + uint256 nftOutFee; bool isSet; } @@ -35,26 +33,23 @@ contract ControllerModel is InitializableOwnable { //==================== Ownable ==================== - function addFilterAdminFeeInfo(address filterAdminAddr, uint256 nftInFee, uint256 nftRandomOutFee, uint256 nftTargetOutFee) external onlyOwner { + function addFilterAdminFeeInfo(address filterAdminAddr, uint256 nftInFee, uint256 nftOutFee) external onlyOwner { FilterAdminFeeInfo memory filterAdmin = FilterAdminFeeInfo({ nftInFee: nftInFee, - nftRandomOutFee: nftRandomOutFee, - nftTargetOutFee: nftTargetOutFee, + nftOutFee: nftOutFee, isSet: true }); filterAdminFees[filterAdminAddr] = filterAdmin; } - function setFilterAdminFeeInfo(address filterAdminAddr, uint256 nftInFee, uint256 nftRandomOutFee, uint256 nftTargetOutFee) external onlyOwner { + function setFilterAdminFeeInfo(address filterAdminAddr, uint256 nftInFee, uint256 nftOutFee) external onlyOwner { filterAdminFees[filterAdminAddr].nftInFee = nftInFee; - filterAdminFees[filterAdminAddr].nftRandomOutFee = nftRandomOutFee; - filterAdminFees[filterAdminAddr].nftTargetOutFee = nftTargetOutFee; + filterAdminFees[filterAdminAddr].nftOutFee = nftOutFee; } - function setGlobalParam(uint256 nftInFee, uint256 nftRandomOutFee, uint256 nftTargetOutFee) external onlyOwner { + function setGlobalParam(uint256 nftInFee, uint256 nftOutFee) external onlyOwner { _GLOBAL_NFT_IN_FEE_ = nftInFee; - _GLOBAL_NFT_RANDOM_OUT_FEE_ = nftRandomOutFee; - _GLOBAL_NFT_TARGET_OUT_FEE_ = nftTargetOutFee; + _GLOBAL_NFT_OUT_FEE_ = nftOutFee; } function setEmergencyWithdraw(address filter, bool isOpen) external onlyOwner { @@ -63,7 +58,7 @@ contract ControllerModel is InitializableOwnable { } //===================== View ======================== - function getNFTInFee(address filterAdminAddr, address) external view returns(uint256) { + function getMintFee(address filterAdminAddr) external view returns(uint256) { FilterAdminFeeInfo memory filterAdminFeeInfo = filterAdminFees[filterAdminAddr]; if(filterAdminFeeInfo.isSet) { @@ -73,26 +68,16 @@ contract ControllerModel is InitializableOwnable { } } - function getNFTRandomOutFee(address filterAdminAddr, address) external view returns(uint256) { + function getBurnFee(address filterAdminAddr) external view returns(uint256) { FilterAdminFeeInfo memory filterAdminFeeInfo = filterAdminFees[filterAdminAddr]; if(filterAdminFeeInfo.isSet) { - return filterAdminFeeInfo.nftRandomOutFee; + return filterAdminFeeInfo.nftOutFee; }else { - return _GLOBAL_NFT_RANDOM_OUT_FEE_; + return _GLOBAL_NFT_OUT_FEE_; } } - function getNFTTargetOutFee(address filterAdminAddr, address) external view returns(uint256) { - FilterAdminFeeInfo memory filterAdminFeeInfo = filterAdminFees[filterAdminAddr]; - - if(filterAdminFeeInfo.isSet) { - return filterAdminFeeInfo.nftTargetOutFee; - }else { - return _GLOBAL_NFT_TARGET_OUT_FEE_; - } - } - function getEmergencySwitch(address filter) external view returns(bool) { return isEmergencyWithdraw[filter]; } diff --git a/contracts/NFTPool/impl/FilterAdmin.sol b/contracts/NFTPool/impl/FilterAdmin.sol index 68ff2ac..161f4a4 100644 --- a/contracts/NFTPool/impl/FilterAdmin.sol +++ b/contracts/NFTPool/impl/FilterAdmin.sol @@ -10,12 +10,10 @@ pragma experimental ABIEncoderV2; import {InitializableInternalMintableERC20} from "../../external/ERC20/InitializableInternalMintableERC20.sol"; import {SafeMath} from "../../lib/SafeMath.sol"; -import {IFilterModel} from "../intf/IFilterModel.sol"; import {IControllerModel} from "../intf/IControllerModel.sol"; -import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; import {DecimalMath} from "../../lib/DecimalMath.sol"; -contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { +contract FilterAdmin is InitializableInternalMintableERC20 { using SafeMath for uint256; // ============ Storage ============ @@ -28,69 +26,57 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { event ChangeFee(uint256 fee); function init( - address _owner, - string memory _name, - string memory _symbol, + address owner, + uint256 initSupply, + string memory name, + string memory symbol, uint256 fee, address controllerModel, address defaultMaintainer, address[] memory filters ) external { - super.init(_owner, 0, _name, _symbol, 18); + super.init(owner, initSupply, name, symbol, 18); _FILTER_REGISTRY_ = filters; _FEE_ = fee; _CONTROLLER_MODEL_ = controllerModel; _DEFAULT_MAINTAINER_ = defaultMaintainer; } - function chargeMint(address user, uint256 totalMintAmount) external { + function mintFragTo(address to, uint256 rawAmount) external returns (uint256 received){ require(isIncludeFilter(msg.sender), "FILTER_NOT_REGISTRIED"); - (uint256 poolFeeAmount, uint256 mtFeeAmount) = getMintFee(user, totalMintAmount); - if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); - if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); + (uint256 poolFee, uint256 mtFee) = queryChargeMintFee(rawAmount); + if(poolFee > 0) _mint(_OWNER_, poolFee); + if(mtFee > 0) _mint(_DEFAULT_MAINTAINER_, mtFee); - _mint(user, totalMintAmount.sub(poolFeeAmount).sub(mtFeeAmount)); + received = rawAmount.sub(poolFee).sub(mtFee); + _mint(to, received); } - function chargeRandomBurn(address user, uint256 totalBurnAmount) external { + function burnFragFrom(address from, uint256 rawAmount) external returns (uint256 paid){ require(isIncludeFilter(msg.sender), "FILTER_NOT_REGISTRIED"); - (uint256 poolFeeAmount, uint256 mtFeeAmount) = getRandomBurnFee(user, totalBurnAmount); - if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); - if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); + (uint256 poolFee, uint256 mtFee) = queryChargeBurnFee(rawAmount); + if(poolFee > 0) _mint(_OWNER_, poolFee); + if(mtFee > 0) _mint(_DEFAULT_MAINTAINER_, mtFee); - _burn(user, totalBurnAmount); - } - - function chargeTargetBurn(address user, uint256 totalBurnAmount) external { - require(isIncludeFilter(msg.sender), "FILTER_NOT_REGISTRIED"); - - (uint256 poolFeeAmount, uint256 mtFeeAmount) = getTargetBurnFee(user, totalBurnAmount); - if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); - if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); - - _burn(user, totalBurnAmount); + paid = rawAmount.add(poolFee).add(mtFee); + _burn(from, paid); } //================ View ================ - function getMintFee(address user, uint256 totalMintAmount) public returns (uint256 poolFeeAmount, uint256 mtFeeAmount) { - uint256 mtFeeRate = IControllerModel(_CONTROLLER_MODEL_).getNFTInFee(address(this), user); - poolFeeAmount = DecimalMath.mulFloor(totalMintAmount, _FEE_); - mtFeeAmount = DecimalMath.mulFloor(totalMintAmount, mtFeeRate); + function queryChargeMintFee(uint256 rawAmount) public returns (uint256 poolFee, uint256 mtFee) { + uint256 mtFeeRate = IControllerModel(_CONTROLLER_MODEL_).getMintFee(address(this)); + poolFee = DecimalMath.mulFloor(rawAmount, _FEE_); + mtFee = DecimalMath.mulFloor(rawAmount, mtFeeRate); } - function getRandomBurnFee(address user, uint256 totalBurnAmount) public returns (uint256 poolFeeAmount, uint256 mtFeeAmount) { - uint256 mtFeeRate = IControllerModel(_CONTROLLER_MODEL_).getNFTRandomOutFee(address(this), user); - poolFeeAmount = DecimalMath.mulFloor(totalBurnAmount, _FEE_); - mtFeeAmount = DecimalMath.mulFloor(totalBurnAmount, mtFeeRate); + function queryChargeBurnFee(uint256 rawAmount) public returns (uint256 poolFee, uint256 mtFee) { + uint256 mtFeeRate = IControllerModel(_CONTROLLER_MODEL_).getBurnFee(address(this)); + poolFee = DecimalMath.mulFloor(rawAmount, _FEE_); + mtFee = DecimalMath.mulFloor(rawAmount, mtFeeRate); } - function getTargetBurnFee(address user, uint256 totalBurnAmount) public returns (uint256 poolFeeAmount, uint256 mtFeeAmount) { - uint256 mtFeeRate = IControllerModel(_CONTROLLER_MODEL_).getNFTTargetOutFee(address(this), user); - poolFeeAmount = DecimalMath.mulFloor(totalBurnAmount, _FEE_); - mtFeeAmount = DecimalMath.mulFloor(totalBurnAmount, mtFeeRate); - } function isIncludeFilter(address filter) view public returns (bool) { uint256 i = 0; diff --git a/contracts/NFTPool/impl/FilterModel01.sol b/contracts/NFTPool/impl/FilterERC721V1.sol similarity index 55% rename from contracts/NFTPool/impl/FilterModel01.sol rename to contracts/NFTPool/impl/FilterERC721V1.sol index 4d24d14..05c7d6d 100644 --- a/contracts/NFTPool/impl/FilterModel01.sol +++ b/contracts/NFTPool/impl/FilterERC721V1.sol @@ -15,9 +15,9 @@ import {IControllerModel} from "../intf/IControllerModel.sol"; import {IERC721} from "../../intf/IERC721.sol"; import {IERC721Receiver} from "../../intf/IERC721Receiver.sol"; import {DecimalMath} from "../../lib/DecimalMath.sol"; +import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; - -contract FilterModel01 is InitializableOwnable, IERC721Receiver { +contract FilterERC721V1 is InitializableOwnable, IERC721Receiver, ReentrancyGuard { using SafeMath for uint256; //=================== Storage =================== @@ -27,6 +27,7 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { //tokenId => isRegistered mapping(uint256 => bool) public _SPREAD_IDS_REGISTRY_; + //tokenId => 1 mapping(uint256 => uint256) public _NFT_RESERVE_; uint256[] public _NFT_IDS_; @@ -54,9 +55,25 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { function init( address filterAdmin, address nftCollection, + bool[] memory switches, + uint256[] memory tokenRanges, + uint256[] memory nftAmounts, + uint256[] memory priceRules, + uint256[] memory spreadIds ) external { initOwner(filterAdmin); _NFT_COLLECTION_ = nftCollection; + + _changeNFTInPrice(priceRules[0],priceRules[1],switches[0]); + _changeNFTRandomInPrice(priceRules[2],priceRules[3],switches[1]); + _changeNFTTargetOutPrice(priceRules[4],priceRules[5],switches[2]); + + _changeNFTAmount(nftAmounts[0],nftAmounts[1]); + + _changeTokenIdRange(tokenRanges[0],tokenRanges[1]); + for(uint256 i = 0; i < spreadIds.length; i++) { + _SPREAD_IDS_REGISTRY_[spreadIds[i]] = true; + } } //==================== Query ================== @@ -69,54 +86,69 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { } } - function isNFTIDValid(uint256 nftId) public view returns(bool){ - if((nftId >= _TOKEN_ID_START_ && nftId <= _TOKEN_ID_END_) || _SPREAD_IDS_REGISTRY_[nftId]) { - return true; - } else { - return false; - } + function isNFTIDValid(uint256 nftId) public view returns(bool) { + if((nftId >= _NFT_ID_START_ && nftId <= _NFT_ID_END_) || _SPREAD_IDS_REGISTRY_[nftId]) { + return true; + } else { + return false; + } } function getAvaliableNFTIn() public view returns(uint256) { - if(_MAX_NFT_AMOUNT_ < _TOKEN_IDS_.length) { + if(_MAX_NFT_AMOUNT_ < _NFT_IDS_.length) { return 0; }else { - return _MAX_NFT_AMOUNT_ - _TOKEN_IDS_.length; + return _MAX_NFT_AMOUNT_ - _NFT_IDS_.length; } } function getAvaliableNFTOut() public view returns(uint256) { - if(_TOKEN_IDS_.length < _MIN_NFT_AMOUNT_) { + if(_NFT_IDS_.length < _MIN_NFT_AMOUNT_) { return 0; }else { - return _TOKEN_IDS_.length - _MIN_NFT_AMOUNT_; + return _NFT_IDS_.length - _MIN_NFT_AMOUNT_; } } - function queryNFTIn(uint256 NFTInAmount) external view returns (uint256 rawReceive, uint256 receive) { + function getNFTIndexById(uint256 tokenId) public view returns(uint256) { + uint256 i = 0; + for(; i < _NFT_IDS_.length; i++) { + if(_NFT_IDS_[i] == tokenId) break; + } + require(i < _NFT_IDS_.length, "TOKEN_ID_NOT_EXSIT"); + return i; + } + + function queryNFTIn(uint256 NFTInAmount) public view returns (uint256 rawReceive, uint256 received) { require(NFTInAmount <= getAvaliableNFTIn(), "EXCEDD_IN_AMOUNT"); - rawReceive = geometricCalc(_GS_START_IN_, _CR_IN_, _NFT_AMOUNT_, _NFT_AMOUNT_+NFTInAmount); - receive = IFilterAdmin(_OWNER_).queryChargeMintFee(rawReceive); + uint256 nftAmount = _NFT_IDS_.length; + rawReceive = geometricCalc(_GS_START_IN_, _CR_IN_, nftAmount, nftAmount + NFTInAmount); + (uint256 poolFee, uint256 mtFee) = IFilterAdmin(_OWNER_).queryChargeMintFee(rawReceive); + received = rawReceive.sub(poolFee).sub(mtFee); } - function queryNFTTargetOut(uint256 NFTOutAmount) external view returns (uint256 rawPay, uint256 pay) { + function queryNFTTargetOut(uint256 NFTOutAmount) public view returns (uint256 rawPay, uint256 pay) { require(NFTOutAmount <= getAvaliableNFTOut(), "EXCEED_OUT_AMOUNT"); - rawPay = geometricCalc(_GS_START_TARGET_OUT_,_CR_TARGET_OUT_, _NFT_AMOUNT_-NFTOutAmount,_NFT_AMOUNT_); - pay = IFilterAdmin(_OWNER_).queryChargeBurnFee(rawPay); + uint256 nftAmount = _NFT_IDS_.length; + rawPay = geometricCalc(_GS_START_TARGET_OUT_,_CR_TARGET_OUT_, nftAmount - NFTOutAmount, nftAmount); + (uint256 poolFee, uint256 mtFee) = IFilterAdmin(_OWNER_).queryChargeBurnFee(rawPay); + pay = rawPay.add(poolFee).add(mtFee); } - function queryNFTRandomOut(uint256 NFTOutAmount) external view returns (uint256 rawPay, uint256 pay) { + function queryNFTRandomOut(uint256 NFTOutAmount) public view returns (uint256 rawPay, uint256 pay) { require(NFTOutAmount <= getAvaliableNFTOut(), "EXCEED_OUT_AMOUNT"); - rawPay = geometricCalc(_GS_START_RANDOM_OUT_,_CR_RANDOM_OUT_, _NFT_AMOUNT_-NFTOutAmount,_NFT_AMOUNT_); - pay = IFilterAdmin(_OWNER_).chargeBurnFee(rawPay); + uint256 nftAmount = _NFT_IDS_.length; + rawPay = geometricCalc(_GS_START_RANDOM_OUT_,_CR_RANDOM_OUT_, nftAmount - NFTOutAmount, nftAmount); + (uint256 poolFee, uint256 mtFee) = IFilterAdmin(_OWNER_).queryChargeBurnFee(rawPay); + pay = rawPay.add(poolFee).add(mtFee); } // ================= Trading ================ function ERC721In(uint256[] memory tokenIds, address to) external preventReentrant returns(uint256 received) { for (uint256 i = 0; i < tokenIds.length; i++) { - require(!_NFT_RESERVE_[tokenIds[i]] && IERC721(_NFT_COLLECTION_).ownerOf(tokenIds[i])==address(this), "NFT_NOT_SEND"); - _NFT_IDS_.push(tokenId); + require(_NFT_RESERVE_[tokenIds[i]] == 0 && IERC721(_NFT_COLLECTION_).ownerOf(tokenIds[i])==address(this), "NFT_NOT_SEND"); + _NFT_IDS_.push(tokenIds[i]); _NFT_RESERVE_[tokenIds[i]] = 1; } (uint256 rawReceive,) = queryNFTIn(tokenIds.length); @@ -124,7 +156,7 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { } function ERC721TargetOut(uint256[] memory indexes, address to) external preventReentrant returns(uint256 paid) { - (uint256 rawPay, ) = queryNFTOut(tokenIds.length); + (uint256 rawPay, ) = queryNFTTargetOut(indexes.length); paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); for (uint256 i = 0; i < indexes.length; i++) { _transferOutERC721(to, indexes[i]); @@ -132,7 +164,7 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { } function ERC721RandomOut(uint256 amount, address to) external preventReentrant returns (uint256 paid) { - (uint256 rawPay, ) = queryNFTOut(amount); + (uint256 rawPay, ) = queryNFTRandomOut(amount); paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); for (uint256 i = 0; i < amount; i++) { _transferOutERC721(to, getRandomOutId()); @@ -144,18 +176,18 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { function onERC721Received( address, address, - uint256 tokenId, + uint256, bytes calldata ) external override returns (bytes4) { return IERC721Receiver.onERC721Received.selector; } function _transferOutERC721(address to, uint256 index) internal { - require(index<_TOKEN_IDS_.length, "INDEX_NOT_EXIST"); - uint256 tokenId = _TOKEN_IDS_[index]; - IERC721(_NFT_COLLECTION_).safeTransfer(to, tokenId); - _TOKEN_IDS_[index] = _TOKEN_IDS_[_TOKEN_IDS_.length - 1]; - _TOKEN_IDS_.pop(); + require(index < _NFT_IDS_.length, "INDEX_NOT_EXIST"); + uint256 tokenId = _NFT_IDS_[index]; + IERC721(_NFT_COLLECTION_).safeTransferFrom(address(this), to, tokenId); + _NFT_IDS_[index] = _NFT_IDS_[_NFT_IDS_.length - 1]; + _NFT_IDS_.pop(); _NFT_RESERVE_[tokenId] = 0; } @@ -166,89 +198,115 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { require(IControllerModel(controllerModel).getEmergencySwitch(address(this)), "NOT_OPEN"); for(uint256 i = 0; i< nftContract.length; i++) { - if(_NFT_RESERVE_[tokenIds[i]] && nftContract[i]==_NFT_COLLECTION_){ - uint256 index = getNFTIndex(tokenIds[i]); - _TOKEN_IDS_[index] = _TOKEN_IDS_[_TOKEN_IDS_.length - 1]; - _TOKEN_IDS_.pop(); - _NFT_RESERVE_[tokenId] = 0; + if(_NFT_RESERVE_[tokenIds[i]] == 1 && nftContract[i] == _NFT_COLLECTION_){ + uint256 index = getNFTIndexById(tokenIds[i]); + _NFT_IDS_[index] = _NFT_IDS_[_NFT_IDS_.length - 1]; + _NFT_IDS_.pop(); + _NFT_RESERVE_[tokenIds[i]] = 0; } - IERC721(nftContract[i]).safeTransfer(to, tokenId); + IERC721(nftContract[i]).safeTransferFrom(address(this), to, tokenIds[i]); } } // ============ Math ============= - function geometricCalc(uint256 base, uint256 ratio, uint256 times) internal view returns(uint256 newBase, uint256 sum) { - sum = 0; - for(uint256 i = 0; i < times; i++) { - base = DecimalMath.mulFloor(base, ratio); - sum = sum.add(base); - } - newBase = base; + function geometricCalc(uint256 a1, uint256 q, uint256 start, uint256 end) internal view returns(uint256) { + //Sn=a1*(q^n-1)/(q-1) + //Sn-Sm = a1*(q^n-q^m)/(q-1) + + //q^n + uint256 qn = DecimalMath.powFloor(q, end); + //q^m + uint256 qm = DecimalMath.powFloor(q, start); + return a1.mul(qn.sub(qm)).div(q.sub(DecimalMath.ONE)); } - function getRandomOutId() external view returns (uint256 index) { - uint256 nftAmount = _TOKEN_IDS_.length; + function getRandomOutId() public view returns (uint256 index) { + uint256 nftAmount = _NFT_IDS_.length; index = uint256(keccak256(abi.encodePacked(tx.origin, blockhash(block.number-1), gasleft()))) % nftAmount; } // ================= Ownable ================ - function changeNFTInPrice(uint256 newGsStart, uint256 newCr, bool switch) external { + function changeNFTInPrice(uint256 newGsStart, uint256 newCr, bool switchFlag) external { require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); - if (!switch) { + _changeNFTInPrice(newGsStart, newCr, switchFlag); + } + + function _changeNFTInPrice(uint256 newGsStart, uint256 newCr, bool switchFlag) internal { + if (!switchFlag) { _NFT_IN_SWITCH_ = false; } else { - require(newCr>DecimalMath.ONE, "CR_ZERO"); + require(newCr > DecimalMath.ONE, "CR_INVALID"); _GS_START_IN_ = newGsStart; _CR_IN_ = newCr; _NFT_IN_SWITCH_ = true; } } - function changeNFTRandomInPrice(uint256 newGsStart, uint256 newCr, bool switch) external { + function changeNFTRandomInPrice(uint256 newGsStart, uint256 newCr, bool switchFlag) external { require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); - if (!switch) { - _NFT_RANDOM_OUT_SWITCH_ = false; + _changeNFTRandomInPrice(newGsStart, newCr, switchFlag); + } + + function _changeNFTRandomInPrice(uint256 newGsStart, uint256 newCr, bool switchFlag) internal { + if (!switchFlag) { + _NFT_RANDOM_SWITCH_ = false; } else { - require(newCr>DecimalMath.ONE, "CR_ZERO"); + require(newCr > DecimalMath.ONE, "CR_INVALID"); _GS_START_RANDOM_OUT_ = newGsStart; _CR_RANDOM_OUT_ = newCr; - _NFT_RANDOM_OUT_SWITCH_ = true; + _NFT_RANDOM_SWITCH_ = true; } } - function changeNFTTargetOutPrice(uint256 newGsStart, uint256 newCr, bool switch) external { + function changeNFTTargetOutPrice(uint256 newGsStart, uint256 newCr, bool switchFlag) external { require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); - if (!switch) { - _NFT_TARGET_OUT_SWITCH_ = false; + _changeNFTTargetOutPrice(newGsStart, newCr, switchFlag); + } + + function _changeNFTTargetOutPrice(uint256 newGsStart, uint256 newCr, bool switchFlag) internal { + if (!switchFlag) { + _NFT_TARGET_SWITCH_ = false; } else { - require(newCr>DecimalMath.ONE, "CR_ZERO"); + require(newCr > DecimalMath.ONE, "CR_INVALID"); _GS_START_TARGET_OUT_ = newGsStart; _CR_TARGET_OUT_ = newCr; - _NFT_TARGET_OUT_SWITCH_ = true; + _NFT_TARGET_SWITCH_ = true; } } function changeNFTAmount(uint256 maxNFTAmount, uint256 minNFTAmount) external { require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); + _changeNFTAmount(maxNFTAmount, minNFTAmount); + } + + function _changeNFTAmount(uint256 maxNFTAmount, uint256 minNFTAmount) internal { require(maxNFTAmount >= minNFTAmount, "AMOUNT_INVALID"); _MAX_NFT_AMOUNT_ = maxNFTAmount; _MIN_NFT_AMOUNT_ = minNFTAmount; } - function changeTokenIdRange(uint256 tokenIdStart, uint256 tokenIdEnd) external { + function changeTokenIdRange(uint256 nftIdStart, uint256 nftIdEnd) external { require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); - require(tokenIdStart <= tokenIdEnd, "TOKEN_RANGE_INVALID"); + _changeTokenIdRange(nftIdStart, nftIdEnd); + } + + function _changeTokenIdRange(uint256 nftIdStart, uint256 nftIdEnd) internal { + require(nftIdStart <= nftIdEnd, "TOKEN_RANGE_INVALID"); - _TOKEN_ID_START_ = tokenIdStart; - _TOKEN_ID_END_ = tokenIdEnd; + _NFT_ID_START_ = nftIdStart; + _NFT_ID_END_ = nftIdEnd; } function changeTokenIdMap(uint256[] memory tokenIds, bool[] memory isRegistrieds) external { require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); + _changeTokenIdMap(tokenIds, isRegistrieds); + } + + function _changeTokenIdMap(uint256[] memory tokenIds, bool[] memory isRegistrieds) internal { require(tokenIds.length == isRegistrieds.length, "PARAM_NOT_MATCH"); for(uint256 i = 0; i < tokenIds.length; i++) { @@ -262,42 +320,8 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { return interfaceId == type(IERC721Receiver).interfaceId; } - function version() virtual external pure returns (string memory) { return "FILTER_1_ERC721 1.0.0"; - //TODO: - function geometricCalc1(uint256 base, uint256 ratio, uint256 times) internal view returns(uint256 newBase, uint256 sum) { - require(times > 0); - //q^(n-1) - uint256 general_coefficient = ratio.powFloor(times - 1); - //an=a1*q^n-1 - newBase = base.mul(general_coefficient); - - if(ratio == 1e18) { - //na1 - sum = base.mul(times); - } else { - //a1(1-q^n)/(1-q) - uint256 denominator = base.mul(1e18.sub(DecimalMath.mulFloor(general_coefficient, ratio))); - sum = denominator.div(1e18.sub(ratio)); - } } - function removeTokenId(uint256 id) internal returns(bool){ - uint256[] memory tokenIds = _TOKEN_IDS_; - uint256 i; - for (; i < tokenIds.length; i++) { - if (tokenIds[i] == id) { - tokenIds[i] = tokenIds[tokenIds.length - 1]; - break; - } - } - if(i < tokenIds.length) { - _TOKEN_IDS_ = tokenIds; - _TOKEN_IDS_.pop(); - return true; - }else { - return false; - } - } } \ No newline at end of file diff --git a/contracts/NFTPool/intf/IControllerModel.sol b/contracts/NFTPool/intf/IControllerModel.sol index e8bfd1f..ebf2ec2 100644 --- a/contracts/NFTPool/intf/IControllerModel.sol +++ b/contracts/NFTPool/intf/IControllerModel.sol @@ -8,11 +8,9 @@ pragma solidity 0.6.9; interface IControllerModel { - function getNFTInFee(address filterAdminAddr, address user) external view returns(uint256); + function getMintFee(address filterAdminAddr) external view returns (uint256); - function getNFTRandomOutFee(address filterAdminAddr, address user) external view returns(uint256); + function getBurnFee(address filterAdminAddr) external view returns (uint256); - function getNFTTargetOutFee(address filterAdminAddr, address user) external view returns(uint256); - - function getEmergencySwitch(address filter) external view returns(bool); + function getEmergencySwitch(address filter) external view returns (bool); } \ No newline at end of file diff --git a/contracts/NFTPool/intf/IFilterAdmin.sol b/contracts/NFTPool/intf/IFilterAdmin.sol index 06d5f3d..52fbd7d 100644 --- a/contracts/NFTPool/intf/IFilterAdmin.sol +++ b/contracts/NFTPool/intf/IFilterAdmin.sol @@ -13,27 +13,21 @@ interface IFilterAdmin { function _CONTROLLER_MODEL_() external returns (address); function init( - address _owner, - string memory _name, - string memory _symbol, + address owner, + uint256 initSupply, + string memory name, + string memory symbol, uint256 fee, address mtFeeModel, address defaultMaintainer, address[] memory filters ) external; - function ERC721In( - address filter, - address nftContract, - uint256[] memory tokenIds, - uint256 minMintAmount - ) external returns (uint256 actualMintAmount); + function mintFragTo(address to, uint256 rawAmount) external returns (uint256 received); - function mintFragTo(address to, uint256 rawAmount) external; + function burnFragFrom(address from, uint256 rawAmount) external returns (uint256 paid); - function burnFragFrom(address from, uint256 rawAmount) external; + function queryChargeMintFee(uint256 rawAmount) external view returns (uint256 poolFee, uint256 mtFee); - function queryChargeMintFee(uint256 rawAmount) external; - - function queryChargeBurnFee(uint256 rawAmount) external; + function queryChargeBurnFee(uint256 rawAmount) external view returns (uint256 poolFee, uint256 mtFee); } diff --git a/contracts/NFTPool/intf/IFilterModel.sol b/contracts/NFTPool/intf/IFilterModel.sol index 2d626af..9cf0f55 100644 --- a/contracts/NFTPool/intf/IFilterModel.sol +++ b/contracts/NFTPool/intf/IFilterModel.sol @@ -8,35 +8,21 @@ pragma solidity 0.6.9; interface IFilterModel { - function isFilterERC721Pass(address nftCollectionAddress, uint256 nftId) external view returns (bool); + function isNFTValid(address nftCollectionAddress, uint256 nftId) external view returns (bool); + + function _NFT_COLLECTION_() external view returns(address); - function isFilterERC1155Pass(address nftCollectionAddress, uint256 nftId, uint256 amount) external view returns (bool); + function queryNFTIn(uint256 NFTInAmount) external view returns (uint256 rawReceive, uint256 received); - function getAvaliableNFTIn() external view returns(uint256); + function queryNFTTargetOut(uint256 NFTOutAmount) external view returns (uint256 rawPay, uint256 pay); - function getAvaliableNFTOut() external view returns(uint256); + function queryNFTRandomOut(uint256 NFTOutAmount) external view returns (uint256 rawPay, uint256 pay); - function _NFT_IN_SWITCH_() external view returns(bool); + function ERC721In(uint256[] memory tokenIds, address to) external returns(uint256 received); - function _NFT_RANDOM_SWITCH_() external view returns(bool); + function ERC721TargetOut(uint256[] memory indexes, address to) external returns(uint256 paid); - function _NFT_TARGET_SWITCH_() external view returns(bool); + function ERC721RandomOut(uint256 amount, address to) external returns (uint256 paid); + - function getNFTInPrice(address nftCollectionAddress, uint256 nftId) external view returns (uint256); - - function getNFTRandomOutPrice() external view returns (uint256); - - function getNFTTargetOutPrice(address nftCollectionAddress, uint256 nftId) external view returns (uint256); - - function getRandomOutId() external view returns (address nftCollection, uint256 nftId); - - function transferOutERC721(address nftContract, address assetTo, uint256 nftId) external; - - function transferInERC721(address nftContract, address assetFrom, uint256 nftId) external; - - function transferOutERC1155(address nftContract, address assetTo, uint256 nftId, uint256 amount) external; - - function transferBatchOutERC1155(address nftContract, address assetTo, uint256[] memory nftIds, uint256[] memory amounts) external; - - function transferBatchInERC1155(address nftContract, address assetFrom, uint256[] memory nftIds, uint256[] memory amounts) external; } \ No newline at end of file diff --git a/contracts/SmartRoute/DODONFTApprove.sol b/contracts/SmartRoute/DODONFTApprove.sol new file mode 100644 index 0000000..b291e4a --- /dev/null +++ b/contracts/SmartRoute/DODONFTApprove.sol @@ -0,0 +1,103 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +import {IERC721} from "../intf/IERC721.sol"; +import {IERC1155} from "../intf/IERC1155.sol"; +import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; + +/** + * @title DODONFTApprove + * @author DODO Breeder + * + * @notice Handle NFT authorizations in DODO platform + */ +contract DODONFTApprove is InitializableOwnable { + + // ============ Storage ============ + uint256 private constant _TIMELOCK_DURATION_ = 3 days; + mapping (address => bool) public _IS_ALLOWED_PROXY_; + uint256 public _TIMELOCK_; + address public _PENDING_ADD_DODO_PROXY_; + + // ============ Events ============ + event AddDODOProxy(address dodoProxy); + + // ============ Modifiers ============ + modifier notLocked() { + require( + _TIMELOCK_ <= block.timestamp, + "SetProxy is timelocked" + ); + _; + } + + function init(address owner, address[] memory proxies) external { + initOwner(owner); + for(uint i = 0; i < proxies.length; i++) + _IS_ALLOWED_PROXY_[proxies[i]] = true; + } + + function unlockAddProxy(address newDodoProxy) public onlyOwner { + _TIMELOCK_ = block.timestamp + _TIMELOCK_DURATION_; + _PENDING_ADD_DODO_PROXY_ = newDodoProxy; + } + + function lockAddProxy() public onlyOwner { + _PENDING_ADD_DODO_PROXY_ = address(0); + _TIMELOCK_ = 0; + } + + + function addDODOProxy() external onlyOwner notLocked() { + _IS_ALLOWED_PROXY_[_PENDING_ADD_DODO_PROXY_] = true; + lockAddProxy(); + emit AddDODOProxy(_PENDING_ADD_DODO_PROXY_); + } + + function removeDODOProxy (address oldDodoProxy) public onlyOwner { + _IS_ALLOWED_PROXY_[oldDodoProxy] = false; + } + + + function claimERC721( + address nftContract, + address who, + address dest, + uint256 tokenId + ) external { + require(_IS_ALLOWED_PROXY_[msg.sender], "DODONFTApprove:Access restricted"); + IERC721(nftContract).safeTransferFrom(who, dest, tokenId); + } + + function claimERC1155( + address nftContract, + address who, + address dest, + uint256 tokenId, + uint256 amount + ) external { + require(_IS_ALLOWED_PROXY_[msg.sender], "DODONFTApprove:Access restricted"); + IERC1155(nftContract).safeTransferFrom(who, dest, tokenId, amount, ""); + } + + function claimERC1155Batch( + address nftContract, + address who, + address dest, + uint256[] memory tokenIds, + uint256[] memory amounts + ) external { + require(_IS_ALLOWED_PROXY_[msg.sender], "DODONFTApprove:Access restricted"); + IERC1155(nftContract).safeBatchTransferFrom(who, dest, tokenIds, amounts, ""); + } + + function isAllowedProxy(address _proxy) external view returns (bool) { + return _IS_ALLOWED_PROXY_[_proxy]; + } +} diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index 8430652..d247819 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -9,12 +9,13 @@ import {SafeMath} from "../../lib/SafeMath.sol"; import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; import {ICloneFactory} from "../../lib/CloneFactory.sol"; import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; +import {IFilterModel} from "../../NFTPool/intf/IFilterModel.sol"; import {IFilterAdmin} from "../../NFTPool/intf/IFilterAdmin.sol"; -import {IERC721} from "../../intf/IERC721.sol"; +import {IDODONFTApprove} from "../../intf/IDODONFTApprove.sol"; import {IERC20} from "../../intf/IERC20.sol"; import {SafeERC20} from "../../lib/SafeERC20.sol"; -interface IFilter01 { +interface IFilterERC721V1 { function init( address filterAdmin, address nftCollection, @@ -35,8 +36,10 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { mapping(uint256 => address) public _FILTER_TEMPLATES_; address public _FILTER_ADMIN_TEMPLATE_; address public _DEFAULT_MAINTAINER_; - address public _NFT_POOL_FEE_MODEL_; + address public _CONTROLLER_MODEL_; address public immutable _CLONE_FACTORY_; + address public immutable _DODO_NFT_APPROVE_; + address public immutable _DODO_APPROVE_; // ============ Event ============== @@ -45,16 +48,60 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { constructor( address cloneFactory, address filterAdminTemplate, - address nftPoolFeeModel, - address defaultMaintainer + address controllerModel, + address defaultMaintainer, + address dodoNftApprove, + address dodoApprove ) public { _CLONE_FACTORY_ = cloneFactory; _FILTER_ADMIN_TEMPLATE_ = filterAdminTemplate; - _NFT_POOL_FEE_MODEL_ = nftPoolFeeModel; + _CONTROLLER_MODEL_ = controllerModel; _DEFAULT_MAINTAINER_ = defaultMaintainer; + _DODO_NFT_APPROVE_ = dodoNftApprove; + _DODO_APPROVE_ = dodoApprove; } + // ================ NFT In and Out =================== + function nftIn( + address filter, + address nftCollection, + uint256[] memory tokenIds, + address to, + uint256 minMintAmount + ) external { + for(uint256 i = 0; i < tokenIds.length; i++) { + require(IFilterModel(filter).isNFTValid(nftCollection,tokenIds[i]), "NOT_REGISTRIED"); + IDODONFTApprove(_DODO_NFT_APPROVE_).claimERC721(nftCollection, msg.sender, filter, tokenIds[i]); + } + uint256 received = IFilterModel(filter).ERC721In(tokenIds, to); + require(received >= minMintAmount, "MINT_AMOUNT_NOT_ENOUGH"); + } + + function nftTargetOut( + address filter, + uint256[] memory indexes, + address to, + uint256 maxBurnAmount + ) external { + uint256 paid = IFilterModel(filter).ERC721TargetOut(indexes, to); + require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); + } + + function nftRandomOut( + address filter, + uint256 amount, + address to, + uint256 maxBurnAmount + ) external { + uint256 paid = IFilterModel(filter).ERC721RandomOut(amount, to); + require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); + } + + + // ================== Create NFTPool =================== + function createNewNFTPool01( + uint256 initSupply, string memory name, string memory symbol, uint256 fee, @@ -67,7 +114,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { ) external returns(address newFilterAdmin) { newFilterAdmin = ICloneFactory(_CLONE_FACTORY_).clone(_FILTER_ADMIN_TEMPLATE_); - address filter01 = createFilter01( + address filter01 = createFilterERC721V1( newFilterAdmin, nftCollection, switches, @@ -82,16 +129,19 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { IFilterAdmin(newFilterAdmin).init( msg.sender, + initSupply, name, symbol, fee, - _NFT_POOL_FEE_MODEL_, + _CONTROLLER_MODEL_, _DEFAULT_MAINTAINER_, filters ); } - function createFilter01( + + // ================== Create Filter =================== + function createFilterERC721V1( address filterAdmin, address nftCollection, bool[] memory switches, @@ -99,34 +149,42 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { uint256[] memory nftAmounts, uint256[] memory priceRules, uint256[] memory spreadIds - ) public returns(address newFilter01) { - newFilter01 = ICloneFactory(_CLONE_FACTORY_).clone(_FILTER_TEMPLATES_[1]); - IFilter01(newFilter01).init(filterAdmin, nftCollection, switches, tokenRanges, nftAmounts, priceRules, spreadIds); + ) public returns(address newFilterERC721V1) { + //key = 1 => FilterERC721V1 + newFilterERC721V1 = ICloneFactory(_CLONE_FACTORY_).clone(_FILTER_TEMPLATES_[1]); + IFilterERC721V1(newFilterERC721V1).init( + filterAdmin, + nftCollection, + switches, + tokenRanges, + nftAmounts, + priceRules, + spreadIds + ); } + + // ================== NFT ERC20 Swap ====================== function erc721ToErc20( address filterAdmin, address filter, address nftContract, uint256 tokenId, address toToken, - address dodoApprove, address dodoProxy, bytes memory dodoSwapData ) external preventReentrant { - IERC721(nftContract).safeTransferFrom(msg.sender, address(this), tokenId); - IERC721(nftContract).approve(filter, tokenId); + IDODONFTApprove(_DODO_NFT_APPROVE_).claimERC721(nftContract, msg.sender, filter, tokenId); uint256[] memory tokenIds = new uint256[](1); tokenIds[0] = tokenId; - //TODO: - uint256 mintAmount = IFilterAdmin(filterAdmin).ERC721In(filter, nftContract, tokenIds, 0); + uint256 receivedFragAmount = IFilterModel(filter).ERC721In(tokenIds, address(this)); - _generalApproveMax(filterAdmin, dodoApprove, mintAmount); + _generalApproveMax(filterAdmin, _DODO_APPROVE_, receivedFragAmount); (bool success, ) = dodoProxy.call(dodoSwapData); require(success, "API_SWAP_FAILED"); @@ -146,8 +204,8 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { _FILTER_ADMIN_TEMPLATE_ = newFilterAdminTemplate; } - function changeNftPoolFeeModel(address newNftPoolFeeModel) external onlyOwner { - _NFT_POOL_FEE_MODEL_ = newNftPoolFeeModel; + function changeControllerModel(address newControllerModel) external onlyOwner { + _CONTROLLER_MODEL_ = newControllerModel; } function setFilterTemplate(uint256 idx, address newFilterTemplate) external onlyOwner { diff --git a/contracts/intf/IDODONFTApprove.sol b/contracts/intf/IDODONFTApprove.sol new file mode 100644 index 0000000..16d8dfd --- /dev/null +++ b/contracts/intf/IDODONFTApprove.sol @@ -0,0 +1,18 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +interface IDODONFTApprove { + function isAllowedProxy(address _proxy) external view returns (bool); + + function claimERC721(address nftContract, address who, address dest, uint256 tokenId) external; + + function claimERC1155(address nftContract, address who, address dest, uint256 tokenId, uint256 amount) external; + + function claimERC1155Batch(address nftContract, address who, address dest, uint256[] memory tokenIds, uint256[] memory amounts) external; +}