diff --git a/README.md b/README.md index 79a63e7..c686fa4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,11 @@ - contracts/NFTPool/impl/FilterAdmin.sol -- contracts/NFTPool/impl/FilterModel01.sol +- contracts/NFTPool/impl/BaseFilterV1.sol + +- contracts/NFTPool/impl/FilterERC721V1.sol + +- contracts/NFTPool/impl/FilterERC1155V1.sol - contracts/NFTPool/impl/ControllerModel.sol @@ -10,4 +14,6 @@ - contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +- contracts/SmartRoute/DODONFTApprove.sol + diff --git a/contracts/NFTPool/impl/BaseFilterV1.sol b/contracts/NFTPool/impl/BaseFilterV1.sol new file mode 100644 index 0000000..c58f685 --- /dev/null +++ b/contracts/NFTPool/impl/BaseFilterV1.sol @@ -0,0 +1,200 @@ +/* + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; +import {SafeMath} from "../../lib/SafeMath.sol"; +import {IFilterAdmin} from "../intf/IFilterAdmin.sol"; +import {DecimalMath} from "../../lib/DecimalMath.sol"; +import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; + +contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { + using SafeMath for uint256; + + //=================== Storage =================== + address public _NFT_COLLECTION_; + uint256 public _NFT_ID_START_; + uint256 public _NFT_ID_END_; + + //tokenId => isRegistered + mapping(uint256 => bool) public _SPREAD_IDS_REGISTRY_; + //tokenId => amount + mapping(uint256 => uint256) public _NFT_RESERVE_; + + uint256[] public _NFT_IDS_; + + uint256 public _MAX_NFT_AMOUNT_; + uint256 public _MIN_NFT_AMOUNT_; + + // GS -> Geometric sequence + // CR -> Common Ratio + + //For NFT IN + uint256 public _GS_START_IN_; + uint256 public _CR_IN_; + bool public _NFT_IN_SWITCH_ = false; + + //For NFT Random OUT + uint256 public _GS_START_RANDOM_OUT_; + uint256 public _CR_RANDOM_OUT_; + bool public _NFT_RANDOM_SWITCH_ = false; + + //For NFT Target OUT + uint256 public _GS_START_TARGET_OUT_; + uint256 public _CR_TARGET_OUT_; + bool public _NFT_TARGET_SWITCH_ = false; + + + //==================== Query ================== + + function isNFTValid(address nftCollectionAddress, uint256 nftId) external view returns (bool) { + if(nftCollectionAddress == _NFT_COLLECTION_) { + isNFTIDValid(nftId); + } 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_ < _NFT_IDS_.length) { + return 0; + }else { + return _MAX_NFT_AMOUNT_ - _NFT_IDS_.length; + } + } + + function getAvaliableNFTOut() public view returns(uint256) { + if(_NFT_IDS_.length < _MIN_NFT_AMOUNT_) { + return 0; + }else { + return _NFT_IDS_.length - _MIN_NFT_AMOUNT_; + } + } + + 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; + } + + // ============ Math ============= + + 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() 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 switchFlag) external { + require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); + _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_INVALID"); + _GS_START_IN_ = newGsStart; + _CR_IN_ = newCr; + _NFT_IN_SWITCH_ = true; + } + } + + function changeNFTRandomInPrice(uint256 newGsStart, uint256 newCr, bool switchFlag) external { + require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); + _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_INVALID"); + _GS_START_RANDOM_OUT_ = newGsStart; + _CR_RANDOM_OUT_ = newCr; + _NFT_RANDOM_SWITCH_ = true; + } + } + + function changeNFTTargetOutPrice(uint256 newGsStart, uint256 newCr, bool switchFlag) external { + require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); + _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_INVALID"); + _GS_START_TARGET_OUT_ = newGsStart; + _CR_TARGET_OUT_ = newCr; + _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 nftIdStart, uint256 nftIdEnd) external { + require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); + _changeTokenIdRange(nftIdStart, nftIdEnd); + } + + function _changeTokenIdRange(uint256 nftIdStart, uint256 nftIdEnd) internal { + require(nftIdStart <= nftIdEnd, "TOKEN_RANGE_INVALID"); + + _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++) { + _SPREAD_IDS_REGISTRY_[tokenIds[i]] = isRegistrieds[i]; + } + } +} \ No newline at end of file diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol new file mode 100644 index 0000000..a244170 --- /dev/null +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -0,0 +1,176 @@ +/* + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; +import {SafeMath} from "../../lib/SafeMath.sol"; +import {IFilterAdmin} from "../intf/IFilterAdmin.sol"; +import {IControllerModel} from "../intf/IControllerModel.sol"; +import {IERC1155} from "../../intf/IERC1155.sol"; +import {IERC1155Receiver} from "../../intf/IERC1155Receiver.sol"; +import {DecimalMath} from "../../lib/DecimalMath.sol"; +import {BaseFilterV1} from "./BaseFilterV1.sol"; + +contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { + using SafeMath for uint256; + + //=================== Storage =================== + uint256 public _TOTAL_NFT_AMOUNT_; + + function init( + address filterAdmin, + address nftCollection, + bool[] memory switches, + uint256[] memory numParams, //0 - startId, 1 - endId, 2 - maxAmount, 3 - minAmount + 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(numParams[2],numParams[3]); + + _changeTokenIdRange(numParams[0],numParams[1]); + for(uint256 i = 0; i < spreadIds.length; i++) { + _SPREAD_IDS_REGISTRY_[spreadIds[i]] = true; + } + } + + //==================== Query ================== + + function queryNFTIn(uint256 NFTInAmount) public view returns (uint256 rawReceive, uint256 received) { + require(NFTInAmount <= getAvaliableNFTIn(), "EXCEDD_IN_AMOUNT"); + rawReceive = geometricCalc(_GS_START_IN_, _CR_IN_, _TOTAL_NFT_AMOUNT_, _TOTAL_NFT_AMOUNT_ + NFTInAmount); + (uint256 poolFee, uint256 mtFee) = IFilterAdmin(_OWNER_).queryChargeMintFee(rawReceive); + received = rawReceive.sub(poolFee).sub(mtFee); + } + + 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_, _TOTAL_NFT_AMOUNT_ - NFTOutAmount, _TOTAL_NFT_AMOUNT_); + (uint256 poolFee, uint256 mtFee) = IFilterAdmin(_OWNER_).queryChargeBurnFee(rawPay); + pay = rawPay.add(poolFee).add(mtFee); + } + + 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_, _TOTAL_NFT_AMOUNT_ - NFTOutAmount, _TOTAL_NFT_AMOUNT_); + (uint256 poolFee, uint256 mtFee) = IFilterAdmin(_OWNER_).queryChargeBurnFee(rawPay); + pay = rawPay.add(poolFee).add(mtFee); + } + + // ================= Trading ================ + + function ERC1155In(uint256[] memory tokenIds, address to) external preventReentrant returns(uint256 received) { + uint256 totalAmount = 0; + for (uint256 i = 0; i < tokenIds.length; i++) { + uint256 tokenId = tokenIds[i]; + require(isNFTIDValid(tokenId), "NFT_ID_NOT_SUPPORT"); + totalAmount += _maintainERC1155In(tokenId); + } + (uint256 rawReceive,) = queryNFTIn(totalAmount); + received = IFilterAdmin(_OWNER_).mintFragTo(to, rawReceive); + } + + function ERC1155TargetOut(uint256[] memory indexes, uint256[] memory amounts, address to) external preventReentrant returns(uint256 paid) { + uint256 totalAmount = 0; + for (uint256 i = 0; i < indexes.length; i++) { + _transferOutERC1155(to, indexes[i], amounts[i]); + } + (uint256 rawPay, ) = queryNFTTargetOut(totalAmount); + paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); + } + + function ERC1155RandomOut(uint256 amount, address to) external preventReentrant returns (uint256 paid) { + (uint256 rawPay, ) = queryNFTRandomOut(amount); + paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); + for (uint256 i = 0; i < amount; i++) { + _transferOutERC1155(to, getRandomOutId(), 1); + } + } + + // ============ Transfer ============= + + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes calldata + ) external override returns (bytes4){ + return IERC1155Receiver.onERC1155Received.selector; + } + + function onERC1155BatchReceived( + address, + address, + uint256[] calldata, + uint256[] calldata, + bytes calldata + ) external override returns (bytes4){ + return IERC1155Receiver.onERC1155BatchReceived.selector; + } + + function _transferOutERC1155(address to, uint256 index, uint256 amount) internal { + require(index < _NFT_IDS_.length, "INDEX_NOT_EXIST"); + uint256 tokenId = _NFT_IDS_[index]; + IERC1155(_NFT_COLLECTION_).safeTransferFrom(address(this), to, tokenId, amount, ""); + _maintainERC1155Out(index, tokenId); + } + + function emergencyWithdraw(address[] memory nftContract, uint256[] memory tokenIds, uint256[] memory amounts, address to) external { + require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); + require(nftContract.length == tokenIds.length, "PARAM_INVALID"); + require(nftContract.length == amounts.length, "PARAM_INVALID"); + address controllerModel = IFilterAdmin(_OWNER_)._CONTROLLER_MODEL_(); + require(IControllerModel(controllerModel).getEmergencySwitch(address(this)), "NOT_OPEN"); + + for(uint256 i = 0; i< nftContract.length; i++) { + uint256 tokenId = tokenIds[i]; + IERC1155(nftContract[i]).safeTransferFrom(address(this), to, tokenId, amounts[i], ""); + if(isNFTIDValid(tokenId) && nftContract[i] == _NFT_COLLECTION_){ + _maintainERC1155Out(getNFTIndexById(tokenId), tokenId); + } + } + } + + function _maintainERC1155Out(uint256 index, uint256 tokenId) internal { + uint256 currentAmount = IERC1155(_NFT_COLLECTION_).balanceOf(address(this), tokenId); + uint256 outAmount = _NFT_RESERVE_[tokenId].sub(currentAmount); + _NFT_RESERVE_[tokenId] = currentAmount; + _TOTAL_NFT_AMOUNT_ -= outAmount; + if (currentAmount == 0) { + _NFT_IDS_[index] = _NFT_IDS_[_NFT_IDS_.length - 1]; + _NFT_IDS_.pop(); + } + } + + function _maintainERC1155In(uint256 tokenId) internal returns(uint256 inAmount){ + uint256 currentAmount = IERC1155(_NFT_COLLECTION_).balanceOf(address(this), tokenId); + inAmount = currentAmount.sub(_NFT_RESERVE_[tokenId]); + if(_NFT_RESERVE_[tokenId]==0 && currentAmount > 0) { + _NFT_IDS_.push(tokenId); + } + _NFT_RESERVE_[tokenId] = currentAmount; + _TOTAL_NFT_AMOUNT_ += inAmount; + } + + // ============ Support ============ + + function supportsInterface(bytes4 interfaceId) override public view returns (bool) { + return interfaceId == type(IERC1155Receiver).interfaceId; + } + + + function version() virtual external pure returns (string memory) { + return "FILTER_1_ERC1155 1.0.0"; + } +} \ No newline at end of file diff --git a/contracts/NFTPool/impl/FilterERC721V1.sol b/contracts/NFTPool/impl/FilterERC721V1.sol index 05c7d6d..d661f7c 100644 --- a/contracts/NFTPool/impl/FilterERC721V1.sol +++ b/contracts/NFTPool/impl/FilterERC721V1.sol @@ -16,48 +16,16 @@ import {IERC721} from "../../intf/IERC721.sol"; import {IERC721Receiver} from "../../intf/IERC721Receiver.sol"; import {DecimalMath} from "../../lib/DecimalMath.sol"; import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; +import {BaseFilterV1} from "./BaseFilterV1.sol"; -contract FilterERC721V1 is InitializableOwnable, IERC721Receiver, ReentrancyGuard { +contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { using SafeMath for uint256; - //=================== Storage =================== - address public _NFT_COLLECTION_; - uint256 public _NFT_ID_START_; - uint256 public _NFT_ID_END_; - - //tokenId => isRegistered - mapping(uint256 => bool) public _SPREAD_IDS_REGISTRY_; - //tokenId => 1 - mapping(uint256 => uint256) public _NFT_RESERVE_; - - uint256[] public _NFT_IDS_; - uint256 public _MAX_NFT_AMOUNT_; - uint256 public _MIN_NFT_AMOUNT_; - - // GS -> Geometric sequence - // CR -> Common Ratio - - //For NFT IN - uint256 public _GS_START_IN_; - uint256 public _CR_IN_; - bool public _NFT_IN_SWITCH_ = false; - - //For NFT Random OUT - uint256 public _GS_START_RANDOM_OUT_; - uint256 public _CR_RANDOM_OUT_; - bool public _NFT_RANDOM_SWITCH_ = false; - - //For NFT Target OUT - uint256 public _GS_START_TARGET_OUT_; - uint256 public _CR_TARGET_OUT_; - bool public _NFT_TARGET_SWITCH_ = false; - function init( address filterAdmin, address nftCollection, bool[] memory switches, - uint256[] memory tokenRanges, - uint256[] memory nftAmounts, + uint256[] memory numParams, //0 - startId, 1 - endId, 2 - maxAmount, 3 - minAmount uint256[] memory priceRules, uint256[] memory spreadIds ) external { @@ -68,9 +36,9 @@ contract FilterERC721V1 is InitializableOwnable, IERC721Receiver, ReentrancyGuar _changeNFTRandomInPrice(priceRules[2],priceRules[3],switches[1]); _changeNFTTargetOutPrice(priceRules[4],priceRules[5],switches[2]); - _changeNFTAmount(nftAmounts[0],nftAmounts[1]); + _changeNFTAmount(numParams[2],numParams[3]); - _changeTokenIdRange(tokenRanges[0],tokenRanges[1]); + _changeTokenIdRange(numParams[0],numParams[1]); for(uint256 i = 0; i < spreadIds.length; i++) { _SPREAD_IDS_REGISTRY_[spreadIds[i]] = true; } @@ -78,47 +46,6 @@ contract FilterERC721V1 is InitializableOwnable, IERC721Receiver, ReentrancyGuar //==================== Query ================== - function isNFTValid(address nftCollectionAddress, uint256 nftId) external view returns (bool) { - if(nftCollectionAddress == _NFT_COLLECTION_) { - isNFTIDValid(nftId); - } 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_ < _NFT_IDS_.length) { - return 0; - }else { - return _MAX_NFT_AMOUNT_ - _NFT_IDS_.length; - } - } - - function getAvaliableNFTOut() public view returns(uint256) { - if(_NFT_IDS_.length < _MIN_NFT_AMOUNT_) { - return 0; - }else { - return _NFT_IDS_.length - _MIN_NFT_AMOUNT_; - } - } - - 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"); uint256 nftAmount = _NFT_IDS_.length; @@ -147,9 +74,11 @@ contract FilterERC721V1 is InitializableOwnable, IERC721Receiver, ReentrancyGuar 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]] == 0 && IERC721(_NFT_COLLECTION_).ownerOf(tokenIds[i])==address(this), "NFT_NOT_SEND"); - _NFT_IDS_.push(tokenIds[i]); - _NFT_RESERVE_[tokenIds[i]] = 1; + uint256 tokenId = tokenIds[i]; + require(isNFTIDValid(tokenId), "NFT_ID_NOT_SUPPORT"); + require(_NFT_RESERVE_[tokenId] == 0 && IERC721(_NFT_COLLECTION_).ownerOf(tokenId)==address(this), "NFT_NOT_SEND"); + _NFT_IDS_.push(tokenId); + _NFT_RESERVE_[tokenId] = 1; } (uint256 rawReceive,) = queryNFTIn(tokenIds.length); received = IFilterAdmin(_OWNER_).mintFragTo(to, rawReceive); @@ -208,112 +137,6 @@ contract FilterERC721V1 is InitializableOwnable, IERC721Receiver, ReentrancyGuar } } - - // ============ Math ============= - - 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() 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 switchFlag) external { - require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); - _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_INVALID"); - _GS_START_IN_ = newGsStart; - _CR_IN_ = newCr; - _NFT_IN_SWITCH_ = true; - } - } - - function changeNFTRandomInPrice(uint256 newGsStart, uint256 newCr, bool switchFlag) external { - require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); - _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_INVALID"); - _GS_START_RANDOM_OUT_ = newGsStart; - _CR_RANDOM_OUT_ = newCr; - _NFT_RANDOM_SWITCH_ = true; - } - } - - function changeNFTTargetOutPrice(uint256 newGsStart, uint256 newCr, bool switchFlag) external { - require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); - _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_INVALID"); - _GS_START_TARGET_OUT_ = newGsStart; - _CR_TARGET_OUT_ = newCr; - _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 nftIdStart, uint256 nftIdEnd) external { - require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); - _changeTokenIdRange(nftIdStart, nftIdEnd); - } - - function _changeTokenIdRange(uint256 nftIdStart, uint256 nftIdEnd) internal { - require(nftIdStart <= nftIdEnd, "TOKEN_RANGE_INVALID"); - - _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++) { - _SPREAD_IDS_REGISTRY_[tokenIds[i]] = isRegistrieds[i]; - } - } - // ============ Support ============ function supportsInterface(bytes4 interfaceId) public view returns (bool) { diff --git a/contracts/NFTPool/intf/IFilterModel.sol b/contracts/NFTPool/intf/IFilterModel.sol index 9cf0f55..3c7006b 100644 --- a/contracts/NFTPool/intf/IFilterModel.sol +++ b/contracts/NFTPool/intf/IFilterModel.sol @@ -24,5 +24,9 @@ interface IFilterModel { function ERC721RandomOut(uint256 amount, address to) external returns (uint256 paid); - + function ERC1155In(uint256[] memory tokenIds, address to) external returns(uint256 received); + + function ERC1155TargetOut(uint256[] memory indexes, uint256[] memory amounts, address to) external returns(uint256 paid); + + function ERC1155RandomOut(uint256 amount, address to) external returns (uint256 paid); } \ No newline at end of file diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index d247819..7cb9d5b 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -4,6 +4,7 @@ */ pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; import {SafeMath} from "../../lib/SafeMath.sol"; import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; @@ -15,13 +16,12 @@ import {IDODONFTApprove} from "../../intf/IDODONFTApprove.sol"; import {IERC20} from "../../intf/IERC20.sol"; import {SafeERC20} from "../../lib/SafeERC20.sol"; -interface IFilterERC721V1 { +interface IFilterV1 { function init( address filterAdmin, address nftCollection, bool[] memory switches, - uint256[] memory tokenRanges, - uint256[] memory nftAmounts, + uint256[] memory numParams, uint256[] memory priceRules, uint256[] memory spreadIds ) external; @@ -61,8 +61,8 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { _DODO_APPROVE_ = dodoApprove; } - // ================ NFT In and Out =================== - function nftIn( + // ================ ERC721 In and Out =================== + function erc721In( address filter, address nftCollection, uint256[] memory tokenIds, @@ -77,7 +77,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { require(received >= minMintAmount, "MINT_AMOUNT_NOT_ENOUGH"); } - function nftTargetOut( + function erc721TargetOut( address filter, uint256[] memory indexes, address to, @@ -87,7 +87,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); } - function nftRandomOut( + function erc721RandomOut( address filter, uint256 amount, address to, @@ -97,67 +97,99 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); } + // ================== ERC1155 In and Out =================== + function erc1155In( + address filter, + address nftCollection, + uint256[] memory tokenIds, + uint256[] memory amounts, + 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_).claimERC1155Batch(nftCollection, msg.sender, filter, tokenIds, amounts); + uint256 received = IFilterModel(filter).ERC1155In(tokenIds, to); + require(received >= minMintAmount, "MINT_AMOUNT_NOT_ENOUGH"); + } + + function erc1155TargetOut( + address filter, + uint256[] memory indexes, + uint256[] memory amounts, + address to, + uint256 maxBurnAmount + ) external { + uint256 paid = IFilterModel(filter).ERC1155TargetOut(indexes, amounts, to); + require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); + } + + function erc1155RandomOut( + address filter, + uint256 amount, + address to, + uint256 maxBurnAmount + ) external { + uint256 paid = IFilterModel(filter).ERC1155RandomOut(amount, to); + require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); + } + // ================== Create NFTPool =================== - - function createNewNFTPool01( - uint256 initSupply, - string memory name, - string memory symbol, - uint256 fee, + function createNewNFTPoolV1( address nftCollection, + uint256 filterKey, //1 => FilterERC721V1, 2 => FilterERC1155V1 + string[] memory tokenInfo, + uint256[] memory numParams,//0 - initSupply, 1 - fee bool[] memory switches, - uint256[] memory tokenRanges, - uint256[] memory nftAmounts, + uint256[] memory filterNumParams, //0 - startId, 1 - endId, 2 - maxAmount, 3 - minAmount uint256[] memory priceRules, uint256[] memory spreadIds ) external returns(address newFilterAdmin) { newFilterAdmin = ICloneFactory(_CLONE_FACTORY_).clone(_FILTER_ADMIN_TEMPLATE_); - address filter01 = createFilterERC721V1( + address filterV1 = createFilterV1( + filterKey, newFilterAdmin, nftCollection, switches, - tokenRanges, - nftAmounts, + filterNumParams, priceRules, spreadIds ); address[] memory filters = new address[](1); - filters[0] = filter01; + filters[0] = filterV1; IFilterAdmin(newFilterAdmin).init( msg.sender, - initSupply, - name, - symbol, - fee, + numParams[0], + tokenInfo[0], + tokenInfo[1], + numParams[1], _CONTROLLER_MODEL_, _DEFAULT_MAINTAINER_, filters ); } - // ================== Create Filter =================== - function createFilterERC721V1( + function createFilterV1( + uint256 key, address filterAdmin, address nftCollection, bool[] memory switches, - uint256[] memory tokenRanges, - uint256[] memory nftAmounts, + uint256[] memory numParams, //0 - startId, 1 - endId, 2 - maxAmount, 3 - minAmount uint256[] memory priceRules, uint256[] memory spreadIds - ) public returns(address newFilterERC721V1) { - //key = 1 => FilterERC721V1 - newFilterERC721V1 = ICloneFactory(_CLONE_FACTORY_).clone(_FILTER_TEMPLATES_[1]); - IFilterERC721V1(newFilterERC721V1).init( + ) public returns(address newFilterV1) { + newFilterV1 = ICloneFactory(_CLONE_FACTORY_).clone(_FILTER_TEMPLATES_[key]); + IFilterV1(newFilterV1).init( filterAdmin, nftCollection, switches, - tokenRanges, - nftAmounts, + numParams, priceRules, spreadIds );