From 3ef45bc79937f72b07af6895ea9e5efab21737c4 Mon Sep 17 00:00:00 2001 From: owen05 Date: Mon, 6 Sep 2021 19:15:17 +0800 Subject: [PATCH 01/42] init nftPool --- contracts/NFTPool/impl/FilterAdmin.sol | 146 ++++++++++++++++++ contracts/NFTPool/impl/FilterERC1155Model.sol | 130 ++++++++++++++++ contracts/NFTPool/impl/FilterERC721Model.sol | 129 ++++++++++++++++ .../NFTPool/intf/IFilterERC1155Model.sol | 20 +++ contracts/NFTPool/intf/IFilterERC721Model.sol | 20 +++ contracts/SmartRoute/intf/IDODOV2Proxy01.sol | 12 ++ .../InitializableInternalMintableERC20.sol | 98 ++++++++++++ 7 files changed, 555 insertions(+) create mode 100644 contracts/NFTPool/impl/FilterAdmin.sol create mode 100644 contracts/NFTPool/impl/FilterERC1155Model.sol create mode 100644 contracts/NFTPool/impl/FilterERC721Model.sol create mode 100644 contracts/NFTPool/intf/IFilterERC1155Model.sol create mode 100644 contracts/NFTPool/intf/IFilterERC721Model.sol create mode 100644 contracts/external/ERC20/InitializableInternalMintableERC20.sol diff --git a/contracts/NFTPool/impl/FilterAdmin.sol b/contracts/NFTPool/impl/FilterAdmin.sol new file mode 100644 index 0000000..8a3250e --- /dev/null +++ b/contracts/NFTPool/impl/FilterAdmin.sol @@ -0,0 +1,146 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {InitializableInternalMintableERC20} from "../../external/ERC20/InitializableInternalMintableERC20.sol"; +import {SafeMath} from "../../lib/SafeMath.sol"; +import {IFilterERC721Model} from "../intf/IFilterERC721Model.sol"; +import {IFilterERC1155Model} from "../intf/IFilterERC1155Model.sol"; +import {IERC721} from "../../intf/IERC721.sol"; +import {IERC721Receiver} from "../../intf/IERC721Receiver.sol"; +import {IERC1155} from "../../intf/IERC1155.sol"; +import {IERC1155Receiver} from "../../intf/IERC1155Receiver.sol"; + +contract FilterAdmin is InitializableInternalMintableERC20, IERC721Receiver, IERC1155Receiver { + using SafeMath for uint256; + + // ============ Storage ============ + address public _ERC721_FILTER_MODEL_; + address public _ERC1155_FILTER_MODEL_; + + function init( + address _owner, + uint256 _initSupply, + string memory _name, + string memory _symbol, + uint8 _decimals, + address _erc721FilterModel, + address _erc1155FilterModel + ) external { + super.init(_owner, _initSupply, _name, _symbol, _decimals); + _ERC721_FILTER_MODEL_ = _erc721FilterModel; + _ERC1155_FILTER_MODEL_ = _erc1155FilterModel; + } + + // ============ Event ============ + event RemoveNftToken(address nftContract, uint256 tokenId, uint256 amount); + event AddNftToken(address nftContract, uint256 tokenId, uint256 amount); + + + function depositERC721(address nftContract, uint256[] memory tokenIds) public { + require(nftContract != address(0), "ZERO_ADDRESS"); + for(uint256 i = 0; i < tokenIds.length; i++) { + uint256 price = IFilterERC721Model(_ERC721_FILTER_MODEL_).saveNFTPrice(nftContract, tokenIds[i]); + _mint(msg.sender, price); + + IERC721(nftContract).safeTransferFrom(msg.sender, address(this), tokenIds[i]); + emit AddNftToken(nftContract, tokenIds[i], 1); + } + } + + function depoistERC1155(address nftContract, uint256[] memory tokenIds, uint256[] memory amounts) public { + require(nftContract != address(0), "ZERO_ADDRESS"); + require(tokenIds.length == amounts.length, "PARAMS_NOT_MATCH"); + for(uint256 i = 0; i < tokenIds.length; i++) { + uint256 price = IFilterERC1155Model(_ERC1155_FILTER_MODEL_).saveNFTPrice(nftContract, tokenIds[i], amounts[i]); + _mint(msg.sender, price); + + emit AddNftToken(nftContract, tokenIds[i], amounts[i]); + } + + IERC1155(nftContract).safeBatchTransferFrom(msg.sender, address(this), tokenIds, amounts, ""); + } + + function doLotteryERC721() external { + uint256 lotteryPrice = IFilterERC721Model(_ERC721_FILTER_MODEL_).buyLotteryNFTPrice(); + _burn(msg.sender, lotteryPrice); + (address nftContract, uint256 tokenId) = IFilterERC721Model(_ERC721_FILTER_MODEL_).lottery(); + + IERC721(nftContract).safeTransferFrom(address(this), msg.sender, tokenId); + emit RemoveNftToken(nftContract, tokenId, 1); + } + + function doLotteryERC1155() external { + uint256 lotteryPrice = IFilterERC1155Model(_ERC1155_FILTER_MODEL_).buyLotteryNFTPrice(); + _burn(msg.sender, lotteryPrice); + (address nftContract, uint256 tokenId) = IFilterERC1155Model(_ERC721_FILTER_MODEL_).lottery(); + + //TODO: amount + IERC1155(nftContract).safeTransferFrom(address(this), msg.sender, tokenId, 1, ""); + emit RemoveNftToken(nftContract, tokenId, 1); + } + + function buySpecERC721(address nftContract, uint256 tokenId) external { + uint256 price = IFilterERC721Model(_ERC721_FILTER_MODEL_).buySpecNFTPrice(nftContract, tokenId); + _burn(msg.sender, price); + + IERC721(nftContract).safeTransferFrom(address(this), msg.sender, tokenId); + emit RemoveNftToken(nftContract, tokenId, 1); + } + + function buySpecERC1155(address nftContract, uint256 tokenId, uint256 amount) external { + uint256 price = IFilterERC1155Model(_ERC1155_FILTER_MODEL_).buySpecNFTPrice(nftContract, tokenId, amount); + _burn(msg.sender, price); + IERC1155(nftContract).safeTransferFrom(address(this), msg.sender, tokenId, amount, ""); + + emit RemoveNftToken(nftContract, tokenId, amount); + } + + + function supportsInterface(bytes4 interfaceId) public override view returns (bool) { + return interfaceId == type(IERC1155Receiver).interfaceId + || interfaceId == type(IERC721Receiver).interfaceId; + } + + // ============ Callback ============ + function onERC721Received( + address, + address, + uint256 tokenId, + bytes calldata + ) external override returns (bytes4) { + emit AddNftToken(msg.sender, tokenId, 1); + return IERC721Receiver.onERC721Received.selector; + } + + function onERC1155Received( + address, + address, + uint256 id, + uint256 value, + bytes calldata + ) external override returns (bytes4){ + emit AddNftToken(msg.sender, id, value); + return IERC1155Receiver.onERC1155Received.selector; + } + + function onERC1155BatchReceived( + address, + address, + uint256[] calldata ids, + uint256[] calldata values, + bytes calldata + ) external override returns (bytes4){ + require(ids.length == values.length, "PARAMS_NOT_MATCH"); + for(uint256 i = 0; i < ids.length; i++) { + emit AddNftToken(msg.sender, ids[i], values[i]); + } + return IERC1155Receiver.onERC1155BatchReceived.selector; + } +} diff --git a/contracts/NFTPool/impl/FilterERC1155Model.sol b/contracts/NFTPool/impl/FilterERC1155Model.sol new file mode 100644 index 0000000..41f2224 --- /dev/null +++ b/contracts/NFTPool/impl/FilterERC1155Model.sol @@ -0,0 +1,130 @@ +/* + + 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 {IFilterERC1155Model} from "../intf/IFilterERC1155Model.sol"; + +contract FilterERC1155Model is InitializableOwnable, IFilterERC1155Model { + using SafeMath for uint256; + + //=================== Storage ===================== + // nftCollection -> nftId -> price(frag) + mapping(address => mapping(uint256 => uint256)) public _PRICES_; + // nftCollection -> nftId -> amount + mapping(address => mapping(uint256 => uint256)) public _ERC1155_AMOUNT_; + // nftCollection -> nftId -> specPrice(frag) + mapping(address => mapping(uint256 => uint256)) public _SPEC_PRICES_; + + uint256 public _SPEC_FACTOR_ = 200; + + // nftColletcion -> nftIds + mapping(address => uint256[]) public _NFT_COLLECTION_IDS_; + + address[] public _NFT_COLLECTIONS_; + + uint256 public _LOTTERY_THRESHOLD_; + + uint256 public _TOTAL_NFT_AMOUNT_; + + uint256 public _CURRENT_NFT_AMOUNT_; + + function init( + address owner + ) external { + //TODO: + initOwner(owner); + } + + //================== View ====================== + function isFilterERC1155Pass(address nftCollectionAddress, uint256 nftId, uint256 amount) override external view returns (bool) { + if(_PRICES_[nftCollectionAddress][nftId] == 0) + return false; + else { + if(_ERC1155_AMOUNT_[nftCollectionAddress][nftId] >= amount) + return true; + else + return false; + } + } + + + //TODO: nftInCap + //TODO: nftOutCap + + function saveNFTPrice(address nftCollectionAddress, uint256 nftId, uint256 amount) override external view returns(uint256) { + return _PRICES_[nftCollectionAddress][nftId].mul(amount); + } + + function buySpecNFTPrice(address nftCollectionAddress, uint256 nftId, uint256 amount) override external view returns(uint256) { + require(_ERC1155_AMOUNT_[nftCollectionAddress][nftId] >= amount, "BUY_OVERFLOW"); + return _SPEC_PRICES_[nftCollectionAddress][nftId].mul(amount); + } + + //TODO: amount = 1 + function buyLotteryNFTPrice() override external view returns(uint256) { + return _LOTTERY_THRESHOLD_; + } + + function lottery() override external view returns(address nftCollection, uint256 nftId) { + //random + } + + + //================== Owner ===================== + function setNFTFilter( + address[] memory nftCollections, + uint256[] memory nftIds, + uint256[] memory amounts, + uint256[] memory prices, + uint256[] memory specPrices + ) external onlyOwner { + require(nftCollections.length == nftIds.length, "PARAMS_INVALID"); + require(nftCollections.length == amounts.length, "PARAMS_INVALID"); + require(nftCollections.length == prices.length, "PARAMS_INVALID"); + require(nftCollections.length == specPrices.length, "PARAMS_INVALID"); + + for(uint256 i = 0; i < nftCollections.length; i++){ + _PRICES_[nftCollections[i]][nftIds[i]] = prices[i]; + _PRICES_[nftCollections[i]][nftIds[i]] = amounts[i]; + + if(specPrices[i] == 0){ + _SPEC_PRICES_[nftCollections[i]][nftIds[i]] = prices[i].mul(_SPEC_FACTOR_).div(100); + }else { + _SPEC_PRICES_[nftCollections[i]][nftIds[i]] = specPrices[i]; + } + + if(_NFT_COLLECTION_IDS_[nftCollections[i]].length == 0) { + _NFT_COLLECTIONS_.push(nftCollections[i]); + _NFT_COLLECTION_IDS_[nftCollections[i]] = [nftIds[i]]; + require(++_CURRENT_NFT_AMOUNT_ <= _TOTAL_NFT_AMOUNT_, "OVERFLOW_NFT_AMOUNT"); + } else { + uint256 j = 0; + for(; j < _NFT_COLLECTION_IDS_[nftCollections[i]].length; i++) { + if(_NFT_COLLECTION_IDS_[nftCollections[i]][j] == nftIds[i]) { + break; + } + } + if(j == _NFT_COLLECTION_IDS_[nftCollections[i]].length) { + _NFT_COLLECTION_IDS_[nftCollections[i]].push(nftIds[i]); + require(++_CURRENT_NFT_AMOUNT_ <= _TOTAL_NFT_AMOUNT_, "OVERFLOW_NFT_AMOUNT"); + } + } + } + } + + function setLotteryThreshold(uint256 newLotteryThreshold) external onlyOwner { + _LOTTERY_THRESHOLD_ = newLotteryThreshold; + } + + function setSpecFactor(uint256 newSpecFactor) external onlyOwner { + _SPEC_FACTOR_ = newSpecFactor; + } +} diff --git a/contracts/NFTPool/impl/FilterERC721Model.sol b/contracts/NFTPool/impl/FilterERC721Model.sol new file mode 100644 index 0000000..9a28b1a --- /dev/null +++ b/contracts/NFTPool/impl/FilterERC721Model.sol @@ -0,0 +1,129 @@ +/* + + 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 {IFilterERC721Model} from "../intf/IFilterERC721Model.sol"; + +contract FilterERC721Model is InitializableOwnable, IFilterERC721Model { + using SafeMath for uint256; + + //=================== Storage =================== + // nftCollection -> nftId -> price(frag) + mapping(address => mapping(uint256 => uint256)) public _PRICES_; + + // nftCollection -> nftId -> specPrice(frag) + mapping(address => mapping(uint256 => uint256)) public _SPEC_PRICES_; + + uint256 public _SPEC_FACTOR_ = 200; + + // nftColletcion -> nftIds + mapping(address => uint256[]) public _NFT_COLLECTION_IDS_; + + address[] public _NFT_COLLECTIONS_; + + uint256 public _LOTTERY_THRESHOLD_; + + uint256 public _TOTAL_NFT_AMOUNT_; + + uint256 public _CURRENT_NFT_AMOUNT_; + + function init( + address owner + ) external { + //TODO: + initOwner(owner); + } + + //==================== View ================== + function isFilterERC721Pass(address nftCollectionAddress, uint256 nftId) override external view returns (bool) { + if(_PRICES_[nftCollectionAddress][nftId] == 0) + return false; + else + return true; + } + + function saveNFTPrice(address nftCollectionAddress, uint256 nftId) override external view returns(uint256) { + return _PRICES_[nftCollectionAddress][nftId]; + } + + function buySpecNFTPrice(address nftCollectionAddress, uint256 nftId) override external view returns(uint256) { + return _SPEC_PRICES_[nftCollectionAddress][nftId]; + } + + function buyLotteryNFTPrice() override external view returns(uint256) { + return _LOTTERY_THRESHOLD_; + } + + function lottery() override external view returns(address nftCollection, uint256 nftId) { + //random + } + + + + //TODO: nftInCap + //TODO: nftOutCap + + + //============= Owner =============== + function setNFTFilter( + address[] memory nftCollections, + uint256[] memory nftIds, + uint256[] memory prices, + uint256[] memory specPrices + ) external onlyOwner { + require(nftCollections.length == nftIds.length, "PARAMS_INVALID"); + require(nftCollections.length == prices.length, "PARAMS_INVALID"); + require(nftCollections.length == specPrices.length, "PARAMS_INVALID"); + + for(uint256 i = 0; i < nftCollections.length; i++){ + _PRICES_[nftCollections[i]][nftIds[i]] = prices[i]; + + if(specPrices[i] == 0){ + _SPEC_PRICES_[nftCollections[i]][nftIds[i]] = prices[i].mul(_SPEC_FACTOR_).div(100); + }else { + _SPEC_PRICES_[nftCollections[i]][nftIds[i]] = specPrices[i]; + } + + if(_NFT_COLLECTION_IDS_[nftCollections[i]].length == 0) { + _NFT_COLLECTIONS_.push(nftCollections[i]); + _NFT_COLLECTION_IDS_[nftCollections[i]] = [nftIds[i]]; + + require(++_CURRENT_NFT_AMOUNT_ <= _TOTAL_NFT_AMOUNT_, "OVERFLOW_NFT_AMOUNT"); + + } else { + uint256 j = 0; + for(; j < _NFT_COLLECTION_IDS_[nftCollections[i]].length; i++) { + if(_NFT_COLLECTION_IDS_[nftCollections[i]][j] == nftIds[i]) { + break; + } + } + if(j == _NFT_COLLECTION_IDS_[nftCollections[i]].length) { + _NFT_COLLECTION_IDS_[nftCollections[i]].push(nftIds[i]); + require(++_CURRENT_NFT_AMOUNT_ <= _TOTAL_NFT_AMOUNT_, "OVERFLOW_NFT_AMOUNT"); + } + } + } + } + + + function setLotteryThreshold(uint256 newLotteryThreshold) external onlyOwner { + _LOTTERY_THRESHOLD_ = newLotteryThreshold; + } + + function setSpecFactor(uint256 newSpecFactor) external onlyOwner { + _SPEC_FACTOR_ = newSpecFactor; + } + + function setTotalNFTAmount(uint256 newTotalNFTAmount) external onlyOwner { + _TOTAL_NFT_AMOUNT_ = newTotalNFTAmount; + } + +} diff --git a/contracts/NFTPool/intf/IFilterERC1155Model.sol b/contracts/NFTPool/intf/IFilterERC1155Model.sol new file mode 100644 index 0000000..f4fae33 --- /dev/null +++ b/contracts/NFTPool/intf/IFilterERC1155Model.sol @@ -0,0 +1,20 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +interface IFilterERC1155Model { + function isFilterERC1155Pass(address nftCollectionAddress, uint256 nftId, uint256 amount) external view returns (bool); + + function saveNFTPrice(address nftCollectionAddress, uint256 nftId, uint256 amount) external view returns(uint256); + + function buySpecNFTPrice(address nftCollectionAddress, uint256 nftId, uint256 amount) external view returns(uint256); + + function buyLotteryNFTPrice() external view returns(uint256); + + function lottery() external view returns(address nftCollection, uint256 nftId); +} \ No newline at end of file diff --git a/contracts/NFTPool/intf/IFilterERC721Model.sol b/contracts/NFTPool/intf/IFilterERC721Model.sol new file mode 100644 index 0000000..2e2cd42 --- /dev/null +++ b/contracts/NFTPool/intf/IFilterERC721Model.sol @@ -0,0 +1,20 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +interface IFilterERC721Model { + function isFilterERC721Pass(address nftCollectionAddress, uint256 nftId) external view returns (bool); + + function saveNFTPrice(address nftCollectionAddress, uint256 nftId) external view returns(uint256); + + function buySpecNFTPrice(address nftCollectionAddress, uint256 nftId) external view returns(uint256); + + function buyLotteryNFTPrice() external view returns(uint256); + + function lottery() external view returns(address nftCollection, uint256 nftId); +} \ No newline at end of file diff --git a/contracts/SmartRoute/intf/IDODOV2Proxy01.sol b/contracts/SmartRoute/intf/IDODOV2Proxy01.sol index f9c1ff0..5821138 100644 --- a/contracts/SmartRoute/intf/IDODOV2Proxy01.sol +++ b/contracts/SmartRoute/intf/IDODOV2Proxy01.sol @@ -132,4 +132,16 @@ interface IDODOV2Proxy01 { uint256 deadLine ) external payable returns (uint256 returnAmount); + // function mixSwap( + // address fromToken, + // address toToken, + // uint256 fromTokenAmount, + // uint256 minReturnAmount, + // address[] memory mixAdapters, + // address[] memory mixPairs, + // address[] memory assetTo, + // uint256 directions, + // bool isIncentive, + // uint256 deadLine + // ) external payable returns (uint256 returnAmount); } diff --git a/contracts/external/ERC20/InitializableInternalMintableERC20.sol b/contracts/external/ERC20/InitializableInternalMintableERC20.sol new file mode 100644 index 0000000..ec3eb49 --- /dev/null +++ b/contracts/external/ERC20/InitializableInternalMintableERC20.sol @@ -0,0 +1,98 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +import {SafeMath} from "../../lib/SafeMath.sol"; +import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; + +contract InitializableInternalMintableERC20 is InitializableOwnable { + using SafeMath for uint256; + + string public name; + uint8 public decimals; + string public symbol; + uint256 public totalSupply; + + mapping(address => uint256) internal balances; + mapping(address => mapping(address => uint256)) internal allowed; + + event Transfer(address indexed from, address indexed to, uint256 amount); + event Approval(address indexed owner, address indexed spender, uint256 amount); + event Mint(address indexed user, uint256 value); + event Burn(address indexed user, uint256 value); + + function init( + address _creator, + uint256 _initSupply, + string memory _name, + string memory _symbol, + uint8 _decimals + ) public { + initOwner(_creator); + name = _name; + symbol = _symbol; + decimals = _decimals; + totalSupply = _initSupply; + balances[_creator] = _initSupply; + emit Transfer(address(0), _creator, _initSupply); + } + + function transfer(address to, uint256 amount) public virtual returns (bool) { + require(to != address(0), "TO_ADDRESS_IS_EMPTY"); + require(amount <= balances[msg.sender], "BALANCE_NOT_ENOUGH"); + + balances[msg.sender] = balances[msg.sender].sub(amount); + balances[to] = balances[to].add(amount); + emit Transfer(msg.sender, to, amount); + return true; + } + + function balanceOf(address owner) public view returns (uint256 balance) { + return balances[owner]; + } + + function transferFrom( + address from, + address to, + uint256 amount + ) public virtual returns (bool) { + require(to != address(0), "TO_ADDRESS_IS_EMPTY"); + require(amount <= balances[from], "BALANCE_NOT_ENOUGH"); + require(amount <= allowed[from][msg.sender], "ALLOWANCE_NOT_ENOUGH"); + + balances[from] = balances[from].sub(amount); + balances[to] = balances[to].add(amount); + allowed[from][msg.sender] = allowed[from][msg.sender].sub(amount); + emit Transfer(from, to, amount); + return true; + } + + function approve(address spender, uint256 amount) public virtual returns (bool) { + allowed[msg.sender][spender] = amount; + emit Approval(msg.sender, spender, amount); + return true; + } + + function allowance(address owner, address spender) public view returns (uint256) { + return allowed[owner][spender]; + } + + 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) internal { + balances[user] = balances[user].sub(value); + totalSupply = totalSupply.sub(value); + emit Burn(user, value); + emit Transfer(user, address(0), value); + } +} From 6a659a4b8276642db2d7bfa3a937d3b08d6843bc Mon Sep 17 00:00:00 2001 From: owen05 Date: Tue, 7 Sep 2021 15:56:37 +0800 Subject: [PATCH 02/42] dev --- .../Registries/DODONFTPoolRegistry.sol | 0 contracts/NFTPool/impl/FilterERC721Model.sol | 183 ++++++++++++++---- .../SmartRoute/proxies/DODONFTPoolProxy.sol | 0 .../InitializableInternalMintableERC20.sol | 4 +- 4 files changed, 149 insertions(+), 38 deletions(-) create mode 100644 contracts/Factory/Registries/DODONFTPoolRegistry.sol create mode 100644 contracts/SmartRoute/proxies/DODONFTPoolProxy.sol diff --git a/contracts/Factory/Registries/DODONFTPoolRegistry.sol b/contracts/Factory/Registries/DODONFTPoolRegistry.sol new file mode 100644 index 0000000..e69de29 diff --git a/contracts/NFTPool/impl/FilterERC721Model.sol b/contracts/NFTPool/impl/FilterERC721Model.sol index 9a28b1a..dabdae4 100644 --- a/contracts/NFTPool/impl/FilterERC721Model.sol +++ b/contracts/NFTPool/impl/FilterERC721Model.sol @@ -18,36 +18,52 @@ contract FilterERC721Model is InitializableOwnable, IFilterERC721Model { //=================== Storage =================== // nftCollection -> nftId -> price(frag) mapping(address => mapping(uint256 => uint256)) public _PRICES_; - // nftCollection -> nftId -> specPrice(frag) mapping(address => mapping(uint256 => uint256)) public _SPEC_PRICES_; - uint256 public _SPEC_FACTOR_ = 200; - // nftColletcion -> nftIds mapping(address => uint256[]) public _NFT_COLLECTION_IDS_; - address[] public _NFT_COLLECTIONS_; - + uint256 public _LOTTERY_THRESHOLD_; - uint256 public _TOTAL_NFT_AMOUNT_; - uint256 public _CURRENT_NFT_AMOUNT_; + + uint256 public _TIMELOCK_DURATION_; + + struct LockFilterInfo { + address[] nftCollections; + uint256[] nftIds; + uint256[] prices; + uint256[] specPrices; + uint256 releaseTime; + } + mapping(bytes32 => LockFilterInfo) public _TIME_LOCKS_; + + uint256 public _PENDING_LOTTERY_THRESHOLD_; + uint256 public _PENDING_TOTAL_NFT_AMOUNT_; + uint256 public _PENDING_SPEC_FACTOR_; + uint256 public _GLOBAL_TIME_LOCK_; + + function init( - address owner + address owner, + uint256 specFactor, + uint256 lotteryThreshold, + uint256 totalNftAmount, + uint256 timeLockDuration ) external { - //TODO: initOwner(owner); + _SPEC_FACTOR_ = specFactor; + _LOTTERY_THRESHOLD_ = lotteryThreshold; + _TOTAL_NFT_AMOUNT_ = totalNftAmount; + _TIMELOCK_DURATION_ = timeLockDuration; } //==================== View ================== function isFilterERC721Pass(address nftCollectionAddress, uint256 nftId) override external view returns (bool) { - if(_PRICES_[nftCollectionAddress][nftId] == 0) - return false; - else - return true; + return _isInclude(nftCollectionAddress, nftId); } function saveNFTPrice(address nftCollectionAddress, uint256 nftId) override external view returns(uint256) { @@ -73,7 +89,8 @@ contract FilterERC721Model is InitializableOwnable, IFilterERC721Model { //============= Owner =============== - function setNFTFilter( + + function addNFTFilter( address[] memory nftCollections, uint256[] memory nftIds, uint256[] memory prices, @@ -84,6 +101,8 @@ contract FilterERC721Model is InitializableOwnable, IFilterERC721Model { require(nftCollections.length == specPrices.length, "PARAMS_INVALID"); for(uint256 i = 0; i < nftCollections.length; i++){ + if(_isInclude(nftCollections[i], nftIds[i])) continue; + _PRICES_[nftCollections[i]][nftIds[i]] = prices[i]; if(specPrices[i] == 0){ @@ -94,36 +113,130 @@ contract FilterERC721Model is InitializableOwnable, IFilterERC721Model { if(_NFT_COLLECTION_IDS_[nftCollections[i]].length == 0) { _NFT_COLLECTIONS_.push(nftCollections[i]); - _NFT_COLLECTION_IDS_[nftCollections[i]] = [nftIds[i]]; + } - require(++_CURRENT_NFT_AMOUNT_ <= _TOTAL_NFT_AMOUNT_, "OVERFLOW_NFT_AMOUNT"); - - } else { - uint256 j = 0; - for(; j < _NFT_COLLECTION_IDS_[nftCollections[i]].length; i++) { - if(_NFT_COLLECTION_IDS_[nftCollections[i]][j] == nftIds[i]) { - break; - } - } - if(j == _NFT_COLLECTION_IDS_[nftCollections[i]].length) { - _NFT_COLLECTION_IDS_[nftCollections[i]].push(nftIds[i]); - require(++_CURRENT_NFT_AMOUNT_ <= _TOTAL_NFT_AMOUNT_, "OVERFLOW_NFT_AMOUNT"); - } - } + _NFT_COLLECTION_IDS_[nftCollections[i]].push(nftIds[i]); + require(++_CURRENT_NFT_AMOUNT_ <= _TOTAL_NFT_AMOUNT_, "OVERFLOW_NFT_AMOUNT"); } } - function setLotteryThreshold(uint256 newLotteryThreshold) external onlyOwner { - _LOTTERY_THRESHOLD_ = newLotteryThreshold; + function scheduleUpdateNftFilter( + address[] memory nftCollections, + uint256[] memory nftIds, + uint256[] memory prices, + uint256[] memory specPrices + ) external onlyOwner { + require(nftCollections.length == nftIds.length, "PARAMS_INVALID"); + require(nftCollections.length == prices.length, "PARAMS_INVALID"); + require(nftCollections.length == specPrices.length, "PARAMS_INVALID"); + + uint256 releaseTime = block.timestamp.add(_TIMELOCK_DURATION_); + bytes32 id = keccak256(abi.encode(nftCollections, nftIds, prices, specPrices, releaseTime)); + LockFilterInfo memory lockFilterInfo = LockFilterInfo({ + nftCollections: nftCollections, + nftIds: nftIds, + prices: prices, + specPrices: specPrices, + releaseTime: releaseTime + }); + require(_TIME_LOCKS_[id].releaseTime == 0, "ALREADY_ADDED"); + for(uint256 i = 0; i< nftCollections.length; i++) { + require(_isInclude(nftCollections[i],nftIds[i])); + } + _TIME_LOCKS_[id] = lockFilterInfo; } - function setSpecFactor(uint256 newSpecFactor) external onlyOwner { - _SPEC_FACTOR_ = newSpecFactor; + + function executeUpdateNftFilter( + bytes32 id + ) external onlyOwner { + LockFilterInfo memory lockFilterInfo = _TIME_LOCKS_[id]; + uint256 releaseTime = lockFilterInfo.releaseTime; + require(releaseTime != 0 && releaseTime !=1 && block.timestamp > releaseTime, "TIMELOCKED"); + + address[] memory nftCollections = lockFilterInfo.nftCollections; + uint256[] memory nftIds = lockFilterInfo.nftIds; + uint256[] memory prices = lockFilterInfo.prices; + uint256[] memory specPrices = lockFilterInfo.specPrices; + + + for(uint256 i = 0; i < nftCollections.length; i++){ + _PRICES_[nftCollections[i]][nftIds[i]] = prices[i]; + + if(specPrices[i] == 0){ + _SPEC_PRICES_[nftCollections[i]][nftIds[i]] = prices[i].mul(_SPEC_FACTOR_).div(100); + }else { + _SPEC_PRICES_[nftCollections[i]][nftIds[i]] = specPrices[i]; + } + } + + lockFilterInfo.releaseTime = 1; + _TIME_LOCKS_[id] = lockFilterInfo; } - function setTotalNFTAmount(uint256 newTotalNFTAmount) external onlyOwner { - _TOTAL_NFT_AMOUNT_ = newTotalNFTAmount; + + //TODO:价格修改为0,是否是同样的效果 + function scheduleRemoveNFTFilter( + address[] memory nftCollections, + uint256[] memory nftIds + ) external onlyOwner { + require(nftCollections.length == nftIds.length, "PARAMS_INVALID"); } + function executeRemoveNFTFilter( + bytes32 id + ) external onlyOwner { + + } + + + function scheduleUpdateGlobalState( + uint256 newLotteryThreshold, + uint256 newSpecFactor, + uint256 newTotalNFTAmount + ) external onlyOwner { + require(newTotalNFTAmount >= _CURRENT_NFT_AMOUNT_, "NFT_ALREADY_EXCEED"); + require(newSpecFactor > 100, "SPEC_FACTOR_TOO_LOW"); + + _PENDING_LOTTERY_THRESHOLD_ = newLotteryThreshold; + _PENDING_TOTAL_NFT_AMOUNT_ = newSpecFactor; + _PENDING_SPEC_FACTOR_ = newTotalNFTAmount; + + _GLOBAL_TIME_LOCK_ = block.timestamp.add(_TIMELOCK_DURATION_); + } + + function executeUpdateGlobalState() external onlyOwner { + require(block.timestamp > _GLOBAL_TIME_LOCK_ && _GLOBAL_TIME_LOCK_ !=0, "TIMELOCKED"); + + require(_PENDING_TOTAL_NFT_AMOUNT_ >= _CURRENT_NFT_AMOUNT_, "NFT_ALREADY_EXCEED"); + require(_PENDING_SPEC_FACTOR_ > 100, "SPEC_FACTOR_TOO_LOW"); + + _LOTTERY_THRESHOLD_ = _PENDING_LOTTERY_THRESHOLD_; + _SPEC_FACTOR_ = _PENDING_SPEC_FACTOR_; + _TOTAL_NFT_AMOUNT_ = _PENDING_TOTAL_NFT_AMOUNT_; + + lockGlobalState(); + } + + function lockGlobalState() public onlyOwner { + _PENDING_LOTTERY_THRESHOLD_ = 0; + _PENDING_TOTAL_NFT_AMOUNT_ = 0; + _PENDING_SPEC_FACTOR_ = 0; + _GLOBAL_TIME_LOCK_ = 0; + } + + + //==================== internal =================== + function _isInclude(address nftCollection, uint256 nftId) internal view returns (bool) { + uint256[] memory ids = _NFT_COLLECTION_IDS_[nftCollection]; + uint256 i = 0; + for(;i < ids.length; i++) { + if(nftId == i) break; + } + if(i == ids.length) + return false; + else + return true; + } } diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol new file mode 100644 index 0000000..e69de29 diff --git a/contracts/external/ERC20/InitializableInternalMintableERC20.sol b/contracts/external/ERC20/InitializableInternalMintableERC20.sol index ec3eb49..d22349d 100644 --- a/contracts/external/ERC20/InitializableInternalMintableERC20.sol +++ b/contracts/external/ERC20/InitializableInternalMintableERC20.sol @@ -8,9 +8,8 @@ pragma solidity 0.6.9; import {SafeMath} from "../../lib/SafeMath.sol"; -import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; -contract InitializableInternalMintableERC20 is InitializableOwnable { +contract InitializableInternalMintableERC20 { using SafeMath for uint256; string public name; @@ -33,7 +32,6 @@ contract InitializableInternalMintableERC20 is InitializableOwnable { string memory _symbol, uint8 _decimals ) public { - initOwner(_creator); name = _name; symbol = _symbol; decimals = _decimals; From 7c34e0ef5dfd08bb0abfc7b2aa463f7416fe885e Mon Sep 17 00:00:00 2001 From: owen05 Date: Wed, 8 Sep 2021 23:37:40 +0800 Subject: [PATCH 03/42] dev --- .../Registries/DODONFTPoolRegistry.sol | 0 contracts/NFTPool/impl/FilterAdmin.sol | 315 ++++++++++++------ contracts/NFTPool/impl/FilterERC1155Model.sol | 130 -------- contracts/NFTPool/impl/FilterERC721Model.sol | 242 -------------- contracts/NFTPool/impl/FilterModel01.sol | 260 +++++++++++++++ contracts/NFTPool/impl/NFTPoolFeeModel.sol | 85 +++++ contracts/NFTPool/intf/IFeeModel.sol | 16 + contracts/NFTPool/intf/IFilterAdmin.sol | 12 + .../NFTPool/intf/IFilterERC1155Model.sol | 20 -- contracts/NFTPool/intf/IFilterERC721Model.sol | 20 -- contracts/NFTPool/intf/IFilterModel.sol | 42 +++ .../SmartRoute/proxies/DODONFTPoolProxy.sol | 81 +++++ .../InitializableInternalMintableERC20.sol | 4 +- 13 files changed, 707 insertions(+), 520 deletions(-) delete mode 100644 contracts/Factory/Registries/DODONFTPoolRegistry.sol delete mode 100644 contracts/NFTPool/impl/FilterERC1155Model.sol delete mode 100644 contracts/NFTPool/impl/FilterERC721Model.sol create mode 100644 contracts/NFTPool/impl/FilterModel01.sol create mode 100644 contracts/NFTPool/impl/NFTPoolFeeModel.sol create mode 100644 contracts/NFTPool/intf/IFeeModel.sol create mode 100644 contracts/NFTPool/intf/IFilterAdmin.sol delete mode 100644 contracts/NFTPool/intf/IFilterERC1155Model.sol delete mode 100644 contracts/NFTPool/intf/IFilterERC721Model.sol create mode 100644 contracts/NFTPool/intf/IFilterModel.sol diff --git a/contracts/Factory/Registries/DODONFTPoolRegistry.sol b/contracts/Factory/Registries/DODONFTPoolRegistry.sol deleted file mode 100644 index e69de29..0000000 diff --git a/contracts/NFTPool/impl/FilterAdmin.sol b/contracts/NFTPool/impl/FilterAdmin.sol index 8a3250e..a76fd91 100644 --- a/contracts/NFTPool/impl/FilterAdmin.sol +++ b/contracts/NFTPool/impl/FilterAdmin.sol @@ -10,137 +10,238 @@ pragma experimental ABIEncoderV2; import {InitializableInternalMintableERC20} from "../../external/ERC20/InitializableInternalMintableERC20.sol"; import {SafeMath} from "../../lib/SafeMath.sol"; -import {IFilterERC721Model} from "../intf/IFilterERC721Model.sol"; -import {IFilterERC1155Model} from "../intf/IFilterERC1155Model.sol"; -import {IERC721} from "../../intf/IERC721.sol"; -import {IERC721Receiver} from "../../intf/IERC721Receiver.sol"; -import {IERC1155} from "../../intf/IERC1155.sol"; -import {IERC1155Receiver} from "../../intf/IERC1155Receiver.sol"; +import {IFilterModel} from "../intf/IFilterModel.sol"; +import {IFeeModel} from "../intf/IFeeModel.sol"; +import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; +import {DecimalMath} from "../../lib/DecimalMath.sol"; -contract FilterAdmin is InitializableInternalMintableERC20, IERC721Receiver, IERC1155Receiver { +contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { using SafeMath for uint256; // ============ Storage ============ - address public _ERC721_FILTER_MODEL_; - address public _ERC1155_FILTER_MODEL_; + address[] public _FILTER_REGISTRY_; + uint256 public _FEE_; + address public _MT_FEE_MODEL_; + address public _DEFAULT_MAINTAINER_; function init( address _owner, - uint256 _initSupply, string memory _name, string memory _symbol, - uint8 _decimals, - address _erc721FilterModel, - address _erc1155FilterModel + uint256 fee, + address mtFeeModel, + address defaultMaintainer, + address[] memory filters ) external { - super.init(_owner, _initSupply, _name, _symbol, _decimals); - _ERC721_FILTER_MODEL_ = _erc721FilterModel; - _ERC1155_FILTER_MODEL_ = _erc1155FilterModel; + super.init(_owner, 0, _name, _symbol, 18); + _FILTER_REGISTRY_ = filters; + _FEE_ = fee; + _MT_FEE_MODEL_ = mtFeeModel; + _DEFAULT_MAINTAINER_ = defaultMaintainer; } - // ============ Event ============ - event RemoveNftToken(address nftContract, uint256 tokenId, uint256 amount); - event AddNftToken(address nftContract, uint256 tokenId, uint256 amount); - - - function depositERC721(address nftContract, uint256[] memory tokenIds) public { - require(nftContract != address(0), "ZERO_ADDRESS"); + function ERC721In( + address filter, + address nftContract, + uint256[] memory tokenIds + ) + external + preventReentrant + { + require(isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); + require(IFilterModel(filter)._NFT_IN_SWITCH_(), "NFT_IN_CLOSED"); + require(IFilterModel(filter).getAvaliableNFTIn() >= tokenIds.length, "EXCEED_MAX_AMOUNT"); + uint256 totalPrice = 0; for(uint256 i = 0; i < tokenIds.length; i++) { - uint256 price = IFilterERC721Model(_ERC721_FILTER_MODEL_).saveNFTPrice(nftContract, tokenIds[i]); - _mint(msg.sender, price); - - IERC721(nftContract).safeTransferFrom(msg.sender, address(this), tokenIds[i]); - emit AddNftToken(nftContract, tokenIds[i], 1); + require(IFilterModel(filter).isFilterERC721Pass(nftContract, tokenIds[i]), "NOT_REGISTERED"); + totalPrice = totalPrice.add(IFilterModel(filter).getNFTInPrice(nftContract, tokenIds[i])); + IFilterModel(filter).transferInERC721(nftContract, msg.sender, tokenIds[i]); } + + (uint256 poolFeeAmount, uint256 mtFeeAmount) = _nftInFeeTransfer(totalPrice); + if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); + if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); + + _mint(msg.sender, totalPrice.sub(mtFeeAmount).sub(poolFeeAmount)); } - function depoistERC1155(address nftContract, uint256[] memory tokenIds, uint256[] memory amounts) public { - require(nftContract != address(0), "ZERO_ADDRESS"); + function ERC1155In( + address filter, + address nftContract, + uint256[] memory tokenIds, + uint256[] memory amounts + ) + external + preventReentrant + { require(tokenIds.length == amounts.length, "PARAMS_NOT_MATCH"); + require(isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); + require(IFilterModel(filter)._NFT_IN_SWITCH_(), "NFT_IN_CLOSED"); + require(IFilterModel(filter).getAvaliableNFTIn() >= tokenIds.length, "EXCEED_MAX_AMOUNT"); + uint256 totalPrice = 0; for(uint256 i = 0; i < tokenIds.length; i++) { - uint256 price = IFilterERC1155Model(_ERC1155_FILTER_MODEL_).saveNFTPrice(nftContract, tokenIds[i], amounts[i]); - _mint(msg.sender, price); - - emit AddNftToken(nftContract, tokenIds[i], amounts[i]); + require(IFilterModel(filter).isFilterERC1155Pass(nftContract, tokenIds[i], amounts[i]), "NOT_REGISTERED"); + totalPrice = totalPrice.add(IFilterModel(filter).getNFTInPrice(nftContract, tokenIds[i]).mul(amounts[i])); } - IERC1155(nftContract).safeBatchTransferFrom(msg.sender, address(this), tokenIds, amounts, ""); - } + (uint256 poolFeeAmount, uint256 mtFeeAmount) = _nftInFeeTransfer(totalPrice); + if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); + if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); + + _mint(msg.sender, totalPrice.sub(mtFeeAmount).sub(poolFeeAmount)); - function doLotteryERC721() external { - uint256 lotteryPrice = IFilterERC721Model(_ERC721_FILTER_MODEL_).buyLotteryNFTPrice(); - _burn(msg.sender, lotteryPrice); - (address nftContract, uint256 tokenId) = IFilterERC721Model(_ERC721_FILTER_MODEL_).lottery(); - - IERC721(nftContract).safeTransferFrom(address(this), msg.sender, tokenId); - emit RemoveNftToken(nftContract, tokenId, 1); - } - - function doLotteryERC1155() external { - uint256 lotteryPrice = IFilterERC1155Model(_ERC1155_FILTER_MODEL_).buyLotteryNFTPrice(); - _burn(msg.sender, lotteryPrice); - (address nftContract, uint256 tokenId) = IFilterERC1155Model(_ERC721_FILTER_MODEL_).lottery(); - - //TODO: amount - IERC1155(nftContract).safeTransferFrom(address(this), msg.sender, tokenId, 1, ""); - emit RemoveNftToken(nftContract, tokenId, 1); - } - - function buySpecERC721(address nftContract, uint256 tokenId) external { - uint256 price = IFilterERC721Model(_ERC721_FILTER_MODEL_).buySpecNFTPrice(nftContract, tokenId); - _burn(msg.sender, price); - - IERC721(nftContract).safeTransferFrom(address(this), msg.sender, tokenId); - emit RemoveNftToken(nftContract, tokenId, 1); - } - - function buySpecERC1155(address nftContract, uint256 tokenId, uint256 amount) external { - uint256 price = IFilterERC1155Model(_ERC1155_FILTER_MODEL_).buySpecNFTPrice(nftContract, tokenId, amount); - _burn(msg.sender, price); - IERC1155(nftContract).safeTransferFrom(address(this), msg.sender, tokenId, amount, ""); - - emit RemoveNftToken(nftContract, tokenId, amount); + IFilterModel(filter).transferBatchInERC1155(nftContract, msg.sender, tokenIds, amounts); } - function supportsInterface(bytes4 interfaceId) public override view returns (bool) { - return interfaceId == type(IERC1155Receiver).interfaceId - || interfaceId == type(IERC721Receiver).interfaceId; - } - - // ============ Callback ============ - function onERC721Received( - address, - address, - uint256 tokenId, - bytes calldata - ) external override returns (bytes4) { - emit AddNftToken(msg.sender, tokenId, 1); - return IERC721Receiver.onERC721Received.selector; - } - - function onERC1155Received( - address, - address, - uint256 id, - uint256 value, - bytes calldata - ) external override returns (bytes4){ - emit AddNftToken(msg.sender, id, value); - return IERC1155Receiver.onERC1155Received.selector; - } - - function onERC1155BatchReceived( - address, - address, - uint256[] calldata ids, - uint256[] calldata values, - bytes calldata - ) external override returns (bytes4){ - require(ids.length == values.length, "PARAMS_NOT_MATCH"); - for(uint256 i = 0; i < ids.length; i++) { - emit AddNftToken(msg.sender, ids[i], values[i]); + function ERC721RandomOut( + address filter, + uint256 times + ) + external + preventReentrant + { + require(msg.sender == tx.origin, "ONLY_ALLOW_EOA"); + require(isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); + require(IFilterModel(filter)._NFT_RANDOM_SWITCH_(), "NFT_RANDOM_CLOSED"); + require(IFilterModel(filter).getAvaliableNFTOut() >= times, "EXCEED_MAX_AMOUNT"); + uint256 totalPrice = 0; + for(uint256 i = 0; i < times; i++) { + totalPrice = totalPrice.add(IFilterModel(filter).getNFTRandomOutPrice()); + (address nftContract, uint256 tokenId) = IFilterModel(filter).getRandomOutId(); + IFilterModel(filter).transferOutERC721(nftContract, msg.sender, tokenId); } - return IERC1155Receiver.onERC1155BatchReceived.selector; + + (uint256 poolFeeAmount, uint256 mtFeeAmount) = _nftRandomOutFeeTransfer(totalPrice); + if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); + if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); + + _burn(msg.sender, totalPrice); + } + + + //TODO: amount == 1 + function ERC1155RandomOut( + address filter, + uint256 times + ) + external + preventReentrant + { + require(msg.sender == tx.origin, "ONLY_ALLOW_EOA"); + require(isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); + require(IFilterModel(filter)._NFT_RANDOM_SWITCH_(), "NFT_RANDOM_OUT_CLOSED"); + require(IFilterModel(filter).getAvaliableNFTOut() >= times, "EXCEED_MAX_AMOUNT"); + + uint256 totalPrice = 0; + uint256[] memory tokenIds; + uint256[] memory amounts; + for(uint256 i = 0; i < times; i++) { + totalPrice = totalPrice.add(IFilterModel(filter).getNFTRandomOutPrice()); + (address nftContract, uint256 tokenId) = IFilterModel(filter).getRandomOutId(); + IFilterModel(filter).transferOutERC1155(nftContract, msg.sender, tokenId, 1); + } + + (uint256 poolFeeAmount, uint256 mtFeeAmount) = _nftRandomOutFeeTransfer(totalPrice); + if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); + if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); + + _burn(msg.sender, totalPrice); + } + + function ERC721TargetOut( + address filter, + address nftContract, + uint256[] memory tokenIds + ) + external + preventReentrant + { + require(isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); + require(IFilterModel(filter)._NFT_TARGET_SWITCH_(), "NFT_TARGET_OUT_CLOSED"); + require(IFilterModel(filter).getAvaliableNFTOut() >= tokenIds.length, "EXCEED_MAX_AMOUNT"); + uint256 totalPrice = 0; + for(uint256 i = 0; i < tokenIds.length; i++) { + totalPrice = totalPrice.add(IFilterModel(filter).getNFTTargetOutPrice(nftContract, tokenIds[i])); + IFilterModel(filter).transferOutERC721(nftContract, msg.sender, tokenIds[i]); + } + + (uint256 poolFeeAmount, uint256 mtFeeAmount) = _nftTargetOutFeeTransfer(totalPrice); + if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); + if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); + + _burn(msg.sender, totalPrice); + } + + function ERC1155TargetOut( + address filter, + address nftContract, + uint256[] memory tokenIds, + uint256[] memory amounts + ) + external + preventReentrant + { + require(tokenIds.length == amounts.length, "PARAMS_NOT_MATCH"); + require(isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); + require(IFilterModel(filter)._NFT_TARGET_SWITCH_(), "NFT_TARGET_OUT_CLOSED"); + require(IFilterModel(filter).getAvaliableNFTOut() >= tokenIds.length, "EXCEED_MAX_AMOUNT"); + uint256 totalPrice = 0; + for(uint256 i = 0; i < tokenIds.length; i++) { + totalPrice = totalPrice.add(IFilterModel(filter).getNFTTargetOutPrice(nftContract, tokenIds[i]).mul(amounts[i])); + } + (uint256 poolFeeAmount, uint256 mtFeeAmount) = _nftTargetOutFeeTransfer(totalPrice); + if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); + if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); + + _burn(msg.sender, totalPrice); + + IFilterModel(filter).transferBatchOutERC1155(nftContract, msg.sender, tokenIds, amounts); + } + + + //================ View ================ + function isIncludeFilter(address filter) view public returns (bool) { + uint256 i = 0; + for(; i < _FILTER_REGISTRY_.length; i++) { + if(filter == _FILTER_REGISTRY_[i]) break; + } + return i == _FILTER_REGISTRY_.length ? false : true; + } + + function getFilters() view public returns (address[] memory) { + return _FILTER_REGISTRY_; + } + + function version() virtual external pure returns (string memory) { + return "FADMIN 1.0.0"; + } + + //=============== Owner ============== + function addFilter(address filter) external onlyOwner { + require(!isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); + _FILTER_REGISTRY_.push(filter); + } + + //TODO: remove Filter是否有必要? + + + //=============== Internal ============== + function _nftInFeeTransfer(uint256 totalPrice) internal returns (uint256 poolFeeAmount, uint256 mtFeeAmount) { + uint256 mtFeeRate = IFeeModel(_MT_FEE_MODEL_).getNFTInFee(address(this), msg.sender); + poolFeeAmount = DecimalMath.mulFloor(totalPrice, _FEE_); + mtFeeAmount = DecimalMath.mulFloor(totalPrice, mtFeeRate); + } + + function _nftRandomOutFeeTransfer(uint256 totalPrice) internal returns (uint256 poolFeeAmount, uint256 mtFeeAmount) { + uint256 mtFeeRate = IFeeModel(_MT_FEE_MODEL_).getNFTRandomOutFee(address(this), msg.sender); + poolFeeAmount = DecimalMath.mulFloor(totalPrice, _FEE_); + mtFeeAmount = DecimalMath.mulFloor(totalPrice, mtFeeRate); + } + + function _nftTargetOutFeeTransfer(uint256 totalPrice) internal returns (uint256 poolFeeAmount, uint256 mtFeeAmount) { + uint256 mtFeeRate = IFeeModel(_MT_FEE_MODEL_).getNFTTargetOutFee(address(this), msg.sender); + poolFeeAmount = DecimalMath.mulFloor(totalPrice, _FEE_); + mtFeeAmount = DecimalMath.mulFloor(totalPrice, mtFeeRate); } } diff --git a/contracts/NFTPool/impl/FilterERC1155Model.sol b/contracts/NFTPool/impl/FilterERC1155Model.sol deleted file mode 100644 index 41f2224..0000000 --- a/contracts/NFTPool/impl/FilterERC1155Model.sol +++ /dev/null @@ -1,130 +0,0 @@ -/* - - 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 {IFilterERC1155Model} from "../intf/IFilterERC1155Model.sol"; - -contract FilterERC1155Model is InitializableOwnable, IFilterERC1155Model { - using SafeMath for uint256; - - //=================== Storage ===================== - // nftCollection -> nftId -> price(frag) - mapping(address => mapping(uint256 => uint256)) public _PRICES_; - // nftCollection -> nftId -> amount - mapping(address => mapping(uint256 => uint256)) public _ERC1155_AMOUNT_; - // nftCollection -> nftId -> specPrice(frag) - mapping(address => mapping(uint256 => uint256)) public _SPEC_PRICES_; - - uint256 public _SPEC_FACTOR_ = 200; - - // nftColletcion -> nftIds - mapping(address => uint256[]) public _NFT_COLLECTION_IDS_; - - address[] public _NFT_COLLECTIONS_; - - uint256 public _LOTTERY_THRESHOLD_; - - uint256 public _TOTAL_NFT_AMOUNT_; - - uint256 public _CURRENT_NFT_AMOUNT_; - - function init( - address owner - ) external { - //TODO: - initOwner(owner); - } - - //================== View ====================== - function isFilterERC1155Pass(address nftCollectionAddress, uint256 nftId, uint256 amount) override external view returns (bool) { - if(_PRICES_[nftCollectionAddress][nftId] == 0) - return false; - else { - if(_ERC1155_AMOUNT_[nftCollectionAddress][nftId] >= amount) - return true; - else - return false; - } - } - - - //TODO: nftInCap - //TODO: nftOutCap - - function saveNFTPrice(address nftCollectionAddress, uint256 nftId, uint256 amount) override external view returns(uint256) { - return _PRICES_[nftCollectionAddress][nftId].mul(amount); - } - - function buySpecNFTPrice(address nftCollectionAddress, uint256 nftId, uint256 amount) override external view returns(uint256) { - require(_ERC1155_AMOUNT_[nftCollectionAddress][nftId] >= amount, "BUY_OVERFLOW"); - return _SPEC_PRICES_[nftCollectionAddress][nftId].mul(amount); - } - - //TODO: amount = 1 - function buyLotteryNFTPrice() override external view returns(uint256) { - return _LOTTERY_THRESHOLD_; - } - - function lottery() override external view returns(address nftCollection, uint256 nftId) { - //random - } - - - //================== Owner ===================== - function setNFTFilter( - address[] memory nftCollections, - uint256[] memory nftIds, - uint256[] memory amounts, - uint256[] memory prices, - uint256[] memory specPrices - ) external onlyOwner { - require(nftCollections.length == nftIds.length, "PARAMS_INVALID"); - require(nftCollections.length == amounts.length, "PARAMS_INVALID"); - require(nftCollections.length == prices.length, "PARAMS_INVALID"); - require(nftCollections.length == specPrices.length, "PARAMS_INVALID"); - - for(uint256 i = 0; i < nftCollections.length; i++){ - _PRICES_[nftCollections[i]][nftIds[i]] = prices[i]; - _PRICES_[nftCollections[i]][nftIds[i]] = amounts[i]; - - if(specPrices[i] == 0){ - _SPEC_PRICES_[nftCollections[i]][nftIds[i]] = prices[i].mul(_SPEC_FACTOR_).div(100); - }else { - _SPEC_PRICES_[nftCollections[i]][nftIds[i]] = specPrices[i]; - } - - if(_NFT_COLLECTION_IDS_[nftCollections[i]].length == 0) { - _NFT_COLLECTIONS_.push(nftCollections[i]); - _NFT_COLLECTION_IDS_[nftCollections[i]] = [nftIds[i]]; - require(++_CURRENT_NFT_AMOUNT_ <= _TOTAL_NFT_AMOUNT_, "OVERFLOW_NFT_AMOUNT"); - } else { - uint256 j = 0; - for(; j < _NFT_COLLECTION_IDS_[nftCollections[i]].length; i++) { - if(_NFT_COLLECTION_IDS_[nftCollections[i]][j] == nftIds[i]) { - break; - } - } - if(j == _NFT_COLLECTION_IDS_[nftCollections[i]].length) { - _NFT_COLLECTION_IDS_[nftCollections[i]].push(nftIds[i]); - require(++_CURRENT_NFT_AMOUNT_ <= _TOTAL_NFT_AMOUNT_, "OVERFLOW_NFT_AMOUNT"); - } - } - } - } - - function setLotteryThreshold(uint256 newLotteryThreshold) external onlyOwner { - _LOTTERY_THRESHOLD_ = newLotteryThreshold; - } - - function setSpecFactor(uint256 newSpecFactor) external onlyOwner { - _SPEC_FACTOR_ = newSpecFactor; - } -} diff --git a/contracts/NFTPool/impl/FilterERC721Model.sol b/contracts/NFTPool/impl/FilterERC721Model.sol deleted file mode 100644 index dabdae4..0000000 --- a/contracts/NFTPool/impl/FilterERC721Model.sol +++ /dev/null @@ -1,242 +0,0 @@ -/* - - 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 {IFilterERC721Model} from "../intf/IFilterERC721Model.sol"; - -contract FilterERC721Model is InitializableOwnable, IFilterERC721Model { - using SafeMath for uint256; - - //=================== Storage =================== - // nftCollection -> nftId -> price(frag) - mapping(address => mapping(uint256 => uint256)) public _PRICES_; - // nftCollection -> nftId -> specPrice(frag) - mapping(address => mapping(uint256 => uint256)) public _SPEC_PRICES_; - uint256 public _SPEC_FACTOR_ = 200; - // nftColletcion -> nftIds - mapping(address => uint256[]) public _NFT_COLLECTION_IDS_; - address[] public _NFT_COLLECTIONS_; - - uint256 public _LOTTERY_THRESHOLD_; - uint256 public _TOTAL_NFT_AMOUNT_; - uint256 public _CURRENT_NFT_AMOUNT_; - - - uint256 public _TIMELOCK_DURATION_; - - struct LockFilterInfo { - address[] nftCollections; - uint256[] nftIds; - uint256[] prices; - uint256[] specPrices; - uint256 releaseTime; - } - mapping(bytes32 => LockFilterInfo) public _TIME_LOCKS_; - - uint256 public _PENDING_LOTTERY_THRESHOLD_; - uint256 public _PENDING_TOTAL_NFT_AMOUNT_; - uint256 public _PENDING_SPEC_FACTOR_; - uint256 public _GLOBAL_TIME_LOCK_; - - - function init( - address owner, - uint256 specFactor, - uint256 lotteryThreshold, - uint256 totalNftAmount, - uint256 timeLockDuration - ) external { - initOwner(owner); - _SPEC_FACTOR_ = specFactor; - _LOTTERY_THRESHOLD_ = lotteryThreshold; - _TOTAL_NFT_AMOUNT_ = totalNftAmount; - _TIMELOCK_DURATION_ = timeLockDuration; - } - - //==================== View ================== - function isFilterERC721Pass(address nftCollectionAddress, uint256 nftId) override external view returns (bool) { - return _isInclude(nftCollectionAddress, nftId); - } - - function saveNFTPrice(address nftCollectionAddress, uint256 nftId) override external view returns(uint256) { - return _PRICES_[nftCollectionAddress][nftId]; - } - - function buySpecNFTPrice(address nftCollectionAddress, uint256 nftId) override external view returns(uint256) { - return _SPEC_PRICES_[nftCollectionAddress][nftId]; - } - - function buyLotteryNFTPrice() override external view returns(uint256) { - return _LOTTERY_THRESHOLD_; - } - - function lottery() override external view returns(address nftCollection, uint256 nftId) { - //random - } - - - - //TODO: nftInCap - //TODO: nftOutCap - - - //============= Owner =============== - - function addNFTFilter( - address[] memory nftCollections, - uint256[] memory nftIds, - uint256[] memory prices, - uint256[] memory specPrices - ) external onlyOwner { - require(nftCollections.length == nftIds.length, "PARAMS_INVALID"); - require(nftCollections.length == prices.length, "PARAMS_INVALID"); - require(nftCollections.length == specPrices.length, "PARAMS_INVALID"); - - for(uint256 i = 0; i < nftCollections.length; i++){ - if(_isInclude(nftCollections[i], nftIds[i])) continue; - - _PRICES_[nftCollections[i]][nftIds[i]] = prices[i]; - - if(specPrices[i] == 0){ - _SPEC_PRICES_[nftCollections[i]][nftIds[i]] = prices[i].mul(_SPEC_FACTOR_).div(100); - }else { - _SPEC_PRICES_[nftCollections[i]][nftIds[i]] = specPrices[i]; - } - - if(_NFT_COLLECTION_IDS_[nftCollections[i]].length == 0) { - _NFT_COLLECTIONS_.push(nftCollections[i]); - } - - _NFT_COLLECTION_IDS_[nftCollections[i]].push(nftIds[i]); - require(++_CURRENT_NFT_AMOUNT_ <= _TOTAL_NFT_AMOUNT_, "OVERFLOW_NFT_AMOUNT"); - } - } - - - function scheduleUpdateNftFilter( - address[] memory nftCollections, - uint256[] memory nftIds, - uint256[] memory prices, - uint256[] memory specPrices - ) external onlyOwner { - require(nftCollections.length == nftIds.length, "PARAMS_INVALID"); - require(nftCollections.length == prices.length, "PARAMS_INVALID"); - require(nftCollections.length == specPrices.length, "PARAMS_INVALID"); - - uint256 releaseTime = block.timestamp.add(_TIMELOCK_DURATION_); - bytes32 id = keccak256(abi.encode(nftCollections, nftIds, prices, specPrices, releaseTime)); - LockFilterInfo memory lockFilterInfo = LockFilterInfo({ - nftCollections: nftCollections, - nftIds: nftIds, - prices: prices, - specPrices: specPrices, - releaseTime: releaseTime - }); - require(_TIME_LOCKS_[id].releaseTime == 0, "ALREADY_ADDED"); - for(uint256 i = 0; i< nftCollections.length; i++) { - require(_isInclude(nftCollections[i],nftIds[i])); - } - _TIME_LOCKS_[id] = lockFilterInfo; - } - - - function executeUpdateNftFilter( - bytes32 id - ) external onlyOwner { - LockFilterInfo memory lockFilterInfo = _TIME_LOCKS_[id]; - uint256 releaseTime = lockFilterInfo.releaseTime; - require(releaseTime != 0 && releaseTime !=1 && block.timestamp > releaseTime, "TIMELOCKED"); - - address[] memory nftCollections = lockFilterInfo.nftCollections; - uint256[] memory nftIds = lockFilterInfo.nftIds; - uint256[] memory prices = lockFilterInfo.prices; - uint256[] memory specPrices = lockFilterInfo.specPrices; - - - for(uint256 i = 0; i < nftCollections.length; i++){ - _PRICES_[nftCollections[i]][nftIds[i]] = prices[i]; - - if(specPrices[i] == 0){ - _SPEC_PRICES_[nftCollections[i]][nftIds[i]] = prices[i].mul(_SPEC_FACTOR_).div(100); - }else { - _SPEC_PRICES_[nftCollections[i]][nftIds[i]] = specPrices[i]; - } - } - - lockFilterInfo.releaseTime = 1; - _TIME_LOCKS_[id] = lockFilterInfo; - } - - - //TODO:价格修改为0,是否是同样的效果 - function scheduleRemoveNFTFilter( - address[] memory nftCollections, - uint256[] memory nftIds - ) external onlyOwner { - require(nftCollections.length == nftIds.length, "PARAMS_INVALID"); - } - - function executeRemoveNFTFilter( - bytes32 id - ) external onlyOwner { - - } - - - function scheduleUpdateGlobalState( - uint256 newLotteryThreshold, - uint256 newSpecFactor, - uint256 newTotalNFTAmount - ) external onlyOwner { - require(newTotalNFTAmount >= _CURRENT_NFT_AMOUNT_, "NFT_ALREADY_EXCEED"); - require(newSpecFactor > 100, "SPEC_FACTOR_TOO_LOW"); - - _PENDING_LOTTERY_THRESHOLD_ = newLotteryThreshold; - _PENDING_TOTAL_NFT_AMOUNT_ = newSpecFactor; - _PENDING_SPEC_FACTOR_ = newTotalNFTAmount; - - _GLOBAL_TIME_LOCK_ = block.timestamp.add(_TIMELOCK_DURATION_); - } - - function executeUpdateGlobalState() external onlyOwner { - require(block.timestamp > _GLOBAL_TIME_LOCK_ && _GLOBAL_TIME_LOCK_ !=0, "TIMELOCKED"); - - require(_PENDING_TOTAL_NFT_AMOUNT_ >= _CURRENT_NFT_AMOUNT_, "NFT_ALREADY_EXCEED"); - require(_PENDING_SPEC_FACTOR_ > 100, "SPEC_FACTOR_TOO_LOW"); - - _LOTTERY_THRESHOLD_ = _PENDING_LOTTERY_THRESHOLD_; - _SPEC_FACTOR_ = _PENDING_SPEC_FACTOR_; - _TOTAL_NFT_AMOUNT_ = _PENDING_TOTAL_NFT_AMOUNT_; - - lockGlobalState(); - } - - function lockGlobalState() public onlyOwner { - _PENDING_LOTTERY_THRESHOLD_ = 0; - _PENDING_TOTAL_NFT_AMOUNT_ = 0; - _PENDING_SPEC_FACTOR_ = 0; - _GLOBAL_TIME_LOCK_ = 0; - } - - - //==================== internal =================== - function _isInclude(address nftCollection, uint256 nftId) internal view returns (bool) { - uint256[] memory ids = _NFT_COLLECTION_IDS_[nftCollection]; - uint256 i = 0; - for(;i < ids.length; i++) { - if(nftId == i) break; - } - if(i == ids.length) - return false; - else - return true; - } -} diff --git a/contracts/NFTPool/impl/FilterModel01.sol b/contracts/NFTPool/impl/FilterModel01.sol new file mode 100644 index 0000000..b85a721 --- /dev/null +++ b/contracts/NFTPool/impl/FilterModel01.sol @@ -0,0 +1,260 @@ +/* + + 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 {IERC721} from "../../intf/IERC721.sol"; +import {IERC721Receiver} from "../../intf/IERC721Receiver.sol"; +import {DecimalMath} from "../../lib/DecimalMath.sol"; + + +contract FilterModel01 is InitializableOwnable, IERC721Receiver { + using SafeMath for uint256; + + //=================== Storage =================== + address public _NFT_COLLECTION_; + uint256 public _TOKEN_ID_START_; + uint256 public _TOKEN_ID_END_; + //tokenId => isRegistered + mapping(uint256 => bool) public _SPREAD_IDS_REGISTRY_; + + uint256 public _MAX_NFT_AMOUNT_; + uint256 public _MIN_NFT_AMOUNT_; + + //For NFT IN + uint256 public _GS_START_IN_; + uint256 public _CR_IN_; + bool public _NFT_IN_SWITCH_ = true; + + //For NFT Random OUT + uint256 public _GS_START_RANDOM_OUT_; + uint256 public _CR_RANDOM_OUT_; + bool public _NFT_RANDOM_SWITCH_ = true; + + //For NFT Target OUT + uint256 public _GS_START_TARGET_OUT_; + uint256 public _CR_TARGET_OUT_; + bool public _NFT_TARGET_SWITCH_ = true; + + uint256[] public _TOKEN_IDS_; + + function init( + address filterAdmin, + address nftCollection, + bool[] memory switches, + uint256[] memory tokenRanges, + uint256[] memory nftAmounts, + uint256[] memory priceRules, + uint256[] memory spreadIds + ) external { + require(priceRules[1] != 0, "CR_IN_INVALID"); + require(priceRules[3] != 0, "CR_RANDOM_OUT_INVALID"); + require(priceRules[5] != 0, "CR_TARGET_OUT_INVALID"); + require(tokenRanges[1] >= tokenRanges[0], "TOKEN_RANGE_INVALID"); + require(nftAmounts[0] >= nftAmounts[1], "AMOUNT_INVALID"); + + initOwner(filterAdmin); + _NFT_COLLECTION_ = nftCollection; + + _TOKEN_ID_START_ = tokenRanges[0]; + _TOKEN_ID_END_ = tokenRanges[1]; + + _MAX_NFT_AMOUNT_ = nftAmounts[0]; + _MIN_NFT_AMOUNT_ = nftAmounts[1]; + + _GS_START_IN_ = priceRules[0]; + _CR_IN_ = priceRules[1]; + _NFT_IN_SWITCH_ = switches[0]; + + _GS_START_RANDOM_OUT_ = priceRules[2]; + _CR_RANDOM_OUT_ = priceRules[3]; + _NFT_RANDOM_SWITCH_ = switches[1]; + + _GS_START_TARGET_OUT_ = priceRules[4]; + _CR_TARGET_OUT_ = priceRules[5]; + _NFT_TARGET_SWITCH_ = switches[2]; + + for(uint256 i = 0; i < spreadIds.length; i++) { + _SPREAD_IDS_REGISTRY_[spreadIds[i]] = true; + } + } + + //==================== View ================== + function isFilterERC721Pass(address nftCollectionAddress, uint256 nftId) external view returns (bool) { + if(nftCollectionAddress == _NFT_COLLECTION_) { + if((nftId >= _TOKEN_ID_START_ && nftId <= _TOKEN_ID_END_) || _SPREAD_IDS_REGISTRY_[nftId]) { + return true; + } else { + return false; + } + } else { + return false; + } + } + + function getAvaliableNFTIn() external view returns(uint256) { + if(_MAX_NFT_AMOUNT_ < _TOKEN_IDS_.length) { + return 0; + }else { + return _MAX_NFT_AMOUNT_ - _TOKEN_IDS_.length; + } + } + + function getAvaliableNFTOut() external view returns(uint256) { + if(_TOKEN_IDS_.length < _MIN_NFT_AMOUNT_) { + return 0; + }else { + return _TOKEN_IDS_.length - _MIN_NFT_AMOUNT_; + } + } + + function getNFTInPrice(address, uint256) external view returns(uint256) { + uint256 nftAmount = _TOKEN_IDS_.length; + if(nftAmount == 0) { + return _GS_START_IN_; + }else { + uint256 price = _GS_START_IN_; + //TODO:gas + for(uint256 i = 0; i < nftAmount; i++) { + price = DecimalMath.mulFloor(price, _CR_IN_); + } + return price; + } + } + + function getNFTRandomOutPrice() external view returns (uint256) { + uint256 nftAmount = _TOKEN_IDS_.length; + require(nftAmount != 0, "EMPTY"); + uint256 price = _GS_START_RANDOM_OUT_; + for(uint256 i = 0; i < nftAmount; i++) { + price = DecimalMath.mulFloor(price, _CR_RANDOM_OUT_); + } + return price; + } + + function getNFTTargetOutPrice(address, uint256) external view returns (uint256) { + uint256 nftAmount = _TOKEN_IDS_.length; + require(nftAmount != 0, "EMPTY"); + uint256 price = _GS_START_TARGET_OUT_; + for(uint256 i = 0; i < nftAmount; i++) { + price = DecimalMath.mulFloor(price, _CR_TARGET_OUT_); + } + return price; + } + + function supportsInterface(bytes4 interfaceId) public view returns (bool) { + return interfaceId == type(IERC721Receiver).interfaceId; + } + + //Pseudorandomness + function getRandomOutId() external view returns (address nftCollection, uint256 nftId) { + uint256 nftAmount = _TOKEN_IDS_.length; + uint256 idx = uint256(keccak256(abi.encodePacked(blockhash(block.number-1), gasleft(), msg.sender, nftAmount))) % nftAmount; + nftCollection = _NFT_COLLECTION_; + nftId = _TOKEN_IDS_[idx]; + } + + // ================= Ownable ================ + function transferOutERC721(address nftContract, address assetTo, uint256 nftId) external onlyOwner { + require(nftContract == _NFT_COLLECTION_, "WRONG_NFT_COLLECTION"); + uint256[] memory tokenIds = _TOKEN_IDS_; + uint256 i; + for (; i < tokenIds.length; i++) { + if (tokenIds[i] == nftId) { + tokenIds[i] = tokenIds[tokenIds.length - 1]; + break; + } + } + require(i < tokenIds.length, "NOT_EXIST_ID"); + _TOKEN_IDS_ = tokenIds; + _TOKEN_IDS_.pop(); + + IERC721(nftContract).safeTransferFrom(address(this), assetTo, nftId); + } + + function transferInERC721(address nftContract, address assetFrom, uint256 nftId) external onlyOwner { + require(nftContract == _NFT_COLLECTION_, "WRONG_NFT_COLLECTION"); + uint256 i; + for(; i < _TOKEN_IDS_.length; i++) { + if(_TOKEN_IDS_[i] == nftId) break; + } + require(i == _TOKEN_IDS_.length, "EXIST_ID"); + + _TOKEN_IDS_.push(nftId); + IERC721(nftContract).safeTransferFrom(assetFrom, address(this), nftId); + } + + function changeNFTInPrice(uint256 newGsStart, uint256 newCr) external { + require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); + require(newCr != 0, "CR_ZERO"); + _GS_START_IN_ = newGsStart; + _CR_IN_ = newCr; + } + + function changeNFTROutPrice(uint256 newGsStart, uint256 newCr) external { + require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); + require(newCr != 0, "CR_ZERO"); + _GS_START_RANDOM_OUT_ = newGsStart; + _CR_RANDOM_OUT_ = newCr; + } + + function changeNFTTOutPrice(uint256 newGsStart, uint256 newCr) external { + require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); + require(newCr != 0, "CR_ZERO"); + _GS_START_TARGET_OUT_ = newGsStart; + _CR_TARGET_OUT_ = newCr; + } + + function changeNFTAmount(uint256 maxNFTAmount, uint256 minNFTAmount) external { + require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); + require(maxNFTAmount >= minNFTAmount, "AMOUNT_INVALID"); + _MAX_NFT_AMOUNT_ = maxNFTAmount; + _MIN_NFT_AMOUNT_ = minNFTAmount; + } + + function changeSwitches( + bool newNFTInSwitch, + bool newNFTRandomSwitch, + bool newNFTTargetSwitch + ) external { + require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); + _NFT_IN_SWITCH_ = newNFTInSwitch; + _NFT_RANDOM_SWITCH_ = newNFTRandomSwitch; + _NFT_TARGET_SWITCH_ = newNFTTargetSwitch; + } + + function changeTokenIdRange(uint256 tokenIdStart, uint256 tokenIdEnd) external { + require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); + require(tokenIdStart <= tokenIdEnd, "TOKEN_RANGE_INVALID"); + + _TOKEN_ID_START_ = tokenIdStart; + _TOKEN_ID_END_ = tokenIdEnd; + } + + function changeTokenIdMap(uint256[] memory tokenIds, bool[] memory isRegistrieds) external { + require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); + require(tokenIds.length == isRegistrieds.length, "PARAM_NOT_MATCH"); + + for(uint256 i = 0; i < tokenIds.length; i++) { + _SPREAD_IDS_REGISTRY_[tokenIds[i]] = isRegistrieds[i]; + } + } + + // ============ Callback ============ + function onERC721Received( + address, + address, + uint256 tokenId, + bytes calldata + ) external override returns (bytes4) { + return IERC721Receiver.onERC721Received.selector; + } +} \ No newline at end of file diff --git a/contracts/NFTPool/impl/NFTPoolFeeModel.sol b/contracts/NFTPool/impl/NFTPoolFeeModel.sol new file mode 100644 index 0000000..c5818a2 --- /dev/null +++ b/contracts/NFTPool/impl/NFTPoolFeeModel.sol @@ -0,0 +1,85 @@ +/* + + 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 {IERC20} from "../../intf/IERC20.sol"; +import {SafeMath} from "../../lib/SafeMath.sol"; + +contract NFTPoolFeeModel 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 + + struct FilterAdminInfo { + uint256 nftInFee; + uint256 nftRandomOutFee; + uint256 nftTargetOutFee; + bool isSet; + } + + mapping(address => FilterAdminInfo) filterAdmins; + + function addFilterAdminInfo(address filterAdminAddr, uint256 nftInFee, uint256 nftRandomOutFee, uint256 nftTargetOutFee) external onlyOwner { + FilterAdminInfo memory filterAdmin = FilterAdminInfo({ + nftInFee: nftInFee, + nftRandomOutFee: nftRandomOutFee, + nftTargetOutFee: nftTargetOutFee, + isSet: true + }); + filterAdmins[filterAdminAddr] = filterAdmin; + } + + function setFilterAdminInfo(address filterAdminAddr, uint256 nftInFee, uint256 nftRandomOutFee, uint256 nftTargetOutFee) external onlyOwner { + filterAdmins[filterAdminAddr].nftInFee = nftInFee; + filterAdmins[filterAdminAddr].nftRandomOutFee = nftRandomOutFee; + filterAdmins[filterAdminAddr].nftTargetOutFee = nftTargetOutFee; + } + + function setGlobalParam(uint256 nftInFee, uint256 nftRandomOutFee, uint256 nftTargetOutFee) external onlyOwner { + _GLOBAL_NFT_IN_FEE_ = nftInFee; + _GLOBAL_NFT_RANDOM_OUT_FEE_ = nftRandomOutFee; + _GLOBAL_NFT_TARGET_OUT_FEE_ = nftTargetOutFee; + } + + + function getNFTInFee(address filterAdminAddr, address) external view returns(uint256) { + FilterAdminInfo memory filterAdminInfo = filterAdmins[filterAdminAddr]; + + if(filterAdminInfo.isSet) { + return filterAdminInfo.nftInFee; + }else { + return _GLOBAL_NFT_IN_FEE_; + } + } + + + function getNFTRandomOutFee(address filterAdminAddr, address) external view returns(uint256) { + FilterAdminInfo memory filterAdminInfo = filterAdmins[filterAdminAddr]; + + if(filterAdminInfo.isSet) { + return filterAdminInfo.nftRandomOutFee; + }else { + return _GLOBAL_NFT_RANDOM_OUT_FEE_; + } + } + + function getNFTTargetOutFee(address filterAdminAddr, address) external view returns(uint256) { + FilterAdminInfo memory filterAdminInfo = filterAdmins[filterAdminAddr]; + + if(filterAdminInfo.isSet) { + return filterAdminInfo.nftTargetOutFee; + }else { + return _GLOBAL_NFT_TARGET_OUT_FEE_; + } + } + +} diff --git a/contracts/NFTPool/intf/IFeeModel.sol b/contracts/NFTPool/intf/IFeeModel.sol new file mode 100644 index 0000000..79a745b --- /dev/null +++ b/contracts/NFTPool/intf/IFeeModel.sol @@ -0,0 +1,16 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +interface IFeeModel { + function getNFTInFee(address filterAdminAddr, address user) external view returns(uint256); + + function getNFTRandomOutFee(address filterAdminAddr, address user) external view returns(uint256); + + function getNFTTargetOutFee(address filterAdminAddr, address user) external view returns(uint256); +} \ No newline at end of file diff --git a/contracts/NFTPool/intf/IFilterAdmin.sol b/contracts/NFTPool/intf/IFilterAdmin.sol new file mode 100644 index 0000000..542539c --- /dev/null +++ b/contracts/NFTPool/intf/IFilterAdmin.sol @@ -0,0 +1,12 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +interface IFilterAdmin { + function _OWNER_() external returns (address); +} \ No newline at end of file diff --git a/contracts/NFTPool/intf/IFilterERC1155Model.sol b/contracts/NFTPool/intf/IFilterERC1155Model.sol deleted file mode 100644 index f4fae33..0000000 --- a/contracts/NFTPool/intf/IFilterERC1155Model.sol +++ /dev/null @@ -1,20 +0,0 @@ -/* - - Copyright 2021 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; - -interface IFilterERC1155Model { - function isFilterERC1155Pass(address nftCollectionAddress, uint256 nftId, uint256 amount) external view returns (bool); - - function saveNFTPrice(address nftCollectionAddress, uint256 nftId, uint256 amount) external view returns(uint256); - - function buySpecNFTPrice(address nftCollectionAddress, uint256 nftId, uint256 amount) external view returns(uint256); - - function buyLotteryNFTPrice() external view returns(uint256); - - function lottery() external view returns(address nftCollection, uint256 nftId); -} \ No newline at end of file diff --git a/contracts/NFTPool/intf/IFilterERC721Model.sol b/contracts/NFTPool/intf/IFilterERC721Model.sol deleted file mode 100644 index 2e2cd42..0000000 --- a/contracts/NFTPool/intf/IFilterERC721Model.sol +++ /dev/null @@ -1,20 +0,0 @@ -/* - - Copyright 2021 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; - -interface IFilterERC721Model { - function isFilterERC721Pass(address nftCollectionAddress, uint256 nftId) external view returns (bool); - - function saveNFTPrice(address nftCollectionAddress, uint256 nftId) external view returns(uint256); - - function buySpecNFTPrice(address nftCollectionAddress, uint256 nftId) external view returns(uint256); - - function buyLotteryNFTPrice() external view returns(uint256); - - function lottery() external view returns(address nftCollection, uint256 nftId); -} \ No newline at end of file diff --git a/contracts/NFTPool/intf/IFilterModel.sol b/contracts/NFTPool/intf/IFilterModel.sol new file mode 100644 index 0000000..2d626af --- /dev/null +++ b/contracts/NFTPool/intf/IFilterModel.sol @@ -0,0 +1,42 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +interface IFilterModel { + function isFilterERC721Pass(address nftCollectionAddress, uint256 nftId) external view returns (bool); + + function isFilterERC1155Pass(address nftCollectionAddress, uint256 nftId, uint256 amount) external view returns (bool); + + function getAvaliableNFTIn() external view returns(uint256); + + function getAvaliableNFTOut() external view returns(uint256); + + function _NFT_IN_SWITCH_() external view returns(bool); + + function _NFT_RANDOM_SWITCH_() external view returns(bool); + + function _NFT_TARGET_SWITCH_() external view returns(bool); + + 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/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index e69de29..d641290 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -0,0 +1,81 @@ +/* + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 +*/ + +pragma solidity 0.6.9; + +import {SafeMath} from "../../lib/SafeMath.sol"; +import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; +import {ICloneFactory} from "../../lib/CloneFactory.sol"; +import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; + + +contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { + using SafeMath for uint256; + + // ============ Storage ============ + mapping(uint256 => address) public _FILTER_TEMPLATES_; + address public _FILTER_ADMIN_TEMPLATE_; + address public _DEFAULT_MAINTAINER_; + address public _NFT_POOL_FEE_MODEL_; + address public immutable _CLONE_FACTORY_; + + + // ============ Event ============== + event SetFilterTemplate(uint256 idx, address filterTemplate); + + constructor( + address cloneFactory, + address filterAdminTemplate, + address nftPoolFeeModel, + address defaultMaintainer + ) public { + _CLONE_FACTORY_ = cloneFactory; + _FILTER_ADMIN_TEMPLATE_ = filterAdminTemplate; + _NFT_POOL_FEE_MODEL_ = nftPoolFeeModel; + _DEFAULT_MAINTAINER_ = defaultMaintainer; + } + + //TODO:一笔交易 + + function createFilterAdmin( + string memory name, + string memory symbol, + uint256 fee + ) external returns(address) { + + } + + + function createFilter01( + address filterAdmin, + address nftCollection, + bool[] memory switches, + uint256[] memory tokenRanges, + uint256[] memory nftAmounts, + uint256[] memory priceRules, + uint256[] memory spreadIds + ) external returns(address) { + + } + + + //====================== Ownable ======================== + function changeDefaultMaintainer(address newMaintainer) external onlyOwner { + _DEFAULT_MAINTAINER_ = newMaintainer; + } + + function changeFilterAdminTemplate(address newFilterAdminTemplate) external onlyOwner { + _FILTER_ADMIN_TEMPLATE_ = newFilterAdminTemplate; + } + + function changeNftPoolFeeModel(address newNftPoolFeeModel) external onlyOwner { + _NFT_POOL_FEE_MODEL_ = newNftPoolFeeModel; + } + + function setFilterTemplate(uint256 idx, address newFilterTemplate) external onlyOwner { + _FILTER_TEMPLATES_[idx] = newFilterTemplate; + emit SetFilterTemplate(idx, newFilterTemplate); + } +} \ No newline at end of file diff --git a/contracts/external/ERC20/InitializableInternalMintableERC20.sol b/contracts/external/ERC20/InitializableInternalMintableERC20.sol index d22349d..ec3eb49 100644 --- a/contracts/external/ERC20/InitializableInternalMintableERC20.sol +++ b/contracts/external/ERC20/InitializableInternalMintableERC20.sol @@ -8,8 +8,9 @@ pragma solidity 0.6.9; import {SafeMath} from "../../lib/SafeMath.sol"; +import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; -contract InitializableInternalMintableERC20 { +contract InitializableInternalMintableERC20 is InitializableOwnable { using SafeMath for uint256; string public name; @@ -32,6 +33,7 @@ contract InitializableInternalMintableERC20 { string memory _symbol, uint8 _decimals ) public { + initOwner(_creator); name = _name; symbol = _symbol; decimals = _decimals; From 6dfb96b86e145e537feba35fa0f3d323d6c34daa Mon Sep 17 00:00:00 2001 From: owen05 Date: Thu, 9 Sep 2021 10:27:09 +0800 Subject: [PATCH 04/42] add nftPoolProxy --- contracts/NFTPool/impl/FilterModel01.sol | 67 ++++++++++++------- contracts/NFTPool/intf/IFilterAdmin.sol | 10 +++ .../SmartRoute/proxies/DODONFTPoolProxy.sol | 56 +++++++++++++--- 3 files changed, 99 insertions(+), 34 deletions(-) diff --git a/contracts/NFTPool/impl/FilterModel01.sol b/contracts/NFTPool/impl/FilterModel01.sol index b85a721..233b274 100644 --- a/contracts/NFTPool/impl/FilterModel01.sol +++ b/contracts/NFTPool/impl/FilterModel01.sol @@ -100,7 +100,7 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { } } - function getAvaliableNFTIn() external view returns(uint256) { + function getAvaliableNFTIn() public view returns(uint256) { if(_MAX_NFT_AMOUNT_ < _TOKEN_IDS_.length) { return 0; }else { @@ -108,7 +108,7 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { } } - function getAvaliableNFTOut() external view returns(uint256) { + function getAvaliableNFTOut() public view returns(uint256) { if(_TOKEN_IDS_.length < _MIN_NFT_AMOUNT_) { return 0; }else { @@ -117,36 +117,21 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { } function getNFTInPrice(address, uint256) external view returns(uint256) { - uint256 nftAmount = _TOKEN_IDS_.length; - if(nftAmount == 0) { - return _GS_START_IN_; - }else { - uint256 price = _GS_START_IN_; - //TODO:gas - for(uint256 i = 0; i < nftAmount; i++) { - price = DecimalMath.mulFloor(price, _CR_IN_); - } - return price; - } + (uint256 price, ) = geometricCalc(_GS_START_IN_,_CR_IN_, _TOKEN_IDS_.length); + return price; } function getNFTRandomOutPrice() external view returns (uint256) { - uint256 nftAmount = _TOKEN_IDS_.length; - require(nftAmount != 0, "EMPTY"); - uint256 price = _GS_START_RANDOM_OUT_; - for(uint256 i = 0; i < nftAmount; i++) { - price = DecimalMath.mulFloor(price, _CR_RANDOM_OUT_); - } + require(_TOKEN_IDS_.length != 0, "EMPTY"); + + (uint256 price, ) = geometricCalc(_GS_START_RANDOM_OUT_,_CR_RANDOM_OUT_, _TOKEN_IDS_.length); return price; } function getNFTTargetOutPrice(address, uint256) external view returns (uint256) { - uint256 nftAmount = _TOKEN_IDS_.length; - require(nftAmount != 0, "EMPTY"); - uint256 price = _GS_START_TARGET_OUT_; - for(uint256 i = 0; i < nftAmount; i++) { - price = DecimalMath.mulFloor(price, _CR_TARGET_OUT_); - } + require(_TOKEN_IDS_.length != 0, "EMPTY"); + + (uint256 price, ) = geometricCalc(_GS_START_TARGET_OUT_,_CR_TARGET_OUT_, _TOKEN_IDS_.length); return price; } @@ -162,6 +147,28 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { nftId = _TOKEN_IDS_[idx]; } + + function getTotalNFTInPrice(uint256 amount) external view returns (uint256 totalPrice) { + require(amount <= getAvaliableNFTIn(), "EXCEDD_IN_AMOUNT"); + + (uint256 base, ) = geometricCalc(_GS_START_IN_,_CR_IN_, _TOKEN_IDS_.length); + (, totalPrice) = geometricCalc(base, _CR_IN_, amount); + } + + function getTotalTargetNFTOutPrice(uint256 amount) external view returns (uint256 totalPrice) { + require(amount <= getAvaliableNFTOut(), "EXCEED_OUT_AMOUNT"); + + (uint256 base, ) = geometricCalc(_GS_START_TARGET_OUT_,_CR_TARGET_OUT_, _TOKEN_IDS_.length); + (, totalPrice) = geometricCalc(base, _CR_TARGET_OUT_, amount); + } + + function getTotalRandomNFTOutPrice(uint256 amount) external view returns (uint256 totalPrice) { + require(amount <= getAvaliableNFTOut(), "EXCEED_OUT_AMOUNT"); + + (uint256 base, ) = geometricCalc(_GS_START_RANDOM_OUT_,_CR_RANDOM_OUT_, _TOKEN_IDS_.length); + (, totalPrice) = geometricCalc(base, _CR_RANDOM_OUT_, amount); + } + // ================= Ownable ================ function transferOutERC721(address nftContract, address assetTo, uint256 nftId) external onlyOwner { require(nftContract == _NFT_COLLECTION_, "WRONG_NFT_COLLECTION"); @@ -257,4 +264,14 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { ) external override returns (bytes4) { return IERC721Receiver.onERC721Received.selector; } + + // ============ Internal ============= + 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; + } } \ No newline at end of file diff --git a/contracts/NFTPool/intf/IFilterAdmin.sol b/contracts/NFTPool/intf/IFilterAdmin.sol index 542539c..cf1cb37 100644 --- a/contracts/NFTPool/intf/IFilterAdmin.sol +++ b/contracts/NFTPool/intf/IFilterAdmin.sol @@ -9,4 +9,14 @@ pragma solidity 0.6.9; interface IFilterAdmin { function _OWNER_() external returns (address); + + function init( + address _owner, + string memory _name, + string memory _symbol, + uint256 fee, + address mtFeeModel, + address defaultMaintainer, + address[] memory filters + ) external; } \ No newline at end of file diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index d641290..4d2a367 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -9,7 +9,19 @@ 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 {IFilterAdmin} from "../../NFTPool/intf/IFilterAdmin.sol"; +interface IFilter01 { + function init( + address filterAdmin, + address nftCollection, + bool[] memory switches, + uint256[] memory tokenRanges, + uint256[] memory nftAmounts, + uint256[] memory priceRules, + uint256[] memory spreadIds + ) external; +} contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { using SafeMath for uint256; @@ -37,17 +49,43 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { _DEFAULT_MAINTAINER_ = defaultMaintainer; } - //TODO:一笔交易 - - function createFilterAdmin( + function createNewNFTPool01( string memory name, string memory symbol, - uint256 fee - ) external returns(address) { + uint256 fee, + address nftCollection, + bool[] memory switches, + uint256[] memory tokenRanges, + uint256[] memory nftAmounts, + uint256[] memory priceRules, + uint256[] memory spreadIds + ) external returns(address newFilterAdmin) { + newFilterAdmin = ICloneFactory(_CLONE_FACTORY_).clone(_FILTER_ADMIN_TEMPLATE_); + address filter01 = createFilter01( + newFilterAdmin, + nftCollection, + switches, + tokenRanges, + nftAmounts, + priceRules, + spreadIds + ); + + address[] memory filters = new address[](1); + filters[0] = filter01; + + IFilterAdmin(newFilterAdmin).init( + msg.sender, + name, + symbol, + fee, + _NFT_POOL_FEE_MODEL_, + _DEFAULT_MAINTAINER_, + filters + ); } - function createFilter01( address filterAdmin, address nftCollection, @@ -56,11 +94,11 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { uint256[] memory nftAmounts, uint256[] memory priceRules, uint256[] memory spreadIds - ) external returns(address) { - + ) public returns(address newFilter01) { + newFilter01 = ICloneFactory(_CLONE_FACTORY_).clone(_FILTER_TEMPLATES_[1]); + IFilter01(newFilter01).init(filterAdmin, nftCollection, switches, tokenRanges, nftAmounts, priceRules, spreadIds); } - //====================== Ownable ======================== function changeDefaultMaintainer(address newMaintainer) external onlyOwner { _DEFAULT_MAINTAINER_ = newMaintainer; From 66891972b194b07d512f8ca943ef810f88d3c52c Mon Sep 17 00:00:00 2001 From: owen05 Date: Thu, 9 Sep 2021 11:07:21 +0800 Subject: [PATCH 05/42] dev --- README.md | 33 +++++------------------- contracts/NFTPool/impl/FilterAdmin.sol | 5 ---- contracts/NFTPool/impl/FilterModel01.sol | 4 +++ 3 files changed, 10 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 07f6e66..18ea3e9 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,13 @@ -# DODO V2: Help 1 Trillion People Issue Token +## Audit Scope -## Audit Report +- contracts/NFTPool/impl/FilterAdmin.sol -[Audited by Peckshield](https://github.com/DODOEX/contractV2/blob/main/audit/PeckShield-Audit-DODOV2-v1.0.pdf) +- contracts/NFTPool/impl/FilterModel01.sol -## Bug Bounty 💰 +- contracts/NFTPool/impl/NFTPoolFeeModel.sol -### Rewards +- contracts/external/ERC20/InitializableInternalMintableERC20.sol -Severity of bugs will be assessed under the [CVSS Risk Rating](https://www.first.org/cvss/calculator/3.0) scale, as follows: +- contracts/SmartRoute/proxies/DODONFTPoolProxy.sol - - Critical (9.0-10.0): Up to $100,000 - - High (7.0-8.9): Up to $10,000 - - Medium (4.0-6.9): Up to $5,000 - - Low (0.1-3.9): Up to $1,000 -In addition to assessing severity, rewards will be considered based on the impact of the discovered vulnerability as well as the level of difficulty in discovering such vulnerability. - -### Disclosure - -Any vulnerability or bug discovered must be reported only to the following email: contact@dodoex.io; must not be disclosed publicly; must not be disclosed to any other person, entity or email address prior to disclosure to the contact@dodoex.io email; and must not be disclosed in any way other than to the contact@dodoex.io email. In addition, disclosure to contact@dodoex.io must be made promptly following discovery of the vulnerability. Please include as much information about the vulnerability as possible, including: - - - The conditions on which reproducing the bug is contingent. - - The steps needed to reproduce the bug or, preferably, a proof of concept. - - The potential implications of the vulnerability being abused. - -A detailed report of a vulnerability increases the likelihood of a reward and may increase the reward amount. - -Anyone who reports a unique, previously-unreported vulnerability that results in a change to the code or a configuration change and who keeps such vulnerability confidential until it has been resolved by our engineers will be recognized publicly for their contribution, if agreed. - -## Contact Us - -Send E-mail to contact@dodoex.io \ No newline at end of file diff --git a/contracts/NFTPool/impl/FilterAdmin.sol b/contracts/NFTPool/impl/FilterAdmin.sol index a76fd91..2284311 100644 --- a/contracts/NFTPool/impl/FilterAdmin.sol +++ b/contracts/NFTPool/impl/FilterAdmin.sol @@ -134,8 +134,6 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { require(IFilterModel(filter).getAvaliableNFTOut() >= times, "EXCEED_MAX_AMOUNT"); uint256 totalPrice = 0; - uint256[] memory tokenIds; - uint256[] memory amounts; for(uint256 i = 0; i < times; i++) { totalPrice = totalPrice.add(IFilterModel(filter).getNFTRandomOutPrice()); (address nftContract, uint256 tokenId) = IFilterModel(filter).getRandomOutId(); @@ -222,10 +220,7 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { require(!isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); _FILTER_REGISTRY_.push(filter); } - - //TODO: remove Filter是否有必要? - //=============== Internal ============== function _nftInFeeTransfer(uint256 totalPrice) internal returns (uint256 poolFeeAmount, uint256 mtFeeAmount) { uint256 mtFeeRate = IFeeModel(_MT_FEE_MODEL_).getNFTInFee(address(this), msg.sender); diff --git a/contracts/NFTPool/impl/FilterModel01.sol b/contracts/NFTPool/impl/FilterModel01.sol index 233b274..f307daa 100644 --- a/contracts/NFTPool/impl/FilterModel01.sol +++ b/contracts/NFTPool/impl/FilterModel01.sol @@ -169,6 +169,10 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { (, totalPrice) = geometricCalc(base, _CR_RANDOM_OUT_, amount); } + function version() virtual external pure returns (string memory) { + return "FILTER_01 1.0.0"; + } + // ================= Ownable ================ function transferOutERC721(address nftContract, address assetTo, uint256 nftId) external onlyOwner { require(nftContract == _NFT_COLLECTION_, "WRONG_NFT_COLLECTION"); From aff42e7108ebd3d22e8d6b3d0d71d12d0c15e1dc Mon Sep 17 00:00:00 2001 From: owen05 Date: Thu, 9 Sep 2021 15:53:53 +0800 Subject: [PATCH 06/42] dev --- README.md | 2 +- contracts/NFTPool/impl/ControllerModel.sol | 100 ++++++++++++++++++ contracts/NFTPool/impl/FilterAdmin.sol | 46 +++++--- contracts/NFTPool/impl/FilterModel01.sol | 55 +++++++--- contracts/NFTPool/impl/NFTPoolFeeModel.sol | 85 --------------- .../{IFeeModel.sol => IControllerModel.sol} | 4 +- contracts/NFTPool/intf/IFilterAdmin.sol | 9 ++ .../SmartRoute/proxies/DODONFTPoolProxy.sol | 42 ++++++++ 8 files changed, 225 insertions(+), 118 deletions(-) create mode 100644 contracts/NFTPool/impl/ControllerModel.sol delete mode 100644 contracts/NFTPool/impl/NFTPoolFeeModel.sol rename contracts/NFTPool/intf/{IFeeModel.sol => IControllerModel.sol} (78%) diff --git a/README.md b/README.md index 18ea3e9..79a63e7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ - contracts/NFTPool/impl/FilterModel01.sol -- contracts/NFTPool/impl/NFTPoolFeeModel.sol +- contracts/NFTPool/impl/ControllerModel.sol - contracts/external/ERC20/InitializableInternalMintableERC20.sol diff --git a/contracts/NFTPool/impl/ControllerModel.sol b/contracts/NFTPool/impl/ControllerModel.sol new file mode 100644 index 0000000..b651837 --- /dev/null +++ b/contracts/NFTPool/impl/ControllerModel.sol @@ -0,0 +1,100 @@ +/* + + 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 {IERC20} from "../../intf/IERC20.sol"; +import {SafeMath} from "../../lib/SafeMath.sol"; + +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 + + struct FilterAdminFeeInfo { + uint256 nftInFee; + uint256 nftRandomOutFee; + uint256 nftTargetOutFee; + bool isSet; + } + + mapping(address => FilterAdminFeeInfo) filterAdminFees; + + mapping(address => bool) isEmergencyWithdraw; + + //==================== Event ===================== + event SetEmergencyWithdraw(address filter, bool isOpen); + + //==================== Ownable ==================== + + function addFilterAdminFeeInfo(address filterAdminAddr, uint256 nftInFee, uint256 nftRandomOutFee, uint256 nftTargetOutFee) external onlyOwner { + FilterAdminFeeInfo memory filterAdmin = FilterAdminFeeInfo({ + nftInFee: nftInFee, + nftRandomOutFee: nftRandomOutFee, + nftTargetOutFee: nftTargetOutFee, + isSet: true + }); + filterAdminFees[filterAdminAddr] = filterAdmin; + } + + function setFilterAdminFeeInfo(address filterAdminAddr, uint256 nftInFee, uint256 nftRandomOutFee, uint256 nftTargetOutFee) external onlyOwner { + filterAdminFees[filterAdminAddr].nftInFee = nftInFee; + filterAdminFees[filterAdminAddr].nftRandomOutFee = nftRandomOutFee; + filterAdminFees[filterAdminAddr].nftTargetOutFee = nftTargetOutFee; + } + + function setGlobalParam(uint256 nftInFee, uint256 nftRandomOutFee, uint256 nftTargetOutFee) external onlyOwner { + _GLOBAL_NFT_IN_FEE_ = nftInFee; + _GLOBAL_NFT_RANDOM_OUT_FEE_ = nftRandomOutFee; + _GLOBAL_NFT_TARGET_OUT_FEE_ = nftTargetOutFee; + } + + function setEmergencyWithdraw(address filter, bool isOpen) external onlyOwner { + isEmergencyWithdraw[filter] = isOpen; + emit SetEmergencyWithdraw(filter, isOpen); + } + + //===================== View ======================== + function getNFTInFee(address filterAdminAddr, address) external view returns(uint256) { + FilterAdminFeeInfo memory FilterAdminFeeInfo = filterAdminFees[filterAdminAddr]; + + if(FilterAdminFeeInfo.isSet) { + return FilterAdminFeeInfo.nftInFee; + }else { + return _GLOBAL_NFT_IN_FEE_; + } + } + + function getNFTRandomOutFee(address filterAdminAddr, address) external view returns(uint256) { + FilterAdminFeeInfo memory FilterAdminFeeInfo = filterAdminFees[filterAdminAddr]; + + if(FilterAdminFeeInfo.isSet) { + return FilterAdminFeeInfo.nftRandomOutFee; + }else { + return _GLOBAL_NFT_RANDOM_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 2284311..831635d 100644 --- a/contracts/NFTPool/impl/FilterAdmin.sol +++ b/contracts/NFTPool/impl/FilterAdmin.sol @@ -11,7 +11,7 @@ pragma experimental ABIEncoderV2; import {InitializableInternalMintableERC20} from "../../external/ERC20/InitializableInternalMintableERC20.sol"; import {SafeMath} from "../../lib/SafeMath.sol"; import {IFilterModel} from "../intf/IFilterModel.sol"; -import {IFeeModel} from "../intf/IFeeModel.sol"; +import {IControllerModel} from "../intf/IControllerModel.sol"; import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; import {DecimalMath} from "../../lib/DecimalMath.sol"; @@ -21,7 +21,7 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { // ============ Storage ============ address[] public _FILTER_REGISTRY_; uint256 public _FEE_; - address public _MT_FEE_MODEL_; + address public _CONTROLLER_MODEL_; address public _DEFAULT_MAINTAINER_; function init( @@ -36,14 +36,15 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { super.init(_owner, 0, _name, _symbol, 18); _FILTER_REGISTRY_ = filters; _FEE_ = fee; - _MT_FEE_MODEL_ = mtFeeModel; + _CONTROLLER_MODEL_ = mtFeeModel; _DEFAULT_MAINTAINER_ = defaultMaintainer; } function ERC721In( address filter, address nftContract, - uint256[] memory tokenIds + uint256[] memory tokenIds, + uint256 minMintAmount ) external preventReentrant @@ -62,14 +63,17 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); - _mint(msg.sender, totalPrice.sub(mtFeeAmount).sub(poolFeeAmount)); + uint256 actualMintAmount = totalPrice.sub(mtFeeAmount).sub(poolFeeAmount); + require(actualMintAmount >= minMintAmount, "MINT_AMOUNT_NOT_ENOUGH"); + _mint(msg.sender, actualMintAmount); } function ERC1155In( address filter, address nftContract, uint256[] memory tokenIds, - uint256[] memory amounts + uint256[] memory amounts, + uint256 minMintAmount ) external preventReentrant @@ -87,16 +91,18 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { (uint256 poolFeeAmount, uint256 mtFeeAmount) = _nftInFeeTransfer(totalPrice); if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); - - _mint(msg.sender, totalPrice.sub(mtFeeAmount).sub(poolFeeAmount)); + + uint256 actualMintAmount = totalPrice.sub(mtFeeAmount).sub(poolFeeAmount); + require(actualMintAmount >= minMintAmount, "MINT_AMOUNT_NOT_ENOUGH"); + _mint(msg.sender, actualMintAmount); IFilterModel(filter).transferBatchInERC1155(nftContract, msg.sender, tokenIds, amounts); } - function ERC721RandomOut( address filter, - uint256 times + uint256 times, + uint256 maxBurnAmount ) external preventReentrant @@ -116,14 +122,15 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); + require(totalPrice <= maxBurnAmount, "EXTRA_BURN_AMOUNT"); _burn(msg.sender, totalPrice); } - //TODO: amount == 1 function ERC1155RandomOut( address filter, - uint256 times + uint256 times, + uint256 maxBurnAmount ) external preventReentrant @@ -144,13 +151,15 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); + require(totalPrice <= maxBurnAmount, "EXTRA_BURN_AMOUNT"); _burn(msg.sender, totalPrice); } function ERC721TargetOut( address filter, address nftContract, - uint256[] memory tokenIds + uint256[] memory tokenIds, + uint256 maxBurnAmount ) external preventReentrant @@ -168,6 +177,7 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); + require(totalPrice <= maxBurnAmount, "EXTRA_BURN_AMOUNT"); _burn(msg.sender, totalPrice); } @@ -175,7 +185,8 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { address filter, address nftContract, uint256[] memory tokenIds, - uint256[] memory amounts + uint256[] memory amounts, + uint256 maxBurnAmount ) external preventReentrant @@ -192,6 +203,7 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); + require(totalPrice <= maxBurnAmount, "EXTRA_BURN_AMOUNT"); _burn(msg.sender, totalPrice); IFilterModel(filter).transferBatchOutERC1155(nftContract, msg.sender, tokenIds, amounts); @@ -223,19 +235,19 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { //=============== Internal ============== function _nftInFeeTransfer(uint256 totalPrice) internal returns (uint256 poolFeeAmount, uint256 mtFeeAmount) { - uint256 mtFeeRate = IFeeModel(_MT_FEE_MODEL_).getNFTInFee(address(this), msg.sender); + uint256 mtFeeRate = IControllerModel(_CONTROLLER_MODEL_).getNFTInFee(address(this), msg.sender); poolFeeAmount = DecimalMath.mulFloor(totalPrice, _FEE_); mtFeeAmount = DecimalMath.mulFloor(totalPrice, mtFeeRate); } function _nftRandomOutFeeTransfer(uint256 totalPrice) internal returns (uint256 poolFeeAmount, uint256 mtFeeAmount) { - uint256 mtFeeRate = IFeeModel(_MT_FEE_MODEL_).getNFTRandomOutFee(address(this), msg.sender); + uint256 mtFeeRate = IControllerModel(_CONTROLLER_MODEL_).getNFTRandomOutFee(address(this), msg.sender); poolFeeAmount = DecimalMath.mulFloor(totalPrice, _FEE_); mtFeeAmount = DecimalMath.mulFloor(totalPrice, mtFeeRate); } function _nftTargetOutFeeTransfer(uint256 totalPrice) internal returns (uint256 poolFeeAmount, uint256 mtFeeAmount) { - uint256 mtFeeRate = IFeeModel(_MT_FEE_MODEL_).getNFTTargetOutFee(address(this), msg.sender); + uint256 mtFeeRate = IControllerModel(_CONTROLLER_MODEL_).getNFTTargetOutFee(address(this), msg.sender); poolFeeAmount = DecimalMath.mulFloor(totalPrice, _FEE_); mtFeeAmount = DecimalMath.mulFloor(totalPrice, mtFeeRate); } diff --git a/contracts/NFTPool/impl/FilterModel01.sol b/contracts/NFTPool/impl/FilterModel01.sol index f307daa..7a765d3 100644 --- a/contracts/NFTPool/impl/FilterModel01.sol +++ b/contracts/NFTPool/impl/FilterModel01.sol @@ -11,6 +11,7 @@ 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 {IERC721} from "../../intf/IERC721.sol"; import {IERC721Receiver} from "../../intf/IERC721Receiver.sol"; import {DecimalMath} from "../../lib/DecimalMath.sol"; @@ -142,7 +143,11 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { //Pseudorandomness function getRandomOutId() external view returns (address nftCollection, uint256 nftId) { uint256 nftAmount = _TOKEN_IDS_.length; - uint256 idx = uint256(keccak256(abi.encodePacked(blockhash(block.number-1), gasleft(), msg.sender, nftAmount))) % nftAmount; + uint256 sumSeed; + for(uint256 i = 0; i < gasleft() % 10; i++) { + sumSeed = sumSeed.add(uint256(keccak256(abi.encodePacked(blockhash(block.number-1), gasleft(), msg.sender, nftAmount)))); + } + uint256 idx = sumSeed % nftAmount; nftCollection = _NFT_COLLECTION_; nftId = _TOKEN_IDS_[idx]; } @@ -176,19 +181,9 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { // ================= Ownable ================ function transferOutERC721(address nftContract, address assetTo, uint256 nftId) external onlyOwner { require(nftContract == _NFT_COLLECTION_, "WRONG_NFT_COLLECTION"); - uint256[] memory tokenIds = _TOKEN_IDS_; - uint256 i; - for (; i < tokenIds.length; i++) { - if (tokenIds[i] == nftId) { - tokenIds[i] = tokenIds[tokenIds.length - 1]; - break; - } - } - require(i < tokenIds.length, "NOT_EXIST_ID"); - _TOKEN_IDS_ = tokenIds; - _TOKEN_IDS_.pop(); - - IERC721(nftContract).safeTransferFrom(address(this), assetTo, nftId); + bool isRemove = removeTokenId(nftId); + if(isRemove) + IERC721(nftContract).safeTransferFrom(address(this), assetTo, nftId); } function transferInERC721(address nftContract, address assetFrom, uint256 nftId) external onlyOwner { @@ -259,6 +254,20 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { } } + function emergencyWithdraw(address[] memory nftContract, uint256[] memory tokenIds, address assetTo) external { + require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); + require(nftContract.length == tokenIds.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++) { + if(nftContract[i] == _NFT_COLLECTION_) { + removeTokenId(tokenIds[i]); + } + IERC721(nftContract[i]).safeTransferFrom(address(this), assetTo, tokenIds[i]); + } + } + // ============ Callback ============ function onERC721Received( address, @@ -278,4 +287,22 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { } newBase = base; } + + 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/impl/NFTPoolFeeModel.sol b/contracts/NFTPool/impl/NFTPoolFeeModel.sol deleted file mode 100644 index c5818a2..0000000 --- a/contracts/NFTPool/impl/NFTPoolFeeModel.sol +++ /dev/null @@ -1,85 +0,0 @@ -/* - - 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 {IERC20} from "../../intf/IERC20.sol"; -import {SafeMath} from "../../lib/SafeMath.sol"; - -contract NFTPoolFeeModel 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 - - struct FilterAdminInfo { - uint256 nftInFee; - uint256 nftRandomOutFee; - uint256 nftTargetOutFee; - bool isSet; - } - - mapping(address => FilterAdminInfo) filterAdmins; - - function addFilterAdminInfo(address filterAdminAddr, uint256 nftInFee, uint256 nftRandomOutFee, uint256 nftTargetOutFee) external onlyOwner { - FilterAdminInfo memory filterAdmin = FilterAdminInfo({ - nftInFee: nftInFee, - nftRandomOutFee: nftRandomOutFee, - nftTargetOutFee: nftTargetOutFee, - isSet: true - }); - filterAdmins[filterAdminAddr] = filterAdmin; - } - - function setFilterAdminInfo(address filterAdminAddr, uint256 nftInFee, uint256 nftRandomOutFee, uint256 nftTargetOutFee) external onlyOwner { - filterAdmins[filterAdminAddr].nftInFee = nftInFee; - filterAdmins[filterAdminAddr].nftRandomOutFee = nftRandomOutFee; - filterAdmins[filterAdminAddr].nftTargetOutFee = nftTargetOutFee; - } - - function setGlobalParam(uint256 nftInFee, uint256 nftRandomOutFee, uint256 nftTargetOutFee) external onlyOwner { - _GLOBAL_NFT_IN_FEE_ = nftInFee; - _GLOBAL_NFT_RANDOM_OUT_FEE_ = nftRandomOutFee; - _GLOBAL_NFT_TARGET_OUT_FEE_ = nftTargetOutFee; - } - - - function getNFTInFee(address filterAdminAddr, address) external view returns(uint256) { - FilterAdminInfo memory filterAdminInfo = filterAdmins[filterAdminAddr]; - - if(filterAdminInfo.isSet) { - return filterAdminInfo.nftInFee; - }else { - return _GLOBAL_NFT_IN_FEE_; - } - } - - - function getNFTRandomOutFee(address filterAdminAddr, address) external view returns(uint256) { - FilterAdminInfo memory filterAdminInfo = filterAdmins[filterAdminAddr]; - - if(filterAdminInfo.isSet) { - return filterAdminInfo.nftRandomOutFee; - }else { - return _GLOBAL_NFT_RANDOM_OUT_FEE_; - } - } - - function getNFTTargetOutFee(address filterAdminAddr, address) external view returns(uint256) { - FilterAdminInfo memory filterAdminInfo = filterAdmins[filterAdminAddr]; - - if(filterAdminInfo.isSet) { - return filterAdminInfo.nftTargetOutFee; - }else { - return _GLOBAL_NFT_TARGET_OUT_FEE_; - } - } - -} diff --git a/contracts/NFTPool/intf/IFeeModel.sol b/contracts/NFTPool/intf/IControllerModel.sol similarity index 78% rename from contracts/NFTPool/intf/IFeeModel.sol rename to contracts/NFTPool/intf/IControllerModel.sol index 79a745b..e8bfd1f 100644 --- a/contracts/NFTPool/intf/IFeeModel.sol +++ b/contracts/NFTPool/intf/IControllerModel.sol @@ -7,10 +7,12 @@ pragma solidity 0.6.9; -interface IFeeModel { +interface IControllerModel { function getNFTInFee(address filterAdminAddr, address user) external view returns(uint256); function getNFTRandomOutFee(address filterAdminAddr, address user) external view returns(uint256); function getNFTTargetOutFee(address filterAdminAddr, address user) external view returns(uint256); + + 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 cf1cb37..6dc4f5a 100644 --- a/contracts/NFTPool/intf/IFilterAdmin.sol +++ b/contracts/NFTPool/intf/IFilterAdmin.sol @@ -10,6 +10,8 @@ pragma solidity 0.6.9; interface IFilterAdmin { function _OWNER_() external returns (address); + function _CONTROLLER_MODEL_() external returns (address); + function init( address _owner, string memory _name, @@ -19,4 +21,11 @@ interface IFilterAdmin { address defaultMaintainer, address[] memory filters ) external; + + function ERC721In( + address filter, + address nftContract, + uint256[] memory tokenIds, + uint256 minMintAmount + ) external; } \ No newline at end of file diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index 4d2a367..359570f 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -10,6 +10,7 @@ import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; import {ICloneFactory} from "../../lib/CloneFactory.sol"; import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; import {IFilterAdmin} from "../../NFTPool/intf/IFilterAdmin.sol"; +import {IERC721} from "../../intf/IERC721.sol"; interface IFilter01 { function init( @@ -99,6 +100,31 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { IFilter01(newFilter01).init(filterAdmin, nftCollection, switches, tokenRanges, nftAmounts, priceRules, spreadIds); } + 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); + + uint256[] memory tokenIds = new uint256[1]; + tokenIds[0] = tokenId; + IFilterAdmin(filterAdmin).ERC721In(filter, nftContract, tokenIds, 0); + + + + } + + //====================== Ownable ======================== function changeDefaultMaintainer(address newMaintainer) external onlyOwner { _DEFAULT_MAINTAINER_ = newMaintainer; @@ -116,4 +142,20 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { _FILTER_TEMPLATES_[idx] = newFilterTemplate; emit SetFilterTemplate(idx, newFilterTemplate); } + + + //======================= Internal ===================== + function _generalApproveMax( + address token, + address to, + uint256 amount + ) internal { + uint256 allowance = IERC20(token).allowance(address(this), to); + if (allowance < amount) { + if (allowance > 0) { + IERC20(token).safeApprove(to, 0); + } + IERC20(token).safeApprove(to, uint256(-1)); + } + } } \ No newline at end of file From 3d1e543f652309b9f42a2a5baeb6b9913751eadd Mon Sep 17 00:00:00 2001 From: owen05 Date: Thu, 9 Sep 2021 16:18:27 +0800 Subject: [PATCH 07/42] nftPoolProxy --- contracts/NFTPool/impl/ControllerModel.sol | 18 ++++----- contracts/NFTPool/impl/FilterAdmin.sol | 33 ++++++++++------ contracts/NFTPool/intf/IFilterAdmin.sol | 2 +- .../SmartRoute/proxies/DODONFTPoolProxy.sol | 39 ++++++++++++++++++- 4 files changed, 68 insertions(+), 24 deletions(-) diff --git a/contracts/NFTPool/impl/ControllerModel.sol b/contracts/NFTPool/impl/ControllerModel.sol index b651837..1bded27 100644 --- a/contracts/NFTPool/impl/ControllerModel.sol +++ b/contracts/NFTPool/impl/ControllerModel.sol @@ -64,30 +64,30 @@ contract ControllerModel is InitializableOwnable { //===================== View ======================== function getNFTInFee(address filterAdminAddr, address) external view returns(uint256) { - FilterAdminFeeInfo memory FilterAdminFeeInfo = filterAdminFees[filterAdminAddr]; + FilterAdminFeeInfo memory filterAdminFeeInfo = filterAdminFees[filterAdminAddr]; - if(FilterAdminFeeInfo.isSet) { - return FilterAdminFeeInfo.nftInFee; + if(filterAdminFeeInfo.isSet) { + return filterAdminFeeInfo.nftInFee; }else { return _GLOBAL_NFT_IN_FEE_; } } function getNFTRandomOutFee(address filterAdminAddr, address) external view returns(uint256) { - FilterAdminFeeInfo memory FilterAdminFeeInfo = filterAdminFees[filterAdminAddr]; + FilterAdminFeeInfo memory filterAdminFeeInfo = filterAdminFees[filterAdminAddr]; - if(FilterAdminFeeInfo.isSet) { - return FilterAdminFeeInfo.nftRandomOutFee; + if(filterAdminFeeInfo.isSet) { + return filterAdminFeeInfo.nftRandomOutFee; }else { return _GLOBAL_NFT_RANDOM_OUT_FEE_; } } function getNFTTargetOutFee(address filterAdminAddr, address) external view returns(uint256) { - FilterAdminFeeInfo memory FilterAdminFeeInfo = filterAdminFees[filterAdminAddr]; + FilterAdminFeeInfo memory filterAdminFeeInfo = filterAdminFees[filterAdminAddr]; - if(FilterAdminFeeInfo.isSet) { - return FilterAdminFeeInfo.nftTargetOutFee; + if(filterAdminFeeInfo.isSet) { + return filterAdminFeeInfo.nftTargetOutFee; }else { return _GLOBAL_NFT_TARGET_OUT_FEE_; } diff --git a/contracts/NFTPool/impl/FilterAdmin.sol b/contracts/NFTPool/impl/FilterAdmin.sol index 831635d..760d869 100644 --- a/contracts/NFTPool/impl/FilterAdmin.sol +++ b/contracts/NFTPool/impl/FilterAdmin.sol @@ -48,6 +48,7 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { ) external preventReentrant + returns (uint256 actualMintAmount) { require(isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); require(IFilterModel(filter)._NFT_IN_SWITCH_(), "NFT_IN_CLOSED"); @@ -63,7 +64,7 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); - uint256 actualMintAmount = totalPrice.sub(mtFeeAmount).sub(poolFeeAmount); + actualMintAmount = totalPrice.sub(mtFeeAmount).sub(poolFeeAmount); require(actualMintAmount >= minMintAmount, "MINT_AMOUNT_NOT_ENOUGH"); _mint(msg.sender, actualMintAmount); } @@ -77,6 +78,7 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { ) external preventReentrant + returns (uint256 actualMintAmount) { require(tokenIds.length == amounts.length, "PARAMS_NOT_MATCH"); require(isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); @@ -92,7 +94,7 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); - uint256 actualMintAmount = totalPrice.sub(mtFeeAmount).sub(poolFeeAmount); + actualMintAmount = totalPrice.sub(mtFeeAmount).sub(poolFeeAmount); require(actualMintAmount >= minMintAmount, "MINT_AMOUNT_NOT_ENOUGH"); _mint(msg.sender, actualMintAmount); @@ -105,7 +107,8 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { uint256 maxBurnAmount ) external - preventReentrant + preventReentrant + returns (uint256 actualBurnAmount) { require(msg.sender == tx.origin, "ONLY_ALLOW_EOA"); require(isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); @@ -121,9 +124,9 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { (uint256 poolFeeAmount, uint256 mtFeeAmount) = _nftRandomOutFeeTransfer(totalPrice); if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); - - require(totalPrice <= maxBurnAmount, "EXTRA_BURN_AMOUNT"); - _burn(msg.sender, totalPrice); + actualBurnAmount = totalPrice; + require(actualBurnAmount <= maxBurnAmount, "EXTRA_BURN_AMOUNT"); + _burn(msg.sender, actualBurnAmount); } @@ -134,6 +137,7 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { ) external preventReentrant + returns (uint256 actualBurnAmount) { require(msg.sender == tx.origin, "ONLY_ALLOW_EOA"); require(isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); @@ -151,8 +155,9 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); - require(totalPrice <= maxBurnAmount, "EXTRA_BURN_AMOUNT"); - _burn(msg.sender, totalPrice); + actualBurnAmount = totalPrice; + require(actualBurnAmount <= maxBurnAmount, "EXTRA_BURN_AMOUNT"); + _burn(msg.sender, actualBurnAmount); } function ERC721TargetOut( @@ -163,6 +168,7 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { ) external preventReentrant + returns(uint256 actualBurnAmount) { require(isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); require(IFilterModel(filter)._NFT_TARGET_SWITCH_(), "NFT_TARGET_OUT_CLOSED"); @@ -177,8 +183,9 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); - require(totalPrice <= maxBurnAmount, "EXTRA_BURN_AMOUNT"); - _burn(msg.sender, totalPrice); + actualBurnAmount = totalPrice; + require(actualBurnAmount <= maxBurnAmount, "EXTRA_BURN_AMOUNT"); + _burn(msg.sender, actualBurnAmount); } function ERC1155TargetOut( @@ -190,6 +197,7 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { ) external preventReentrant + returns(uint256 actualBurnAmount) { require(tokenIds.length == amounts.length, "PARAMS_NOT_MATCH"); require(isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); @@ -203,8 +211,9 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); - require(totalPrice <= maxBurnAmount, "EXTRA_BURN_AMOUNT"); - _burn(msg.sender, totalPrice); + actualBurnAmount = totalPrice; + require(actualBurnAmount <= maxBurnAmount, "EXTRA_BURN_AMOUNT"); + _burn(msg.sender, actualBurnAmount); IFilterModel(filter).transferBatchOutERC1155(nftContract, msg.sender, tokenIds, amounts); } diff --git a/contracts/NFTPool/intf/IFilterAdmin.sol b/contracts/NFTPool/intf/IFilterAdmin.sol index 6dc4f5a..94aa8c2 100644 --- a/contracts/NFTPool/intf/IFilterAdmin.sol +++ b/contracts/NFTPool/intf/IFilterAdmin.sol @@ -27,5 +27,5 @@ interface IFilterAdmin { address nftContract, uint256[] memory tokenIds, uint256 minMintAmount - ) external; + ) external returns(uint256 actualMintAmount); } \ No newline at end of file diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index 359570f..f084c20 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -11,6 +11,8 @@ import {ICloneFactory} from "../../lib/CloneFactory.sol"; import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; import {IFilterAdmin} from "../../NFTPool/intf/IFilterAdmin.sol"; import {IERC721} from "../../intf/IERC721.sol"; +import {IERC20} from "../../intf/IERC20.sol"; +import {SafeERC20} from "../../lib/SafeERC20.sol"; interface IFilter01 { function init( @@ -26,8 +28,10 @@ interface IFilter01 { contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { using SafeMath for uint256; + using SafeERC20 for IERC20; // ============ Storage ============ + address constant _ETH_ADDRESS_ = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; mapping(uint256 => address) public _FILTER_TEMPLATES_; address public _FILTER_ADMIN_TEMPLATE_; address public _DEFAULT_MAINTAINER_; @@ -116,12 +120,18 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { IERC721(nftContract).safeTransferFrom(msg.sender, address(this), tokenId); IERC721(nftContract).approve(filter, tokenId); - uint256[] memory tokenIds = new uint256[1]; + uint256[] memory tokenIds = new uint256[](1); tokenIds[0] = tokenId; - IFilterAdmin(filterAdmin).ERC721In(filter, nftContract, tokenIds, 0); + uint256 mintAmount = IFilterAdmin(filterAdmin).ERC721In(filter, nftContract, tokenIds, 0); + _generalApproveMax(filterAdmin, dodoApprove, mintAmount); + (bool success, ) = dodoProxy.call(dodoSwapData); + require(success, "API_SWAP_FAILED"); + uint256 returnAmount = _generalBalanceOf(toToken, address(this)); + + _generalTransfer(toToken, msg.sender, returnAmount); } @@ -158,4 +168,29 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { IERC20(token).safeApprove(to, uint256(-1)); } } + + function _generalBalanceOf( + address token, + address who + ) internal view returns (uint256) { + if (token == _ETH_ADDRESS_) { + return who.balance; + } else { + return IERC20(token).balanceOf(who); + } + } + + function _generalTransfer( + address token, + address payable to, + uint256 amount + ) internal { + if (amount > 0) { + if (token == _ETH_ADDRESS_) { + to.transfer(amount); + } else { + IERC20(token).safeTransfer(to, amount); + } + } + } } \ No newline at end of file From 72b81731bbef897cfe87cabbcef82b135689fe77 Mon Sep 17 00:00:00 2001 From: mingda Date: Thu, 9 Sep 2021 17:45:49 +0800 Subject: [PATCH 08/42] user call filter instead of admin --- contracts/NFTPool/impl/FilterModel01.sol | 178 +++++++++-------------- 1 file changed, 69 insertions(+), 109 deletions(-) diff --git a/contracts/NFTPool/impl/FilterModel01.sol b/contracts/NFTPool/impl/FilterModel01.sol index 7a765d3..9de4c06 100644 --- a/contracts/NFTPool/impl/FilterModel01.sol +++ b/contracts/NFTPool/impl/FilterModel01.sol @@ -24,12 +24,10 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { address public _NFT_COLLECTION_; uint256 public _TOKEN_ID_START_; uint256 public _TOKEN_ID_END_; + //tokenId => isRegistered mapping(uint256 => bool) public _SPREAD_IDS_REGISTRY_; - uint256 public _MAX_NFT_AMOUNT_; - uint256 public _MIN_NFT_AMOUNT_; - //For NFT IN uint256 public _GS_START_IN_; uint256 public _CR_IN_; @@ -45,60 +43,35 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { uint256 public _CR_TARGET_OUT_; bool public _NFT_TARGET_SWITCH_ = true; - uint256[] public _TOKEN_IDS_; + mapping(uint256=>uint256) public _INTERNAL_TOKEN_IDS_; + uint256 public _NFT_AMOUNT_; + uint256 public _MAX_NFT_AMOUNT_; + uint256 public _MIN_NFT_AMOUNT_; function init( address filterAdmin, address nftCollection, - bool[] memory switches, - uint256[] memory tokenRanges, - uint256[] memory nftAmounts, - uint256[] memory priceRules, - uint256[] memory spreadIds ) external { - require(priceRules[1] != 0, "CR_IN_INVALID"); - require(priceRules[3] != 0, "CR_RANDOM_OUT_INVALID"); - require(priceRules[5] != 0, "CR_TARGET_OUT_INVALID"); - require(tokenRanges[1] >= tokenRanges[0], "TOKEN_RANGE_INVALID"); - require(nftAmounts[0] >= nftAmounts[1], "AMOUNT_INVALID"); - initOwner(filterAdmin); _NFT_COLLECTION_ = nftCollection; - - _TOKEN_ID_START_ = tokenRanges[0]; - _TOKEN_ID_END_ = tokenRanges[1]; - - _MAX_NFT_AMOUNT_ = nftAmounts[0]; - _MIN_NFT_AMOUNT_ = nftAmounts[1]; - - _GS_START_IN_ = priceRules[0]; - _CR_IN_ = priceRules[1]; - _NFT_IN_SWITCH_ = switches[0]; - - _GS_START_RANDOM_OUT_ = priceRules[2]; - _CR_RANDOM_OUT_ = priceRules[3]; - _NFT_RANDOM_SWITCH_ = switches[1]; - - _GS_START_TARGET_OUT_ = priceRules[4]; - _CR_TARGET_OUT_ = priceRules[5]; - _NFT_TARGET_SWITCH_ = switches[2]; - - for(uint256 i = 0; i < spreadIds.length; i++) { - _SPREAD_IDS_REGISTRY_[spreadIds[i]] = true; - } } //==================== View ================== - function isFilterERC721Pass(address nftCollectionAddress, uint256 nftId) external view returns (bool) { + + function isNFTValid(address nftCollectionAddress, uint256 nftId) external view returns (bool) { if(nftCollectionAddress == _NFT_COLLECTION_) { - if((nftId >= _TOKEN_ID_START_ && nftId <= _TOKEN_ID_END_) || _SPREAD_IDS_REGISTRY_[nftId]) { + isIDValid(nftId); + } else { + return false; + } + } + + function isIDValid(uint256 nftId) public view returns(bool){ + if((nftId >= _TOKEN_ID_START_ && nftId <= _TOKEN_ID_END_) || _SPREAD_IDS_REGISTRY_[nftId]) { return true; } else { return false; } - } else { - return false; - } } function getAvaliableNFTIn() public view returns(uint256) { @@ -117,86 +90,63 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { } } - function getNFTInPrice(address, uint256) external view returns(uint256) { - (uint256 price, ) = geometricCalc(_GS_START_IN_,_CR_IN_, _TOKEN_IDS_.length); - return price; - } - - function getNFTRandomOutPrice() external view returns (uint256) { - require(_TOKEN_IDS_.length != 0, "EMPTY"); - - (uint256 price, ) = geometricCalc(_GS_START_RANDOM_OUT_,_CR_RANDOM_OUT_, _TOKEN_IDS_.length); - return price; - } - - function getNFTTargetOutPrice(address, uint256) external view returns (uint256) { - require(_TOKEN_IDS_.length != 0, "EMPTY"); - - (uint256 price, ) = geometricCalc(_GS_START_TARGET_OUT_,_CR_TARGET_OUT_, _TOKEN_IDS_.length); - return price; - } - function supportsInterface(bytes4 interfaceId) public view returns (bool) { return interfaceId == type(IERC721Receiver).interfaceId; } - //Pseudorandomness - function getRandomOutId() external view returns (address nftCollection, uint256 nftId) { - uint256 nftAmount = _TOKEN_IDS_.length; - uint256 sumSeed; - for(uint256 i = 0; i < gasleft() % 10; i++) { - sumSeed = sumSeed.add(uint256(keccak256(abi.encodePacked(blockhash(block.number-1), gasleft(), msg.sender, nftAmount)))); - } - uint256 idx = sumSeed % nftAmount; - nftCollection = _NFT_COLLECTION_; - nftId = _TOKEN_IDS_[idx]; + function queryNFTIn(uint256 NFTInAmount) external view returns (uint256 rawReceive, uint256 receive) { + require(NFTInAmount <= getAvaliableNFTIn(), "EXCEDD_IN_AMOUNT"); + rawReceive = geometricCalc(_GS_START_IN_, _CR_IN_, _TOKEN_IDS_.length, _TOKEN_IDS_.length+amount); + receive = IFilterAdmin(_OWNER_).chargeMintFee(rawReceive); } - - function getTotalNFTInPrice(uint256 amount) external view returns (uint256 totalPrice) { - require(amount <= getAvaliableNFTIn(), "EXCEDD_IN_AMOUNT"); - - (uint256 base, ) = geometricCalc(_GS_START_IN_,_CR_IN_, _TOKEN_IDS_.length); - (, totalPrice) = geometricCalc(base, _CR_IN_, amount); + function queryNFTTargetOut(uint256 NFTOutAmount) external view returns (uint256 rawPay, uint256 pay) { + require(NFTOutAmount <= getAvaliableNFTOut(), "EXCEED_OUT_AMOUNT"); + rawPay = geometricCalc(_GS_START_TARGET_OUT_,_CR_TARGET_OUT_, _TOKEN_IDS_.length-NFTOutAmount,_TOKEN_IDS_.length); + pay = IFilterAdmin(_OWNER_).chargeBurnFee(rawPay); } - function getTotalTargetNFTOutPrice(uint256 amount) external view returns (uint256 totalPrice) { - require(amount <= getAvaliableNFTOut(), "EXCEED_OUT_AMOUNT"); - - (uint256 base, ) = geometricCalc(_GS_START_TARGET_OUT_,_CR_TARGET_OUT_, _TOKEN_IDS_.length); - (, totalPrice) = geometricCalc(base, _CR_TARGET_OUT_, amount); - } - - function getTotalRandomNFTOutPrice(uint256 amount) external view returns (uint256 totalPrice) { - require(amount <= getAvaliableNFTOut(), "EXCEED_OUT_AMOUNT"); - - (uint256 base, ) = geometricCalc(_GS_START_RANDOM_OUT_,_CR_RANDOM_OUT_, _TOKEN_IDS_.length); - (, totalPrice) = geometricCalc(base, _CR_RANDOM_OUT_, amount); + function queryNFTRandomOut(uint256 NFTOutAmount) external view returns (uint256 rawPay, uint256 pay) { + require(NFTOutAmount <= getAvaliableNFTOut(), "EXCEED_OUT_AMOUNT"); + rawPay = geometricCalc(_GS_START_RANDOM_OUT_,_CR_RANDOM_OUT_, _TOKEN_IDS_.length-NFTOutAmount,_TOKEN_IDS_.length); + pay = IFilterAdmin(_OWNER_).chargeBurnFee(rawPay); } function version() virtual external pure returns (string memory) { - return "FILTER_01 1.0.0"; + return "FILTER_1_ERC721 1.0.0"; + } + + // ================= Trading ================ + + function ERC721In(uint256[] memory tokenIds, address to) external returns(uint256 received) { + (uint256 rawReceive,) = queryNFTIn(tokenIds.length); + for (uint256 i = 0; i < tokenIds.length; i++) { + IFilterAdmin(_OWNER_).transferNFT(msg.sender, _NFT_COLLECTION_, tokenIds[i]); + _INTERNAL_TOKEN_IDS_[_NFT_AMOUNT_+i] = tokenIds[i]; + } + _NFT_AMOUNT_ += tokenIds.length; + received = IFilterAdmin(_OWNER_).mintFragTo(to, rawReceive); + } + + function ERC721TargetOut(uint256[] memory internalIDs, address to) external returns(uint256 paid) { + (uint256 rawPay, ) = queryNFTOut(tokenIds.length); + for (uint256 index = 0; index < array.length; index++) { + uint256 tokenId = _INTERNAL_TOKEN_IDS_[internalIDs[i]]; + require(isIDValid(tokenId), "NFT_NOT_LISTED"); + _transferOutERC721(to, tokenId, internalIDs[i]); + } + paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); + } + + function ERC721RandomOut(uint256 number, address to) external returns (uint256 paid) {} + + function _transferOutERC721(address to, uint256 tokenId, uint256 internalId) internal { + IERC721(_NFT_COLLECTION_).safeTransfer(to, tokenId); + _INTERNAL_TOKEN_IDS_[internalId] = _INTERNAL_TOKEN_IDS_[_NFT_AMOUNT_-1]; + _NFT_AMOUNT_-=1; } // ================= Ownable ================ - function transferOutERC721(address nftContract, address assetTo, uint256 nftId) external onlyOwner { - require(nftContract == _NFT_COLLECTION_, "WRONG_NFT_COLLECTION"); - bool isRemove = removeTokenId(nftId); - if(isRemove) - IERC721(nftContract).safeTransferFrom(address(this), assetTo, nftId); - } - - function transferInERC721(address nftContract, address assetFrom, uint256 nftId) external onlyOwner { - require(nftContract == _NFT_COLLECTION_, "WRONG_NFT_COLLECTION"); - uint256 i; - for(; i < _TOKEN_IDS_.length; i++) { - if(_TOKEN_IDS_[i] == nftId) break; - } - require(i == _TOKEN_IDS_.length, "EXIST_ID"); - - _TOKEN_IDS_.push(nftId); - IERC721(nftContract).safeTransferFrom(assetFrom, address(this), nftId); - } function changeNFTInPrice(uint256 newGsStart, uint256 newCr) external { require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); @@ -305,4 +255,14 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { return false; } } -} \ No newline at end of file +} //Pseudorandomness + function getRandomOutId() external view returns (address nftCollection, uint256 nftId) { + uint256 nftAmount = _TOKEN_IDS_.length; + uint256 sumSeed; + for(uint256 i = 0; i < gasleft() % 10; i++) { + sumSeed = sumSeed.add(uint256(keccak256(abi.encodePacked(blockhash(block.number-1), gasleft(), msg.sender, nftAmount)))); + } + uint256 idx = sumSeed % nftAmount; + nftCollection = _NFT_COLLECTION_; + nftId = _TOKEN_IDS_[idx]; + } \ No newline at end of file From e7f6a20cef45aa16730db1c936702373a63c2400 Mon Sep 17 00:00:00 2001 From: mingda Date: Thu, 9 Sep 2021 19:27:13 +0800 Subject: [PATCH 09/42] Filter ERC721 first version --- contracts/NFTPool/impl/FilterModel01.sol | 245 ++++++++++++----------- contracts/NFTPool/intf/IFilterAdmin.sol | 16 +- 2 files changed, 135 insertions(+), 126 deletions(-) diff --git a/contracts/NFTPool/impl/FilterModel01.sol b/contracts/NFTPool/impl/FilterModel01.sol index 9de4c06..17a52dc 100644 --- a/contracts/NFTPool/impl/FilterModel01.sol +++ b/contracts/NFTPool/impl/FilterModel01.sol @@ -22,31 +22,34 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { //=================== Storage =================== address public _NFT_COLLECTION_; - uint256 public _TOKEN_ID_START_; - uint256 public _TOKEN_ID_END_; - + uint256 public _NFT_ID_START_; + uint256 public _NFT_ID_END_; + //tokenId => isRegistered mapping(uint256 => bool) public _SPREAD_IDS_REGISTRY_; + 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_ = true; + 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_ = true; + 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_ = true; - - mapping(uint256=>uint256) public _INTERNAL_TOKEN_IDS_; - uint256 public _NFT_AMOUNT_; - uint256 public _MAX_NFT_AMOUNT_; - uint256 public _MIN_NFT_AMOUNT_; + bool public _NFT_TARGET_SWITCH_ = false; function init( address filterAdmin, @@ -56,17 +59,17 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { _NFT_COLLECTION_ = nftCollection; } - //==================== View ================== + //==================== Query ================== function isNFTValid(address nftCollectionAddress, uint256 nftId) external view returns (bool) { if(nftCollectionAddress == _NFT_COLLECTION_) { - isIDValid(nftId); + isNFTIDValid(nftId); } else { return false; } } - function isIDValid(uint256 nftId) public view returns(bool){ + function isNFTIDValid(uint256 nftId) public view returns(bool){ if((nftId >= _TOKEN_ID_START_ && nftId <= _TOKEN_ID_END_) || _SPREAD_IDS_REGISTRY_[nftId]) { return true; } else { @@ -90,83 +93,143 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { } } - function supportsInterface(bytes4 interfaceId) public view returns (bool) { - return interfaceId == type(IERC721Receiver).interfaceId; - } - function queryNFTIn(uint256 NFTInAmount) external view returns (uint256 rawReceive, uint256 receive) { require(NFTInAmount <= getAvaliableNFTIn(), "EXCEDD_IN_AMOUNT"); - rawReceive = geometricCalc(_GS_START_IN_, _CR_IN_, _TOKEN_IDS_.length, _TOKEN_IDS_.length+amount); - receive = IFilterAdmin(_OWNER_).chargeMintFee(rawReceive); + rawReceive = geometricCalc(_GS_START_IN_, _CR_IN_, _NFT_AMOUNT_, _NFT_AMOUNT_+NFTInAmount); + receive = IFilterAdmin(_OWNER_).queryChargeMintFee(rawReceive); } function queryNFTTargetOut(uint256 NFTOutAmount) external view returns (uint256 rawPay, uint256 pay) { require(NFTOutAmount <= getAvaliableNFTOut(), "EXCEED_OUT_AMOUNT"); - rawPay = geometricCalc(_GS_START_TARGET_OUT_,_CR_TARGET_OUT_, _TOKEN_IDS_.length-NFTOutAmount,_TOKEN_IDS_.length); - pay = IFilterAdmin(_OWNER_).chargeBurnFee(rawPay); + rawPay = geometricCalc(_GS_START_TARGET_OUT_,_CR_TARGET_OUT_, _NFT_AMOUNT_-NFTOutAmount,_NFT_AMOUNT_); + pay = IFilterAdmin(_OWNER_).queryChargeBurnFee(rawPay); } function queryNFTRandomOut(uint256 NFTOutAmount) external view returns (uint256 rawPay, uint256 pay) { require(NFTOutAmount <= getAvaliableNFTOut(), "EXCEED_OUT_AMOUNT"); - rawPay = geometricCalc(_GS_START_RANDOM_OUT_,_CR_RANDOM_OUT_, _TOKEN_IDS_.length-NFTOutAmount,_TOKEN_IDS_.length); + rawPay = geometricCalc(_GS_START_RANDOM_OUT_,_CR_RANDOM_OUT_, _NFT_AMOUNT_-NFTOutAmount,_NFT_AMOUNT_); pay = IFilterAdmin(_OWNER_).chargeBurnFee(rawPay); } - function version() virtual external pure returns (string memory) { - return "FILTER_1_ERC721 1.0.0"; - } - // ================= Trading ================ - function ERC721In(uint256[] memory tokenIds, address to) external returns(uint256 received) { - (uint256 rawReceive,) = queryNFTIn(tokenIds.length); + function ERC721In(uint256[] memory tokenIds, address to) external preventReentrant returns(uint256 received) { for (uint256 i = 0; i < tokenIds.length; i++) { - IFilterAdmin(_OWNER_).transferNFT(msg.sender, _NFT_COLLECTION_, tokenIds[i]); - _INTERNAL_TOKEN_IDS_[_NFT_AMOUNT_+i] = tokenIds[i]; + require(!_NFT_RESERVE_[tokenIds[i]] && IERC721(_NFT_COLLECTION_).ownerOf(tokenIds[i])==address(this), "NFT_NOT_SEND"); + _NFT_IDS_.push(tokenId); + _NFT_RESERVE_[tokenIds[i]] = 1; } - _NFT_AMOUNT_ += tokenIds.length; + (uint256 rawReceive,) = queryNFTIn(tokenIds.length); received = IFilterAdmin(_OWNER_).mintFragTo(to, rawReceive); } - function ERC721TargetOut(uint256[] memory internalIDs, address to) external returns(uint256 paid) { + function ERC721TargetOut(uint256[] memory indexes, address to) external preventReentrant returns(uint256 paid) { (uint256 rawPay, ) = queryNFTOut(tokenIds.length); - for (uint256 index = 0; index < array.length; index++) { - uint256 tokenId = _INTERNAL_TOKEN_IDS_[internalIDs[i]]; - require(isIDValid(tokenId), "NFT_NOT_LISTED"); - _transferOutERC721(to, tokenId, internalIDs[i]); - } paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); + for (uint256 i = 0; i < indexes.length; i++) { + _transferOutERC721(to, indexes[i]); + } } - function ERC721RandomOut(uint256 number, address to) external returns (uint256 paid) {} + function ERC721RandomOut(uint256 amount, address to) external preventReentrant returns (uint256 paid) { + (uint256 rawPay, ) = queryNFTOut(amount); + paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); + for (uint256 i = 0; i < amount; i++) { + _transferOutERC721(to, getRandomOutId()); + } + } - function _transferOutERC721(address to, uint256 tokenId, uint256 internalId) internal { + // ============ Transfer ============= + + function onERC721Received( + address, + address, + uint256 tokenId, + 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); - _INTERNAL_TOKEN_IDS_[internalId] = _INTERNAL_TOKEN_IDS_[_NFT_AMOUNT_-1]; - _NFT_AMOUNT_-=1; + _TOKEN_IDS_[index] = _TOKEN_IDS_[_TOKEN_IDS_.length - 1]; + _TOKEN_IDS_.pop(); + _NFT_RESERVE_[tokenId] = 0; } + function emergencyWithdraw(address[] memory nftContract, uint256[] memory tokenIds, address to) external { + require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); + require(nftContract.length == tokenIds.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++) { + 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; + } + IERC721(nftContract[i]).safeTransfer(to, tokenId); + } + } + + + // ============ 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 getRandomOutId() external view returns (uint256 index) { + uint256 nftAmount = _TOKEN_IDS_.length; + index = uint256(keccak256(abi.encodePacked(tx.origin, blockhash(block.number-1), gasleft()))) % nftAmount; + } + + // ================= Ownable ================ - function changeNFTInPrice(uint256 newGsStart, uint256 newCr) external { + function changeNFTInPrice(uint256 newGsStart, uint256 newCr, bool switch) external { require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); - require(newCr != 0, "CR_ZERO"); - _GS_START_IN_ = newGsStart; - _CR_IN_ = newCr; + if (!switch) { + _NFT_IN_SWITCH_ = false; + } else { + require(newCr>DecimalMath.ONE, "CR_ZERO"); + _GS_START_IN_ = newGsStart; + _CR_IN_ = newCr; + _NFT_IN_SWITCH_ = true; + } } - function changeNFTROutPrice(uint256 newGsStart, uint256 newCr) external { + function changeNFTRandomInPrice(uint256 newGsStart, uint256 newCr, bool switch) external { require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); - require(newCr != 0, "CR_ZERO"); - _GS_START_RANDOM_OUT_ = newGsStart; - _CR_RANDOM_OUT_ = newCr; + if (!switch) { + _NFT_RANDOM_OUT_SWITCH_ = false; + } else { + require(newCr>DecimalMath.ONE, "CR_ZERO"); + _GS_START_RANDOM_OUT_ = newGsStart; + _CR_RANDOM_OUT_ = newCr; + _NFT_RANDOM_OUT_SWITCH_ = true; + } } - function changeNFTTOutPrice(uint256 newGsStart, uint256 newCr) external { + function changeNFTTargetOutPrice(uint256 newGsStart, uint256 newCr, bool switch) external { require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); - require(newCr != 0, "CR_ZERO"); - _GS_START_TARGET_OUT_ = newGsStart; - _CR_TARGET_OUT_ = newCr; + if (!switch) { + _NFT_TARGET_OUT_SWITCH_ = false; + } else { + require(newCr>DecimalMath.ONE, "CR_ZERO"); + _GS_START_TARGET_OUT_ = newGsStart; + _CR_TARGET_OUT_ = newCr; + _NFT_TARGET_OUT_SWITCH_ = true; + } } function changeNFTAmount(uint256 maxNFTAmount, uint256 minNFTAmount) external { @@ -176,17 +239,6 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { _MIN_NFT_AMOUNT_ = minNFTAmount; } - function changeSwitches( - bool newNFTInSwitch, - bool newNFTRandomSwitch, - bool newNFTTargetSwitch - ) external { - require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); - _NFT_IN_SWITCH_ = newNFTInSwitch; - _NFT_RANDOM_SWITCH_ = newNFTRandomSwitch; - _NFT_TARGET_SWITCH_ = newNFTTargetSwitch; - } - function changeTokenIdRange(uint256 tokenIdStart, uint256 tokenIdEnd) external { require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); require(tokenIdStart <= tokenIdEnd, "TOKEN_RANGE_INVALID"); @@ -204,65 +256,14 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { } } - function emergencyWithdraw(address[] memory nftContract, uint256[] memory tokenIds, address assetTo) external { - require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); - require(nftContract.length == tokenIds.length, "PARAM_INVALID"); - address controllerModel = IFilterAdmin(_OWNER_)._CONTROLLER_MODEL_(); - require(IControllerModel(controllerModel).getEmergencySwitch(address(this)), "NOT_OPEN"); + // ============ Support ============ - for(uint256 i = 0; i< nftContract.length; i++) { - if(nftContract[i] == _NFT_COLLECTION_) { - removeTokenId(tokenIds[i]); - } - IERC721(nftContract[i]).safeTransferFrom(address(this), assetTo, tokenIds[i]); - } + function supportsInterface(bytes4 interfaceId) public view returns (bool) { + return interfaceId == type(IERC721Receiver).interfaceId; } - // ============ Callback ============ - function onERC721Received( - address, - address, - uint256 tokenId, - bytes calldata - ) external override returns (bytes4) { - return IERC721Receiver.onERC721Received.selector; - } - // ============ Internal ============= - 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 version() virtual external pure returns (string memory) { + return "FILTER_1_ERC721 1.0.0"; } - - 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; - } - } -} //Pseudorandomness - function getRandomOutId() external view returns (address nftCollection, uint256 nftId) { - uint256 nftAmount = _TOKEN_IDS_.length; - uint256 sumSeed; - for(uint256 i = 0; i < gasleft() % 10; i++) { - sumSeed = sumSeed.add(uint256(keccak256(abi.encodePacked(blockhash(block.number-1), gasleft(), msg.sender, nftAmount)))); - } - uint256 idx = sumSeed % nftAmount; - nftCollection = _NFT_COLLECTION_; - nftId = _TOKEN_IDS_[idx]; - } \ No newline at end of file +} \ No newline at end of file diff --git a/contracts/NFTPool/intf/IFilterAdmin.sol b/contracts/NFTPool/intf/IFilterAdmin.sol index 94aa8c2..06d5f3d 100644 --- a/contracts/NFTPool/intf/IFilterAdmin.sol +++ b/contracts/NFTPool/intf/IFilterAdmin.sol @@ -23,9 +23,17 @@ interface IFilterAdmin { ) external; function ERC721In( - address filter, - address nftContract, + address filter, + address nftContract, uint256[] memory tokenIds, uint256 minMintAmount - ) external returns(uint256 actualMintAmount); -} \ No newline at end of file + ) external returns (uint256 actualMintAmount); + + function mintFragTo(address to, uint256 rawAmount) external; + + function burnFragFrom(address from, uint256 rawAmount) external; + + function queryChargeMintFee(uint256 rawAmount) external; + + function queryChargeBurnFee(uint256 rawAmount) external; +} From b652974a60814c3453b916dbfa49a76edadae57b Mon Sep 17 00:00:00 2001 From: owen05 Date: Thu, 9 Sep 2021 19:29:50 +0800 Subject: [PATCH 10/42] dev --- contracts/NFTPool/impl/FilterAdmin.sol | 223 ++++-------------- contracts/NFTPool/impl/FilterModel01.sol | 34 +++ .../SmartRoute/proxies/DODONFTPoolProxy.sol | 2 + contracts/lib/DecimalMath.sol | 15 ++ 4 files changed, 92 insertions(+), 182 deletions(-) diff --git a/contracts/NFTPool/impl/FilterAdmin.sol b/contracts/NFTPool/impl/FilterAdmin.sol index 760d869..68ff2ac 100644 --- a/contracts/NFTPool/impl/FilterAdmin.sol +++ b/contracts/NFTPool/impl/FilterAdmin.sol @@ -24,202 +24,74 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { address public _CONTROLLER_MODEL_; address public _DEFAULT_MAINTAINER_; + // ============ Event ============ + event ChangeFee(uint256 fee); + function init( address _owner, string memory _name, string memory _symbol, uint256 fee, - address mtFeeModel, + address controllerModel, address defaultMaintainer, address[] memory filters ) external { super.init(_owner, 0, _name, _symbol, 18); _FILTER_REGISTRY_ = filters; _FEE_ = fee; - _CONTROLLER_MODEL_ = mtFeeModel; + _CONTROLLER_MODEL_ = controllerModel; _DEFAULT_MAINTAINER_ = defaultMaintainer; } - function ERC721In( - address filter, - address nftContract, - uint256[] memory tokenIds, - uint256 minMintAmount - ) - external - preventReentrant - returns (uint256 actualMintAmount) - { - require(isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); - require(IFilterModel(filter)._NFT_IN_SWITCH_(), "NFT_IN_CLOSED"); - require(IFilterModel(filter).getAvaliableNFTIn() >= tokenIds.length, "EXCEED_MAX_AMOUNT"); - uint256 totalPrice = 0; - for(uint256 i = 0; i < tokenIds.length; i++) { - require(IFilterModel(filter).isFilterERC721Pass(nftContract, tokenIds[i]), "NOT_REGISTERED"); - totalPrice = totalPrice.add(IFilterModel(filter).getNFTInPrice(nftContract, tokenIds[i])); - IFilterModel(filter).transferInERC721(nftContract, msg.sender, tokenIds[i]); - } + function chargeMint(address user, uint256 totalMintAmount) external { + require(isIncludeFilter(msg.sender), "FILTER_NOT_REGISTRIED"); - (uint256 poolFeeAmount, uint256 mtFeeAmount) = _nftInFeeTransfer(totalPrice); + (uint256 poolFeeAmount, uint256 mtFeeAmount) = getMintFee(user, totalMintAmount); if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); - actualMintAmount = totalPrice.sub(mtFeeAmount).sub(poolFeeAmount); - require(actualMintAmount >= minMintAmount, "MINT_AMOUNT_NOT_ENOUGH"); - _mint(msg.sender, actualMintAmount); + _mint(user, totalMintAmount.sub(poolFeeAmount).sub(mtFeeAmount)); } - function ERC1155In( - address filter, - address nftContract, - uint256[] memory tokenIds, - uint256[] memory amounts, - uint256 minMintAmount - ) - external - preventReentrant - returns (uint256 actualMintAmount) - { - require(tokenIds.length == amounts.length, "PARAMS_NOT_MATCH"); - require(isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); - require(IFilterModel(filter)._NFT_IN_SWITCH_(), "NFT_IN_CLOSED"); - require(IFilterModel(filter).getAvaliableNFTIn() >= tokenIds.length, "EXCEED_MAX_AMOUNT"); - uint256 totalPrice = 0; - for(uint256 i = 0; i < tokenIds.length; i++) { - require(IFilterModel(filter).isFilterERC1155Pass(nftContract, tokenIds[i], amounts[i]), "NOT_REGISTERED"); - totalPrice = totalPrice.add(IFilterModel(filter).getNFTInPrice(nftContract, tokenIds[i]).mul(amounts[i])); - } + function chargeRandomBurn(address user, uint256 totalBurnAmount) external { + require(isIncludeFilter(msg.sender), "FILTER_NOT_REGISTRIED"); - (uint256 poolFeeAmount, uint256 mtFeeAmount) = _nftInFeeTransfer(totalPrice); + (uint256 poolFeeAmount, uint256 mtFeeAmount) = getRandomBurnFee(user, totalBurnAmount); if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); - actualMintAmount = totalPrice.sub(mtFeeAmount).sub(poolFeeAmount); - require(actualMintAmount >= minMintAmount, "MINT_AMOUNT_NOT_ENOUGH"); - _mint(msg.sender, actualMintAmount); - - IFilterModel(filter).transferBatchInERC1155(nftContract, msg.sender, tokenIds, amounts); + _burn(user, totalBurnAmount); } - function ERC721RandomOut( - address filter, - uint256 times, - uint256 maxBurnAmount - ) - external - preventReentrant - returns (uint256 actualBurnAmount) - { - require(msg.sender == tx.origin, "ONLY_ALLOW_EOA"); - require(isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); - require(IFilterModel(filter)._NFT_RANDOM_SWITCH_(), "NFT_RANDOM_CLOSED"); - require(IFilterModel(filter).getAvaliableNFTOut() >= times, "EXCEED_MAX_AMOUNT"); - uint256 totalPrice = 0; - for(uint256 i = 0; i < times; i++) { - totalPrice = totalPrice.add(IFilterModel(filter).getNFTRandomOutPrice()); - (address nftContract, uint256 tokenId) = IFilterModel(filter).getRandomOutId(); - IFilterModel(filter).transferOutERC721(nftContract, msg.sender, tokenId); - } + function chargeTargetBurn(address user, uint256 totalBurnAmount) external { + require(isIncludeFilter(msg.sender), "FILTER_NOT_REGISTRIED"); - (uint256 poolFeeAmount, uint256 mtFeeAmount) = _nftRandomOutFeeTransfer(totalPrice); + (uint256 poolFeeAmount, uint256 mtFeeAmount) = getTargetBurnFee(user, totalBurnAmount); if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); - actualBurnAmount = totalPrice; - require(actualBurnAmount <= maxBurnAmount, "EXTRA_BURN_AMOUNT"); - _burn(msg.sender, actualBurnAmount); + + _burn(user, totalBurnAmount); } - - function ERC1155RandomOut( - address filter, - uint256 times, - uint256 maxBurnAmount - ) - external - preventReentrant - returns (uint256 actualBurnAmount) - { - require(msg.sender == tx.origin, "ONLY_ALLOW_EOA"); - require(isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); - require(IFilterModel(filter)._NFT_RANDOM_SWITCH_(), "NFT_RANDOM_OUT_CLOSED"); - require(IFilterModel(filter).getAvaliableNFTOut() >= times, "EXCEED_MAX_AMOUNT"); - - uint256 totalPrice = 0; - for(uint256 i = 0; i < times; i++) { - totalPrice = totalPrice.add(IFilterModel(filter).getNFTRandomOutPrice()); - (address nftContract, uint256 tokenId) = IFilterModel(filter).getRandomOutId(); - IFilterModel(filter).transferOutERC1155(nftContract, msg.sender, tokenId, 1); - } - - (uint256 poolFeeAmount, uint256 mtFeeAmount) = _nftRandomOutFeeTransfer(totalPrice); - if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); - if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); - - actualBurnAmount = totalPrice; - require(actualBurnAmount <= maxBurnAmount, "EXTRA_BURN_AMOUNT"); - _burn(msg.sender, actualBurnAmount); - } - - function ERC721TargetOut( - address filter, - address nftContract, - uint256[] memory tokenIds, - uint256 maxBurnAmount - ) - external - preventReentrant - returns(uint256 actualBurnAmount) - { - require(isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); - require(IFilterModel(filter)._NFT_TARGET_SWITCH_(), "NFT_TARGET_OUT_CLOSED"); - require(IFilterModel(filter).getAvaliableNFTOut() >= tokenIds.length, "EXCEED_MAX_AMOUNT"); - uint256 totalPrice = 0; - for(uint256 i = 0; i < tokenIds.length; i++) { - totalPrice = totalPrice.add(IFilterModel(filter).getNFTTargetOutPrice(nftContract, tokenIds[i])); - IFilterModel(filter).transferOutERC721(nftContract, msg.sender, tokenIds[i]); - } - - (uint256 poolFeeAmount, uint256 mtFeeAmount) = _nftTargetOutFeeTransfer(totalPrice); - if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); - if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); - - actualBurnAmount = totalPrice; - require(actualBurnAmount <= maxBurnAmount, "EXTRA_BURN_AMOUNT"); - _burn(msg.sender, actualBurnAmount); - } - - function ERC1155TargetOut( - address filter, - address nftContract, - uint256[] memory tokenIds, - uint256[] memory amounts, - uint256 maxBurnAmount - ) - external - preventReentrant - returns(uint256 actualBurnAmount) - { - require(tokenIds.length == amounts.length, "PARAMS_NOT_MATCH"); - require(isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); - require(IFilterModel(filter)._NFT_TARGET_SWITCH_(), "NFT_TARGET_OUT_CLOSED"); - require(IFilterModel(filter).getAvaliableNFTOut() >= tokenIds.length, "EXCEED_MAX_AMOUNT"); - uint256 totalPrice = 0; - for(uint256 i = 0; i < tokenIds.length; i++) { - totalPrice = totalPrice.add(IFilterModel(filter).getNFTTargetOutPrice(nftContract, tokenIds[i]).mul(amounts[i])); - } - (uint256 poolFeeAmount, uint256 mtFeeAmount) = _nftTargetOutFeeTransfer(totalPrice); - if(poolFeeAmount > 0) _mint(_OWNER_, poolFeeAmount); - if(mtFeeAmount > 0) _mint(_DEFAULT_MAINTAINER_, mtFeeAmount); - - actualBurnAmount = totalPrice; - require(actualBurnAmount <= maxBurnAmount, "EXTRA_BURN_AMOUNT"); - _burn(msg.sender, actualBurnAmount); - - IFilterModel(filter).transferBatchOutERC1155(nftContract, msg.sender, tokenIds, amounts); - } - - //================ 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 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 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; for(; i < _FILTER_REGISTRY_.length; i++) { @@ -236,28 +108,15 @@ contract FilterAdmin is InitializableInternalMintableERC20, ReentrancyGuard { return "FADMIN 1.0.0"; } - //=============== Owner ============== + //================= Owner ================ function addFilter(address filter) external onlyOwner { require(!isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); _FILTER_REGISTRY_.push(filter); } - - //=============== Internal ============== - function _nftInFeeTransfer(uint256 totalPrice) internal returns (uint256 poolFeeAmount, uint256 mtFeeAmount) { - uint256 mtFeeRate = IControllerModel(_CONTROLLER_MODEL_).getNFTInFee(address(this), msg.sender); - poolFeeAmount = DecimalMath.mulFloor(totalPrice, _FEE_); - mtFeeAmount = DecimalMath.mulFloor(totalPrice, mtFeeRate); - } - function _nftRandomOutFeeTransfer(uint256 totalPrice) internal returns (uint256 poolFeeAmount, uint256 mtFeeAmount) { - uint256 mtFeeRate = IControllerModel(_CONTROLLER_MODEL_).getNFTRandomOutFee(address(this), msg.sender); - poolFeeAmount = DecimalMath.mulFloor(totalPrice, _FEE_); - mtFeeAmount = DecimalMath.mulFloor(totalPrice, mtFeeRate); - } - - function _nftTargetOutFeeTransfer(uint256 totalPrice) internal returns (uint256 poolFeeAmount, uint256 mtFeeAmount) { - uint256 mtFeeRate = IControllerModel(_CONTROLLER_MODEL_).getNFTTargetOutFee(address(this), msg.sender); - poolFeeAmount = DecimalMath.mulFloor(totalPrice, _FEE_); - mtFeeAmount = DecimalMath.mulFloor(totalPrice, mtFeeRate); + function changeFee(uint256 newFee) external onlyOwner { + require(newFee <= 1e18, "FEE_TOO_LARGE"); + _FEE_ = newFee; + emit ChangeFee(newFee); } } diff --git a/contracts/NFTPool/impl/FilterModel01.sol b/contracts/NFTPool/impl/FilterModel01.sol index 17a52dc..4d24d14 100644 --- a/contracts/NFTPool/impl/FilterModel01.sol +++ b/contracts/NFTPool/impl/FilterModel01.sol @@ -265,5 +265,39 @@ contract FilterModel01 is InitializableOwnable, IERC721Receiver { 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/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index f084c20..8430652 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -122,6 +122,8 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { uint256[] memory tokenIds = new uint256[](1); tokenIds[0] = tokenId; + + //TODO: uint256 mintAmount = IFilterAdmin(filterAdmin).ERC721In(filter, nftContract, tokenIds, 0); _generalApproveMax(filterAdmin, dodoApprove, mintAmount); diff --git a/contracts/lib/DecimalMath.sol b/contracts/lib/DecimalMath.sol index 542bc77..f740048 100644 --- a/contracts/lib/DecimalMath.sol +++ b/contracts/lib/DecimalMath.sol @@ -45,4 +45,19 @@ library DecimalMath { function reciprocalCeil(uint256 target) internal pure returns (uint256) { return uint256(10**36).divCeil(target); } + + function powFloor(uint256 target, uint256 e) internal pure returns (uint256) { + if (e == 0) { + return 1; + } else if (e == 1) { + return target; + } else { + uint p = powFloor(target, e.div(2)); + p = p.mul(p) / (10**18); + if (e % 2 == 1) { + p = p.mul(target) / (10**18); + } + return p; + } + } } From e617e61fe6a67c4218a243b4f92b06de2a6cdda4 Mon Sep 17 00:00:00 2001 From: owen05 Date: Thu, 9 Sep 2021 22:38:19 +0800 Subject: [PATCH 11/42] 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; +} From 70de66da449e387370b7419f9d7b7e18f23e6c6f Mon Sep 17 00:00:00 2001 From: owen05 Date: Fri, 10 Sep 2021 00:05:16 +0800 Subject: [PATCH 12/42] add filter1155 --- README.md | 8 +- contracts/NFTPool/impl/BaseFilterV1.sol | 200 ++++++++++++++++++ contracts/NFTPool/impl/FilterERC1155V1.sol | 176 +++++++++++++++ contracts/NFTPool/impl/FilterERC721V1.sol | 197 +---------------- contracts/NFTPool/intf/IFilterModel.sol | 6 +- .../SmartRoute/proxies/DODONFTPoolProxy.sol | 98 ++++++--- 6 files changed, 463 insertions(+), 222 deletions(-) create mode 100644 contracts/NFTPool/impl/BaseFilterV1.sol create mode 100644 contracts/NFTPool/impl/FilterERC1155V1.sol 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 ); From 8d40444a898776661a8556bf0aae2aa481618921 Mon Sep 17 00:00:00 2001 From: owen05 Date: Fri, 10 Sep 2021 10:40:37 +0800 Subject: [PATCH 13/42] fix --- contracts/NFTPool/impl/BaseFilterV1.sol | 11 +++++------ contracts/NFTPool/impl/ControllerModel.sol | 1 - contracts/NFTPool/impl/FilterAdmin.sol | 2 +- contracts/NFTPool/impl/FilterERC1155V1.sol | 12 ++++++------ contracts/NFTPool/impl/FilterERC721V1.sol | 11 +++++------ contracts/SmartRoute/DODONFTApprove.sol | 6 +++--- 6 files changed, 20 insertions(+), 23 deletions(-) diff --git a/contracts/NFTPool/impl/BaseFilterV1.sol b/contracts/NFTPool/impl/BaseFilterV1.sol index c58f685..b4c528c 100644 --- a/contracts/NFTPool/impl/BaseFilterV1.sol +++ b/contracts/NFTPool/impl/BaseFilterV1.sol @@ -53,7 +53,7 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { function isNFTValid(address nftCollectionAddress, uint256 nftId) external view returns (bool) { if(nftCollectionAddress == _NFT_COLLECTION_) { - isNFTIDValid(nftId); + return isNFTIDValid(nftId); } else { return false; } @@ -68,7 +68,7 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { } function getAvaliableNFTIn() public view returns(uint256) { - if(_MAX_NFT_AMOUNT_ < _NFT_IDS_.length) { + if(_MAX_NFT_AMOUNT_ <= _NFT_IDS_.length) { return 0; }else { return _MAX_NFT_AMOUNT_ - _NFT_IDS_.length; @@ -76,7 +76,7 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { } function getAvaliableNFTOut() public view returns(uint256) { - if(_NFT_IDS_.length < _MIN_NFT_AMOUNT_) { + if(_NFT_IDS_.length <= _MIN_NFT_AMOUNT_) { return 0; }else { return _NFT_IDS_.length - _MIN_NFT_AMOUNT_; @@ -94,7 +94,7 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { // ============ Math ============= - function geometricCalc(uint256 a1, uint256 q, uint256 start, uint256 end) internal view returns(uint256) { + 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) @@ -105,7 +105,7 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { return a1.mul(qn.sub(qm)).div(q.sub(DecimalMath.ONE)); } - function getRandomOutId() public view returns (uint256 index) { + 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; } @@ -113,7 +113,6 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { // ================= Ownable ================ - function changeNFTInPrice(uint256 newGsStart, uint256 newCr, bool switchFlag) external { require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); _changeNFTInPrice(newGsStart, newCr, switchFlag); diff --git a/contracts/NFTPool/impl/ControllerModel.sol b/contracts/NFTPool/impl/ControllerModel.sol index 1ef135e..ce398da 100644 --- a/contracts/NFTPool/impl/ControllerModel.sol +++ b/contracts/NFTPool/impl/ControllerModel.sol @@ -9,7 +9,6 @@ pragma solidity 0.6.9; pragma experimental ABIEncoderV2; import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; -import {IERC20} from "../../intf/IERC20.sol"; import {SafeMath} from "../../lib/SafeMath.sol"; contract ControllerModel is InitializableOwnable { diff --git a/contracts/NFTPool/impl/FilterAdmin.sol b/contracts/NFTPool/impl/FilterAdmin.sol index 161f4a4..603736d 100644 --- a/contracts/NFTPool/impl/FilterAdmin.sol +++ b/contracts/NFTPool/impl/FilterAdmin.sol @@ -101,7 +101,7 @@ contract FilterAdmin is InitializableInternalMintableERC20 { } function changeFee(uint256 newFee) external onlyOwner { - require(newFee <= 1e18, "FEE_TOO_LARGE"); + require(newFee <= DecimalMath.ONE, "FEE_TOO_LARGE"); _FEE_ = newFee; emit ChangeFee(newFee); } diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index a244170..dcaf052 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -6,7 +6,6 @@ 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"; @@ -48,21 +47,21 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { 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); + 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_); + 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_); + 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); } @@ -83,6 +82,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { 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++) { + totalAmount += amounts[i]; _transferOutERC1155(to, indexes[i], amounts[i]); } (uint256 rawPay, ) = queryNFTTargetOut(totalAmount); @@ -93,7 +93,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { (uint256 rawPay, ) = queryNFTRandomOut(amount); paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); for (uint256 i = 0; i < amount; i++) { - _transferOutERC1155(to, getRandomOutId(), 1); + _transferOutERC1155(to, _getRandomOutId(), 1); } } @@ -156,7 +156,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { 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) { + if(_NFT_RESERVE_[tokenId] == 0 && currentAmount > 0) { _NFT_IDS_.push(tokenId); } _NFT_RESERVE_[tokenId] = currentAmount; diff --git a/contracts/NFTPool/impl/FilterERC721V1.sol b/contracts/NFTPool/impl/FilterERC721V1.sol index d661f7c..66ae7d6 100644 --- a/contracts/NFTPool/impl/FilterERC721V1.sol +++ b/contracts/NFTPool/impl/FilterERC721V1.sol @@ -8,7 +8,6 @@ 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"; @@ -49,7 +48,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { function queryNFTIn(uint256 NFTInAmount) public view returns (uint256 rawReceive, uint256 received) { require(NFTInAmount <= getAvaliableNFTIn(), "EXCEDD_IN_AMOUNT"); uint256 nftAmount = _NFT_IDS_.length; - rawReceive = geometricCalc(_GS_START_IN_, _CR_IN_, nftAmount, nftAmount + NFTInAmount); + rawReceive = _geometricCalc(_GS_START_IN_, _CR_IN_, nftAmount, nftAmount + NFTInAmount); (uint256 poolFee, uint256 mtFee) = IFilterAdmin(_OWNER_).queryChargeMintFee(rawReceive); received = rawReceive.sub(poolFee).sub(mtFee); } @@ -57,7 +56,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { function queryNFTTargetOut(uint256 NFTOutAmount) public view returns (uint256 rawPay, uint256 pay) { require(NFTOutAmount <= getAvaliableNFTOut(), "EXCEED_OUT_AMOUNT"); uint256 nftAmount = _NFT_IDS_.length; - rawPay = geometricCalc(_GS_START_TARGET_OUT_,_CR_TARGET_OUT_, nftAmount - NFTOutAmount, nftAmount); + 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); } @@ -65,7 +64,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { function queryNFTRandomOut(uint256 NFTOutAmount) public view returns (uint256 rawPay, uint256 pay) { require(NFTOutAmount <= getAvaliableNFTOut(), "EXCEED_OUT_AMOUNT"); uint256 nftAmount = _NFT_IDS_.length; - rawPay = geometricCalc(_GS_START_RANDOM_OUT_,_CR_RANDOM_OUT_, nftAmount - NFTOutAmount, nftAmount); + 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); } @@ -76,7 +75,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { for (uint256 i = 0; i < tokenIds.length; i++) { 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"); + require(_NFT_RESERVE_[tokenId] == 0 && IERC721(_NFT_COLLECTION_).ownerOf(tokenId) == address(this), "NFT_NOT_SEND"); _NFT_IDS_.push(tokenId); _NFT_RESERVE_[tokenId] = 1; } @@ -96,7 +95,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { (uint256 rawPay, ) = queryNFTRandomOut(amount); paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); for (uint256 i = 0; i < amount; i++) { - _transferOutERC721(to, getRandomOutId()); + _transferOutERC721(to, _getRandomOutId()); } } diff --git a/contracts/SmartRoute/DODONFTApprove.sol b/contracts/SmartRoute/DODONFTApprove.sol index b291e4a..999d87d 100644 --- a/contracts/SmartRoute/DODONFTApprove.sol +++ b/contracts/SmartRoute/DODONFTApprove.sol @@ -32,7 +32,7 @@ contract DODONFTApprove is InitializableOwnable { modifier notLocked() { require( _TIMELOCK_ <= block.timestamp, - "SetProxy is timelocked" + "AddProxy is timelocked" ); _; } @@ -43,7 +43,7 @@ contract DODONFTApprove is InitializableOwnable { _IS_ALLOWED_PROXY_[proxies[i]] = true; } - function unlockAddProxy(address newDodoProxy) public onlyOwner { + function unlockAddProxy(address newDodoProxy) external onlyOwner { _TIMELOCK_ = block.timestamp + _TIMELOCK_DURATION_; _PENDING_ADD_DODO_PROXY_ = newDodoProxy; } @@ -60,7 +60,7 @@ contract DODONFTApprove is InitializableOwnable { emit AddDODOProxy(_PENDING_ADD_DODO_PROXY_); } - function removeDODOProxy (address oldDodoProxy) public onlyOwner { + function removeDODOProxy (address oldDodoProxy) external onlyOwner { _IS_ALLOWED_PROXY_[oldDodoProxy] = false; } From 551e7063ddf70df6fdf98133796309488705575a Mon Sep 17 00:00:00 2001 From: mingda Date: Fri, 10 Sep 2021 19:37:06 +0800 Subject: [PATCH 14/42] some changes & rename --- contracts/NFTPool/impl/BaseFilterV1.sol | 256 +++++++++++++------- contracts/NFTPool/impl/Controller.sol | 79 ++++++ contracts/NFTPool/impl/ControllerModel.sol | 84 ------- contracts/NFTPool/impl/FilterAdmin.sol | 123 ++++++---- contracts/NFTPool/impl/FilterERC1155V1.sol | 106 ++++---- contracts/NFTPool/impl/FilterERC721V1.sol | 100 ++++---- contracts/NFTPool/intf/IController.sol | 16 ++ contracts/NFTPool/intf/IControllerModel.sol | 16 -- contracts/NFTPool/intf/IFilter.sol | 45 ++++ contracts/NFTPool/intf/IFilterAdmin.sol | 26 +- contracts/NFTPool/intf/IFilterModel.sol | 32 --- 11 files changed, 502 insertions(+), 381 deletions(-) create mode 100644 contracts/NFTPool/impl/Controller.sol delete mode 100644 contracts/NFTPool/impl/ControllerModel.sol create mode 100644 contracts/NFTPool/intf/IController.sol delete mode 100644 contracts/NFTPool/intf/IControllerModel.sol create mode 100644 contracts/NFTPool/intf/IFilter.sol delete mode 100644 contracts/NFTPool/intf/IFilterModel.sol diff --git a/contracts/NFTPool/impl/BaseFilterV1.sol b/contracts/NFTPool/impl/BaseFilterV1.sol index b4c528c..ab646e5 100644 --- a/contracts/NFTPool/impl/BaseFilterV1.sol +++ b/contracts/NFTPool/impl/BaseFilterV1.sol @@ -18,83 +18,151 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { //=================== Storage =================== address public _NFT_COLLECTION_; uint256 public _NFT_ID_START_; - uint256 public _NFT_ID_END_; - + uint256 public _NFT_ID_END_ = uint256(-1); + //tokenId => isRegistered mapping(uint256 => bool) public _SPREAD_IDS_REGISTRY_; + //tokenId => amount mapping(uint256 => uint256) public _NFT_RESERVE_; uint256[] public _NFT_IDS_; - + uint256 public _TOTAL_NFT_AMOUNT_; uint256 public _MAX_NFT_AMOUNT_; uint256 public _MIN_NFT_AMOUNT_; // GS -> Geometric sequence // CR -> Common Ratio - //For NFT IN + //For Deposit NFT INto Pool uint256 public _GS_START_IN_; uint256 public _CR_IN_; - bool public _NFT_IN_SWITCH_ = false; - - //For NFT Random OUT + bool public _NFT_IN_TOGGLE_ = false; + + //For NFT Random OUT from Pool uint256 public _GS_START_RANDOM_OUT_; uint256 public _CR_RANDOM_OUT_; - bool public _NFT_RANDOM_SWITCH_ = false; + bool public _NFT_RANDOM_OUT_TOGGLE_ = false; - //For NFT Target OUT + //For NFT Target OUT from Pool uint256 public _GS_START_TARGET_OUT_; uint256 public _CR_TARGET_OUT_; - bool public _NFT_TARGET_SWITCH_ = false; + bool public _NFT_TARGET_OUT_TOGGLE_ = false; + modifier onlySuperOwner() { + require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ONLY_SUPER_OWNER"); + _; + } - //==================== Query ================== + //==================== Query Prop ================== function isNFTValid(address nftCollectionAddress, uint256 nftId) external view returns (bool) { - if(nftCollectionAddress == _NFT_COLLECTION_) { + if (nftCollectionAddress == _NFT_COLLECTION_) { return 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 isNFTIDValid(uint256 nftId) public view returns (bool) { + return (nftId >= _NFT_ID_START_ && nftId <= _NFT_ID_END_) || _SPREAD_IDS_REGISTRY_[nftId]; } - function getAvaliableNFTIn() public view returns(uint256) { - if(_MAX_NFT_AMOUNT_ <= _NFT_IDS_.length) { + function getAvaliableNFTInAmount() public view returns (uint256) { + if (_MAX_NFT_AMOUNT_ <= _TOTAL_NFT_AMOUNT_) { return 0; - }else { - return _MAX_NFT_AMOUNT_ - _NFT_IDS_.length; + } else { + return _MAX_NFT_AMOUNT_ - _TOTAL_NFT_AMOUNT_; } } - function getAvaliableNFTOut() public view returns(uint256) { - if(_NFT_IDS_.length <= _MIN_NFT_AMOUNT_) { + function getAvaliableNFTOutAmount() public view returns (uint256) { + if (_TOTAL_NFT_AMOUNT_ <= _MIN_NFT_AMOUNT_) { return 0; - }else { - return _NFT_IDS_.length - _MIN_NFT_AMOUNT_; + } else { + return _TOTAL_NFT_AMOUNT_ - _MIN_NFT_AMOUNT_; } } - function getNFTIndexById(uint256 tokenId) public view returns(uint256) { + function getNFTIndexById(uint256 tokenId) public view returns (uint256) { uint256 i = 0; - for(; i < _NFT_IDS_.length; i++) { - if(_NFT_IDS_[i] == tokenId) break; + for (; i < _NFT_IDS_.length; i++) { + if (_NFT_IDS_[i] == tokenId) break; } require(i < _NFT_IDS_.length, "TOKEN_ID_NOT_EXSIT"); return i; } + //==================== Query Price ================== + + function queryNFTIn(uint256 NFTInAmount) + public + view + returns ( + uint256 received, + uint256 poolFee, + uint256 mtFee + ) + { + require(NFTInAmount <= getAvaliableNFTIn(), "EXCEDD_IN_AMOUNT"); + rawReceive = _geometricCalc( + _GS_START_IN_, + _CR_IN_, + _TOTAL_NFT_AMOUNT_, + _TOTAL_NFT_AMOUNT_ + NFTInAmount + ); + (poolFee, mtFee, received) = IFilterAdmin(_OWNER_).queryChargeMintFee(rawReceive); + } + + function queryNFTTargetOut(uint256 NFTOutAmount) + public + view + returns ( + uint256 paid, + uint256 poolFee, + uint256 mtFee + ) + { + require(NFTOutAmount <= getAvaliableNFTOut(), "EXCEED_OUT_AMOUNT"); + rawPay = _geometricCalc( + _GS_START_TARGET_OUT_, + _CR_TARGET_OUT_, + _TOTAL_NFT_AMOUNT_ - NFTOutAmount, + _TOTAL_NFT_AMOUNT_ + ); + (poolFee, mtFee, paid) = IFilterAdmin(_OWNER_).queryChargeBurnFee(rawPay); + } + + function queryNFTRandomOut(uint256 NFTOutAmount) + public + view + returns ( + uint256 paid, + uint256 poolFee, + uint256 mtFee + ) + { + require(NFTOutAmount <= getAvaliableNFTOut(), "EXCEED_OUT_AMOUNT"); + rawPay = _geometricCalc( + _GS_START_RANDOM_OUT_, + _CR_RANDOM_OUT_, + _TOTAL_NFT_AMOUNT_ - NFTOutAmount, + _TOTAL_NFT_AMOUNT_ + ); + (poolFee, mtFee, paid) = IFilterAdmin(_OWNER_).queryChargeBurnFee(rawPay); + } + // ============ Math ============= - function _geometricCalc(uint256 a1, uint256 q, uint256 start, uint256 end) internal view returns(uint256) { + function _geometricCalc( + uint256 a1, + uint256 q, + uint256 start, + uint256 end + ) internal view returns (uint256) { + if (q == DecimalMath.ONE) { + return end.sub(start).ml(a1); + } //Sn=a1*(q^n-1)/(q-1) //Sn-Sm = a1*(q^n-q^m)/(q-1) @@ -104,96 +172,110 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { 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; - } + function _getRandomOutId() public view returns (uint256 index) { + index = + uint256( + keccak256(abi.encodePacked(tx.origin, blockhash(block.number - 1), gasleft())) + ) % + _TOTAL_NFT_AMOUNT_; + } // ================= 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 toggleFlag + ) external onlySuperOwner { + _changeNFTInPrice(newGsStart, newCr, toggleFlag); } - 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 _changeNFTInPrice( + uint256 newGsStart, + uint256 newCr, + bool toggleFlag + ) internal { + require(newCr > DecimalMath.ONE, "CR_INVALID"); + _GS_START_IN_ = newGsStart; + _CR_IN_ = newCr; + _NFT_IN_TOGGLE_ = 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 toggleFlag + ) external onlySuperOwner { + _changeNFTRandomInPrice(newGsStart, newCr, toggleFlag); } - 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 _changeNFTRandomInPrice( + uint256 newGsStart, + uint256 newCr, + bool toggleFlag + ) internal { + require(newCr > DecimalMath.ONE, "CR_INVALID"); + _GS_START_RANDOM_OUT_ = newGsStart; + _CR_RANDOM_OUT_ = newCr; + _NFT_RANDOM_OUT_TOGGLE_ = 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 toggleFlag + ) external onlySuperOwner { + _changeNFTTargetOutPrice(newGsStart, newCr, toggleFlag); } - 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 _changeNFTTargetOutPrice( + uint256 newGsStart, + uint256 newCr, + bool toggleFlag + ) internal { + require(newCr > DecimalMath.ONE, "CR_INVALID"); + _GS_START_TARGET_OUT_ = newGsStart; + _CR_TARGET_OUT_ = newCr; + _NFT_TARGET_OUT_TOGGLE_ = true; } - function changeNFTAmount(uint256 maxNFTAmount, uint256 minNFTAmount) external { - require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); - _changeNFTAmount(maxNFTAmount, minNFTAmount); + function changeNFTAmountRange(uint256 maxNFTAmount, uint256 minNFTAmount) + external + onlySuperOwner + { + _changeNFTAmountRange(maxNFTAmount, minNFTAmount); } - function _changeNFTAmount(uint256 maxNFTAmount, uint256 minNFTAmount) internal { + function _changeNFTAmountRange(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"); + function changeTokenIdRange(uint256 nftIdStart, uint256 nftIdEnd) external onlySuperOwner { _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 isRegistered) + external + onlySuperOwner + { + _changeTokenIdMap(tokenIds, isRegistered); } - 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]; + function _changeTokenIdMap(uint256[] memory tokenIds, bool[] memory isRegistered) internal { + require(tokenIds.length == isRegistered.length, "PARAM_NOT_MATCH"); + + for (uint256 i = 0; i < tokenIds.length; i++) { + _SPREAD_IDS_REGISTRY_[tokenIds[i]] = isRegistered[i]; } } -} \ No newline at end of file +} diff --git a/contracts/NFTPool/impl/Controller.sol b/contracts/NFTPool/impl/Controller.sol new file mode 100644 index 0000000..03be197 --- /dev/null +++ b/contracts/NFTPool/impl/Controller.sol @@ -0,0 +1,79 @@ +/* + + 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"; + +contract Controller is InitializableOwnable { + using SafeMath for uint256; + + uint256 public _GLOBAL_NFT_IN_FEE_RATE_ = 0; + uint256 public _GLOBAL_NFT_OUT_FEE_RATE_ = 0; + + struct FilterAdminFeeRateInfo { + uint256 nftInFeeRate; + uint256 nftOutFeeRate; + bool isOpen; + } + + mapping(address => FilterAdminFeeRateInfo) filterAdminFeeRates; + + mapping(address => bool) public isEmergencyWithdrawOpen; + + //==================== Event ===================== + event SetEmergencyWithdraw(address filter, bool isOpen); + + //==================== Ownable ==================== + + function setFilterAdminFeeRateInfo( + address filterAdminAddr, + uint256 nftInFeeRate, + uint256 nftOutFeeRate, + bool isOpen + ) external onlyOwner { + FilterAdminFeeRateInfo memory feeRateInfo = FilterAdminFeeRateInfo({ + nftInFeeRate: nftInFeeRate, + nftOutFeeRate: nftOutFeeRate, + isOpen: isOpen + }); + filterAdminFeeRates[filterAdminAddr] = feeRateInfo; + } + + function setGlobalParam(uint256 nftInFeeRate, uint256 nftOutFeeRate) external onlyOwner { + _GLOBAL_NFT_IN_FEE_RATE_ = nftInFeeRate; + _GLOBAL_NFT_OUT_FEE_RATE_ = nftOutFeeRate; + } + + function setEmergencyWithdraw(address filter, bool isOpen) external onlyOwner { + isEmergencyWithdrawOpen[filter] = isOpen; + emit SetEmergencyWithdraw(filter, isOpen); + } + + //===================== View ======================== + function getMintFeeRate(address filterAdminAddr) external view returns (uint256) { + FilterAdminFeeRateInfo memory filterAdminFeeRateInfo = filterAdminFeeRates[filterAdminAddr]; + + if (filterAdminFeeRateInfo.isSet) { + return filterAdminFeeRateInfo.nftInFeeRate; + } else { + return _GLOBAL_NFT_IN_FEERate_; + } + } + + function getBurnFeeRate(address filterAdminAddr) external view returns (uint256) { + FilterAdminFeeInfo memory filterAdminFeeInfo = filterAdminFees[filterAdminAddr]; + + if (filterAdminFeeInfo.isSet) { + return filterAdminFeeInfo.nftOutFee; + } else { + return _GLOBAL_NFT_OUT_FEE_; + } + } +} diff --git a/contracts/NFTPool/impl/ControllerModel.sol b/contracts/NFTPool/impl/ControllerModel.sol deleted file mode 100644 index ce398da..0000000 --- a/contracts/NFTPool/impl/ControllerModel.sol +++ /dev/null @@ -1,84 +0,0 @@ -/* - - 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"; - -contract ControllerModel is InitializableOwnable { - using SafeMath for uint256; - - uint256 public _GLOBAL_NFT_IN_FEE_ = 0; - uint256 public _GLOBAL_NFT_OUT_FEE_ = 0; - - struct FilterAdminFeeInfo { - uint256 nftInFee; - uint256 nftOutFee; - bool isSet; - } - - mapping(address => FilterAdminFeeInfo) filterAdminFees; - - mapping(address => bool) isEmergencyWithdraw; - - //==================== Event ===================== - event SetEmergencyWithdraw(address filter, bool isOpen); - - //==================== Ownable ==================== - - function addFilterAdminFeeInfo(address filterAdminAddr, uint256 nftInFee, uint256 nftOutFee) external onlyOwner { - FilterAdminFeeInfo memory filterAdmin = FilterAdminFeeInfo({ - nftInFee: nftInFee, - nftOutFee: nftOutFee, - isSet: true - }); - filterAdminFees[filterAdminAddr] = filterAdmin; - } - - function setFilterAdminFeeInfo(address filterAdminAddr, uint256 nftInFee, uint256 nftOutFee) external onlyOwner { - filterAdminFees[filterAdminAddr].nftInFee = nftInFee; - filterAdminFees[filterAdminAddr].nftOutFee = nftOutFee; - } - - function setGlobalParam(uint256 nftInFee, uint256 nftOutFee) external onlyOwner { - _GLOBAL_NFT_IN_FEE_ = nftInFee; - _GLOBAL_NFT_OUT_FEE_ = nftOutFee; - } - - function setEmergencyWithdraw(address filter, bool isOpen) external onlyOwner { - isEmergencyWithdraw[filter] = isOpen; - emit SetEmergencyWithdraw(filter, isOpen); - } - - //===================== View ======================== - function getMintFee(address filterAdminAddr) external view returns(uint256) { - FilterAdminFeeInfo memory filterAdminFeeInfo = filterAdminFees[filterAdminAddr]; - - if(filterAdminFeeInfo.isSet) { - return filterAdminFeeInfo.nftInFee; - }else { - return _GLOBAL_NFT_IN_FEE_; - } - } - - function getBurnFee(address filterAdminAddr) external view returns(uint256) { - FilterAdminFeeInfo memory filterAdminFeeInfo = filterAdminFees[filterAdminAddr]; - - if(filterAdminFeeInfo.isSet) { - return filterAdminFeeInfo.nftOutFee; - }else { - return _GLOBAL_NFT_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 603736d..07a8a0d 100644 --- a/contracts/NFTPool/impl/FilterAdmin.sol +++ b/contracts/NFTPool/impl/FilterAdmin.sol @@ -10,99 +10,118 @@ pragma experimental ABIEncoderV2; import {InitializableInternalMintableERC20} from "../../external/ERC20/InitializableInternalMintableERC20.sol"; import {SafeMath} from "../../lib/SafeMath.sol"; -import {IControllerModel} from "../intf/IControllerModel.sol"; +import {IController} from "../intf/IController.sol"; import {DecimalMath} from "../../lib/DecimalMath.sol"; contract FilterAdmin is InitializableInternalMintableERC20 { using SafeMath for uint256; // ============ Storage ============ - address[] public _FILTER_REGISTRY_; - uint256 public _FEE_; - address public _CONTROLLER_MODEL_; - address public _DEFAULT_MAINTAINER_; + address[] public _FILTERS_; + mapping(address => bool) public _FILTER_REGISTRY_; + uint256 public _FEE_RATE_; + address public _CONTROLLER_; + address public _MAINTAINER_; + uint256 public _INIT_SUPPLY_; // ============ Event ============ - event ChangeFee(uint256 fee); + event ChangeFeeRate(uint256 fee); function init( address owner, uint256 initSupply, string memory name, string memory symbol, - uint256 fee, - address controllerModel, - address defaultMaintainer, + uint256 feeRate, + address controller, + address maintainer, address[] memory filters ) external { super.init(owner, initSupply, name, symbol, 18); - _FILTER_REGISTRY_ = filters; - _FEE_ = fee; - _CONTROLLER_MODEL_ = controllerModel; - _DEFAULT_MAINTAINER_ = defaultMaintainer; + _INIT_SUPPLY_ = initSupply; + _FEE_RATE_ = feeRate; + _CONTROLLER_ = controller; + _MAINTAINER_ = maintainer; + _FILTERS_ = filters; + for (uint256 i = 0; i < filters.length; i++) { + _FILTER_REGISTRY_[filters[i]] = true; + } } - function mintFragTo(address to, uint256 rawAmount) external returns (uint256 received){ - require(isIncludeFilter(msg.sender), "FILTER_NOT_REGISTRIED"); + function mintFragTo(address to, uint256 rawAmount) external returns (uint256) { + require(isRegisteredFilter(msg.sender), "FILTER_NOT_REGISTERED"); - (uint256 poolFee, uint256 mtFee) = queryChargeMintFee(rawAmount); - if(poolFee > 0) _mint(_OWNER_, poolFee); - if(mtFee > 0) _mint(_DEFAULT_MAINTAINER_, mtFee); - - received = rawAmount.sub(poolFee).sub(mtFee); - _mint(to, received); + (uint256 poolFee, uint256 mtFee, uint256 received) = queryMintFee(rawAmount); + if (poolFee > 0) _mint(_OWNER_, poolFee); + if (mtFee > 0) _mint(_MAINTAINER_, mtFee); + + _mint(to, received); + return received } - function burnFragFrom(address from, uint256 rawAmount) external returns (uint256 paid){ - require(isIncludeFilter(msg.sender), "FILTER_NOT_REGISTRIED"); + function burnFragFrom(address from, uint256 rawAmount) external returns (uint256) { + require(isRegisteredFilter(msg.sender), "FILTER_NOT_REGISTERED"); - (uint256 poolFee, uint256 mtFee) = queryChargeBurnFee(rawAmount); - if(poolFee > 0) _mint(_OWNER_, poolFee); - if(mtFee > 0) _mint(_DEFAULT_MAINTAINER_, mtFee); + (uint256 poolFee, uint256 mtFee, uint256 paid) = queryBurnFee(rawAmount); + if (poolFee > 0) _mint(_OWNER_, poolFee); + if (mtFee > 0) _mint(_MAINTAINER_, mtFee); - paid = rawAmount.add(poolFee).add(mtFee); _burn(from, paid); + return paid; } //================ View ================ - function queryChargeMintFee(uint256 rawAmount) public returns (uint256 poolFee, uint256 mtFee) { - uint256 mtFeeRate = IControllerModel(_CONTROLLER_MODEL_).getMintFee(address(this)); - poolFee = DecimalMath.mulFloor(rawAmount, _FEE_); + function queryMintFee(uint256 rawAmount) + public + returns ( + uint256 poolFee, + uint256 mtFee, + uint256 afterChargedAmount + ) + { + uint256 mtFeeRate = IController(_CONTROLLER_).getMintFee(address(this)); + poolFee = DecimalMath.mulFloor(rawAmount, _FEE_RATE_); mtFee = DecimalMath.mulFloor(rawAmount, mtFeeRate); + afterChargedAmount = rawAmount.sub(poolFee).sub(mtFee); } - function queryChargeBurnFee(uint256 rawAmount) public returns (uint256 poolFee, uint256 mtFee) { - uint256 mtFeeRate = IControllerModel(_CONTROLLER_MODEL_).getBurnFee(address(this)); - poolFee = DecimalMath.mulFloor(rawAmount, _FEE_); + function queryBurnFee(uint256 rawAmount) + public + returns ( + uint256 poolFee, + uint256 mtFee, + uint256 afterChargedAmount + ) + { + uint256 mtFeeRate = IController(_CONTROLLER_).getBurnFee(address(this)); + poolFee = DecimalMath.mulFloor(rawAmount, _FEE_RATE_); mtFee = DecimalMath.mulFloor(rawAmount, mtFeeRate); + afterChargedAmount = rawAmount.add(poolFee).add(mtFee); } - - function isIncludeFilter(address filter) view public returns (bool) { - uint256 i = 0; - for(; i < _FILTER_REGISTRY_.length; i++) { - if(filter == _FILTER_REGISTRY_[i]) break; - } - return i == _FILTER_REGISTRY_.length ? false : true; + function isRegisteredFilter(address filter) public view returns (bool) { + return _FILTER_REGISTRY_[i]; } - function getFilters() view public returns (address[] memory) { - return _FILTER_REGISTRY_; - } - - function version() virtual external pure returns (string memory) { - return "FADMIN 1.0.0"; + function getFilters() public view returns (address[] memory) { + return _FILTERS_; } //================= Owner ================ function addFilter(address filter) external onlyOwner { - require(!isIncludeFilter(filter), "FILTER_NOT_INCLUDE"); - _FILTER_REGISTRY_.push(filter); + require(!isRegisteredFilter(filter), "FILTER_ALREADY_EXIST"); + _FILTERS_.push(filter); + _FILTER_REGISTRY_[filter] = true; } - function changeFee(uint256 newFee) external onlyOwner { - require(newFee <= DecimalMath.ONE, "FEE_TOO_LARGE"); - _FEE_ = newFee; - emit ChangeFee(newFee); + function changeFeeRate(uint256 newFeeRate) external onlyOwner { + require(newFeeRate <= DecimalMath.ONE, "FEE_RATE_TOO_LARGE"); + _FEE_RATE_ = newFeeRate; + emit ChangeFeeRate(newFeeRate); + } + + //================= Support ================ + function version() external pure virtual returns (string memory) { + return "FILTER ADMIN 1.0.0"; } } diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index dcaf052..609b86e 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -17,13 +17,10 @@ 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, + bool[] memory toggles, uint256[] memory numParams, //0 - startId, 1 - endId, 2 - maxAmount, 3 - minAmount uint256[] memory priceRules, uint256[] memory spreadIds @@ -31,55 +28,40 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { 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]); + _changeNFTInPrice(priceRules[0], priceRules[1], toggles[0]); + _changeNFTRandomInPrice(priceRules[2], priceRules[3], toggles[1]); + _changeNFTTargetOutPrice(priceRules[4], priceRules[5], toggles[2]); - _changeNFTAmount(numParams[2],numParams[3]); + _changeNFTAmount(numParams[2], numParams[3]); - _changeTokenIdRange(numParams[0],numParams[1]); - for(uint256 i = 0; i < spreadIds.length; i++) { + _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) { + 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); + (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) { + 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++) { totalAmount += amounts[i]; @@ -89,7 +71,11 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); } - function ERC1155RandomOut(uint256 amount, address to) external preventReentrant returns (uint256 paid) { + 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++) { @@ -105,7 +91,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { uint256, uint256, bytes calldata - ) external override returns (bytes4){ + ) external override returns (bytes4) { return IERC1155Receiver.onERC1155Received.selector; } @@ -115,28 +101,41 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { uint256[] calldata, uint256[] calldata, bytes calldata - ) external override returns (bytes4){ + ) external override returns (bytes4) { return IERC1155Receiver.onERC1155BatchReceived.selector; } - function _transferOutERC1155(address to, uint256 index, uint256 amount) internal { + 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"); + function emergencyWithdraw( + address[] memory nftContract, + uint256[] memory tokenIds, + uint256[] memory amounts, + address to + ) external onlySuperOwner { + require( + nftContract.length == tokenIds.length && nftContract.length == amounts.length, + "PARAM_INVALID" + ); + address controller = IFilterAdmin(_OWNER_)._CONTROLLER_(); + require( + IController(controller).isEmergencyWithdrawOpen(address(this)), + "EMERGENCY_WITHDRAW_NOT_OPEN" + ); - for(uint256 i = 0; i< nftContract.length; i++) { + 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_){ + if (_NFT_RESERVE_[tokenId] > 0 && nftContract[i] == _NFT_COLLECTION_) { _maintainERC1155Out(getNFTIndexById(tokenId), tokenId); } } @@ -153,10 +152,10 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { } } - function _maintainERC1155In(uint256 tokenId) internal returns(uint256 inAmount){ + 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) { + if (_NFT_RESERVE_[tokenId] == 0 && currentAmount > 0) { _NFT_IDS_.push(tokenId); } _NFT_RESERVE_[tokenId] = currentAmount; @@ -165,12 +164,11 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { // ============ Support ============ - function supportsInterface(bytes4 interfaceId) override public view returns (bool) { + function supportsInterface(bytes4 interfaceId) public view override returns (bool) { return interfaceId == type(IERC1155Receiver).interfaceId; } - - function version() virtual external pure returns (string memory) { + function version() external pure virtual 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 66ae7d6..bfbc0da 100644 --- a/contracts/NFTPool/impl/FilterERC721V1.sol +++ b/contracts/NFTPool/impl/FilterERC721V1.sol @@ -23,7 +23,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { function init( address filterAdmin, address nftCollection, - bool[] memory switches, + bool[] memory toggles, uint256[] memory numParams, //0 - startId, 1 - endId, 2 - maxAmount, 3 - minAmount uint256[] memory priceRules, uint256[] memory spreadIds @@ -31,72 +31,65 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { 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]); + _changeNFTInPrice(priceRules[0], priceRules[1], toggles[0]); + _changeNFTRandomInPrice(priceRules[2], priceRules[3], toggles[1]); + _changeNFTTargetOutPrice(priceRules[4], priceRules[5], toggles[2]); - _changeNFTAmount(numParams[2],numParams[3]); + _changeNFTAmount(numParams[2], numParams[3]); - _changeTokenIdRange(numParams[0],numParams[1]); - for(uint256 i = 0; i < spreadIds.length; i++) { + _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"); - 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) public view returns (uint256 rawPay, uint256 pay) { - require(NFTOutAmount <= getAvaliableNFTOut(), "EXCEED_OUT_AMOUNT"); - 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) public view returns (uint256 rawPay, uint256 pay) { - require(NFTOutAmount <= getAvaliableNFTOut(), "EXCEED_OUT_AMOUNT"); - 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) { + function ERC721In(uint256[] memory tokenIds, address to) + external + preventReentrant + returns (uint256 received) + { for (uint256 i = 0; i < tokenIds.length; i++) { 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"); + 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); + _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; + (uint256 rawReceive, ) = queryNFTIn(tokenIds.length); received = IFilterAdmin(_OWNER_).mintFragTo(to, rawReceive); } - function ERC721TargetOut(uint256[] memory indexes, address to) external preventReentrant returns(uint256 paid) { + function ERC721TargetOut(uint256[] memory indexes, address to) + external + preventReentrant + returns (uint256 paid) + { (uint256 rawPay, ) = queryNFTTargetOut(indexes.length); paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); for (uint256 i = 0; i < indexes.length; i++) { _transferOutERC721(to, indexes[i]); } + _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; } - function ERC721RandomOut(uint256 amount, address to) external preventReentrant returns (uint256 paid) { + function ERC721RandomOut(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++) { _transferOutERC721(to, _getRandomOutId()); } + _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; } // ============ Transfer ============= @@ -110,7 +103,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { return IERC721Receiver.onERC721Received.selector; } - function _transferOutERC721(address to, uint256 index) internal { + function _transferOutERC721(address to, uint256 index) internal { require(index < _NFT_IDS_.length, "INDEX_NOT_EXIST"); uint256 tokenId = _NFT_IDS_[index]; IERC721(_NFT_COLLECTION_).safeTransferFrom(address(this), to, tokenId); @@ -119,21 +112,29 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { _NFT_RESERVE_[tokenId] = 0; } - function emergencyWithdraw(address[] memory nftContract, uint256[] memory tokenIds, address to) external { - require(msg.sender == IFilterAdmin(_OWNER_)._OWNER_(), "ACCESS_RESTRICTED"); + function emergencyWithdraw( + address[] memory nftContract, + uint256[] memory tokenIds, + address to + ) external onlySuperOwner { require(nftContract.length == tokenIds.length, "PARAM_INVALID"); - address controllerModel = IFilterAdmin(_OWNER_)._CONTROLLER_MODEL_(); - require(IControllerModel(controllerModel).getEmergencySwitch(address(this)), "NOT_OPEN"); + address controller = IFilterAdmin(_OWNER_)._CONTROLLER_(); + require( + IController(controller).isEmergencyWithdrawOpen(address(this)), + "EMERGENCY_WITHDRAW_NOT_OPEN" + ); - for(uint256 i = 0; i< nftContract.length; i++) { - if(_NFT_RESERVE_[tokenIds[i]] == 1 && nftContract[i] == _NFT_COLLECTION_){ - uint256 index = getNFTIndexById(tokenIds[i]); + for (uint256 i = 0; i < nftContract.length; i++) { + uint256 tokenId = tokenIds[i]; + if (_NFT_RESERVE_[tokenId] > 0 && nftContract[i] == _NFT_COLLECTION_) { + uint256 index = getNFTIndexById(tokenId); _NFT_IDS_[index] = _NFT_IDS_[_NFT_IDS_.length - 1]; _NFT_IDS_.pop(); - _NFT_RESERVE_[tokenIds[i]] = 0; + _NFT_RESERVE_[tokenId] = 0; } IERC721(nftContract[i]).safeTransferFrom(address(this), to, tokenIds[i]); } + _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; } // ============ Support ============ @@ -142,8 +143,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { return interfaceId == type(IERC721Receiver).interfaceId; } - function version() virtual external pure returns (string memory) { + function version() external pure virtual returns (string memory) { return "FILTER_1_ERC721 1.0.0"; } - -} \ No newline at end of file +} diff --git a/contracts/NFTPool/intf/IController.sol b/contracts/NFTPool/intf/IController.sol new file mode 100644 index 0000000..224a6f9 --- /dev/null +++ b/contracts/NFTPool/intf/IController.sol @@ -0,0 +1,16 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +interface IController { + function getMintFeeRate(address filterAdminAddr) external view returns (uint256); + + function getBurnFeeRate(address filterAdminAddr) external view returns (uint256); + + function isEmergencyWithdrawOpen(address filter) external view returns (bool); +} diff --git a/contracts/NFTPool/intf/IControllerModel.sol b/contracts/NFTPool/intf/IControllerModel.sol deleted file mode 100644 index ebf2ec2..0000000 --- a/contracts/NFTPool/intf/IControllerModel.sol +++ /dev/null @@ -1,16 +0,0 @@ -/* - - Copyright 2021 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; - -interface IControllerModel { - function getMintFee(address filterAdminAddr) external view returns (uint256); - - function getBurnFee(address filterAdminAddr) external view returns (uint256); - - function getEmergencySwitch(address filter) external view returns (bool); -} \ No newline at end of file diff --git a/contracts/NFTPool/intf/IFilter.sol b/contracts/NFTPool/intf/IFilter.sol new file mode 100644 index 0000000..f10cd89 --- /dev/null +++ b/contracts/NFTPool/intf/IFilter.sol @@ -0,0 +1,45 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +interface IFilter { + function isNFTValid(address nftCollectionAddress, uint256 nftId) external view returns (bool); + + function _NFT_COLLECTION_() external view returns (address); + + function queryNFTIn(uint256 NFTInAmount) + external + view + returns (uint256 rawReceive, uint256 received); + + function queryNFTTargetOut(uint256 NFTOutAmount) + external + view + returns (uint256 rawPay, uint256 pay); + + function queryNFTRandomOut(uint256 NFTOutAmount) + external + view + returns (uint256 rawPay, uint256 pay); + + function ERC721In(uint256[] memory tokenIds, address to) external returns (uint256 received); + + function ERC721TargetOut(uint256[] memory indexes, address to) external returns (uint256 paid); + + 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); +} diff --git a/contracts/NFTPool/intf/IFilterAdmin.sol b/contracts/NFTPool/intf/IFilterAdmin.sol index 52fbd7d..8cd63b7 100644 --- a/contracts/NFTPool/intf/IFilterAdmin.sol +++ b/contracts/NFTPool/intf/IFilterAdmin.sol @@ -10,16 +10,16 @@ pragma solidity 0.6.9; interface IFilterAdmin { function _OWNER_() external returns (address); - function _CONTROLLER_MODEL_() external returns (address); + function _CONTROLLER_() external returns (address); function init( address owner, uint256 initSupply, string memory name, string memory symbol, - uint256 fee, - address mtFeeModel, - address defaultMaintainer, + uint256 feeRate, + address controller, + address maintainer, address[] memory filters ) external; @@ -27,7 +27,21 @@ interface IFilterAdmin { function burnFragFrom(address from, uint256 rawAmount) external returns (uint256 paid); - function queryChargeMintFee(uint256 rawAmount) external view returns (uint256 poolFee, uint256 mtFee); + function queryMintFee(uint256 rawAmount) + external + view + returns ( + uint256 poolFee, + uint256 mtFee, + uint256 afterChargedAmount + ); - function queryChargeBurnFee(uint256 rawAmount) external view returns (uint256 poolFee, uint256 mtFee); + function queryBurnFee(uint256 rawAmount) + external + view + returns ( + uint256 poolFee, + uint256 mtFee, + uint256 afterChargedAmount + ); } diff --git a/contracts/NFTPool/intf/IFilterModel.sol b/contracts/NFTPool/intf/IFilterModel.sol deleted file mode 100644 index 3c7006b..0000000 --- a/contracts/NFTPool/intf/IFilterModel.sol +++ /dev/null @@ -1,32 +0,0 @@ -/* - - Copyright 2021 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; - -interface IFilterModel { - function isNFTValid(address nftCollectionAddress, uint256 nftId) external view returns (bool); - - function _NFT_COLLECTION_() external view returns(address); - - function queryNFTIn(uint256 NFTInAmount) external view returns (uint256 rawReceive, uint256 received); - - function queryNFTTargetOut(uint256 NFTOutAmount) external view returns (uint256 rawPay, uint256 pay); - - function queryNFTRandomOut(uint256 NFTOutAmount) external view returns (uint256 rawPay, uint256 pay); - - function ERC721In(uint256[] memory tokenIds, address to) external returns(uint256 received); - - function ERC721TargetOut(uint256[] memory indexes, address to) external returns(uint256 paid); - - 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 From 09a4cbfc9c49a13247e177934a8a50e74b0a7614 Mon Sep 17 00:00:00 2001 From: mingda Date: Fri, 10 Sep 2021 19:49:29 +0800 Subject: [PATCH 15/42] fix ERC1155 random --- contracts/NFTPool/impl/BaseFilterV1.sol | 10 ++++------ contracts/NFTPool/impl/FilterERC1155V1.sol | 10 +++++++++- contracts/NFTPool/impl/FilterERC721V1.sol | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/contracts/NFTPool/impl/BaseFilterV1.sol b/contracts/NFTPool/impl/BaseFilterV1.sol index ab646e5..e984d99 100644 --- a/contracts/NFTPool/impl/BaseFilterV1.sol +++ b/contracts/NFTPool/impl/BaseFilterV1.sol @@ -173,12 +173,10 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { return a1.mul(qn.sub(qm)).div(q.sub(DecimalMath.ONE)); } - function _getRandomOutId() public view returns (uint256 index) { - index = - uint256( - keccak256(abi.encodePacked(tx.origin, blockhash(block.number - 1), gasleft())) - ) % - _TOTAL_NFT_AMOUNT_; + function _getRandomNum() public view returns (uint256 randomNum) { + randomNum = uint256( + keccak256(abi.encodePacked(tx.origin, blockhash(block.number - 1), gasleft())) + ); } // ================= Ownable ================ diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index 609b86e..fc230e8 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -79,7 +79,15 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { (uint256 rawPay, ) = queryNFTRandomOut(amount); paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); for (uint256 i = 0; i < amount; i++) { - _transferOutERC1155(to, _getRandomOutId(), 1); + uint256 randomNum = _getRandomOutId() % _TOTAL_NFT_AMOUNT_; + uint256 sum; + for (uint256 j = 0; j < _NFT_IDS_.length; j++) { + sum += _NFT_RESERVE_[_NFT_IDS_[j]]; + if (sum >= randomNum) { + _transferOutERC1155(to, _NFT_IDS_[j], 1); + break; + } + } } } diff --git a/contracts/NFTPool/impl/FilterERC721V1.sol b/contracts/NFTPool/impl/FilterERC721V1.sol index bfbc0da..08da766 100644 --- a/contracts/NFTPool/impl/FilterERC721V1.sol +++ b/contracts/NFTPool/impl/FilterERC721V1.sol @@ -87,7 +87,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { (uint256 rawPay, ) = queryNFTRandomOut(amount); paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); for (uint256 i = 0; i < amount; i++) { - _transferOutERC721(to, _getRandomOutId()); + _transferOutERC721(to, _getRandomNum() % _TOTAL_NFT_AMOUNT_); } _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; } From 4deca2be13d6ad682a81589a91dc6ddd388a126d Mon Sep 17 00:00:00 2001 From: owen05 Date: Fri, 10 Sep 2021 21:16:47 +0800 Subject: [PATCH 16/42] fix --- README.md | 4 +- contracts/NFTPool/impl/BaseFilterV1.sol | 29 +++++------ contracts/NFTPool/impl/Controller.sol | 12 ++--- contracts/NFTPool/impl/FilterAdmin.sol | 8 +-- contracts/NFTPool/impl/FilterERC1155V1.sol | 8 +-- contracts/NFTPool/impl/FilterERC721V1.sol | 4 +- .../SmartRoute/proxies/DODONFTPoolProxy.sol | 50 +++++++++---------- 7 files changed, 55 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index c686fa4..0fd71ec 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,7 @@ - contracts/NFTPool/impl/FilterERC1155V1.sol -- contracts/NFTPool/impl/ControllerModel.sol - -- contracts/external/ERC20/InitializableInternalMintableERC20.sol +- contracts/NFTPool/impl/Controller.sol - contracts/SmartRoute/proxies/DODONFTPoolProxy.sol diff --git a/contracts/NFTPool/impl/BaseFilterV1.sol b/contracts/NFTPool/impl/BaseFilterV1.sol index e984d99..438e967 100644 --- a/contracts/NFTPool/impl/BaseFilterV1.sol +++ b/contracts/NFTPool/impl/BaseFilterV1.sol @@ -99,57 +99,54 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { public view returns ( - uint256 received, - uint256 poolFee, - uint256 mtFee + uint256 rawReceive, + uint256 received ) { - require(NFTInAmount <= getAvaliableNFTIn(), "EXCEDD_IN_AMOUNT"); + require(NFTInAmount <= getAvaliableNFTInAmount(), "EXCEDD_IN_AMOUNT"); rawReceive = _geometricCalc( _GS_START_IN_, _CR_IN_, _TOTAL_NFT_AMOUNT_, _TOTAL_NFT_AMOUNT_ + NFTInAmount ); - (poolFee, mtFee, received) = IFilterAdmin(_OWNER_).queryChargeMintFee(rawReceive); + (,, received) = IFilterAdmin(_OWNER_).queryMintFee(rawReceive); } function queryNFTTargetOut(uint256 NFTOutAmount) public view returns ( - uint256 paid, - uint256 poolFee, - uint256 mtFee + uint256 rawPay, + uint256 pay ) { - require(NFTOutAmount <= getAvaliableNFTOut(), "EXCEED_OUT_AMOUNT"); + require(NFTOutAmount <= getAvaliableNFTOutAmount(), "EXCEED_OUT_AMOUNT"); rawPay = _geometricCalc( _GS_START_TARGET_OUT_, _CR_TARGET_OUT_, _TOTAL_NFT_AMOUNT_ - NFTOutAmount, _TOTAL_NFT_AMOUNT_ ); - (poolFee, mtFee, paid) = IFilterAdmin(_OWNER_).queryChargeBurnFee(rawPay); + (,, pay) = IFilterAdmin(_OWNER_).queryBurnFee(rawPay); } function queryNFTRandomOut(uint256 NFTOutAmount) public view returns ( - uint256 paid, - uint256 poolFee, - uint256 mtFee + uint256 rawPay, + uint256 pay ) { - require(NFTOutAmount <= getAvaliableNFTOut(), "EXCEED_OUT_AMOUNT"); + require(NFTOutAmount <= getAvaliableNFTOutAmount(), "EXCEED_OUT_AMOUNT"); rawPay = _geometricCalc( _GS_START_RANDOM_OUT_, _CR_RANDOM_OUT_, _TOTAL_NFT_AMOUNT_ - NFTOutAmount, _TOTAL_NFT_AMOUNT_ ); - (poolFee, mtFee, paid) = IFilterAdmin(_OWNER_).queryChargeBurnFee(rawPay); + (,, pay) = IFilterAdmin(_OWNER_).queryBurnFee(rawPay); } // ============ Math ============= @@ -161,7 +158,7 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { uint256 end ) internal view returns (uint256) { if (q == DecimalMath.ONE) { - return end.sub(start).ml(a1); + return end.sub(start).mul(a1); } //Sn=a1*(q^n-1)/(q-1) //Sn-Sm = a1*(q^n-q^m)/(q-1) diff --git a/contracts/NFTPool/impl/Controller.sol b/contracts/NFTPool/impl/Controller.sol index 03be197..d4e87e6 100644 --- a/contracts/NFTPool/impl/Controller.sol +++ b/contracts/NFTPool/impl/Controller.sol @@ -60,20 +60,20 @@ contract Controller is InitializableOwnable { function getMintFeeRate(address filterAdminAddr) external view returns (uint256) { FilterAdminFeeRateInfo memory filterAdminFeeRateInfo = filterAdminFeeRates[filterAdminAddr]; - if (filterAdminFeeRateInfo.isSet) { + if (filterAdminFeeRateInfo.isOpen) { return filterAdminFeeRateInfo.nftInFeeRate; } else { - return _GLOBAL_NFT_IN_FEERate_; + return _GLOBAL_NFT_IN_FEE_RATE_; } } function getBurnFeeRate(address filterAdminAddr) external view returns (uint256) { - FilterAdminFeeInfo memory filterAdminFeeInfo = filterAdminFees[filterAdminAddr]; + FilterAdminFeeRateInfo memory filterAdminFeeInfo = filterAdminFeeRates[filterAdminAddr]; - if (filterAdminFeeInfo.isSet) { - return filterAdminFeeInfo.nftOutFee; + if (filterAdminFeeInfo.isOpen) { + return filterAdminFeeInfo.nftOutFeeRate; } else { - return _GLOBAL_NFT_OUT_FEE_; + return _GLOBAL_NFT_OUT_FEE_RATE_; } } } diff --git a/contracts/NFTPool/impl/FilterAdmin.sol b/contracts/NFTPool/impl/FilterAdmin.sol index 07a8a0d..7710267 100644 --- a/contracts/NFTPool/impl/FilterAdmin.sol +++ b/contracts/NFTPool/impl/FilterAdmin.sol @@ -56,7 +56,7 @@ contract FilterAdmin is InitializableInternalMintableERC20 { if (mtFee > 0) _mint(_MAINTAINER_, mtFee); _mint(to, received); - return received + return received; } function burnFragFrom(address from, uint256 rawAmount) external returns (uint256) { @@ -79,7 +79,7 @@ contract FilterAdmin is InitializableInternalMintableERC20 { uint256 afterChargedAmount ) { - uint256 mtFeeRate = IController(_CONTROLLER_).getMintFee(address(this)); + uint256 mtFeeRate = IController(_CONTROLLER_).getMintFeeRate(address(this)); poolFee = DecimalMath.mulFloor(rawAmount, _FEE_RATE_); mtFee = DecimalMath.mulFloor(rawAmount, mtFeeRate); afterChargedAmount = rawAmount.sub(poolFee).sub(mtFee); @@ -93,14 +93,14 @@ contract FilterAdmin is InitializableInternalMintableERC20 { uint256 afterChargedAmount ) { - uint256 mtFeeRate = IController(_CONTROLLER_).getBurnFee(address(this)); + uint256 mtFeeRate = IController(_CONTROLLER_).getBurnFeeRate(address(this)); poolFee = DecimalMath.mulFloor(rawAmount, _FEE_RATE_); mtFee = DecimalMath.mulFloor(rawAmount, mtFeeRate); afterChargedAmount = rawAmount.add(poolFee).add(mtFee); } function isRegisteredFilter(address filter) public view returns (bool) { - return _FILTER_REGISTRY_[i]; + return _FILTER_REGISTRY_[filter]; } function getFilters() public view returns (address[] memory) { diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index fc230e8..54a4e5a 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -8,7 +8,7 @@ pragma experimental ABIEncoderV2; import {SafeMath} from "../../lib/SafeMath.sol"; import {IFilterAdmin} from "../intf/IFilterAdmin.sol"; -import {IControllerModel} from "../intf/IControllerModel.sol"; +import {IController} from "../intf/IController.sol"; import {IERC1155} from "../../intf/IERC1155.sol"; import {IERC1155Receiver} from "../../intf/IERC1155Receiver.sol"; import {DecimalMath} from "../../lib/DecimalMath.sol"; @@ -32,7 +32,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { _changeNFTRandomInPrice(priceRules[2], priceRules[3], toggles[1]); _changeNFTTargetOutPrice(priceRules[4], priceRules[5], toggles[2]); - _changeNFTAmount(numParams[2], numParams[3]); + _changeNFTAmountRange(numParams[2], numParams[3]); _changeTokenIdRange(numParams[0], numParams[1]); for (uint256 i = 0; i < spreadIds.length; i++) { @@ -79,12 +79,12 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { (uint256 rawPay, ) = queryNFTRandomOut(amount); paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); for (uint256 i = 0; i < amount; i++) { - uint256 randomNum = _getRandomOutId() % _TOTAL_NFT_AMOUNT_; + uint256 randomNum = _getRandomNum() % _TOTAL_NFT_AMOUNT_; uint256 sum; for (uint256 j = 0; j < _NFT_IDS_.length; j++) { sum += _NFT_RESERVE_[_NFT_IDS_[j]]; if (sum >= randomNum) { - _transferOutERC1155(to, _NFT_IDS_[j], 1); + _transferOutERC1155(to, j, 1); break; } } diff --git a/contracts/NFTPool/impl/FilterERC721V1.sol b/contracts/NFTPool/impl/FilterERC721V1.sol index 08da766..8a3d88a 100644 --- a/contracts/NFTPool/impl/FilterERC721V1.sol +++ b/contracts/NFTPool/impl/FilterERC721V1.sol @@ -10,7 +10,7 @@ pragma experimental ABIEncoderV2; import {SafeMath} from "../../lib/SafeMath.sol"; import {IFilterAdmin} from "../intf/IFilterAdmin.sol"; -import {IControllerModel} from "../intf/IControllerModel.sol"; +import {IController} from "../intf/IController.sol"; import {IERC721} from "../../intf/IERC721.sol"; import {IERC721Receiver} from "../../intf/IERC721Receiver.sol"; import {DecimalMath} from "../../lib/DecimalMath.sol"; @@ -35,7 +35,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { _changeNFTRandomInPrice(priceRules[2], priceRules[3], toggles[1]); _changeNFTTargetOutPrice(priceRules[4], priceRules[5], toggles[2]); - _changeNFTAmount(numParams[2], numParams[3]); + _changeNFTAmountRange(numParams[2], numParams[3]); _changeTokenIdRange(numParams[0], numParams[1]); for (uint256 i = 0; i < spreadIds.length; i++) { diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index 7cb9d5b..596e774 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -10,7 +10,7 @@ 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 {IFilter} from "../../NFTPool/intf/IFilter.sol"; import {IFilterAdmin} from "../../NFTPool/intf/IFilterAdmin.sol"; import {IDODONFTApprove} from "../../intf/IDODONFTApprove.sol"; import {IERC20} from "../../intf/IERC20.sol"; @@ -20,7 +20,7 @@ interface IFilterV1 { function init( address filterAdmin, address nftCollection, - bool[] memory switches, + bool[] memory toggles, uint256[] memory numParams, uint256[] memory priceRules, uint256[] memory spreadIds @@ -35,8 +35,8 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { address constant _ETH_ADDRESS_ = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; mapping(uint256 => address) public _FILTER_TEMPLATES_; address public _FILTER_ADMIN_TEMPLATE_; - address public _DEFAULT_MAINTAINER_; - address public _CONTROLLER_MODEL_; + address public _MAINTAINER_; + address public _CONTROLLER_; address public immutable _CLONE_FACTORY_; address public immutable _DODO_NFT_APPROVE_; address public immutable _DODO_APPROVE_; @@ -55,8 +55,8 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { ) public { _CLONE_FACTORY_ = cloneFactory; _FILTER_ADMIN_TEMPLATE_ = filterAdminTemplate; - _CONTROLLER_MODEL_ = controllerModel; - _DEFAULT_MAINTAINER_ = defaultMaintainer; + _CONTROLLER_ = controllerModel; + _MAINTAINER_ = defaultMaintainer; _DODO_NFT_APPROVE_ = dodoNftApprove; _DODO_APPROVE_ = dodoApprove; } @@ -70,10 +70,10 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { uint256 minMintAmount ) external { for(uint256 i = 0; i < tokenIds.length; i++) { - require(IFilterModel(filter).isNFTValid(nftCollection,tokenIds[i]), "NOT_REGISTRIED"); + require(IFilter(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); + uint256 received = IFilter(filter).ERC721In(tokenIds, to); require(received >= minMintAmount, "MINT_AMOUNT_NOT_ENOUGH"); } @@ -83,7 +83,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { address to, uint256 maxBurnAmount ) external { - uint256 paid = IFilterModel(filter).ERC721TargetOut(indexes, to); + uint256 paid = IFilter(filter).ERC721TargetOut(indexes, to); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); } @@ -93,7 +93,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { address to, uint256 maxBurnAmount ) external { - uint256 paid = IFilterModel(filter).ERC721RandomOut(amount, to); + uint256 paid = IFilter(filter).ERC721RandomOut(amount, to); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); } @@ -107,10 +107,10 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { uint256 minMintAmount ) external { for(uint256 i = 0; i < tokenIds.length; i++) { - require(IFilterModel(filter).isNFTValid(nftCollection,tokenIds[i]), "NOT_REGISTRIED"); + require(IFilter(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); + uint256 received = IFilter(filter).ERC1155In(tokenIds, to); require(received >= minMintAmount, "MINT_AMOUNT_NOT_ENOUGH"); } @@ -121,7 +121,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { address to, uint256 maxBurnAmount ) external { - uint256 paid = IFilterModel(filter).ERC1155TargetOut(indexes, amounts, to); + uint256 paid = IFilter(filter).ERC1155TargetOut(indexes, amounts, to); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); } @@ -131,7 +131,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { address to, uint256 maxBurnAmount ) external { - uint256 paid = IFilterModel(filter).ERC1155RandomOut(amount, to); + uint256 paid = IFilter(filter).ERC1155RandomOut(amount, to); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); } @@ -142,7 +142,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { uint256 filterKey, //1 => FilterERC721V1, 2 => FilterERC1155V1 string[] memory tokenInfo, uint256[] memory numParams,//0 - initSupply, 1 - fee - bool[] memory switches, + bool[] memory toggles, uint256[] memory filterNumParams, //0 - startId, 1 - endId, 2 - maxAmount, 3 - minAmount uint256[] memory priceRules, uint256[] memory spreadIds @@ -153,7 +153,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { filterKey, newFilterAdmin, nftCollection, - switches, + toggles, filterNumParams, priceRules, spreadIds @@ -168,8 +168,8 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { tokenInfo[0], tokenInfo[1], numParams[1], - _CONTROLLER_MODEL_, - _DEFAULT_MAINTAINER_, + _CONTROLLER_, + _MAINTAINER_, filters ); } @@ -179,7 +179,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { uint256 key, address filterAdmin, address nftCollection, - bool[] memory switches, + bool[] memory toggles, uint256[] memory numParams, //0 - startId, 1 - endId, 2 - maxAmount, 3 - minAmount uint256[] memory priceRules, uint256[] memory spreadIds @@ -188,7 +188,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { IFilterV1(newFilterV1).init( filterAdmin, nftCollection, - switches, + toggles, numParams, priceRules, spreadIds @@ -214,7 +214,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { uint256[] memory tokenIds = new uint256[](1); tokenIds[0] = tokenId; - uint256 receivedFragAmount = IFilterModel(filter).ERC721In(tokenIds, address(this)); + uint256 receivedFragAmount = IFilter(filter).ERC721In(tokenIds, address(this)); _generalApproveMax(filterAdmin, _DODO_APPROVE_, receivedFragAmount); @@ -228,16 +228,16 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { //====================== Ownable ======================== - function changeDefaultMaintainer(address newMaintainer) external onlyOwner { - _DEFAULT_MAINTAINER_ = newMaintainer; + function changeMaintainer(address newMaintainer) external onlyOwner { + _MAINTAINER_ = newMaintainer; } function changeFilterAdminTemplate(address newFilterAdminTemplate) external onlyOwner { _FILTER_ADMIN_TEMPLATE_ = newFilterAdminTemplate; } - function changeControllerModel(address newControllerModel) external onlyOwner { - _CONTROLLER_MODEL_ = newControllerModel; + function changeController(address newControllerModel) external onlyOwner { + _CONTROLLER_ = newControllerModel; } function setFilterTemplate(uint256 idx, address newFilterTemplate) external onlyOwner { From fb64de3a0410b1f27dd2577d902a9203fb3c63ab Mon Sep 17 00:00:00 2001 From: owen05 Date: Mon, 13 Sep 2021 14:31:23 +0800 Subject: [PATCH 17/42] add filterName && nftPool test context --- contracts/NFTPool/impl/BaseFilterV1.sol | 9 ++ contracts/NFTPool/impl/FilterERC1155V1.sol | 3 + contracts/NFTPool/impl/FilterERC721V1.sol | 4 +- contracts/NFTPool/intf/IFilter.sol | 10 ++ .../SmartRoute/proxies/DODONFTPoolProxy.sol | 22 ++-- test/utils/Contracts.ts | 10 ++ test/utils/NFTPoolContext.ts | 111 ++++++++++++++++++ truffle-test.sh | 13 +- 8 files changed, 157 insertions(+), 25 deletions(-) create mode 100644 test/utils/NFTPoolContext.ts diff --git a/contracts/NFTPool/impl/BaseFilterV1.sol b/contracts/NFTPool/impl/BaseFilterV1.sol index 438e967..5d00622 100644 --- a/contracts/NFTPool/impl/BaseFilterV1.sol +++ b/contracts/NFTPool/impl/BaseFilterV1.sol @@ -16,6 +16,8 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { using SafeMath for uint256; //=================== Storage =================== + string public _FILTER_NAME_; + address public _NFT_COLLECTION_; uint256 public _NFT_ID_START_; uint256 public _NFT_ID_END_ = uint256(-1); @@ -273,4 +275,11 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { _SPREAD_IDS_REGISTRY_[tokenIds[i]] = isRegistered[i]; } } + + function changeFilterName(string memory newFilterName) + external + onlySuperOwner + { + _FILTER_NAME_ = newFilterName; + } } diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index 54a4e5a..1cc53a2 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -21,11 +21,14 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { address filterAdmin, address nftCollection, bool[] memory toggles, + string memory filterName, uint256[] memory numParams, //0 - startId, 1 - endId, 2 - maxAmount, 3 - minAmount uint256[] memory priceRules, uint256[] memory spreadIds ) external { initOwner(filterAdmin); + + _FILTER_NAME_ = filterName; _NFT_COLLECTION_ = nftCollection; _changeNFTInPrice(priceRules[0], priceRules[1], toggles[0]); diff --git a/contracts/NFTPool/impl/FilterERC721V1.sol b/contracts/NFTPool/impl/FilterERC721V1.sol index 8a3d88a..a8a3493 100644 --- a/contracts/NFTPool/impl/FilterERC721V1.sol +++ b/contracts/NFTPool/impl/FilterERC721V1.sol @@ -24,13 +24,15 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { address filterAdmin, address nftCollection, bool[] memory toggles, + string memory filterName, uint256[] memory numParams, //0 - startId, 1 - endId, 2 - maxAmount, 3 - minAmount uint256[] memory priceRules, uint256[] memory spreadIds ) external { initOwner(filterAdmin); - _NFT_COLLECTION_ = nftCollection; + _FILTER_NAME_ = filterName; + _NFT_COLLECTION_ = nftCollection; _changeNFTInPrice(priceRules[0], priceRules[1], toggles[0]); _changeNFTRandomInPrice(priceRules[2], priceRules[3], toggles[1]); _changeNFTTargetOutPrice(priceRules[4], priceRules[5], toggles[2]); diff --git a/contracts/NFTPool/intf/IFilter.sol b/contracts/NFTPool/intf/IFilter.sol index f10cd89..048a9b4 100644 --- a/contracts/NFTPool/intf/IFilter.sol +++ b/contracts/NFTPool/intf/IFilter.sol @@ -8,6 +8,16 @@ pragma solidity 0.6.9; interface IFilter { + function init( + address filterAdmin, + address nftCollection, + bool[] memory toggles, + string memory filterName, + uint256[] memory numParams, + uint256[] memory priceRules, + uint256[] memory spreadIds + ) external; + function isNFTValid(address nftCollectionAddress, uint256 nftId) external view returns (bool); function _NFT_COLLECTION_() external view returns (address); diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index 596e774..edb8e27 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -16,17 +16,6 @@ import {IDODONFTApprove} from "../../intf/IDODONFTApprove.sol"; import {IERC20} from "../../intf/IERC20.sol"; import {SafeERC20} from "../../lib/SafeERC20.sol"; -interface IFilterV1 { - function init( - address filterAdmin, - address nftCollection, - bool[] memory toggles, - uint256[] memory numParams, - uint256[] memory priceRules, - uint256[] memory spreadIds - ) external; -} - contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { using SafeMath for uint256; using SafeERC20 for IERC20; @@ -140,7 +129,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { function createNewNFTPoolV1( address nftCollection, uint256 filterKey, //1 => FilterERC721V1, 2 => FilterERC1155V1 - string[] memory tokenInfo, + string[] memory infos, // 0 => filterName, 1 => fragName, 2 => fragSymbol uint256[] memory numParams,//0 - initSupply, 1 - fee bool[] memory toggles, uint256[] memory filterNumParams, //0 - startId, 1 - endId, 2 - maxAmount, 3 - minAmount @@ -154,6 +143,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { newFilterAdmin, nftCollection, toggles, + infos[0], filterNumParams, priceRules, spreadIds @@ -165,8 +155,8 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { IFilterAdmin(newFilterAdmin).init( msg.sender, numParams[0], - tokenInfo[0], - tokenInfo[1], + infos[1], + infos[2], numParams[1], _CONTROLLER_, _MAINTAINER_, @@ -180,15 +170,17 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { address filterAdmin, address nftCollection, bool[] memory toggles, + string memory filterName, uint256[] memory numParams, //0 - startId, 1 - endId, 2 - maxAmount, 3 - minAmount uint256[] memory priceRules, uint256[] memory spreadIds ) public returns(address newFilterV1) { newFilterV1 = ICloneFactory(_CLONE_FACTORY_).clone(_FILTER_TEMPLATES_[key]); - IFilterV1(newFilterV1).init( + IFilter(newFilterV1).init( filterAdmin, nftCollection, toggles, + filterName, numParams, priceRules, spreadIds diff --git a/test/utils/Contracts.ts b/test/utils/Contracts.ts index 20190e9..939674b 100644 --- a/test/utils/Contracts.ts +++ b/test/utils/Contracts.ts @@ -74,6 +74,16 @@ export const DROPS_ERC1155 = "DropsERC1155" export const DROPS_FEE_MODEL = "DropsFeeModel" export const DROPS_PROXY = "DODODropsProxy" +export const DODO_NFT = "DODONFT" +export const DODO_NFT_1155 = "DODONFT1155" + +export const FILTER_ERC721_V1 = "FilterERC721" +export const FILTER_ERC1155_V1 = "FilterERC1155" +export const FILTER_ADMIN = "FilterAdmin" +export const CONTROLLER = "Controller" +export const DODO_NFT_APPROVE = "DODONFTApprove" +export const DODO_NFT_POOL_PROXY = "DODONFTPoolProxy" + interface ContractJson { abi: any; diff --git a/test/utils/NFTPoolContext.ts b/test/utils/NFTPoolContext.ts new file mode 100644 index 0000000..5949f03 --- /dev/null +++ b/test/utils/NFTPoolContext.ts @@ -0,0 +1,111 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +import BigNumber from 'bignumber.js'; +import Web3 from 'web3'; +import { Contract } from 'web3-eth-contract'; + +import * as contracts from './Contracts'; +import { decimalStr, mweiStr } from './Converter'; +import { EVM, getDefaultWeb3 } from './EVM'; +import * as log from './Log'; + +BigNumber.config({ + EXPONENTIAL_AT: 1000, + DECIMAL_PLACES: 80, +}); + + +export class NFTPoolContext { + EVM: EVM; + Web3: Web3; + + FilterAdmin: Contract; + FilterERC721V1: Contract; + FilterERC1155V1: Contract; + Controller: Contract; + DODONFTApprove: Contract; + DODONFTPoolProxy: Contract; + + //nft token + DodoNft: Contract; + DodoNft1155: Contract; + + Deployer: string; + Maintainer: string; + SpareAccounts: string[]; + + constructor() { } + + async init() { + this.EVM = new EVM(); + this.Web3 = getDefaultWeb3(); + const allAccounts = await this.Web3.eth.getAccounts(); + this.Deployer = allAccounts[0]; + this.Maintainer = allAccounts[1]; + this.SpareAccounts = allAccounts.slice(2, 10); + + + this.DodoNft = await contracts.newContract(contracts.DODO_NFT); + this.DodoNft1155 = await contracts.newContract(contracts.DODO_NFT_1155); + + await this.DodoNft.methods.init(this.Deployer, "DODONFT", "DODONFT").send(this.sendParam(this.Deployer)); + await this.DodoNft1155.methods.initOwner(this.Deployer).send(this.sendParam(this.Deployer)); + + var cloneFactory = await contracts.newContract( + contracts.CLONE_FACTORY_CONTRACT_NAME + ); + var filterAdminTemplate = await contracts.newContract(contracts.FILTER_ADMIN) + var filterERC721V1Template = await contracts.newContract(contracts.FILTER_ERC721_V1) + var filterERC1155V1Template = await contracts.newContract(contracts.FILTER_ERC1155_V1) + + this.Controller = await contracts.newContract(contracts.CONTROLLER) + await this.Controller.methods.initOwner(this.Deployer).send(this.sendParam(this.Deployer)); + + this.DODONFTApprove = await contracts.newContract( + contracts.DODO_NFT_APPROVE + ); + + this.DODONFTPoolProxy = await contracts.newContract(contracts.DODO_NFT_POOL_PROXY, + [ + cloneFactory.options.address, + filterAdminTemplate.options.address, + this.Controller.options.address, + this.Deployer, + this.DODONFTApprove.options.address, + "" //TODO:ERC721 => ERC20 + ] + ) + + await this.DODONFTPoolProxy.methods.initOwner(this.Deployer).send(this.sendParam(this.Deployer)); + await this.DODONFTPoolProxy.methods.setFilterTemplate(1, filterERC721V1Template.options.address).send(this.sendParam(this.Deployer)); + await this.DODONFTPoolProxy.methods.setFilterTemplate(2, filterERC1155V1Template.options.address).send(this.sendParam(this.Deployer)); + + await this.DODONFTApprove.methods.init(this.Deployer, [this.DODONFTPoolProxy.options.address]).send(this.sendParam(this.Deployer)); + + console.log(log.blueText("[Init NFTPool context]")); + } + + sendParam(sender, value = "0") { + return { + from: sender, + gas: process.env["COVERAGE"] ? 10000000000 : 7000000, + gasPrice: mweiStr("1000"), + value: decimalStr(value), + }; + } + + async mintTestToken(to: string, token: Contract, amount: string) { + await token.methods.mint(to, amount).send(this.sendParam(this.Deployer)); + } +} + +export async function getNFTPoolContext(weth: string): Promise { + var context = new NFTPoolContext(); + await context.init(); + return context; +} diff --git a/truffle-test.sh b/truffle-test.sh index d17e2ec..9770c14 100644 --- a/truffle-test.sh +++ b/truffle-test.sh @@ -71,12 +71,7 @@ then truffle test ./test/DODODrops/dropsV2.test.ts fi -# if [ "$1"x = "route-incentive"x ] -# then -# truffle test ./test/Route/Incentive.test.ts -# fi - -# if [ "$1"x = "route"x ] -# then -# truffle test ./test/Route/route.test.ts -# fi \ No newline at end of file +if [ "$1"x = "NFTPool"x ] +then + truffle test ./test/NFTPool/nftPool.test.ts +fi \ No newline at end of file From 4b60736a7bcf6e7f03ddcc390ad6ebaa38b6036c Mon Sep 17 00:00:00 2001 From: owen05 Date: Tue, 14 Sep 2021 13:43:48 +0800 Subject: [PATCH 18/42] event tmp --- contracts/NFTPool/impl/FilterAdmin.sol | 13 +- contracts/NFTPool/impl/FilterERC1155V1.sol | 2 + contracts/NFTPool/impl/FilterERC721V1.sol | 7 + .../SmartRoute/proxies/DODONFTPoolProxy.sol | 13 +- test/NFTPool/erc721NftPool.test.ts | 204 ++++++++++++++++++ test/utils/NFTPoolContext.ts | 4 +- 6 files changed, 236 insertions(+), 7 deletions(-) create mode 100644 test/NFTPool/erc721NftPool.test.ts diff --git a/contracts/NFTPool/impl/FilterAdmin.sol b/contracts/NFTPool/impl/FilterAdmin.sol index 7710267..8b70970 100644 --- a/contracts/NFTPool/impl/FilterAdmin.sol +++ b/contracts/NFTPool/impl/FilterAdmin.sol @@ -46,6 +46,9 @@ contract FilterAdmin is InitializableInternalMintableERC20 { for (uint256 i = 0; i < filters.length; i++) { _FILTER_REGISTRY_[filters[i]] = true; } + + //event FilterAdminInit(addres owner, uint256 feeRate) + } function mintFragTo(address to, uint256 rawAmount) external returns (uint256) { @@ -108,10 +111,12 @@ contract FilterAdmin is InitializableInternalMintableERC20 { } //================= Owner ================ - function addFilter(address filter) external onlyOwner { - require(!isRegisteredFilter(filter), "FILTER_ALREADY_EXIST"); - _FILTERS_.push(filter); - _FILTER_REGISTRY_[filter] = true; + function addFilter(address[] memory filters) external onlyOwner { + for(uint256 i = 0; i < filters.length; i++) { + require(!isRegisteredFilter(filters[i]), "FILTER_ALREADY_EXIST"); + _FILTERS_.push(filter[i]); + _FILTER_REGISTRY_[filter[i]] = true; + } } function changeFeeRate(uint256 newFeeRate) external onlyOwner { diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index 1cc53a2..4471a0f 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -41,6 +41,8 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { for (uint256 i = 0; i < spreadIds.length; i++) { _SPREAD_IDS_REGISTRY_[spreadIds[i]] = true; } + + //event FilterInit(address filterAdmin, address nftCollection, string memory name); } // ================= Trading ================ diff --git a/contracts/NFTPool/impl/FilterERC721V1.sol b/contracts/NFTPool/impl/FilterERC721V1.sol index a8a3493..243c11d 100644 --- a/contracts/NFTPool/impl/FilterERC721V1.sol +++ b/contracts/NFTPool/impl/FilterERC721V1.sol @@ -43,6 +43,8 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { for (uint256 i = 0; i < spreadIds.length; i++) { _SPREAD_IDS_REGISTRY_[spreadIds[i]] = true; } + + //event FilterInit(address filterAdmin, address nftCollection, string memory name); } // ================= Trading ================ @@ -62,6 +64,8 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { ); _NFT_IDS_.push(tokenId); _NFT_RESERVE_[tokenId] = 1; + + //event Erc721TokenIn(uint256 index, uint256 tokenId); } _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; (uint256 rawReceive, ) = queryNFTIn(tokenIds.length); @@ -112,6 +116,9 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { _NFT_IDS_[index] = _NFT_IDS_[_NFT_IDS_.length - 1]; _NFT_IDS_.pop(); _NFT_RESERVE_[tokenId] = 0; + + //idx,oldTokenId,newTokenId + //event Erc721Out(uint256 index, uint256 tokenId) } function emergencyWithdraw( diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index edb8e27..46fcc1b 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -64,16 +64,20 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { } uint256 received = IFilter(filter).ERC721In(tokenIds, to); require(received >= minMintAmount, "MINT_AMOUNT_NOT_ENOUGH"); + + //event Erc721In(address filter, address to, uint256 received); } function erc721TargetOut( address filter, - uint256[] memory indexes, + uint256[] memory indexes,//tokenId address to, uint256 maxBurnAmount ) external { uint256 paid = IFilter(filter).ERC721TargetOut(indexes, to); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); + + //event Erc721TargetOut(address filter, address to, uint256 paid); } function erc721RandomOut( @@ -84,6 +88,8 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { ) external { uint256 paid = IFilter(filter).ERC721RandomOut(amount, to); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); + + //event Erc721TargetOut(address filter, address to, uint256 paid); } // ================== ERC1155 In and Out =================== @@ -162,6 +168,8 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { _MAINTAINER_, filters ); + + //event CreateNFTPool(address newFilterAdmin, address filterV1); } // ================== Create Filter =================== @@ -185,6 +193,8 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { priceRules, spreadIds ); + + //event CreateFilterV1(address newFilterV1, uint256 filterTemplateKey); } @@ -230,6 +240,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { function changeController(address newControllerModel) external onlyOwner { _CONTROLLER_ = newControllerModel; + //event ChangeContoller(address newController); } function setFilterTemplate(uint256 idx, address newFilterTemplate) external onlyOwner { diff --git a/test/NFTPool/erc721NftPool.test.ts b/test/NFTPool/erc721NftPool.test.ts new file mode 100644 index 0000000..1183eb0 --- /dev/null +++ b/test/NFTPool/erc721NftPool.test.ts @@ -0,0 +1,204 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +// import * as assert from 'assert'; + +import { decimalStr, MAX_UINT256 } from '../utils/Converter'; +import { logGas } from '../utils/Log'; +import { NFTPoolContext, getNFTPoolContext } from '../utils/NFTPoolContext'; +import { assert } from 'chai'; +import BigNumber from 'bignumber.js'; +const truffleAssert = require('truffle-assertions'); + + +//createNFTPool + +//erc721In + +//erc721TargetOut + +//erc721RandomOut + +//createFilter + +describe("ERC721-NFTPool", () => { + let snapshotId: string; + let ctx: DVMContext; + + before(async () => { + ctx = await getNFTPoolContext(); + await init(ctx); + }); + + beforeEach(async () => { + snapshotId = await ctx.EVM.snapshot(); + }); + + afterEach(async () => { + await ctx.EVM.reset(snapshotId); + }); + + describe("buy shares", () => { + + it("buy shares from init states", async () => { + + await ctx.transferBaseToDVM(lp, decimalStr("10")) + await logGas(ctx.DVM.methods.buyShares(lp), ctx.sendParam(lp), "buy shares"); + + // vault balances + assert.equal( + await ctx.BASE.methods.balanceOf(ctx.DVM.options.address).call(), + decimalStr("10") + ); + assert.equal( + await ctx.QUOTE.methods.balanceOf(ctx.DVM.options.address).call(), + decimalStr("0") + ); + assert.equal( + await ctx.DVM.methods._BASE_RESERVE_().call(), + decimalStr("10") + ) + assert.equal( + await ctx.DVM.methods._QUOTE_RESERVE_().call(), + decimalStr("0") + ) + + // shares number + assert.equal(await ctx.DVM.methods.balanceOf(lp).call(), decimalStr("10")) + }); + + it("buy shares from init states with quote != 0", async () => { + await ctx.transferBaseToDVM(lp, decimalStr("10")) + await ctx.transferQuoteToDVM(lp, decimalStr("100")) + await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)); + assert.equal(await ctx.DVM.methods.balanceOf(lp).call(), decimalStr("10")) + assert.equal(await ctx.DVM.methods.getMidPrice().call(), "102078438912577213500") + }) + + it("buy shares with balanced input", async () => { + await ctx.transferBaseToDVM(lp, decimalStr("10")) + await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) + + await ctx.transferQuoteToDVM(trader, decimalStr("200")) + await ctx.DVM.methods.sellQuote(trader).send(ctx.sendParam(trader)) + + var vaultBaseBalance = new BigNumber(await ctx.BASE.methods.balanceOf(ctx.DVM.options.address).call()) + var vaultQuoteBalance = new BigNumber(await ctx.QUOTE.methods.balanceOf(ctx.DVM.options.address).call()) + var increaseRatio = new BigNumber("0.1") + + await ctx.transferBaseToDVM(trader, vaultBaseBalance.multipliedBy(increaseRatio).toFixed(0)) + await ctx.transferQuoteToDVM(trader, vaultQuoteBalance.multipliedBy(increaseRatio).toFixed(0)) + await ctx.DVM.methods.buyShares(trader).send(ctx.sendParam(trader)) + + assert.equal( + await ctx.BASE.methods.balanceOf(ctx.DVM.options.address).call(), + "8852116395368015179" + ); + assert.equal( + await ctx.QUOTE.methods.balanceOf(ctx.DVM.options.address).call(), + "220000000000000000000" + ); + + assert.equal(await ctx.DVM.methods.balanceOf(trader).call(), "999999999999999990") + }) + + it("buy shares with unbalanced input (less quote)", async () => { + await ctx.transferBaseToDVM(lp, decimalStr("10")) + await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) + + await ctx.transferQuoteToDVM(trader, decimalStr("200")) + await ctx.DVM.methods.sellQuote(trader).send(ctx.sendParam(trader)) + + var vaultBaseBalance = new BigNumber(await ctx.BASE.methods.balanceOf(ctx.DVM.options.address).call()) + var vaultQuoteBalance = new BigNumber(await ctx.QUOTE.methods.balanceOf(ctx.DVM.options.address).call()) + var increaseRatio = new BigNumber("0.1") + + await ctx.transferBaseToDVM(trader, vaultBaseBalance.multipliedBy(increaseRatio).toFixed(0)) + await ctx.transferQuoteToDVM(trader, vaultQuoteBalance.multipliedBy(increaseRatio).div(2).toFixed(0)) + await ctx.DVM.methods.buyShares(trader).send(ctx.sendParam(trader)) + + assert.equal(await ctx.DVM.methods.balanceOf(trader).call(), "500000000000000000") + }) + + it("buy shares with unbalanced input (less base)", async () => { + await ctx.transferBaseToDVM(lp, decimalStr("10")) + await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) + + await ctx.transferQuoteToDVM(trader, decimalStr("200")) + await ctx.DVM.methods.sellQuote(trader).send(ctx.sendParam(trader)) + + var vaultBaseBalance = new BigNumber(await ctx.BASE.methods.balanceOf(ctx.DVM.options.address).call()) + var vaultQuoteBalance = new BigNumber(await ctx.QUOTE.methods.balanceOf(ctx.DVM.options.address).call()) + var increaseRatio = new BigNumber("0.1") + + await ctx.transferBaseToDVM(trader, vaultBaseBalance.multipliedBy(increaseRatio).div(2).toFixed(0)) + await ctx.transferQuoteToDVM(trader, vaultQuoteBalance.multipliedBy(increaseRatio).toFixed(0)) + await ctx.DVM.methods.buyShares(trader).send(ctx.sendParam(trader)) + + assert.equal(await ctx.DVM.methods.balanceOf(trader).call(), "499999999999999990") + }) + }); + + describe("sell shares", () => { + it("not the last one sell shares", async () => { + await ctx.transferBaseToDVM(lp, decimalStr("10")) + await ctx.transferQuoteToDVM(lp, decimalStr("100")) + await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) + + await ctx.transferBaseToDVM(trader, decimalStr("1")) + await ctx.transferQuoteToDVM(trader, decimalStr("10")) + await ctx.DVM.methods.buyShares(trader).send(ctx.sendParam(trader)) + + var vaultShares = new BigNumber(await ctx.DVM.methods.balanceOf(lp).call()) + var bob = ctx.SpareAccounts[5] + await ctx.DVM.methods.sellShares(vaultShares.div(2).toFixed(0), bob, 0, 0, "0x", MAX_UINT256).send(ctx.sendParam(lp)) + assert.equal(await ctx.BASE.methods.balanceOf(bob).call(), decimalStr("5")) + assert.equal(await ctx.QUOTE.methods.balanceOf(bob).call(), decimalStr("50")) + + await ctx.DVM.methods.sellShares(vaultShares.div(2).toFixed(0), bob, 0, 0, "0x", MAX_UINT256).send(ctx.sendParam(lp)) + assert.equal(await ctx.BASE.methods.balanceOf(bob).call(), decimalStr("10")) + assert.equal(await ctx.QUOTE.methods.balanceOf(bob).call(), decimalStr("100")) + }) + + it("the last one sell shares", async () => { + await ctx.transferBaseToDVM(lp, decimalStr("10")) + await ctx.transferQuoteToDVM(lp, decimalStr("100")) + await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) + + var vaultShares = await ctx.DVM.methods.balanceOf(lp).call() + var bob = ctx.SpareAccounts[5] + await ctx.DVM.methods.sellShares(vaultShares, bob, 0, 0, "0x", MAX_UINT256).send(ctx.sendParam(lp)) + assert.equal(await ctx.BASE.methods.balanceOf(bob).call(), decimalStr("10")) + assert.equal(await ctx.QUOTE.methods.balanceOf(bob).call(), decimalStr("100")) + }) + + it("revert cases", async () => { + await ctx.transferBaseToDVM(lp, decimalStr("10")) + await ctx.transferQuoteToDVM(lp, decimalStr("100")) + await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) + + var vaultShares = await ctx.DVM.methods.balanceOf(lp).call() + var bob = ctx.SpareAccounts[5] + await truffleAssert.reverts( + ctx.DVM.methods.sellShares(new BigNumber(vaultShares).multipliedBy(2), bob, 0, 0, "0x", MAX_UINT256).send(ctx.sendParam(lp)), + "DLP_NOT_ENOUGH" + ) + await truffleAssert.reverts( + ctx.DVM.methods.sellShares(vaultShares, bob, decimalStr("100"), 0, "0x", MAX_UINT256).send(ctx.sendParam(lp)), + "WITHDRAW_NOT_ENOUGH" + ) + await truffleAssert.reverts( + ctx.DVM.methods.sellShares(vaultShares, bob, 0, decimalStr("10000"), "0x", MAX_UINT256).send(ctx.sendParam(lp)), + "WITHDRAW_NOT_ENOUGH" + ) + await truffleAssert.reverts( + ctx.DVM.methods.sellShares(vaultShares, bob, 0, decimalStr("10000"), "0x", "0").send(ctx.sendParam(lp)), + "TIME_EXPIRED" + ) + }) + }) +}); diff --git a/test/utils/NFTPoolContext.ts b/test/utils/NFTPoolContext.ts index 5949f03..a0f7301 100644 --- a/test/utils/NFTPoolContext.ts +++ b/test/utils/NFTPoolContext.ts @@ -77,7 +77,7 @@ export class NFTPoolContext { this.Controller.options.address, this.Deployer, this.DODONFTApprove.options.address, - "" //TODO:ERC721 => ERC20 + "" //TODO:ERC721 => ERC20 DODOApprove ] ) @@ -104,7 +104,7 @@ export class NFTPoolContext { } } -export async function getNFTPoolContext(weth: string): Promise { +export async function getNFTPoolContext(): Promise { var context = new NFTPoolContext(); await context.init(); return context; From 6086f2ac04e5e745816cd8d44192e853b47e5cbd Mon Sep 17 00:00:00 2001 From: owen05 Date: Tue, 14 Sep 2021 22:02:07 +0800 Subject: [PATCH 19/42] add event --- contracts/NFTPool/impl/BaseFilterV1.sol | 37 ++++++++++---- contracts/NFTPool/impl/Controller.sol | 6 +++ contracts/NFTPool/impl/FilterAdmin.sol | 10 ++-- contracts/NFTPool/impl/FilterERC1155V1.sol | 40 +++++++++++----- contracts/NFTPool/impl/FilterERC721V1.sol | 41 ++++++++++------ contracts/NFTPool/intf/IFilter.sol | 4 +- contracts/SmartRoute/DODONFTApprove.sol | 2 + .../SmartRoute/proxies/DODONFTPoolProxy.sol | 48 ++++++++++++++----- 8 files changed, 134 insertions(+), 54 deletions(-) diff --git a/contracts/NFTPool/impl/BaseFilterV1.sol b/contracts/NFTPool/impl/BaseFilterV1.sol index 5d00622..7b1fc7d 100644 --- a/contracts/NFTPool/impl/BaseFilterV1.sol +++ b/contracts/NFTPool/impl/BaseFilterV1.sol @@ -15,6 +15,15 @@ import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { using SafeMath for uint256; + //=================== Event =================== + event ChangeNFTInPrice(uint256 newGsStart, uint256 newCr, bool toggleFlag); + event ChangeNFTRandomOutPrice(uint256 newGsStart, uint256 newCr, bool toggleFlag); + event ChangeNFTTargetOutPrice(uint256 newGsStart, uint256 newCr, bool toggleFlag); + event ChangeNFTAmountRange(uint256 maxNFTAmount, uint256 minNFTAmount); + event ChangeTokenIdRange(uint256 nftIdStart, uint256 nftIdEnd); + event ChangeTokenIdMap(uint256 tokenIds, bool isRegistered); + event ChangeFilterName(string newFilterName); + //=================== Storage =================== string public _FILTER_NAME_; @@ -29,6 +38,8 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { mapping(uint256 => uint256) public _NFT_RESERVE_; uint256[] public _NFT_IDS_; + //tokenId => index + 1 of _NFT_IDS_ + mapping(uint256 => uint256) public _TOKENID_IDX_; uint256 public _TOTAL_NFT_AMOUNT_; uint256 public _MAX_NFT_AMOUNT_; uint256 public _MIN_NFT_AMOUNT_; @@ -87,12 +98,8 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { } 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; + require(_TOKENID_IDX_[tokenId] > 0, "TOKEN_ID_NOT_EXSIT"); + return _TOKENID_IDX_[tokenId] - 1; } //==================== Query Price ================== @@ -197,17 +204,19 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { _GS_START_IN_ = newGsStart; _CR_IN_ = newCr; _NFT_IN_TOGGLE_ = true; + + emit ChangeNFTInPrice(newGsStart, newCr, toggleFlag); } - function changeNFTRandomInPrice( + function changeNFTRandomOutPrice( uint256 newGsStart, uint256 newCr, bool toggleFlag ) external onlySuperOwner { - _changeNFTRandomInPrice(newGsStart, newCr, toggleFlag); + _changeNFTRandomOutPrice(newGsStart, newCr, toggleFlag); } - function _changeNFTRandomInPrice( + function _changeNFTRandomOutPrice( uint256 newGsStart, uint256 newCr, bool toggleFlag @@ -216,6 +225,8 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { _GS_START_RANDOM_OUT_ = newGsStart; _CR_RANDOM_OUT_ = newCr; _NFT_RANDOM_OUT_TOGGLE_ = true; + + emit ChangeNFTRandomOutPrice(newGsStart, newCr, toggleFlag); } function changeNFTTargetOutPrice( @@ -235,6 +246,8 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { _GS_START_TARGET_OUT_ = newGsStart; _CR_TARGET_OUT_ = newCr; _NFT_TARGET_OUT_TOGGLE_ = true; + + emit ChangeNFTTargetOutPrice(newGsStart, newCr, toggleFlag); } function changeNFTAmountRange(uint256 maxNFTAmount, uint256 minNFTAmount) @@ -248,6 +261,8 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { require(maxNFTAmount >= minNFTAmount, "AMOUNT_INVALID"); _MAX_NFT_AMOUNT_ = maxNFTAmount; _MIN_NFT_AMOUNT_ = minNFTAmount; + + emit ChangeNFTAmountRange(maxNFTAmount, minNFTAmount); } function changeTokenIdRange(uint256 nftIdStart, uint256 nftIdEnd) external onlySuperOwner { @@ -259,6 +274,8 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { _NFT_ID_START_ = nftIdStart; _NFT_ID_END_ = nftIdEnd; + + emit ChangeTokenIdRange(nftIdStart, nftIdEnd); } function changeTokenIdMap(uint256[] memory tokenIds, bool[] memory isRegistered) @@ -273,6 +290,7 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { for (uint256 i = 0; i < tokenIds.length; i++) { _SPREAD_IDS_REGISTRY_[tokenIds[i]] = isRegistered[i]; + emit ChangeTokenIdMap(tokenIds[i], isRegistered[i]); } } @@ -281,5 +299,6 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { onlySuperOwner { _FILTER_NAME_ = newFilterName; + emit ChangeFilterName(newFilterName); } } diff --git a/contracts/NFTPool/impl/Controller.sol b/contracts/NFTPool/impl/Controller.sol index d4e87e6..6a63b07 100644 --- a/contracts/NFTPool/impl/Controller.sol +++ b/contracts/NFTPool/impl/Controller.sol @@ -29,6 +29,8 @@ contract Controller is InitializableOwnable { //==================== Event ===================== event SetEmergencyWithdraw(address filter, bool isOpen); + event SetFilterAdminFeeRateInfo(address filterAdmin, uint256 nftInFee, uint256 nftOutFee, bool isOpen); + event SetGlobalParam(uint256 nftInFee, uint256 nftOutFee); //==================== Ownable ==================== @@ -44,11 +46,15 @@ contract Controller is InitializableOwnable { isOpen: isOpen }); filterAdminFeeRates[filterAdminAddr] = feeRateInfo; + + emit SetFilterAdminFeeRateInfo(filterAdminAddr, nftInFeeRate, nftOutFeeRate, isOpen); } function setGlobalParam(uint256 nftInFeeRate, uint256 nftOutFeeRate) external onlyOwner { _GLOBAL_NFT_IN_FEE_RATE_ = nftInFeeRate; _GLOBAL_NFT_OUT_FEE_RATE_ = nftOutFeeRate; + + emit SetGlobalParam(nftInFeeRate, nftOutFeeRate); } function setEmergencyWithdraw(address filter, bool isOpen) external onlyOwner { diff --git a/contracts/NFTPool/impl/FilterAdmin.sol b/contracts/NFTPool/impl/FilterAdmin.sol index 8b70970..1716f39 100644 --- a/contracts/NFTPool/impl/FilterAdmin.sol +++ b/contracts/NFTPool/impl/FilterAdmin.sol @@ -26,6 +26,8 @@ contract FilterAdmin is InitializableInternalMintableERC20 { // ============ Event ============ event ChangeFeeRate(uint256 fee); + event AddFilter(address filter); + event FilterAdminInit(address owner, uint256 feeRate); function init( address owner, @@ -47,8 +49,7 @@ contract FilterAdmin is InitializableInternalMintableERC20 { _FILTER_REGISTRY_[filters[i]] = true; } - //event FilterAdminInit(addres owner, uint256 feeRate) - + emit FilterAdminInit(owner, feeRate); } function mintFragTo(address to, uint256 rawAmount) external returns (uint256) { @@ -114,8 +115,9 @@ contract FilterAdmin is InitializableInternalMintableERC20 { function addFilter(address[] memory filters) external onlyOwner { for(uint256 i = 0; i < filters.length; i++) { require(!isRegisteredFilter(filters[i]), "FILTER_ALREADY_EXIST"); - _FILTERS_.push(filter[i]); - _FILTER_REGISTRY_[filter[i]] = true; + _FILTERS_.push(filters[i]); + _FILTER_REGISTRY_[filters[i]] = true; + emit AddFilter(filters[i]); } } diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index 4471a0f..99a4a28 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -17,6 +17,13 @@ import {BaseFilterV1} from "./BaseFilterV1.sol"; contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { using SafeMath for uint256; + //=============== Event ================== + event FilterInit(address filterAdmin, address nftCollection, string name); + event NftIn(uint256 tokenId, uint256 amount); + event TargetOut(uint256 tokenId, uint256 amount); + event RandomOut(uint256 tokenId, uint256 amount); + event EmergencyWithdraw(address nftContract,uint256 tokenId, uint256 amount, address to); + function init( address filterAdmin, address nftCollection, @@ -32,7 +39,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { _NFT_COLLECTION_ = nftCollection; _changeNFTInPrice(priceRules[0], priceRules[1], toggles[0]); - _changeNFTRandomInPrice(priceRules[2], priceRules[3], toggles[1]); + _changeNFTRandomOutPrice(priceRules[2], priceRules[3], toggles[1]); _changeNFTTargetOutPrice(priceRules[4], priceRules[5], toggles[2]); _changeNFTAmountRange(numParams[2], numParams[3]); @@ -42,7 +49,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { _SPREAD_IDS_REGISTRY_[spreadIds[i]] = true; } - //event FilterInit(address filterAdmin, address nftCollection, string memory name); + emit FilterInit(filterAdmin, nftCollection, filterName); } // ================= Trading ================ @@ -56,21 +63,24 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { for (uint256 i = 0; i < tokenIds.length; i++) { uint256 tokenId = tokenIds[i]; require(isNFTIDValid(tokenId), "NFT_ID_NOT_SUPPORT"); - totalAmount += _maintainERC1155In(tokenId); + uint256 inAmount = _maintainERC1155In(tokenId); + totalAmount += inAmount; + emit NftIn(tokenId, inAmount); } (uint256 rawReceive, ) = queryNFTIn(totalAmount); received = IFilterAdmin(_OWNER_).mintFragTo(to, rawReceive); } function ERC1155TargetOut( - uint256[] memory indexes, + uint256[] memory tokenIds, uint256[] memory amounts, address to ) external preventReentrant returns (uint256 paid) { uint256 totalAmount = 0; - for (uint256 i = 0; i < indexes.length; i++) { + for (uint256 i = 0; i < tokenIds.length; i++) { totalAmount += amounts[i]; - _transferOutERC1155(to, indexes[i], amounts[i]); + _transferOutERC1155(to, tokenIds[i], amounts[i]); + emit TargetOut(tokenIds[i], amounts[i]); } (uint256 rawPay, ) = queryNFTTargetOut(totalAmount); paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); @@ -89,7 +99,8 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { for (uint256 j = 0; j < _NFT_IDS_.length; j++) { sum += _NFT_RESERVE_[_NFT_IDS_[j]]; if (sum >= randomNum) { - _transferOutERC1155(to, j, 1); + _transferOutERC1155(to, _NFT_IDS_[j], 1); + emit RandomOut( _NFT_IDS_[j], 1); break; } } @@ -120,13 +131,12 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { function _transferOutERC1155( address to, - uint256 index, + uint256 tokenId, uint256 amount ) internal { - require(index < _NFT_IDS_.length, "INDEX_NOT_EXIST"); - uint256 tokenId = _NFT_IDS_[index]; + require(_TOKENID_IDX_[tokenId] > 0, "TOKENID_NOT_EXIST"); IERC1155(_NFT_COLLECTION_).safeTransferFrom(address(this), to, tokenId, amount, ""); - _maintainERC1155Out(index, tokenId); + _maintainERC1155Out(tokenId); } function emergencyWithdraw( @@ -149,19 +159,22 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { uint256 tokenId = tokenIds[i]; IERC1155(nftContract[i]).safeTransferFrom(address(this), to, tokenId, amounts[i], ""); if (_NFT_RESERVE_[tokenId] > 0 && nftContract[i] == _NFT_COLLECTION_) { - _maintainERC1155Out(getNFTIndexById(tokenId), tokenId); + _maintainERC1155Out(tokenId); } + emit EmergencyWithdraw(nftContract[i],tokenIds[i], amounts[i], to); } } - function _maintainERC1155Out(uint256 index, uint256 tokenId) internal { + function _maintainERC1155Out(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) { + uint256 index = _TOKENID_IDX_[tokenId] - 1; _NFT_IDS_[index] = _NFT_IDS_[_NFT_IDS_.length - 1]; _NFT_IDS_.pop(); + _TOKENID_IDX_[tokenId] = 0; } } @@ -171,6 +184,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { if (_NFT_RESERVE_[tokenId] == 0 && currentAmount > 0) { _NFT_IDS_.push(tokenId); } + _TOKENID_IDX_[tokenId] = _NFT_IDS_.length; _NFT_RESERVE_[tokenId] = currentAmount; _TOTAL_NFT_AMOUNT_ += inAmount; } diff --git a/contracts/NFTPool/impl/FilterERC721V1.sol b/contracts/NFTPool/impl/FilterERC721V1.sol index 243c11d..5de302a 100644 --- a/contracts/NFTPool/impl/FilterERC721V1.sol +++ b/contracts/NFTPool/impl/FilterERC721V1.sol @@ -20,6 +20,13 @@ import {BaseFilterV1} from "./BaseFilterV1.sol"; contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { using SafeMath for uint256; + //============== Event ================= + event FilterInit(address filterAdmin, address nftCollection, string name); + event NftIn(uint256 tokenId); + event TargetOut(uint256 tokenId); + event RandomOut(uint256 tokenId); + event EmergencyWithdraw(address nftContract,uint256 tokenId, address to); + function init( address filterAdmin, address nftCollection, @@ -34,7 +41,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { _NFT_COLLECTION_ = nftCollection; _changeNFTInPrice(priceRules[0], priceRules[1], toggles[0]); - _changeNFTRandomInPrice(priceRules[2], priceRules[3], toggles[1]); + _changeNFTRandomOutPrice(priceRules[2], priceRules[3], toggles[1]); _changeNFTTargetOutPrice(priceRules[4], priceRules[5], toggles[2]); _changeNFTAmountRange(numParams[2], numParams[3]); @@ -44,7 +51,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { _SPREAD_IDS_REGISTRY_[spreadIds[i]] = true; } - //event FilterInit(address filterAdmin, address nftCollection, string memory name); + emit FilterInit(filterAdmin, nftCollection, filterName); } // ================= Trading ================ @@ -63,24 +70,27 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { "NFT_NOT_SEND" ); _NFT_IDS_.push(tokenId); + _TOKENID_IDX_[tokenId] = _NFT_IDS_.length; _NFT_RESERVE_[tokenId] = 1; - //event Erc721TokenIn(uint256 index, uint256 tokenId); + emit NftIn(tokenId); } _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; (uint256 rawReceive, ) = queryNFTIn(tokenIds.length); received = IFilterAdmin(_OWNER_).mintFragTo(to, rawReceive); } - function ERC721TargetOut(uint256[] memory indexes, address to) + function ERC721TargetOut(uint256[] memory tokenIds, address to) external preventReentrant returns (uint256 paid) { - (uint256 rawPay, ) = queryNFTTargetOut(indexes.length); + (uint256 rawPay, ) = queryNFTTargetOut(tokenIds.length); paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); - for (uint256 i = 0; i < indexes.length; i++) { - _transferOutERC721(to, indexes[i]); + for (uint256 i = 0; i < tokenIds.length; i++) { + _transferOutERC721(to, tokenIds[i]); + + emit TargetOut(tokenIds[i]); } _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; } @@ -93,7 +103,10 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { (uint256 rawPay, ) = queryNFTRandomOut(amount); paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); for (uint256 i = 0; i < amount; i++) { - _transferOutERC721(to, _getRandomNum() % _TOTAL_NFT_AMOUNT_); + uint256 index = _getRandomNum() % _TOTAL_NFT_AMOUNT_; + _transferOutERC721(to, _NFT_IDS_[index]); + + emit RandomOut(_NFT_IDS_[index]); } _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; } @@ -109,16 +122,15 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { return IERC721Receiver.onERC721Received.selector; } - function _transferOutERC721(address to, uint256 index) internal { - require(index < _NFT_IDS_.length, "INDEX_NOT_EXIST"); - uint256 tokenId = _NFT_IDS_[index]; + function _transferOutERC721(address to, uint256 tokenId) internal { + require(_TOKENID_IDX_[tokenId] > 0, "TOKENID_NOT_EXIST"); + uint256 index = _TOKENID_IDX_[tokenId] - 1; + require(index < _NFT_IDS_.length, "INDEX_INVALID"); IERC721(_NFT_COLLECTION_).safeTransferFrom(address(this), to, tokenId); _NFT_IDS_[index] = _NFT_IDS_[_NFT_IDS_.length - 1]; _NFT_IDS_.pop(); _NFT_RESERVE_[tokenId] = 0; - - //idx,oldTokenId,newTokenId - //event Erc721Out(uint256 index, uint256 tokenId) + _TOKENID_IDX_[tokenId] = 0; } function emergencyWithdraw( @@ -142,6 +154,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { _NFT_RESERVE_[tokenId] = 0; } IERC721(nftContract[i]).safeTransferFrom(address(this), to, tokenIds[i]); + emit EmergencyWithdraw(nftContract[i],tokenIds[i],to); } _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; } diff --git a/contracts/NFTPool/intf/IFilter.sol b/contracts/NFTPool/intf/IFilter.sol index 048a9b4..753851d 100644 --- a/contracts/NFTPool/intf/IFilter.sol +++ b/contracts/NFTPool/intf/IFilter.sol @@ -39,14 +39,14 @@ interface IFilter { function ERC721In(uint256[] memory tokenIds, address to) external returns (uint256 received); - function ERC721TargetOut(uint256[] memory indexes, address to) external returns (uint256 paid); + function ERC721TargetOut(uint256[] memory tokenIds, address to) external returns (uint256 paid); 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 tokenIds, uint256[] memory amounts, address to ) external returns (uint256 paid); diff --git a/contracts/SmartRoute/DODONFTApprove.sol b/contracts/SmartRoute/DODONFTApprove.sol index 999d87d..5267231 100644 --- a/contracts/SmartRoute/DODONFTApprove.sol +++ b/contracts/SmartRoute/DODONFTApprove.sol @@ -27,6 +27,7 @@ contract DODONFTApprove is InitializableOwnable { // ============ Events ============ event AddDODOProxy(address dodoProxy); + event RemoveDODOProxy(address oldProxy); // ============ Modifiers ============ modifier notLocked() { @@ -62,6 +63,7 @@ contract DODONFTApprove is InitializableOwnable { function removeDODOProxy (address oldDodoProxy) external onlyOwner { _IS_ALLOWED_PROXY_[oldDodoProxy] = false; + emit RemoveDODOProxy(oldDodoProxy); } diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index 46fcc1b..ba17095 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -33,6 +33,20 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { // ============ Event ============== event SetFilterTemplate(uint256 idx, address filterTemplate); + event Erc721In(address filter, address to, uint256 received); + event Erc1155In(address filter, address to, uint256 received); + event Erc721TargetOut(address filter, address to, uint256 paid); + event Erc1155TargetOut(address filter, address to, uint256 paid); + event Erc721RandomOut(address filter, address to, uint256 paid); + event Erc1155RandomOut(address filter, address to, uint256 paid); + + event CreateNFTPool(address newFilterAdmin, address filter); + event CreateFilterV1(address newFilterV1, uint256 filterTemplateKey); + event Erc721toErc20(address nftContract, uint256 tokenId, address toToken, uint256 returnAmount); + + event ChangeMaintainer(address newMaintainer); + event ChangeContoller(address newController); + event ChangeFilterAdminTemplate(address newFilterAdminTemplate); constructor( address cloneFactory, @@ -65,19 +79,19 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { uint256 received = IFilter(filter).ERC721In(tokenIds, to); require(received >= minMintAmount, "MINT_AMOUNT_NOT_ENOUGH"); - //event Erc721In(address filter, address to, uint256 received); + emit Erc721In(filter, to, received); } function erc721TargetOut( address filter, - uint256[] memory indexes,//tokenId + uint256[] memory tokenIds, address to, uint256 maxBurnAmount ) external { - uint256 paid = IFilter(filter).ERC721TargetOut(indexes, to); + uint256 paid = IFilter(filter).ERC721TargetOut(tokenIds, to); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); - //event Erc721TargetOut(address filter, address to, uint256 paid); + emit Erc721TargetOut(filter, to, paid); } function erc721RandomOut( @@ -89,7 +103,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { uint256 paid = IFilter(filter).ERC721RandomOut(amount, to); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); - //event Erc721TargetOut(address filter, address to, uint256 paid); + emit Erc721RandomOut(filter, to, paid); } // ================== ERC1155 In and Out =================== @@ -107,17 +121,21 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { IDODONFTApprove(_DODO_NFT_APPROVE_).claimERC1155Batch(nftCollection, msg.sender, filter, tokenIds, amounts); uint256 received = IFilter(filter).ERC1155In(tokenIds, to); require(received >= minMintAmount, "MINT_AMOUNT_NOT_ENOUGH"); + + emit Erc1155In(filter, to, received); } function erc1155TargetOut( address filter, - uint256[] memory indexes, + uint256[] memory tokenIds, uint256[] memory amounts, address to, uint256 maxBurnAmount ) external { - uint256 paid = IFilter(filter).ERC1155TargetOut(indexes, amounts, to); + uint256 paid = IFilter(filter).ERC1155TargetOut(tokenIds, amounts, to); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); + + emit Erc1155TargetOut(filter, to, paid); } function erc1155RandomOut( @@ -128,6 +146,8 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { ) external { uint256 paid = IFilter(filter).ERC1155RandomOut(amount, to); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); + + emit Erc1155RandomOut(filter, to, paid); } @@ -169,7 +189,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { filters ); - //event CreateNFTPool(address newFilterAdmin, address filterV1); + emit CreateNFTPool(newFilterAdmin, filterV1); } // ================== Create Filter =================== @@ -194,7 +214,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { spreadIds ); - //event CreateFilterV1(address newFilterV1, uint256 filterTemplateKey); + emit CreateFilterV1(newFilterV1, key); } @@ -226,21 +246,25 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { uint256 returnAmount = _generalBalanceOf(toToken, address(this)); _generalTransfer(toToken, msg.sender, returnAmount); + + emit Erc721toErc20(nftContract, tokenId, toToken, returnAmount); } //====================== Ownable ======================== function changeMaintainer(address newMaintainer) external onlyOwner { _MAINTAINER_ = newMaintainer; + emit ChangeMaintainer(newMaintainer); } function changeFilterAdminTemplate(address newFilterAdminTemplate) external onlyOwner { _FILTER_ADMIN_TEMPLATE_ = newFilterAdminTemplate; + emit ChangeFilterAdminTemplate(newFilterAdminTemplate); } - function changeController(address newControllerModel) external onlyOwner { - _CONTROLLER_ = newControllerModel; - //event ChangeContoller(address newController); + function changeController(address newController) external onlyOwner { + _CONTROLLER_ = newController; + emit ChangeContoller(newController); } function setFilterTemplate(uint256 idx, address newFilterTemplate) external onlyOwner { From c14e9df9445093c8ad531c30afc53fa9cb6d9599 Mon Sep 17 00:00:00 2001 From: owen05 Date: Tue, 14 Sep 2021 22:28:16 +0800 Subject: [PATCH 20/42] fix --- contracts/NFTPool/impl/FilterERC1155V1.sol | 1 + contracts/NFTPool/impl/FilterERC721V1.sol | 3 +++ 2 files changed, 4 insertions(+) diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index 99a4a28..5cbf06f 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -175,6 +175,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { _NFT_IDS_[index] = _NFT_IDS_[_NFT_IDS_.length - 1]; _NFT_IDS_.pop(); _TOKENID_IDX_[tokenId] = 0; + _TOKENID_IDX_[_NFT_IDS_[index]] = index + 1; } } diff --git a/contracts/NFTPool/impl/FilterERC721V1.sol b/contracts/NFTPool/impl/FilterERC721V1.sol index 5de302a..715f4a3 100644 --- a/contracts/NFTPool/impl/FilterERC721V1.sol +++ b/contracts/NFTPool/impl/FilterERC721V1.sol @@ -131,6 +131,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { _NFT_IDS_.pop(); _NFT_RESERVE_[tokenId] = 0; _TOKENID_IDX_[tokenId] = 0; + _TOKENID_IDX_[_NFT_IDS_[index]] = index + 1; } function emergencyWithdraw( @@ -152,6 +153,8 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { _NFT_IDS_[index] = _NFT_IDS_[_NFT_IDS_.length - 1]; _NFT_IDS_.pop(); _NFT_RESERVE_[tokenId] = 0; + _TOKENID_IDX_[tokenId] = 0; + _TOKENID_IDX_[_NFT_IDS_[index]] = index + 1; } IERC721(nftContract[i]).safeTransferFrom(address(this), to, tokenIds[i]); emit EmergencyWithdraw(nftContract[i],tokenIds[i],to); From 21bcf680cc9dfeaf4dfd3ffe4c1bf1a1046de91d Mon Sep 17 00:00:00 2001 From: owen05 Date: Wed, 15 Sep 2021 09:36:13 +0800 Subject: [PATCH 21/42] fix --- contracts/NFTPool/impl/FilterERC1155V1.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index 5cbf06f..8274166 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -184,8 +184,8 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { inAmount = currentAmount.sub(_NFT_RESERVE_[tokenId]); if (_NFT_RESERVE_[tokenId] == 0 && currentAmount > 0) { _NFT_IDS_.push(tokenId); + _TOKENID_IDX_[tokenId] = _NFT_IDS_.length; } - _TOKENID_IDX_[tokenId] = _NFT_IDS_.length; _NFT_RESERVE_[tokenId] = currentAmount; _TOTAL_NFT_AMOUNT_ += inAmount; } From c18ab985c0433692d351e85836436fbba789d51e Mon Sep 17 00:00:00 2001 From: owen05 Date: Thu, 16 Sep 2021 16:14:16 +0800 Subject: [PATCH 22/42] createLiteNftPool func --- .../SmartRoute/proxies/DODONFTPoolProxy.sol | 27 ++++++++++++++++++- migrations/7_deploy_nftPool.js | 0 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 migrations/7_deploy_nftPool.js diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index ba17095..bcee646 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -40,6 +40,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { event Erc721RandomOut(address filter, address to, uint256 paid); event Erc1155RandomOut(address filter, address to, uint256 paid); + event CreateLiteNFTPool(address newFilterAdmin); event CreateNFTPool(address newFilterAdmin, address filter); event CreateFilterV1(address newFilterV1, uint256 filterTemplateKey); event Erc721toErc20(address nftContract, uint256 tokenId, address toToken, uint256 returnAmount); @@ -152,7 +153,31 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { // ================== Create NFTPool =================== + function createLiteNFTPool( + address filterAdminOwner, + string[] memory infos, // 0 => fragName, 1 => fragSymbol + uint256[] memory numParams //0 - initSupply, 1 - fee + ) external returns(address newFilterAdmin) { + address[] memory filters = new address[](0); + + IFilterAdmin(newFilterAdmin).init( + filterAdminOwner, + numParams[0], + infos[0], + infos[1], + numParams[1], + _CONTROLLER_, + _MAINTAINER_, + filters + ); + + emit CreateLiteNFTPool(newFilterAdmin); + } + + + function createNewNFTPoolV1( + address filterAdminOwner, address nftCollection, uint256 filterKey, //1 => FilterERC721V1, 2 => FilterERC1155V1 string[] memory infos, // 0 => filterName, 1 => fragName, 2 => fragSymbol @@ -179,7 +204,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { filters[0] = filterV1; IFilterAdmin(newFilterAdmin).init( - msg.sender, + filterAdminOwner, numParams[0], infos[1], infos[2], diff --git a/migrations/7_deploy_nftPool.js b/migrations/7_deploy_nftPool.js new file mode 100644 index 0000000..e69de29 From a53e6eedf75a6ac8b5375c6afa5f56ae6cd581f8 Mon Sep 17 00:00:00 2001 From: owen05 Date: Thu, 16 Sep 2021 19:44:30 +0800 Subject: [PATCH 23/42] add nftPool deployScript --- config/rinkeby-config.js | 25 +++-- .../external/ERC20/InitializableFragERC20.sol | 2 +- migrations/7_deploy_nftPool.js | 104 ++++++++++++++++++ truffle-config.js | 3 +- 4 files changed, 124 insertions(+), 10 deletions(-) diff --git a/config/rinkeby-config.js b/config/rinkeby-config.js index 7eb9287..be6134b 100644 --- a/config/rinkeby-config.js +++ b/config/rinkeby-config.js @@ -73,20 +73,20 @@ module.exports = { //DODOMineV2Factory: "0x3932E00a51d0D3b85C8Eb7C3ED0FcCB0dF98B3FF" //================== NFT ==================== - BuyoutModel: "0x98F5aF1E7Fb03A085D2a28713995e4A923860288", - Fragment: "0xDF7eccee9f5C92D1Baf036DB9410456f9382E045", - NFTCollateralVault: "0x23d72eA97a9E43411Eeb908d128DF337aD334582", - DODONFTRouteHelper: "0xb0Ca341b6fbdC607A507D821780e29f9601a58B3", + BuyoutModel: "0x1A18Ccf68040f660Ac7f83B4911d17398eDbC79f", + Fragment: "0xd0578C69e3455a7836456FfEEcd31934bda088d1", + NFTCollateralVault: "0x9d9A560661eD783F99188EFC2cFd5F37bCC30609", + DODONFTRouteHelper: "0xf3194Dcbba7dDC1F8Cf1bC97AD502BC25581Ba0d", InitializableERC721: "0xC0ccfC832BD45Cd3A2d62e47FE92Fc50DD2210ac", InitializableERC1155: "0x9DC9086B65cCBec43F92bFa37Db81150Ed1DDDed", NFTTokenFactory: "0xd2BffcCBC1F2a7356f8DaBB55B33E47D62de1bB1", - DodoNftErc721: "0x3Bc20358B31aD498d5a245B36bC993DDBE9A4405", - DodoNftErc1155: "0xc498F36eF82Aa5dBE0ecF6DD56DD55398E80E13D", + DodoNftErc721: "", + DodoNftErc1155: "", - DODONFTRegistry: "0x69efeCA5070Cb22c1094cffEbacafC09c058c139", - DODONFTProxy: "0x0CF019E13C6527BD34eC6c8323F11aB5DF6f0922", + DODONFTRegistry: "0x3DEf969E84FEAa9Dd2a29E1008D0426c8d89D5C5", + DODONFTProxy: "0xBF243C5626A0766031d57269c01F6eFd57B603fc", //================= DropsV1 ================= MysteryBoxV1: "", @@ -100,5 +100,14 @@ module.exports = { //DODODropsV2: "0x4A2b9f63AE41cF3003A494F2d8Fcd9Ed850b9A6f" // DropsERC721: "0x3df8d553275781C777f432A74EEE9099226B9d13", // DropsERC1155: "0x3a8EcF30428bd4e33Cd7011533DFd596F7705c8F", + + //=================== NFTPool ================== + DODONFTApprove: "0xe4915ABF03d56743c7e66B1CFda4C58bAAc0963C", + FilterAdmin: "0x3893c38aFa42E492b134888c79CAE52744Eb3ab0", + FilterERC721V1: "0x3F87f3d4EeD2C466CCA6E722a3153875C3f2822b", + FilterERC1155V1: "0xf71F758e2c4417758588CA6ff803036cA1EeB396", + DODONFTPoolProxy: "0x4587F2039bfb47D76000b23743993962518Ee0c2", + NFTPoolController: "0xf5d24499dD76C3791ee6D19aa206f55b72270415" + } } \ No newline at end of file diff --git a/contracts/external/ERC20/InitializableFragERC20.sol b/contracts/external/ERC20/InitializableFragERC20.sol index 63f3495..c4c12af 100644 --- a/contracts/external/ERC20/InitializableFragERC20.sol +++ b/contracts/external/ERC20/InitializableFragERC20.sol @@ -39,7 +39,7 @@ contract InitializableFragERC20 { emit Transfer(address(0), _creator, _totalSupply); } - function decimals() public view returns (uint8) { + function decimals() public pure returns (uint8) { return 18; } diff --git a/migrations/7_deploy_nftPool.js b/migrations/7_deploy_nftPool.js index e69de29..6559caa 100644 --- a/migrations/7_deploy_nftPool.js +++ b/migrations/7_deploy_nftPool.js @@ -0,0 +1,104 @@ +const fs = require("fs"); +const { deploySwitch } = require('../truffle-config.js') +const file = fs.createWriteStream("../deploy-nft.txt", { 'flags': 'a' }); +let logger = new console.Console(file, file); +const { GetConfig } = require("../configAdapter.js") + +const DODONFTApprove = artifacts.require("DODONFTApprove"); +const FilterAdmin = artifacts.require("FilterAdmin"); +const FilterERC721V1 = artifacts.require("FilterERC721V1"); +const FilterERC1155V1 = artifacts.require("FilterERC1155V1"); +const DODONFTPoolProxy = artifacts.require("DODONFTPoolProxy") +const Controller = artifacts.require("Controller"); + +module.exports = async (deployer, network, accounts) => { + let CONFIG = GetConfig(network, accounts) + if (CONFIG == null) return; + //Need Deploy first + let DODOApproveAddress = CONFIG.DODOApprove; + let CloneFactoryAddress = CONFIG.CloneFactory; + + if (DODOApproveAddress == "" || CloneFactoryAddress == "") return; + + let DODONFTApproveAddress = CONFIG.DODONFTApprove; + let FilterAdminAddress = CONFIG.FilterAdmin; + let FilterERC721V1Address = CONFIG.FilterERC721V1; + let FilterERC1155V1Address = CONFIG.FilterERC1155V1; + + let DODONFTPoolProxyAddress = CONFIG.DODONFTPoolProxy; + let ControllerAddress = CONFIG.NFTPoolController; + + let multiSigAddress = CONFIG.multiSigAddress; + + if (deploySwitch.NFT_POOL) { + logger.log("===================================================="); + logger.log("network type: " + network); + logger.log("Deploy time: " + new Date().toLocaleString()); + logger.log("Deploy type: NFT_POOL"); + + if (FilterAdminAddress == "") { + await deployer.deploy(FilterAdmin); + FilterAdminAddress = FilterAdmin.address; + logger.log("FilterAdminAddress: ", FilterAdminAddress); + } + + if (FilterERC721V1Address == "") { + await deployer.deploy(FilterERC721V1); + FilterERC721V1Address = FilterERC721V1.address; + logger.log("FilterERC721V1Address: ", FilterERC721V1Address); + } + + if (FilterERC1155V1Address == "") { + await deployer.deploy(FilterERC1155V1); + FilterERC1155V1Address = FilterERC1155V1.address; + logger.log("FilterERC1155V1Address: ", FilterERC1155V1Address); + } + + if (ControllerAddress == "") { + await deployer.deploy(Controller); + ControllerAddress = Controller.address; + logger.log("ControllerAddress: ", ControllerAddress); + const ControllerInstance = await Controller.at(ControllerAddress); + var tx = await ControllerInstance.initOwner(multiSigAddress); + logger.log("Init Controller Tx:", tx.tx); + } + + if (DODONFTApproveAddress == "") { + await deployer.deploy(DODONFTApprove); + DODONFTApproveAddress = DODONFTApprove.address; + logger.log("DODONFTApproveAddress: ", DODONFTApproveAddress); + } + + if (DODONFTPoolProxyAddress == "") { + await deployer.deploy( + DODONFTPoolProxy, + CloneFactoryAddress, + FilterAdminAddress, + ControllerAddress, + multiSigAddress, + DODONFTApproveAddress, + DODOApproveAddress + ); + DODONFTPoolProxyAddress = DODONFTPoolProxy.address; + logger.log("DODONFTPoolProxyAddress: ", DODONFTPoolProxyAddress); + + const DODONFTPoolProxyInstance = await DODONFTPoolProxy.at(DODONFTPoolProxyAddress); + var tx = await DODONFTPoolProxyInstance.initOwner(multiSigAddress); + logger.log("Init DODONFTPoolProxy Tx:", tx.tx); + } + + + if (network == 'kovan' || network == 'rinkeby') { + var tx; + const DODONFTPoolProxyInstance = await DODONFTPoolProxy.at(DODONFTPoolProxyAddress); + tx = await DODONFTPoolProxyInstance.setFilterTemplate(1, FilterERC721V1Address); + logger.log("DODONFTPoolProxy SetFilterTemplate 1 tx: ", tx.tx); + tx = await DODONFTPoolProxyInstance.setFilterTemplate(2, FilterERC1155V1Address); + logger.log("DODONFTPoolProxy SetFilterTemplate 2 tx: ", tx.tx); + + const DODONFTApproveInstance = await DODONFTApprove.at(DODONFTApproveAddress); + var tx = await DODONFTApproveInstance.init(multiSigAddress, [DODONFTPoolProxyAddress]); + logger.log("DODONFTApprove init tx: ", tx.tx); + } + } +}; diff --git a/truffle-config.js b/truffle-config.js index 169c003..2e2dbc7 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -63,7 +63,8 @@ module.exports = { COLLECTIONS: false, MYSTERYBOX_V1: false, Drops_V2: false, - MineV3: false + MineV3: false, + NFT_POOL: false }, networks: { From 16128ce16f1d486f9e87509c2a952443ce9624be Mon Sep 17 00:00:00 2001 From: owen05 Date: Fri, 17 Sep 2021 09:31:20 +0800 Subject: [PATCH 24/42] update rinkeby-config --- config/rinkeby-config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/rinkeby-config.js b/config/rinkeby-config.js index be6134b..5357b10 100644 --- a/config/rinkeby-config.js +++ b/config/rinkeby-config.js @@ -82,8 +82,8 @@ module.exports = { InitializableERC1155: "0x9DC9086B65cCBec43F92bFa37Db81150Ed1DDDed", NFTTokenFactory: "0xd2BffcCBC1F2a7356f8DaBB55B33E47D62de1bB1", - DodoNftErc721: "", - DodoNftErc1155: "", + DodoNftErc721: "0x78B7AFf2E5fA95B1E7E16679645FB65a850ed6AB", + DodoNftErc1155: "0x4C455532af01bc34a0Ec60fDd63c68FE41068c63", DODONFTRegistry: "0x3DEf969E84FEAa9Dd2a29E1008D0426c8d89D5C5", DODONFTProxy: "0xBF243C5626A0766031d57269c01F6eFd57B603fc", From cfece17312e44423831d91d4e18539ad55fdef75 Mon Sep 17 00:00:00 2001 From: owen05 Date: Fri, 17 Sep 2021 17:06:41 +0800 Subject: [PATCH 25/42] redeploy --- config/rinkeby-config.js | 4 ++-- contracts/NFTPool/impl/FilterAdmin.sol | 3 --- contracts/SmartRoute/proxies/DODONFTPoolProxy.sol | 2 ++ 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/config/rinkeby-config.js b/config/rinkeby-config.js index 5357b10..23adc72 100644 --- a/config/rinkeby-config.js +++ b/config/rinkeby-config.js @@ -102,11 +102,11 @@ module.exports = { // DropsERC1155: "0x3a8EcF30428bd4e33Cd7011533DFd596F7705c8F", //=================== NFTPool ================== - DODONFTApprove: "0xe4915ABF03d56743c7e66B1CFda4C58bAAc0963C", + DODONFTApprove: "0xBDAcEcF886a4F0C509260d9678D5673C3E8fa4b7", FilterAdmin: "0x3893c38aFa42E492b134888c79CAE52744Eb3ab0", FilterERC721V1: "0x3F87f3d4EeD2C466CCA6E722a3153875C3f2822b", FilterERC1155V1: "0xf71F758e2c4417758588CA6ff803036cA1EeB396", - DODONFTPoolProxy: "0x4587F2039bfb47D76000b23743993962518Ee0c2", + DODONFTPoolProxy: "0x41Eb1FFC3d6474Ee4f4Aaf335a09cD411ADDDf1f", NFTPoolController: "0xf5d24499dD76C3791ee6D19aa206f55b72270415" } diff --git a/contracts/NFTPool/impl/FilterAdmin.sol b/contracts/NFTPool/impl/FilterAdmin.sol index 1716f39..927b48e 100644 --- a/contracts/NFTPool/impl/FilterAdmin.sol +++ b/contracts/NFTPool/impl/FilterAdmin.sol @@ -27,7 +27,6 @@ contract FilterAdmin is InitializableInternalMintableERC20 { // ============ Event ============ event ChangeFeeRate(uint256 fee); event AddFilter(address filter); - event FilterAdminInit(address owner, uint256 feeRate); function init( address owner, @@ -48,8 +47,6 @@ contract FilterAdmin is InitializableInternalMintableERC20 { for (uint256 i = 0; i < filters.length; i++) { _FILTER_REGISTRY_[filters[i]] = true; } - - emit FilterAdminInit(owner, feeRate); } function mintFragTo(address to, uint256 rawAmount) external returns (uint256) { diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index bcee646..6de2a63 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -158,6 +158,8 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { string[] memory infos, // 0 => fragName, 1 => fragSymbol uint256[] memory numParams //0 - initSupply, 1 - fee ) external returns(address newFilterAdmin) { + newFilterAdmin = ICloneFactory(_CLONE_FACTORY_).clone(_FILTER_ADMIN_TEMPLATE_); + address[] memory filters = new address[](0); IFilterAdmin(newFilterAdmin).init( From 2755de57404690de2832b51aa9f8a31bc53297f1 Mon Sep 17 00:00:00 2001 From: owen05 Date: Sat, 18 Sep 2021 11:52:39 +0800 Subject: [PATCH 26/42] update event && redeploy --- config/rinkeby-config.js | 8 ++++---- contracts/NFTPool/impl/FilterERC1155V1.sol | 1 + contracts/NFTPool/impl/FilterERC721V1.sol | 1 + contracts/SmartRoute/proxies/DODONFTPoolProxy.sol | 12 ++++++------ 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/config/rinkeby-config.js b/config/rinkeby-config.js index 23adc72..8bb4bdf 100644 --- a/config/rinkeby-config.js +++ b/config/rinkeby-config.js @@ -102,11 +102,11 @@ module.exports = { // DropsERC1155: "0x3a8EcF30428bd4e33Cd7011533DFd596F7705c8F", //=================== NFTPool ================== - DODONFTApprove: "0xBDAcEcF886a4F0C509260d9678D5673C3E8fa4b7", + DODONFTApprove: "0xb971B0df71fB1778351F25a0e3bfe0C3eF06E1d1", FilterAdmin: "0x3893c38aFa42E492b134888c79CAE52744Eb3ab0", - FilterERC721V1: "0x3F87f3d4EeD2C466CCA6E722a3153875C3f2822b", - FilterERC1155V1: "0xf71F758e2c4417758588CA6ff803036cA1EeB396", - DODONFTPoolProxy: "0x41Eb1FFC3d6474Ee4f4Aaf335a09cD411ADDDf1f", + FilterERC721V1: "0x5a317a617FBF89d55bBfe40A76E526FCD74E4545", + FilterERC1155V1: "0x41c5eDe987bd61c75925Ab2657256D75270a55D4", + DODONFTPoolProxy: "0x38c109aF4f3454172BA4eecf5676aA213b589e75", NFTPoolController: "0xf5d24499dD76C3791ee6D19aa206f55b72270415" } diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index 8274166..113ec85 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -47,6 +47,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { _changeTokenIdRange(numParams[0], numParams[1]); for (uint256 i = 0; i < spreadIds.length; i++) { _SPREAD_IDS_REGISTRY_[spreadIds[i]] = true; + emit ChangeTokenIdMap(spreadIds[i], true); } emit FilterInit(filterAdmin, nftCollection, filterName); diff --git a/contracts/NFTPool/impl/FilterERC721V1.sol b/contracts/NFTPool/impl/FilterERC721V1.sol index 715f4a3..5e9cbe5 100644 --- a/contracts/NFTPool/impl/FilterERC721V1.sol +++ b/contracts/NFTPool/impl/FilterERC721V1.sol @@ -49,6 +49,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { _changeTokenIdRange(numParams[0], numParams[1]); for (uint256 i = 0; i < spreadIds.length; i++) { _SPREAD_IDS_REGISTRY_[spreadIds[i]] = true; + emit ChangeTokenIdMap(spreadIds[i], true); } emit FilterInit(filterAdmin, nftCollection, filterName); diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index 6de2a63..6c6a793 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -40,9 +40,9 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { event Erc721RandomOut(address filter, address to, uint256 paid); event Erc1155RandomOut(address filter, address to, uint256 paid); - event CreateLiteNFTPool(address newFilterAdmin); - event CreateNFTPool(address newFilterAdmin, address filter); - event CreateFilterV1(address newFilterV1, uint256 filterTemplateKey); + event CreateLiteNFTPool(address newFilterAdmin, address filterAdminOwner); + event CreateNFTPool(address newFilterAdmin, address filterAdminOwner, address filter); + event CreateFilterV1(address newFilterAdmin, address newFilterV1, address nftCollection, uint256 filterTemplateKey); event Erc721toErc20(address nftContract, uint256 tokenId, address toToken, uint256 returnAmount); event ChangeMaintainer(address newMaintainer); @@ -173,7 +173,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { filters ); - emit CreateLiteNFTPool(newFilterAdmin); + emit CreateLiteNFTPool(newFilterAdmin, filterAdminOwner); } @@ -216,7 +216,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { filters ); - emit CreateNFTPool(newFilterAdmin, filterV1); + emit CreateNFTPool(newFilterAdmin, filterAdminOwner, filterV1); } // ================== Create Filter =================== @@ -241,7 +241,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { spreadIds ); - emit CreateFilterV1(newFilterV1, key); + emit CreateFilterV1(filterAdmin, newFilterV1, nftCollection, key); } From 93517e754023d3e053326719f673c02ff2f332e9 Mon Sep 17 00:00:00 2001 From: owen05 Date: Sat, 18 Sep 2021 15:54:07 +0800 Subject: [PATCH 27/42] fix --- contracts/NFTPool/impl/FilterERC1155V1.sol | 4 ++-- contracts/NFTPool/impl/FilterERC721V1.sol | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index 113ec85..10da091 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -84,7 +84,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { emit TargetOut(tokenIds[i], amounts[i]); } (uint256 rawPay, ) = queryNFTTargetOut(totalAmount); - paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); + paid = IFilterAdmin(_OWNER_).burnFragFrom(to, rawPay); } function ERC1155RandomOut(uint256 amount, address to) @@ -93,7 +93,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { returns (uint256 paid) { (uint256 rawPay, ) = queryNFTRandomOut(amount); - paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); + paid = IFilterAdmin(_OWNER_).burnFragFrom(to, rawPay); for (uint256 i = 0; i < amount; i++) { uint256 randomNum = _getRandomNum() % _TOTAL_NFT_AMOUNT_; uint256 sum; diff --git a/contracts/NFTPool/impl/FilterERC721V1.sol b/contracts/NFTPool/impl/FilterERC721V1.sol index 5e9cbe5..7058f3c 100644 --- a/contracts/NFTPool/impl/FilterERC721V1.sol +++ b/contracts/NFTPool/impl/FilterERC721V1.sol @@ -87,7 +87,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { returns (uint256 paid) { (uint256 rawPay, ) = queryNFTTargetOut(tokenIds.length); - paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); + paid = IFilterAdmin(_OWNER_).burnFragFrom(to, rawPay); for (uint256 i = 0; i < tokenIds.length; i++) { _transferOutERC721(to, tokenIds[i]); @@ -102,7 +102,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { returns (uint256 paid) { (uint256 rawPay, ) = queryNFTRandomOut(amount); - paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); + paid = IFilterAdmin(_OWNER_).burnFragFrom(to, rawPay); for (uint256 i = 0; i < amount; i++) { uint256 index = _getRandomNum() % _TOTAL_NFT_AMOUNT_; _transferOutERC721(to, _NFT_IDS_[index]); From 565c0e4b12e9b76e23f511ee5521e10bf7bfaf6a Mon Sep 17 00:00:00 2001 From: owen05 Date: Sat, 18 Sep 2021 16:40:12 +0800 Subject: [PATCH 28/42] add view --- contracts/NFTPool/impl/FilterAdmin.sol | 2 ++ contracts/NFTPool/intf/IFilterAdmin.sol | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/contracts/NFTPool/impl/FilterAdmin.sol b/contracts/NFTPool/impl/FilterAdmin.sol index 927b48e..359167b 100644 --- a/contracts/NFTPool/impl/FilterAdmin.sol +++ b/contracts/NFTPool/impl/FilterAdmin.sol @@ -73,6 +73,7 @@ contract FilterAdmin is InitializableInternalMintableERC20 { //================ View ================ function queryMintFee(uint256 rawAmount) + view public returns ( uint256 poolFee, @@ -87,6 +88,7 @@ contract FilterAdmin is InitializableInternalMintableERC20 { } function queryBurnFee(uint256 rawAmount) + view public returns ( uint256 poolFee, diff --git a/contracts/NFTPool/intf/IFilterAdmin.sol b/contracts/NFTPool/intf/IFilterAdmin.sol index 8cd63b7..29e68d5 100644 --- a/contracts/NFTPool/intf/IFilterAdmin.sol +++ b/contracts/NFTPool/intf/IFilterAdmin.sol @@ -8,9 +8,9 @@ pragma solidity 0.6.9; interface IFilterAdmin { - function _OWNER_() external returns (address); + function _OWNER_() external view returns (address); - function _CONTROLLER_() external returns (address); + function _CONTROLLER_() external view returns (address); function init( address owner, From 98e6cfab161ce9447af9f395d31fa22ae52accac Mon Sep 17 00:00:00 2001 From: owen05 Date: Sat, 18 Sep 2021 20:24:00 +0800 Subject: [PATCH 29/42] ing --- config/rinkeby-config.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/rinkeby-config.js b/config/rinkeby-config.js index 8bb4bdf..d71231e 100644 --- a/config/rinkeby-config.js +++ b/config/rinkeby-config.js @@ -103,9 +103,9 @@ module.exports = { //=================== NFTPool ================== DODONFTApprove: "0xb971B0df71fB1778351F25a0e3bfe0C3eF06E1d1", - FilterAdmin: "0x3893c38aFa42E492b134888c79CAE52744Eb3ab0", - FilterERC721V1: "0x5a317a617FBF89d55bBfe40A76E526FCD74E4545", - FilterERC1155V1: "0x41c5eDe987bd61c75925Ab2657256D75270a55D4", + FilterAdmin: "0xeD662e5391a3aA6F2FF610a5b33c62815b342B35", + FilterERC721V1: "0xB3b12b077Cb57281E361D52bD8b21fC8A4763316", + FilterERC1155V1: "0xB26897b8CF61D90230Af0f8d3Ab1b59ECd42CFDf", DODONFTPoolProxy: "0x38c109aF4f3454172BA4eecf5676aA213b589e75", NFTPoolController: "0xf5d24499dD76C3791ee6D19aa206f55b72270415" From cc57ee2c14ce119bbd009129061beb3516d8ad02 Mon Sep 17 00:00:00 2001 From: owen05 Date: Sat, 18 Sep 2021 23:08:03 +0800 Subject: [PATCH 30/42] add event && redeploy --- config/rinkeby-config.js | 4 ++-- contracts/NFTPool/impl/BaseFilterV1.sol | 4 ++++ contracts/NFTPool/impl/FilterERC1155V1.sol | 6 ++++++ contracts/NFTPool/impl/FilterERC721V1.sol | 6 ++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/config/rinkeby-config.js b/config/rinkeby-config.js index d71231e..0434915 100644 --- a/config/rinkeby-config.js +++ b/config/rinkeby-config.js @@ -104,8 +104,8 @@ module.exports = { //=================== NFTPool ================== DODONFTApprove: "0xb971B0df71fB1778351F25a0e3bfe0C3eF06E1d1", FilterAdmin: "0xeD662e5391a3aA6F2FF610a5b33c62815b342B35", - FilterERC721V1: "0xB3b12b077Cb57281E361D52bD8b21fC8A4763316", - FilterERC1155V1: "0xB26897b8CF61D90230Af0f8d3Ab1b59ECd42CFDf", + FilterERC721V1: "0xce5CD3D57d4f9e781e6a6823D64e157B173e91D9", + FilterERC1155V1: "0xef4A80041B9b7b85038170F19367B93a24abe38B", DODONFTPoolProxy: "0x38c109aF4f3454172BA4eecf5676aA213b589e75", NFTPoolController: "0xf5d24499dD76C3791ee6D19aa206f55b72270415" diff --git a/contracts/NFTPool/impl/BaseFilterV1.sol b/contracts/NFTPool/impl/BaseFilterV1.sol index 7b1fc7d..50a5d1b 100644 --- a/contracts/NFTPool/impl/BaseFilterV1.sol +++ b/contracts/NFTPool/impl/BaseFilterV1.sol @@ -16,6 +16,10 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { using SafeMath for uint256; //=================== Event =================== + event NftInOrder(address user, uint256 receiveAmount); + event TargetOutOrder(address user, uint256 paidAmount); + event RandomOutOrder(address user, uint256 paidAmount); + event ChangeNFTInPrice(uint256 newGsStart, uint256 newCr, bool toggleFlag); event ChangeNFTRandomOutPrice(uint256 newGsStart, uint256 newCr, bool toggleFlag); event ChangeNFTTargetOutPrice(uint256 newGsStart, uint256 newCr, bool toggleFlag); diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index 10da091..bfb99da 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -70,6 +70,8 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { } (uint256 rawReceive, ) = queryNFTIn(totalAmount); received = IFilterAdmin(_OWNER_).mintFragTo(to, rawReceive); + + emit NftInOrder(to, received); } function ERC1155TargetOut( @@ -85,6 +87,8 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { } (uint256 rawPay, ) = queryNFTTargetOut(totalAmount); paid = IFilterAdmin(_OWNER_).burnFragFrom(to, rawPay); + + emit TargetOutOrder(to, paid); } function ERC1155RandomOut(uint256 amount, address to) @@ -106,6 +110,8 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { } } } + + emit RandomOutOrder(to, paid); } // ============ Transfer ============= diff --git a/contracts/NFTPool/impl/FilterERC721V1.sol b/contracts/NFTPool/impl/FilterERC721V1.sol index 7058f3c..860335d 100644 --- a/contracts/NFTPool/impl/FilterERC721V1.sol +++ b/contracts/NFTPool/impl/FilterERC721V1.sol @@ -79,6 +79,8 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; (uint256 rawReceive, ) = queryNFTIn(tokenIds.length); received = IFilterAdmin(_OWNER_).mintFragTo(to, rawReceive); + + emit NftInOrder(to, received); } function ERC721TargetOut(uint256[] memory tokenIds, address to) @@ -94,6 +96,8 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { emit TargetOut(tokenIds[i]); } _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; + + emit TargetOutOrder(to, paid); } function ERC721RandomOut(uint256 amount, address to) @@ -110,6 +114,8 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { emit RandomOut(_NFT_IDS_[index]); } _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; + + emit RandomOutOrder(to, paid); } // ============ Transfer ============= From 013cbf66ca4a01140371f41114e32cd18d8474e9 Mon Sep 17 00:00:00 2001 From: owen05 Date: Mon, 20 Sep 2021 14:52:23 +0800 Subject: [PATCH 31/42] add unit test && fix --- contracts/NFTPool/impl/BaseFilterV1.sol | 49 ++- contracts/NFTPool/impl/FilterERC1155V1.sol | 26 +- contracts/NFTPool/impl/FilterERC721V1.sol | 26 +- .../SmartRoute/proxies/DODONFTPoolProxy.sol | 24 +- contracts/lib/DecimalMath.sol | 2 +- test/NFTPool/erc1155NftPool.test.ts | 225 +++++++++++++ test/NFTPool/erc721NftPool.test.ts | 311 ++++++++++-------- test/utils/Contracts.ts | 4 +- test/utils/NFTPoolContext.ts | 2 +- truffle-test.sh | 11 +- 10 files changed, 491 insertions(+), 189 deletions(-) create mode 100644 test/NFTPool/erc1155NftPool.test.ts diff --git a/contracts/NFTPool/impl/BaseFilterV1.sol b/contracts/NFTPool/impl/BaseFilterV1.sol index 50a5d1b..43c6dea 100644 --- a/contracts/NFTPool/impl/BaseFilterV1.sol +++ b/contracts/NFTPool/impl/BaseFilterV1.sol @@ -117,11 +117,15 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { ) { require(NFTInAmount <= getAvaliableNFTInAmount(), "EXCEDD_IN_AMOUNT"); + (rawReceive, received) = _queryNFTIn(_TOTAL_NFT_AMOUNT_,_TOTAL_NFT_AMOUNT_ + NFTInAmount); + } + + function _queryNFTIn(uint256 start, uint256 end) internal view returns(uint256 rawReceive, uint256 received) { rawReceive = _geometricCalc( _GS_START_IN_, _CR_IN_, - _TOTAL_NFT_AMOUNT_, - _TOTAL_NFT_AMOUNT_ + NFTInAmount + start, + end ); (,, received) = IFilterAdmin(_OWNER_).queryMintFee(rawReceive); } @@ -135,11 +139,15 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { ) { require(NFTOutAmount <= getAvaliableNFTOutAmount(), "EXCEED_OUT_AMOUNT"); + (rawPay, pay) = _queryNFTTargetOut(_TOTAL_NFT_AMOUNT_ - NFTOutAmount, _TOTAL_NFT_AMOUNT_); + } + + function _queryNFTTargetOut(uint256 start, uint256 end) internal view returns(uint256 rawPay, uint256 pay) { rawPay = _geometricCalc( _GS_START_TARGET_OUT_, _CR_TARGET_OUT_, - _TOTAL_NFT_AMOUNT_ - NFTOutAmount, - _TOTAL_NFT_AMOUNT_ + start, + end ); (,, pay) = IFilterAdmin(_OWNER_).queryBurnFee(rawPay); } @@ -153,11 +161,15 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { ) { require(NFTOutAmount <= getAvaliableNFTOutAmount(), "EXCEED_OUT_AMOUNT"); + (rawPay, pay) = _queryNFTRandomOut(_TOTAL_NFT_AMOUNT_ - NFTOutAmount, _TOTAL_NFT_AMOUNT_); + } + + function _queryNFTRandomOut(uint256 start, uint256 end) internal view returns(uint256 rawPay, uint256 pay) { rawPay = _geometricCalc( _GS_START_RANDOM_OUT_, _CR_RANDOM_OUT_, - _TOTAL_NFT_AMOUNT_ - NFTOutAmount, - _TOTAL_NFT_AMOUNT_ + start, + end ); (,, pay) = IFilterAdmin(_OWNER_).queryBurnFee(rawPay); } @@ -165,22 +177,27 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { // ============ Math ============= function _geometricCalc( - uint256 a1, + uint256 a0, uint256 q, uint256 start, uint256 end ) internal view returns (uint256) { if (q == DecimalMath.ONE) { - return end.sub(start).mul(a1); - } - //Sn=a1*(q^n-1)/(q-1) - //Sn-Sm = a1*(q^n-q^m)/(q-1) - + return end.sub(start).mul(a0); + } //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)); + if (q < DecimalMath.ONE) { + //Sn=a0*(1 - q^n)/(1-q) + //Sn-Sm = a0*(q^m - q^n)/(1-q) + return a0.mul(qm.sub(qn)).div(DecimalMath.ONE.sub(q)); + } else { + //Sn=a0*(q^n - 1)/(q - 1) + //Sn-Sm = a0*(q^n - q^m)/(q-1) + return a0.mul(qn.sub(qm)).div(q.sub(DecimalMath.ONE)); + } } function _getRandomNum() public view returns (uint256 randomNum) { @@ -204,7 +221,7 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { uint256 newCr, bool toggleFlag ) internal { - require(newCr > DecimalMath.ONE, "CR_INVALID"); + require(newCr != 0, "CR_INVALID"); _GS_START_IN_ = newGsStart; _CR_IN_ = newCr; _NFT_IN_TOGGLE_ = true; @@ -225,7 +242,7 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { uint256 newCr, bool toggleFlag ) internal { - require(newCr > DecimalMath.ONE, "CR_INVALID"); + require(newCr != 0, "CR_INVALID"); _GS_START_RANDOM_OUT_ = newGsStart; _CR_RANDOM_OUT_ = newCr; _NFT_RANDOM_OUT_TOGGLE_ = true; @@ -246,7 +263,7 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { uint256 newCr, bool toggleFlag ) internal { - require(newCr > DecimalMath.ONE, "CR_INVALID"); + require(newCr != 0, "CR_INVALID"); _GS_START_TARGET_OUT_ = newGsStart; _CR_TARGET_OUT_ = newCr; _NFT_TARGET_OUT_TOGGLE_ = true; diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index bfb99da..c67bd38 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -60,6 +60,9 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { preventReentrant returns (uint256 received) { + uint256 avaliableNFTInAmount = getAvaliableNFTInAmount(); + uint256 originTotalNftAmount = _TOTAL_NFT_AMOUNT_; + uint256 totalAmount = 0; for (uint256 i = 0; i < tokenIds.length; i++) { uint256 tokenId = tokenIds[i]; @@ -68,7 +71,8 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { totalAmount += inAmount; emit NftIn(tokenId, inAmount); } - (uint256 rawReceive, ) = queryNFTIn(totalAmount); + require(totalAmount <= avaliableNFTInAmount, "EXCEDD_IN_AMOUNT"); + (uint256 rawReceive, ) = _queryNFTIn(originTotalNftAmount, originTotalNftAmount + totalAmount); received = IFilterAdmin(_OWNER_).mintFragTo(to, rawReceive); emit NftInOrder(to, received); @@ -79,13 +83,17 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { uint256[] memory amounts, address to ) external preventReentrant returns (uint256 paid) { + uint256 avaliableNFTOutAmount = getAvaliableNFTOutAmount(); + uint256 originTotalNftAmount = _TOTAL_NFT_AMOUNT_; + uint256 totalAmount = 0; for (uint256 i = 0; i < tokenIds.length; i++) { totalAmount += amounts[i]; _transferOutERC1155(to, tokenIds[i], amounts[i]); emit TargetOut(tokenIds[i], amounts[i]); } - (uint256 rawPay, ) = queryNFTTargetOut(totalAmount); + require(totalAmount <= avaliableNFTOutAmount, "EXCEED_OUT_AMOUNT"); + (uint256 rawPay, ) = _queryNFTTargetOut(originTotalNftAmount - totalAmount, originTotalNftAmount); paid = IFilterAdmin(_OWNER_).burnFragFrom(to, rawPay); emit TargetOutOrder(to, paid); @@ -102,10 +110,11 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { uint256 randomNum = _getRandomNum() % _TOTAL_NFT_AMOUNT_; uint256 sum; for (uint256 j = 0; j < _NFT_IDS_.length; j++) { - sum += _NFT_RESERVE_[_NFT_IDS_[j]]; + uint256 tokenId = _NFT_IDS_[j]; + sum += _NFT_RESERVE_[tokenId]; if (sum >= randomNum) { - _transferOutERC1155(to, _NFT_IDS_[j], 1); - emit RandomOut( _NFT_IDS_[j], 1); + _transferOutERC1155(to, tokenId, 1); + emit RandomOut(tokenId, 1); break; } } @@ -179,10 +188,13 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { _TOTAL_NFT_AMOUNT_ -= outAmount; if (currentAmount == 0) { uint256 index = _TOKENID_IDX_[tokenId] - 1; - _NFT_IDS_[index] = _NFT_IDS_[_NFT_IDS_.length - 1]; + if(index != _NFT_IDS_.length - 1) { + uint256 lastTokenId = _NFT_IDS_[_NFT_IDS_.length - 1]; + _NFT_IDS_[index] = lastTokenId; + _TOKENID_IDX_[lastTokenId] = index + 1; + } _NFT_IDS_.pop(); _TOKENID_IDX_[tokenId] = 0; - _TOKENID_IDX_[_NFT_IDS_[index]] = index + 1; } } diff --git a/contracts/NFTPool/impl/FilterERC721V1.sol b/contracts/NFTPool/impl/FilterERC721V1.sol index 860335d..94a2c90 100644 --- a/contracts/NFTPool/impl/FilterERC721V1.sol +++ b/contracts/NFTPool/impl/FilterERC721V1.sol @@ -62,6 +62,8 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { preventReentrant returns (uint256 received) { + require(tokenIds.length <= getAvaliableNFTInAmount(), "EXCEDD_IN_AMOUNT"); + uint256 originTotalNftAmount = _TOTAL_NFT_AMOUNT_; for (uint256 i = 0; i < tokenIds.length; i++) { uint256 tokenId = tokenIds[i]; require(isNFTIDValid(tokenId), "NFT_ID_NOT_SUPPORT"); @@ -77,7 +79,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { emit NftIn(tokenId); } _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; - (uint256 rawReceive, ) = queryNFTIn(tokenIds.length); + (uint256 rawReceive, ) = _queryNFTIn(originTotalNftAmount, originTotalNftAmount + tokenIds.length); received = IFilterAdmin(_OWNER_).mintFragTo(to, rawReceive); emit NftInOrder(to, received); @@ -108,10 +110,10 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { (uint256 rawPay, ) = queryNFTRandomOut(amount); paid = IFilterAdmin(_OWNER_).burnFragFrom(to, rawPay); for (uint256 i = 0; i < amount; i++) { - uint256 index = _getRandomNum() % _TOTAL_NFT_AMOUNT_; - _transferOutERC721(to, _NFT_IDS_[index]); - - emit RandomOut(_NFT_IDS_[index]); + uint256 index = _getRandomNum() % _NFT_IDS_.length; + uint256 tokenId = _NFT_IDS_[index]; + _transferOutERC721(to, tokenId); + emit RandomOut(tokenId); } _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; @@ -134,11 +136,14 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { uint256 index = _TOKENID_IDX_[tokenId] - 1; require(index < _NFT_IDS_.length, "INDEX_INVALID"); IERC721(_NFT_COLLECTION_).safeTransferFrom(address(this), to, tokenId); - _NFT_IDS_[index] = _NFT_IDS_[_NFT_IDS_.length - 1]; + if(index != _NFT_IDS_.length - 1) { + uint256 lastTokenId = _NFT_IDS_[_NFT_IDS_.length - 1]; + _NFT_IDS_[index] = lastTokenId; + _TOKENID_IDX_[lastTokenId] = index + 1; + } _NFT_IDS_.pop(); _NFT_RESERVE_[tokenId] = 0; _TOKENID_IDX_[tokenId] = 0; - _TOKENID_IDX_[_NFT_IDS_[index]] = index + 1; } function emergencyWithdraw( @@ -157,11 +162,14 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { uint256 tokenId = tokenIds[i]; if (_NFT_RESERVE_[tokenId] > 0 && nftContract[i] == _NFT_COLLECTION_) { uint256 index = getNFTIndexById(tokenId); - _NFT_IDS_[index] = _NFT_IDS_[_NFT_IDS_.length - 1]; + if(index != _NFT_IDS_.length - 1) { + uint256 lastTokenId = _NFT_IDS_[_NFT_IDS_.length - 1]; + _NFT_IDS_[index] = lastTokenId; + _TOKENID_IDX_[lastTokenId] = index + 1; + } _NFT_IDS_.pop(); _NFT_RESERVE_[tokenId] = 0; _TOKENID_IDX_[tokenId] = 0; - _TOKENID_IDX_[_NFT_IDS_[index]] = index + 1; } IERC721(nftContract[i]).safeTransferFrom(address(this), to, tokenIds[i]); emit EmergencyWithdraw(nftContract[i],tokenIds[i],to); diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index 6c6a793..f14c7cc 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -86,25 +86,25 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { function erc721TargetOut( address filter, uint256[] memory tokenIds, - address to, + // address to, uint256 maxBurnAmount ) external { - uint256 paid = IFilter(filter).ERC721TargetOut(tokenIds, to); + uint256 paid = IFilter(filter).ERC721TargetOut(tokenIds, msg.sender); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); - emit Erc721TargetOut(filter, to, paid); + emit Erc721TargetOut(filter, msg.sender, paid); } function erc721RandomOut( address filter, uint256 amount, - address to, + // address to, uint256 maxBurnAmount ) external { - uint256 paid = IFilter(filter).ERC721RandomOut(amount, to); + uint256 paid = IFilter(filter).ERC721RandomOut(amount, msg.sender); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); - emit Erc721RandomOut(filter, to, paid); + emit Erc721RandomOut(filter, msg.sender, paid); } // ================== ERC1155 In and Out =================== @@ -130,25 +130,25 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { address filter, uint256[] memory tokenIds, uint256[] memory amounts, - address to, + // address to, uint256 maxBurnAmount ) external { - uint256 paid = IFilter(filter).ERC1155TargetOut(tokenIds, amounts, to); + uint256 paid = IFilter(filter).ERC1155TargetOut(tokenIds, amounts, msg.sender); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); - emit Erc1155TargetOut(filter, to, paid); + emit Erc1155TargetOut(filter, msg.sender, paid); } function erc1155RandomOut( address filter, uint256 amount, - address to, + // address to, uint256 maxBurnAmount ) external { - uint256 paid = IFilter(filter).ERC1155RandomOut(amount, to); + uint256 paid = IFilter(filter).ERC1155RandomOut(amount, msg.sender); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); - emit Erc1155RandomOut(filter, to, paid); + emit Erc1155RandomOut(filter, msg.sender, paid); } diff --git a/contracts/lib/DecimalMath.sol b/contracts/lib/DecimalMath.sol index f740048..5a11899 100644 --- a/contracts/lib/DecimalMath.sol +++ b/contracts/lib/DecimalMath.sol @@ -48,7 +48,7 @@ library DecimalMath { function powFloor(uint256 target, uint256 e) internal pure returns (uint256) { if (e == 0) { - return 1; + return 10 ** 18; } else if (e == 1) { return target; } else { diff --git a/test/NFTPool/erc1155NftPool.test.ts b/test/NFTPool/erc1155NftPool.test.ts new file mode 100644 index 0000000..b0fb6ad --- /dev/null +++ b/test/NFTPool/erc1155NftPool.test.ts @@ -0,0 +1,225 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +// import * as assert from 'assert'; + +import { decimalStr, MAX_UINT256 } from '../utils/Converter'; +import { logGas } from '../utils/Log'; +import { NFTPoolContext, getNFTPoolContext } from '../utils/NFTPoolContext'; +import { assert } from 'chai'; +import * as contracts from '../utils/Contracts'; +import BigNumber from 'bignumber.js'; +const truffleAssert = require('truffle-assertions'); + +let maker: string; +let user: string; + +async function init(ctx: NFTPoolContext): Promise { + maker = ctx.SpareAccounts[0]; + user = ctx.SpareAccounts[1]; +} + +async function createNFTPool(ctx: NFTPoolContext) { + var tx = await logGas(ctx.DODONFTPoolProxy.methods.createNewNFTPoolV1( + maker, + ctx.DodoNft1155.options.address, + 2, + ['Filter01', 'FRAG', 'FRAG'], + [decimalStr("10000000"), decimalStr("0.005")], + [true, true, true], + [0, 4, 12, 4], + [decimalStr("1"), decimalStr("0.9"), decimalStr("1"), decimalStr("0.9"), decimalStr("2"), decimalStr("0.9")], + [7] + ), ctx.sendParam(maker), "createNewNFTPoolV1"); + + var newFilterAdmin = tx.events['CreateNFTPool'].returnValues['newFilterAdmin'] + var filter = tx.events['CreateNFTPool'].returnValues['filter'] + + return [newFilterAdmin, filter]; +} + +async function mintNFT(ctx: NFTPoolContext, amount) { + var tx = await ctx.DodoNft1155.methods.mint( + "http://projectowen.oss-cn-beijing.aliyuncs.com/2021-09-19-035145.png", + amount + ).send(ctx.sendParam(user)); + + var tokenId = tx.events['DODONFTMint'].returnValues['tokenId'] + return tokenId +} + +async function erc1155In(ctx: NFTPoolContext) { + var [filterAdmin, filter] = await createNFTPool(ctx) + var tokenIds = [] + var amounts = [] + for (var i = 0; i < 5; i++) { + var curTokenId = await mintNFT(ctx, 2); + tokenIds.push(curTokenId); + amounts.push(2); + } + + await ctx.DodoNft1155.methods.setApprovalForAll( + ctx.DODONFTApprove.options.address, + true + ).send(ctx.sendParam(user)) + + await ctx.DODONFTPoolProxy.methods.erc1155In( + filter, + ctx.DodoNft1155.options.address, + tokenIds, + amounts, + user, + 1 + ).send(ctx.sendParam(user)); + + return [filterAdmin, filter] +} + +describe("ERC1155-NFTPool", () => { + let snapshotId: string; + let ctx: NFTPoolContext; + + before(async () => { + ctx = await getNFTPoolContext(); + await init(ctx); + }); + + beforeEach(async () => { + snapshotId = await ctx.EVM.snapshot(); + }); + + afterEach(async () => { + await ctx.EVM.reset(snapshotId); + }); + + describe("ERC1155-NFTPool", () => { + it('erc1155In', async () => { + var [filterAdmin, filter] = await createNFTPool(ctx) + var tokenIds = [] + var amounts = [] + for (var i = 0; i < 4; i++) { + var curTokenId = await mintNFT(ctx, 2); + tokenIds.push(curTokenId); + amounts.push(2); + } + + var beforeBalanceTokenId0 = await ctx.DodoNft1155.methods.balanceOf(filter, 0).call(); + assert.equal(beforeBalanceTokenId0, 0) + + await logGas(ctx.DodoNft1155.methods.setApprovalForAll( + ctx.DODONFTApprove.options.address, + true + ), ctx.sendParam(user), "ApproveNFT"); + + var filterAdminInstance = contracts.getContractWithAddress(contracts.FILTER_ADMIN, filterAdmin); + + var beforeBalance = await filterAdminInstance.methods.balanceOf(user).call(); + console.log("beforeBalance:", beforeBalance); + + var tx = await logGas(ctx.DODONFTPoolProxy.methods.erc1155In( + filter, + ctx.DodoNft1155.options.address, + tokenIds, + amounts, + user, + 1 + ), ctx.sendParam(user), "erc1155In"); + + + assert.equal( + tx.events['Erc1155In'].returnValues['received'], + '5666851260500000000' + ) + + var afterBalanceTokenId0 = await ctx.DodoNft1155.methods.balanceOf(filter, 0).call(); + assert.equal(afterBalanceTokenId0, 2) + + }) + + it('ERC1155TargetOut', async () => { + var [, filter] = await erc1155In(ctx); + + var filterInstance = contracts.getContractWithAddress(contracts.FILTER_ERC1155_V1, filter); + + var beforeAmount = await ctx.DodoNft1155.methods.balanceOf(filter, 0).call(); + assert.equal(beforeAmount, 2) + + //maker targetout + var tx = await logGas(ctx.DODONFTPoolProxy.methods.erc1155TargetOut( + filter, + [0, 1, 3], + [2, 1, 1], + MAX_UINT256, + ), ctx.sendParam(maker), "Erc1155TargetOut"); + + var paid = tx.events['Erc1155TargetOut'].returnValues['paid'] + assert.equal(paid, "3673527453990000000"); + + var maxNftOutAmount = await filterInstance.methods.getAvaliableNFTOutAmount().call(); + var totalNftAmount = await filterInstance.methods._TOTAL_NFT_AMOUNT_().call(); + + assert.equal(maxNftOutAmount, 2); + assert.equal(totalNftAmount, 6); + + var afterAmount = await ctx.DodoNft1155.methods.balanceOf(maker, 0).call(); + assert.equal(afterAmount, 2) + }) + + + it('ERC721RandomOut', async () => { + var [, filter] = await erc1155In(ctx); + + var filterInstance = contracts.getContractWithAddress(contracts.FILTER_ERC1155_V1, filter); + + //maker randomOut + var tx = await logGas(ctx.DODONFTPoolProxy.methods.erc1155RandomOut( + filter, + 3, + MAX_UINT256, + ), ctx.sendParam(maker), "Erc1155RandomOut"); + + var paid = tx.events['Erc1155RandomOut'].returnValues['paid'] + assert.equal(paid, "1302665521995000000"); + + var maxNftOutAmount = await filterInstance.methods.getAvaliableNFTOutAmount().call(); + var totalNftAmount = await filterInstance.methods._TOTAL_NFT_AMOUNT_().call(); + + assert.equal(maxNftOutAmount, 3); + assert.equal(totalNftAmount, 7); + }) + + it('emergencyWithdraw', async () => { + var [filterAdmin, filter] = await erc1155In(ctx); + await ctx.Controller.methods.setEmergencyWithdraw(filter, true).send(ctx.sendParam(ctx.Deployer)); + + var beforeAmount = await ctx.DodoNft1155.methods.balanceOf(filter, 0).call(); + assert.equal(beforeAmount, 2) + + var filterInstance = contracts.getContractWithAddress(contracts.FILTER_ERC1155_V1, filter); + + await logGas(filterInstance.methods.emergencyWithdraw( + [ctx.DodoNft1155.options.address, ctx.DodoNft1155.options.address, ctx.DodoNft1155.options.address], + [0, 1, 4], + [1, 2, 2], + maker + ), ctx.sendParam(maker), "EmergencyWithdraw") + + + var afterAmount = await ctx.DodoNft1155.methods.balanceOf(maker, 0).call(); + assert.equal(afterAmount, 1) + afterAmount = await ctx.DodoNft1155.methods.balanceOf(filter, 0).call(); + assert.equal(afterAmount, 1) + + var maxNftOutAmount = await filterInstance.methods.getAvaliableNFTOutAmount().call(); + var totalNftAmount = await filterInstance.methods._TOTAL_NFT_AMOUNT_().call(); + + assert.equal(maxNftOutAmount, 1); + assert.equal(totalNftAmount, 5); + }) + }); +}); + diff --git a/test/NFTPool/erc721NftPool.test.ts b/test/NFTPool/erc721NftPool.test.ts index 1183eb0..74244e7 100644 --- a/test/NFTPool/erc721NftPool.test.ts +++ b/test/NFTPool/erc721NftPool.test.ts @@ -11,23 +11,74 @@ import { decimalStr, MAX_UINT256 } from '../utils/Converter'; import { logGas } from '../utils/Log'; import { NFTPoolContext, getNFTPoolContext } from '../utils/NFTPoolContext'; import { assert } from 'chai'; +import * as contracts from '../utils/Contracts'; import BigNumber from 'bignumber.js'; +import { StringLiteralLike } from 'typescript'; const truffleAssert = require('truffle-assertions'); +let maker: string; +let user: string; -//createNFTPool +async function init(ctx: NFTPoolContext): Promise { + maker = ctx.SpareAccounts[0]; + user = ctx.SpareAccounts[1]; +} -//erc721In +async function createNFTPool(ctx: NFTPoolContext) { + var tx = await logGas(ctx.DODONFTPoolProxy.methods.createNewNFTPoolV1( + maker, + ctx.DodoNft.options.address, + 1, + ['Filter01', 'FRAG', 'FRAG'], + [decimalStr("10000000"), decimalStr("0.005")], + [true, true, true], + [0, 4, 5, 1], + [decimalStr("1"), decimalStr("0.9"), decimalStr("1"), decimalStr("0.9"), decimalStr("2"), decimalStr("0.9")], + [7] + ), ctx.sendParam(maker), "createNewNFTPoolV1"); -//erc721TargetOut + var newFilterAdmin = tx.events['CreateNFTPool'].returnValues['newFilterAdmin'] + var filter = tx.events['CreateNFTPool'].returnValues['filter'] -//erc721RandomOut + return [newFilterAdmin, filter]; +} -//createFilter +async function mintNFT(ctx: NFTPoolContext) { + var tx = await ctx.DodoNft.methods.mint( + "http://projectowen.oss-cn-beijing.aliyuncs.com/2021-09-19-035145.png" + ).send(ctx.sendParam(user)); + + var tokenId = tx.events['DODONFTMint'].returnValues['tokenId'] + return tokenId +} + +async function erc721In(ctx: NFTPoolContext) { + var [filterAdmin, filter] = await createNFTPool(ctx) + var tokenIds = [] + for (var i = 0; i < 5; i++) { + var curTokenId = await mintNFT(ctx); + tokenIds.push(curTokenId); + } + + await ctx.DodoNft.methods.setApprovalForAll( + ctx.DODONFTApprove.options.address, + true + ).send(ctx.sendParam(user)) + + await ctx.DODONFTPoolProxy.methods.erc721In( + filter, + ctx.DodoNft.options.address, + tokenIds, + user, + 1 + ).send(ctx.sendParam(user)); + + return [filterAdmin, filter] +} describe("ERC721-NFTPool", () => { let snapshotId: string; - let ctx: DVMContext; + let ctx: NFTPoolContext; before(async () => { ctx = await getNFTPoolContext(); @@ -42,163 +93,147 @@ describe("ERC721-NFTPool", () => { await ctx.EVM.reset(snapshotId); }); - describe("buy shares", () => { + describe("ERC721-NFTPool", () => { - it("buy shares from init states", async () => { + it("createNewNFTPoolV1", async () => { + var tx = await logGas(ctx.DODONFTPoolProxy.methods.createNewNFTPoolV1( + maker, + ctx.DodoNft.options.address, + 1, + ['Filter01', 'FRAG', 'FRAG'], + [decimalStr("10000000"), decimalStr("0.005")], + [true, true, true], + [0, 3, 2, 1], + [decimalStr("1"), decimalStr("1.1"), decimalStr("1"), decimalStr("1.1"), decimalStr("2"), decimalStr("1.1")], + [5] + ), ctx.sendParam(maker), "createNewNFTPoolV1"); - await ctx.transferBaseToDVM(lp, decimalStr("10")) - await logGas(ctx.DVM.methods.buyShares(lp), ctx.sendParam(lp), "buy shares"); + var newFilterAdmin = tx.events['CreateNFTPool'].returnValues['newFilterAdmin'] + var filter = tx.events['CreateNFTPool'].returnValues['filter'] + + console.log("newFilterAdmin:", newFilterAdmin) + console.log("filterV1:", filter) - // vault balances assert.equal( - await ctx.BASE.methods.balanceOf(ctx.DVM.options.address).call(), - decimalStr("10") - ); - assert.equal( - await ctx.QUOTE.methods.balanceOf(ctx.DVM.options.address).call(), - decimalStr("0") - ); - assert.equal( - await ctx.DVM.methods._BASE_RESERVE_().call(), - decimalStr("10") + tx.events['CreateNFTPool'].returnValues['filterAdminOwner'], + maker ) - assert.equal( - await ctx.DVM.methods._QUOTE_RESERVE_().call(), - decimalStr("0") - ) - - // shares number - assert.equal(await ctx.DVM.methods.balanceOf(lp).call(), decimalStr("10")) }); - it("buy shares from init states with quote != 0", async () => { - await ctx.transferBaseToDVM(lp, decimalStr("10")) - await ctx.transferQuoteToDVM(lp, decimalStr("100")) - await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)); - assert.equal(await ctx.DVM.methods.balanceOf(lp).call(), decimalStr("10")) - assert.equal(await ctx.DVM.methods.getMidPrice().call(), "102078438912577213500") - }) + it('erc721In', async () => { + var [filterAdmin, filter] = await createNFTPool(ctx) + var tokenIds = [] + for (var i = 0; i < 4; i++) { + var curTokenId = await mintNFT(ctx); + tokenIds.push(curTokenId); + } - it("buy shares with balanced input", async () => { - await ctx.transferBaseToDVM(lp, decimalStr("10")) - await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) + await logGas(ctx.DodoNft.methods.setApprovalForAll( + ctx.DODONFTApprove.options.address, + true + ), ctx.sendParam(user), "ApproveNFT"); - await ctx.transferQuoteToDVM(trader, decimalStr("200")) - await ctx.DVM.methods.sellQuote(trader).send(ctx.sendParam(trader)) + var filterAdminInstance = contracts.getContractWithAddress(contracts.FILTER_ADMIN, filterAdmin); - var vaultBaseBalance = new BigNumber(await ctx.BASE.methods.balanceOf(ctx.DVM.options.address).call()) - var vaultQuoteBalance = new BigNumber(await ctx.QUOTE.methods.balanceOf(ctx.DVM.options.address).call()) - var increaseRatio = new BigNumber("0.1") + var beforeBalance = await filterAdminInstance.methods.balanceOf(user).call(); + console.log("beforeBalance:", beforeBalance); - await ctx.transferBaseToDVM(trader, vaultBaseBalance.multipliedBy(increaseRatio).toFixed(0)) - await ctx.transferQuoteToDVM(trader, vaultQuoteBalance.multipliedBy(increaseRatio).toFixed(0)) - await ctx.DVM.methods.buyShares(trader).send(ctx.sendParam(trader)) + var tx = await logGas(ctx.DODONFTPoolProxy.methods.erc721In( + filter, + ctx.DodoNft.options.address, + tokenIds, + user, + 1 + ), ctx.sendParam(user), "erc721In"); + + var afterBalance = await filterAdminInstance.methods.balanceOf(user).call(); + console.log("afterBalance:", afterBalance); assert.equal( - await ctx.BASE.methods.balanceOf(ctx.DVM.options.address).call(), - "8852116395368015179" - ); - assert.equal( - await ctx.QUOTE.methods.balanceOf(ctx.DVM.options.address).call(), - "220000000000000000000" - ); - - assert.equal(await ctx.DVM.methods.balanceOf(trader).call(), "999999999999999990") + tx.events['Erc721In'].returnValues['received'], + '3421805000000000000' + ) }) - it("buy shares with unbalanced input (less quote)", async () => { - await ctx.transferBaseToDVM(lp, decimalStr("10")) - await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) + it('ERC721TargetOut', async () => { + var [, filter] = await erc721In(ctx); - await ctx.transferQuoteToDVM(trader, decimalStr("200")) - await ctx.DVM.methods.sellQuote(trader).send(ctx.sendParam(trader)) + var filterInstance = contracts.getContractWithAddress(contracts.FILTER_ERC721_V1, filter); - var vaultBaseBalance = new BigNumber(await ctx.BASE.methods.balanceOf(ctx.DVM.options.address).call()) - var vaultQuoteBalance = new BigNumber(await ctx.QUOTE.methods.balanceOf(ctx.DVM.options.address).call()) - var increaseRatio = new BigNumber("0.1") + var beforeOwner = await ctx.DodoNft.methods.ownerOf(0).call(); + assert.equal(beforeOwner, filter) - await ctx.transferBaseToDVM(trader, vaultBaseBalance.multipliedBy(increaseRatio).toFixed(0)) - await ctx.transferQuoteToDVM(trader, vaultQuoteBalance.multipliedBy(increaseRatio).div(2).toFixed(0)) - await ctx.DVM.methods.buyShares(trader).send(ctx.sendParam(trader)) + //maker targetout + var tx = await logGas(ctx.DODONFTPoolProxy.methods.erc721TargetOut( + filter, + [0, 1, 3], + MAX_UINT256, + ), ctx.sendParam(maker), "Erc721TargetOut"); - assert.equal(await ctx.DVM.methods.balanceOf(trader).call(), "500000000000000000") + var paid = tx.events['Erc721TargetOut'].returnValues['paid'] + + assert.equal(paid, "4412151000000000000"); + + var maxNftOutAmount = await filterInstance.methods.getAvaliableNFTOutAmount().call(); + var totalNftAmount = await filterInstance.methods._TOTAL_NFT_AMOUNT_().call(); + var tokenId2 = await filterInstance.methods.getNFTIndexById(2).call(); + + assert.equal(maxNftOutAmount, 1); + assert.equal(totalNftAmount, 2); + assert.equal(tokenId2, 1); + + var afterOwner = await ctx.DodoNft.methods.ownerOf(0).call(); + assert.equal(afterOwner, maker) }) - it("buy shares with unbalanced input (less base)", async () => { - await ctx.transferBaseToDVM(lp, decimalStr("10")) - await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) - await ctx.transferQuoteToDVM(trader, decimalStr("200")) - await ctx.DVM.methods.sellQuote(trader).send(ctx.sendParam(trader)) + it('ERC721RandomOut', async () => { + var [, filter] = await erc721In(ctx); - var vaultBaseBalance = new BigNumber(await ctx.BASE.methods.balanceOf(ctx.DVM.options.address).call()) - var vaultQuoteBalance = new BigNumber(await ctx.QUOTE.methods.balanceOf(ctx.DVM.options.address).call()) - var increaseRatio = new BigNumber("0.1") + var filterInstance = contracts.getContractWithAddress(contracts.FILTER_ERC721_V1, filter); - await ctx.transferBaseToDVM(trader, vaultBaseBalance.multipliedBy(increaseRatio).div(2).toFixed(0)) - await ctx.transferQuoteToDVM(trader, vaultQuoteBalance.multipliedBy(increaseRatio).toFixed(0)) - await ctx.DVM.methods.buyShares(trader).send(ctx.sendParam(trader)) + //maker randomOut + var tx = await logGas(ctx.DODONFTPoolProxy.methods.erc721RandomOut( + filter, + 3, + MAX_UINT256, + ), ctx.sendParam(maker), "Erc721RandomOut"); - assert.equal(await ctx.DVM.methods.balanceOf(trader).call(), "499999999999999990") + var paid = tx.events['Erc721RandomOut'].returnValues['paid'] + assert.equal(paid, "2206075500000000000"); + + var maxNftOutAmount = await filterInstance.methods.getAvaliableNFTOutAmount().call(); + var totalNftAmount = await filterInstance.methods._TOTAL_NFT_AMOUNT_().call(); + + assert.equal(maxNftOutAmount, 1); + assert.equal(totalNftAmount, 2); + }) + + it('emergencyWithdraw', async () => { + var [filterAdmin, filter] = await erc721In(ctx); + await ctx.Controller.methods.setEmergencyWithdraw(filter, true).send(ctx.sendParam(ctx.Deployer)); + + var beforeOwner = await ctx.DodoNft.methods.ownerOf(0).call(); + assert.equal(beforeOwner, filter) + + var filterInstance = contracts.getContractWithAddress(contracts.FILTER_ERC721_V1, filter); + + await logGas(filterInstance.methods.emergencyWithdraw( + [ctx.DodoNft.options.address, ctx.DodoNft.options.address, ctx.DodoNft.options.address], + [0, 1, 4], + maker + ), ctx.sendParam(maker), "EmergencyWithdraw") + + + var afterOwner = await ctx.DodoNft.methods.ownerOf(0).call(); + assert.equal(afterOwner, maker) + + var maxNftOutAmount = await filterInstance.methods.getAvaliableNFTOutAmount().call(); + var totalNftAmount = await filterInstance.methods._TOTAL_NFT_AMOUNT_().call(); + + assert.equal(maxNftOutAmount, 1); + assert.equal(totalNftAmount, 2); }) }); - - describe("sell shares", () => { - it("not the last one sell shares", async () => { - await ctx.transferBaseToDVM(lp, decimalStr("10")) - await ctx.transferQuoteToDVM(lp, decimalStr("100")) - await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) - - await ctx.transferBaseToDVM(trader, decimalStr("1")) - await ctx.transferQuoteToDVM(trader, decimalStr("10")) - await ctx.DVM.methods.buyShares(trader).send(ctx.sendParam(trader)) - - var vaultShares = new BigNumber(await ctx.DVM.methods.balanceOf(lp).call()) - var bob = ctx.SpareAccounts[5] - await ctx.DVM.methods.sellShares(vaultShares.div(2).toFixed(0), bob, 0, 0, "0x", MAX_UINT256).send(ctx.sendParam(lp)) - assert.equal(await ctx.BASE.methods.balanceOf(bob).call(), decimalStr("5")) - assert.equal(await ctx.QUOTE.methods.balanceOf(bob).call(), decimalStr("50")) - - await ctx.DVM.methods.sellShares(vaultShares.div(2).toFixed(0), bob, 0, 0, "0x", MAX_UINT256).send(ctx.sendParam(lp)) - assert.equal(await ctx.BASE.methods.balanceOf(bob).call(), decimalStr("10")) - assert.equal(await ctx.QUOTE.methods.balanceOf(bob).call(), decimalStr("100")) - }) - - it("the last one sell shares", async () => { - await ctx.transferBaseToDVM(lp, decimalStr("10")) - await ctx.transferQuoteToDVM(lp, decimalStr("100")) - await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) - - var vaultShares = await ctx.DVM.methods.balanceOf(lp).call() - var bob = ctx.SpareAccounts[5] - await ctx.DVM.methods.sellShares(vaultShares, bob, 0, 0, "0x", MAX_UINT256).send(ctx.sendParam(lp)) - assert.equal(await ctx.BASE.methods.balanceOf(bob).call(), decimalStr("10")) - assert.equal(await ctx.QUOTE.methods.balanceOf(bob).call(), decimalStr("100")) - }) - - it("revert cases", async () => { - await ctx.transferBaseToDVM(lp, decimalStr("10")) - await ctx.transferQuoteToDVM(lp, decimalStr("100")) - await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) - - var vaultShares = await ctx.DVM.methods.balanceOf(lp).call() - var bob = ctx.SpareAccounts[5] - await truffleAssert.reverts( - ctx.DVM.methods.sellShares(new BigNumber(vaultShares).multipliedBy(2), bob, 0, 0, "0x", MAX_UINT256).send(ctx.sendParam(lp)), - "DLP_NOT_ENOUGH" - ) - await truffleAssert.reverts( - ctx.DVM.methods.sellShares(vaultShares, bob, decimalStr("100"), 0, "0x", MAX_UINT256).send(ctx.sendParam(lp)), - "WITHDRAW_NOT_ENOUGH" - ) - await truffleAssert.reverts( - ctx.DVM.methods.sellShares(vaultShares, bob, 0, decimalStr("10000"), "0x", MAX_UINT256).send(ctx.sendParam(lp)), - "WITHDRAW_NOT_ENOUGH" - ) - await truffleAssert.reverts( - ctx.DVM.methods.sellShares(vaultShares, bob, 0, decimalStr("10000"), "0x", "0").send(ctx.sendParam(lp)), - "TIME_EXPIRED" - ) - }) - }) }); + diff --git a/test/utils/Contracts.ts b/test/utils/Contracts.ts index 939674b..0bbe568 100644 --- a/test/utils/Contracts.ts +++ b/test/utils/Contracts.ts @@ -77,8 +77,8 @@ export const DROPS_PROXY = "DODODropsProxy" export const DODO_NFT = "DODONFT" export const DODO_NFT_1155 = "DODONFT1155" -export const FILTER_ERC721_V1 = "FilterERC721" -export const FILTER_ERC1155_V1 = "FilterERC1155" +export const FILTER_ERC721_V1 = "FilterERC721V1" +export const FILTER_ERC1155_V1 = "FilterERC1155V1" export const FILTER_ADMIN = "FilterAdmin" export const CONTROLLER = "Controller" export const DODO_NFT_APPROVE = "DODONFTApprove" diff --git a/test/utils/NFTPoolContext.ts b/test/utils/NFTPoolContext.ts index a0f7301..410ada1 100644 --- a/test/utils/NFTPoolContext.ts +++ b/test/utils/NFTPoolContext.ts @@ -77,7 +77,7 @@ export class NFTPoolContext { this.Controller.options.address, this.Deployer, this.DODONFTApprove.options.address, - "" //TODO:ERC721 => ERC20 DODOApprove + "0x0000000000000000000000000000000000000000" //TODO:ERC721 => ERC20 DODOApprove ] ) diff --git a/truffle-test.sh b/truffle-test.sh index 9770c14..4df5dc4 100644 --- a/truffle-test.sh +++ b/truffle-test.sh @@ -1,5 +1,5 @@ #!/bin/bash -# truffle compile --all +truffle compile --all if [ "$1"x = "proxy-dpp"x ] then @@ -71,7 +71,12 @@ then truffle test ./test/DODODrops/dropsV2.test.ts fi -if [ "$1"x = "NFTPool"x ] +if [ "$1"x = "erc721NFTPool"x ] then - truffle test ./test/NFTPool/nftPool.test.ts + truffle test ./test/NFTPool/erc721NftPool.test.ts +fi + +if [ "$1"x = "erc1155NFTPool"x ] +then + truffle test ./test/NFTPool/erc1155NftPool.test.ts fi \ No newline at end of file From 9fd16aa1f46b96f97e84c3e9692cd4b838f057dd Mon Sep 17 00:00:00 2001 From: owen05 Date: Fri, 24 Sep 2021 09:39:15 +0800 Subject: [PATCH 32/42] update --- config/rinkeby-config.js | 8 ++++---- contracts/NFTPool/impl/BaseFilterV1.sol | 6 +++--- contracts/NFTPool/impl/Controller.sol | 2 ++ contracts/NFTPool/impl/FilterAdmin.sol | 1 + contracts/NFTPool/impl/FilterERC1155V1.sol | 1 + contracts/SmartRoute/proxies/DODONFTPoolProxy.sol | 2 +- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/config/rinkeby-config.js b/config/rinkeby-config.js index 0434915..52d0685 100644 --- a/config/rinkeby-config.js +++ b/config/rinkeby-config.js @@ -102,11 +102,11 @@ module.exports = { // DropsERC1155: "0x3a8EcF30428bd4e33Cd7011533DFd596F7705c8F", //=================== NFTPool ================== - DODONFTApprove: "0xb971B0df71fB1778351F25a0e3bfe0C3eF06E1d1", + DODONFTApprove: "0x9CA2A5FB3771d824FAc3a307ff6B0fbb992C00Bc", FilterAdmin: "0xeD662e5391a3aA6F2FF610a5b33c62815b342B35", - FilterERC721V1: "0xce5CD3D57d4f9e781e6a6823D64e157B173e91D9", - FilterERC1155V1: "0xef4A80041B9b7b85038170F19367B93a24abe38B", - DODONFTPoolProxy: "0x38c109aF4f3454172BA4eecf5676aA213b589e75", + FilterERC721V1: "0xdE39C2901e72A883f7446951fB533219F3622b87", + FilterERC1155V1: "0x0727dEd495E35f4bA4F5D64794145152301Db23f", + DODONFTPoolProxy: "0x888B9d4C25664F8B1BF744e8F4397d1429314155", NFTPoolController: "0xf5d24499dD76C3791ee6D19aa206f55b72270415" } diff --git a/contracts/NFTPool/impl/BaseFilterV1.sol b/contracts/NFTPool/impl/BaseFilterV1.sol index 43c6dea..61cc7b2 100644 --- a/contracts/NFTPool/impl/BaseFilterV1.sol +++ b/contracts/NFTPool/impl/BaseFilterV1.sol @@ -224,7 +224,7 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { require(newCr != 0, "CR_INVALID"); _GS_START_IN_ = newGsStart; _CR_IN_ = newCr; - _NFT_IN_TOGGLE_ = true; + _NFT_IN_TOGGLE_ = toggleFlag; emit ChangeNFTInPrice(newGsStart, newCr, toggleFlag); } @@ -245,7 +245,7 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { require(newCr != 0, "CR_INVALID"); _GS_START_RANDOM_OUT_ = newGsStart; _CR_RANDOM_OUT_ = newCr; - _NFT_RANDOM_OUT_TOGGLE_ = true; + _NFT_RANDOM_OUT_TOGGLE_ = toggleFlag; emit ChangeNFTRandomOutPrice(newGsStart, newCr, toggleFlag); } @@ -266,7 +266,7 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { require(newCr != 0, "CR_INVALID"); _GS_START_TARGET_OUT_ = newGsStart; _CR_TARGET_OUT_ = newCr; - _NFT_TARGET_OUT_TOGGLE_ = true; + _NFT_TARGET_OUT_TOGGLE_ = toggleFlag; emit ChangeNFTTargetOutPrice(newGsStart, newCr, toggleFlag); } diff --git a/contracts/NFTPool/impl/Controller.sol b/contracts/NFTPool/impl/Controller.sol index 6a63b07..63ce782 100644 --- a/contracts/NFTPool/impl/Controller.sol +++ b/contracts/NFTPool/impl/Controller.sol @@ -40,6 +40,7 @@ contract Controller is InitializableOwnable { uint256 nftOutFeeRate, bool isOpen ) external onlyOwner { + require(nftInFeeRate <= 1e18 && nftOutFeeRate <= 1e18, "FEE_RATE_TOO_LARGE"); FilterAdminFeeRateInfo memory feeRateInfo = FilterAdminFeeRateInfo({ nftInFeeRate: nftInFeeRate, nftOutFeeRate: nftOutFeeRate, @@ -51,6 +52,7 @@ contract Controller is InitializableOwnable { } function setGlobalParam(uint256 nftInFeeRate, uint256 nftOutFeeRate) external onlyOwner { + require(nftInFeeRate <= 1e18 && nftOutFeeRate <= 1e18, "FEE_RATE_TOO_LARGE"); _GLOBAL_NFT_IN_FEE_RATE_ = nftInFeeRate; _GLOBAL_NFT_OUT_FEE_RATE_ = nftOutFeeRate; diff --git a/contracts/NFTPool/impl/FilterAdmin.sol b/contracts/NFTPool/impl/FilterAdmin.sol index 359167b..dedc747 100644 --- a/contracts/NFTPool/impl/FilterAdmin.sol +++ b/contracts/NFTPool/impl/FilterAdmin.sol @@ -38,6 +38,7 @@ contract FilterAdmin is InitializableInternalMintableERC20 { address maintainer, address[] memory filters ) external { + require(feeRate <= DecimalMath.ONE, "FEE_RATE_TOO_LARGE"); super.init(owner, initSupply, name, symbol, 18); _INIT_SUPPLY_ = initSupply; _FEE_RATE_ = feeRate; diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index c67bd38..b1c1b96 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -83,6 +83,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { uint256[] memory amounts, address to ) external preventReentrant returns (uint256 paid) { + require(tokenIds.length == amounts.length, "PARAM_INVALID"); uint256 avaliableNFTOutAmount = getAvaliableNFTOutAmount(); uint256 originTotalNftAmount = _TOTAL_NFT_AMOUNT_; diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index f14c7cc..9d822de 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -16,7 +16,7 @@ import {IDODONFTApprove} from "../../intf/IDODONFTApprove.sol"; import {IERC20} from "../../intf/IERC20.sol"; import {SafeERC20} from "../../lib/SafeERC20.sol"; -contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { +contract DODONFTPoolProxy is InitializableOwnable, ReentrancyGuard { using SafeMath for uint256; using SafeERC20 for IERC20; From 685ffa81c47c263fe1120cb31c967f757b32feaf Mon Sep 17 00:00:00 2001 From: owen05 Date: Fri, 24 Sep 2021 20:39:39 +0800 Subject: [PATCH 33/42] add directTransferOwnership --- config/rinkeby-config.js | 2 +- contracts/NFTPool/impl/FilterAdmin.sol | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/config/rinkeby-config.js b/config/rinkeby-config.js index 52d0685..1ce8a01 100644 --- a/config/rinkeby-config.js +++ b/config/rinkeby-config.js @@ -103,7 +103,7 @@ module.exports = { //=================== NFTPool ================== DODONFTApprove: "0x9CA2A5FB3771d824FAc3a307ff6B0fbb992C00Bc", - FilterAdmin: "0xeD662e5391a3aA6F2FF610a5b33c62815b342B35", + FilterAdmin: "0x729f7f44bf64Ce814716b6261e267DbE6cdf021c", FilterERC721V1: "0xdE39C2901e72A883f7446951fB533219F3622b87", FilterERC1155V1: "0x0727dEd495E35f4bA4F5D64794145152301Db23f", DODONFTPoolProxy: "0x888B9d4C25664F8B1BF744e8F4397d1429314155", diff --git a/contracts/NFTPool/impl/FilterAdmin.sol b/contracts/NFTPool/impl/FilterAdmin.sol index dedc747..cb88bcd 100644 --- a/contracts/NFTPool/impl/FilterAdmin.sol +++ b/contracts/NFTPool/impl/FilterAdmin.sol @@ -127,6 +127,11 @@ contract FilterAdmin is InitializableInternalMintableERC20 { emit ChangeFeeRate(newFeeRate); } + function directTransferOwnership(address newOwner) external onlyOwner { + emit OwnershipTransferred(_OWNER_, newOwner); + _OWNER_ = newOwner; + } + //================= Support ================ function version() external pure virtual returns (string memory) { return "FILTER ADMIN 1.0.0"; From 5af15a4effcd4abedfde562a3450a55a61fc584f Mon Sep 17 00:00:00 2001 From: owen05 Date: Fri, 24 Sep 2021 22:29:47 +0800 Subject: [PATCH 34/42] update --- config/rinkeby-config.js | 4 ++-- contracts/NFTPool/impl/BaseFilterV1.sol | 24 ++++++++++++++++++++++ contracts/NFTPool/impl/FilterERC1155V1.sol | 4 ++-- contracts/NFTPool/impl/FilterERC721V1.sol | 2 +- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/config/rinkeby-config.js b/config/rinkeby-config.js index 1ce8a01..16e78c1 100644 --- a/config/rinkeby-config.js +++ b/config/rinkeby-config.js @@ -104,8 +104,8 @@ module.exports = { //=================== NFTPool ================== DODONFTApprove: "0x9CA2A5FB3771d824FAc3a307ff6B0fbb992C00Bc", FilterAdmin: "0x729f7f44bf64Ce814716b6261e267DbE6cdf021c", - FilterERC721V1: "0xdE39C2901e72A883f7446951fB533219F3622b87", - FilterERC1155V1: "0x0727dEd495E35f4bA4F5D64794145152301Db23f", + FilterERC721V1: "0x72d220cE168C4f361dD4deE5D826a01AD8598f6C", + FilterERC1155V1: "0x80930Cb1849F7D42531506fF45E66724338A821b", DODONFTPoolProxy: "0x888B9d4C25664F8B1BF744e8F4397d1429314155", NFTPoolController: "0xf5d24499dD76C3791ee6D19aa206f55b72270415" diff --git a/contracts/NFTPool/impl/BaseFilterV1.sol b/contracts/NFTPool/impl/BaseFilterV1.sol index 61cc7b2..a2385df 100644 --- a/contracts/NFTPool/impl/BaseFilterV1.sol +++ b/contracts/NFTPool/impl/BaseFilterV1.sol @@ -319,7 +319,31 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { external onlySuperOwner { + _changeFilterName(newFilterName); + } + + function _changeFilterName(string memory newFilterName) internal { _FILTER_NAME_ = newFilterName; emit ChangeFilterName(newFilterName); } + + + function resetFilter( + string memory filterName, + bool[] memory toggles, + uint256[] memory numParams, //0 - startId, 1 - endId, 2 - maxAmount, 3 - minAmount + uint256[] memory priceRules, + uint256[] memory spreadIds, + bool[] memory isRegistered + ) external onlySuperOwner { + _changeFilterName(filterName); + _changeNFTInPrice(priceRules[0], priceRules[1], toggles[0]); + _changeNFTRandomOutPrice(priceRules[2], priceRules[3], toggles[1]); + _changeNFTTargetOutPrice(priceRules[4], priceRules[5], toggles[2]); + + _changeNFTAmountRange(numParams[2], numParams[3]); + _changeTokenIdRange(numParams[0], numParams[1]); + + _changeTokenIdMap(spreadIds, isRegistered); + } } diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index b1c1b96..13f974c 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -34,8 +34,8 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { uint256[] memory spreadIds ) external { initOwner(filterAdmin); - - _FILTER_NAME_ = filterName; + + _changeFilterName(filterName); _NFT_COLLECTION_ = nftCollection; _changeNFTInPrice(priceRules[0], priceRules[1], toggles[0]); diff --git a/contracts/NFTPool/impl/FilterERC721V1.sol b/contracts/NFTPool/impl/FilterERC721V1.sol index 94a2c90..d08089a 100644 --- a/contracts/NFTPool/impl/FilterERC721V1.sol +++ b/contracts/NFTPool/impl/FilterERC721V1.sol @@ -37,8 +37,8 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { uint256[] memory spreadIds ) external { initOwner(filterAdmin); - _FILTER_NAME_ = filterName; + _changeFilterName(filterName); _NFT_COLLECTION_ = nftCollection; _changeNFTInPrice(priceRules[0], priceRules[1], toggles[0]); _changeNFTRandomOutPrice(priceRules[2], priceRules[3], toggles[1]); From 15675bf5f274ce00ed43e69eacde28400ae1f441 Mon Sep 17 00:00:00 2001 From: owen05 Date: Mon, 27 Sep 2021 15:11:03 +0800 Subject: [PATCH 35/42] nftPool out fix --- contracts/NFTPool/impl/BaseFilterV1.sol | 2 +- contracts/NFTPool/impl/FilterERC1155V1.sol | 16 +++--- contracts/NFTPool/impl/FilterERC721V1.sol | 15 +++--- .../SmartRoute/proxies/DODONFTPoolProxy.sol | 54 ------------------- test/NFTPool/erc1155NftPool.test.ts | 12 ++--- test/NFTPool/erc721NftPool.test.ts | 12 ++--- truffle-test.sh | 2 +- 7 files changed, 33 insertions(+), 80 deletions(-) diff --git a/contracts/NFTPool/impl/BaseFilterV1.sol b/contracts/NFTPool/impl/BaseFilterV1.sol index a2385df..c003f11 100644 --- a/contracts/NFTPool/impl/BaseFilterV1.sol +++ b/contracts/NFTPool/impl/BaseFilterV1.sol @@ -51,7 +51,7 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { // GS -> Geometric sequence // CR -> Common Ratio - //For Deposit NFT INto Pool + //For Deposit NFT IN to Pool uint256 public _GS_START_IN_; uint256 public _CR_IN_; bool public _NFT_IN_TOGGLE_ = false; diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index 13f974c..642026c 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -81,7 +81,8 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { function ERC1155TargetOut( uint256[] memory tokenIds, uint256[] memory amounts, - address to + address to, + uint256 maxBurnAmount ) external preventReentrant returns (uint256 paid) { require(tokenIds.length == amounts.length, "PARAM_INVALID"); uint256 avaliableNFTOutAmount = getAvaliableNFTOutAmount(); @@ -95,18 +96,21 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { } require(totalAmount <= avaliableNFTOutAmount, "EXCEED_OUT_AMOUNT"); (uint256 rawPay, ) = _queryNFTTargetOut(originTotalNftAmount - totalAmount, originTotalNftAmount); - paid = IFilterAdmin(_OWNER_).burnFragFrom(to, rawPay); + paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); + require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); - emit TargetOutOrder(to, paid); + emit TargetOutOrder(msg.sender, paid); } - function ERC1155RandomOut(uint256 amount, address to) + function ERC1155RandomOut(uint256 amount, address to, uint256 maxBurnAmount) external preventReentrant returns (uint256 paid) { (uint256 rawPay, ) = queryNFTRandomOut(amount); - paid = IFilterAdmin(_OWNER_).burnFragFrom(to, rawPay); + paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); + require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); + for (uint256 i = 0; i < amount; i++) { uint256 randomNum = _getRandomNum() % _TOTAL_NFT_AMOUNT_; uint256 sum; @@ -121,7 +125,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { } } - emit RandomOutOrder(to, paid); + emit RandomOutOrder(msg.sender, paid); } // ============ Transfer ============= diff --git a/contracts/NFTPool/impl/FilterERC721V1.sol b/contracts/NFTPool/impl/FilterERC721V1.sol index d08089a..894bc71 100644 --- a/contracts/NFTPool/impl/FilterERC721V1.sol +++ b/contracts/NFTPool/impl/FilterERC721V1.sol @@ -85,13 +85,15 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { emit NftInOrder(to, received); } - function ERC721TargetOut(uint256[] memory tokenIds, address to) + function ERC721TargetOut(uint256[] memory tokenIds, address to, uint256 maxBurnAmount) external preventReentrant returns (uint256 paid) { (uint256 rawPay, ) = queryNFTTargetOut(tokenIds.length); - paid = IFilterAdmin(_OWNER_).burnFragFrom(to, rawPay); + paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); + require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); + for (uint256 i = 0; i < tokenIds.length; i++) { _transferOutERC721(to, tokenIds[i]); @@ -99,16 +101,17 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { } _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; - emit TargetOutOrder(to, paid); + emit TargetOutOrder(msg.sender, paid); } - function ERC721RandomOut(uint256 amount, address to) + function ERC721RandomOut(uint256 amount, address to, uint256 maxBurnAmount) external preventReentrant returns (uint256 paid) { (uint256 rawPay, ) = queryNFTRandomOut(amount); - paid = IFilterAdmin(_OWNER_).burnFragFrom(to, rawPay); + paid = IFilterAdmin(_OWNER_).burnFragFrom(msg.sender, rawPay); + require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); for (uint256 i = 0; i < amount; i++) { uint256 index = _getRandomNum() % _NFT_IDS_.length; uint256 tokenId = _NFT_IDS_[index]; @@ -117,7 +120,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { } _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; - emit RandomOutOrder(to, paid); + emit RandomOutOrder(msg.sender, paid); } // ============ Transfer ============= diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index 9d822de..c19cbaf 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -35,10 +35,6 @@ contract DODONFTPoolProxy is InitializableOwnable, ReentrancyGuard { event SetFilterTemplate(uint256 idx, address filterTemplate); event Erc721In(address filter, address to, uint256 received); event Erc1155In(address filter, address to, uint256 received); - event Erc721TargetOut(address filter, address to, uint256 paid); - event Erc1155TargetOut(address filter, address to, uint256 paid); - event Erc721RandomOut(address filter, address to, uint256 paid); - event Erc1155RandomOut(address filter, address to, uint256 paid); event CreateLiteNFTPool(address newFilterAdmin, address filterAdminOwner); event CreateNFTPool(address newFilterAdmin, address filterAdminOwner, address filter); @@ -83,30 +79,6 @@ contract DODONFTPoolProxy is InitializableOwnable, ReentrancyGuard { emit Erc721In(filter, to, received); } - function erc721TargetOut( - address filter, - uint256[] memory tokenIds, - // address to, - uint256 maxBurnAmount - ) external { - uint256 paid = IFilter(filter).ERC721TargetOut(tokenIds, msg.sender); - require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); - - emit Erc721TargetOut(filter, msg.sender, paid); - } - - function erc721RandomOut( - address filter, - uint256 amount, - // address to, - uint256 maxBurnAmount - ) external { - uint256 paid = IFilter(filter).ERC721RandomOut(amount, msg.sender); - require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); - - emit Erc721RandomOut(filter, msg.sender, paid); - } - // ================== ERC1155 In and Out =================== function erc1155In( address filter, @@ -126,32 +98,6 @@ contract DODONFTPoolProxy is InitializableOwnable, ReentrancyGuard { emit Erc1155In(filter, to, received); } - function erc1155TargetOut( - address filter, - uint256[] memory tokenIds, - uint256[] memory amounts, - // address to, - uint256 maxBurnAmount - ) external { - uint256 paid = IFilter(filter).ERC1155TargetOut(tokenIds, amounts, msg.sender); - require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); - - emit Erc1155TargetOut(filter, msg.sender, paid); - } - - function erc1155RandomOut( - address filter, - uint256 amount, - // address to, - uint256 maxBurnAmount - ) external { - uint256 paid = IFilter(filter).ERC1155RandomOut(amount, msg.sender); - require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); - - emit Erc1155RandomOut(filter, msg.sender, paid); - } - - // ================== Create NFTPool =================== function createLiteNFTPool( address filterAdminOwner, diff --git a/test/NFTPool/erc1155NftPool.test.ts b/test/NFTPool/erc1155NftPool.test.ts index b0fb6ad..1320dc3 100644 --- a/test/NFTPool/erc1155NftPool.test.ts +++ b/test/NFTPool/erc1155NftPool.test.ts @@ -149,14 +149,14 @@ describe("ERC1155-NFTPool", () => { assert.equal(beforeAmount, 2) //maker targetout - var tx = await logGas(ctx.DODONFTPoolProxy.methods.erc1155TargetOut( - filter, + var tx = await logGas(filterInstance.methods.ERC1155TargetOut( [0, 1, 3], [2, 1, 1], + maker, MAX_UINT256, ), ctx.sendParam(maker), "Erc1155TargetOut"); - var paid = tx.events['Erc1155TargetOut'].returnValues['paid'] + var paid = tx.events['TargetOutOrder'].returnValues['paidAmount'] assert.equal(paid, "3673527453990000000"); var maxNftOutAmount = await filterInstance.methods.getAvaliableNFTOutAmount().call(); @@ -176,13 +176,13 @@ describe("ERC1155-NFTPool", () => { var filterInstance = contracts.getContractWithAddress(contracts.FILTER_ERC1155_V1, filter); //maker randomOut - var tx = await logGas(ctx.DODONFTPoolProxy.methods.erc1155RandomOut( - filter, + var tx = await logGas(filterInstance.methods.ERC1155RandomOut( 3, + maker, MAX_UINT256, ), ctx.sendParam(maker), "Erc1155RandomOut"); - var paid = tx.events['Erc1155RandomOut'].returnValues['paid'] + var paid = tx.events['RandomOutOrder'].returnValues['paidAmount'] assert.equal(paid, "1302665521995000000"); var maxNftOutAmount = await filterInstance.methods.getAvaliableNFTOutAmount().call(); diff --git a/test/NFTPool/erc721NftPool.test.ts b/test/NFTPool/erc721NftPool.test.ts index 74244e7..f5dbc32 100644 --- a/test/NFTPool/erc721NftPool.test.ts +++ b/test/NFTPool/erc721NftPool.test.ts @@ -164,13 +164,13 @@ describe("ERC721-NFTPool", () => { assert.equal(beforeOwner, filter) //maker targetout - var tx = await logGas(ctx.DODONFTPoolProxy.methods.erc721TargetOut( - filter, + var tx = await logGas(filterInstance.methods.ERC721TargetOut( [0, 1, 3], + maker, MAX_UINT256, ), ctx.sendParam(maker), "Erc721TargetOut"); - var paid = tx.events['Erc721TargetOut'].returnValues['paid'] + var paid = tx.events['TargetOutOrder'].returnValues['paidAmount'] assert.equal(paid, "4412151000000000000"); @@ -193,13 +193,13 @@ describe("ERC721-NFTPool", () => { var filterInstance = contracts.getContractWithAddress(contracts.FILTER_ERC721_V1, filter); //maker randomOut - var tx = await logGas(ctx.DODONFTPoolProxy.methods.erc721RandomOut( - filter, + var tx = await logGas(filterInstance.methods.ERC721RandomOut( 3, + maker, MAX_UINT256, ), ctx.sendParam(maker), "Erc721RandomOut"); - var paid = tx.events['Erc721RandomOut'].returnValues['paid'] + var paid = tx.events['RandomOutOrder'].returnValues['paidAmount'] assert.equal(paid, "2206075500000000000"); var maxNftOutAmount = await filterInstance.methods.getAvaliableNFTOutAmount().call(); diff --git a/truffle-test.sh b/truffle-test.sh index 4df5dc4..ab848ec 100644 --- a/truffle-test.sh +++ b/truffle-test.sh @@ -1,5 +1,5 @@ #!/bin/bash -truffle compile --all +# truffle compile --all if [ "$1"x = "proxy-dpp"x ] then From e810adaffe4d48348a2dbd7ce60381674a0a55ea Mon Sep 17 00:00:00 2001 From: owen05 Date: Tue, 28 Sep 2021 14:10:54 +0800 Subject: [PATCH 36/42] fix --- config/rinkeby-config.js | 8 ++++---- contracts/SmartRoute/proxies/DODONFTPoolProxy.sol | 12 ++++++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/config/rinkeby-config.js b/config/rinkeby-config.js index 16e78c1..c2e7687 100644 --- a/config/rinkeby-config.js +++ b/config/rinkeby-config.js @@ -102,11 +102,11 @@ module.exports = { // DropsERC1155: "0x3a8EcF30428bd4e33Cd7011533DFd596F7705c8F", //=================== NFTPool ================== - DODONFTApprove: "0x9CA2A5FB3771d824FAc3a307ff6B0fbb992C00Bc", + DODONFTApprove: "0x40F3bBe2f8C5F2E5f4adFf7d51f652F2B9F77315", FilterAdmin: "0x729f7f44bf64Ce814716b6261e267DbE6cdf021c", - FilterERC721V1: "0x72d220cE168C4f361dD4deE5D826a01AD8598f6C", - FilterERC1155V1: "0x80930Cb1849F7D42531506fF45E66724338A821b", - DODONFTPoolProxy: "0x888B9d4C25664F8B1BF744e8F4397d1429314155", + FilterERC721V1: "0x47E2C563cDCd7F36B4E77cc33a6A5c152663f915", + FilterERC1155V1: "0x55e2e1fe50FfaBd4fE3712Bd1aBfc9307a44c7F4", + DODONFTPoolProxy: "0xb23150F62D3089BB3Fa181ee6185324cdECE1942", NFTPoolController: "0xf5d24499dD76C3791ee6D19aa206f55b72270415" } diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index c19cbaf..351e518 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -30,6 +30,7 @@ contract DODONFTPoolProxy is InitializableOwnable, ReentrancyGuard { address public immutable _DODO_NFT_APPROVE_; address public immutable _DODO_APPROVE_; + mapping (address => bool) public isWhiteListed; // ============ Event ============== event SetFilterTemplate(uint256 idx, address filterTemplate); @@ -44,6 +45,7 @@ contract DODONFTPoolProxy is InitializableOwnable, ReentrancyGuard { event ChangeMaintainer(address newMaintainer); event ChangeContoller(address newController); event ChangeFilterAdminTemplate(address newFilterAdminTemplate); + event ChangeWhiteList(address contractAddr, bool isWhiteListed); constructor( address cloneFactory, @@ -177,6 +179,9 @@ contract DODONFTPoolProxy is InitializableOwnable, ReentrancyGuard { uint256[] memory spreadIds ) public returns(address newFilterV1) { newFilterV1 = ICloneFactory(_CLONE_FACTORY_).clone(_FILTER_TEMPLATES_[key]); + + emit CreateFilterV1(filterAdmin, newFilterV1, nftCollection, key); + IFilter(newFilterV1).init( filterAdmin, nftCollection, @@ -186,8 +191,6 @@ contract DODONFTPoolProxy is InitializableOwnable, ReentrancyGuard { priceRules, spreadIds ); - - emit CreateFilterV1(filterAdmin, newFilterV1, nftCollection, key); } @@ -213,6 +216,7 @@ contract DODONFTPoolProxy is InitializableOwnable, ReentrancyGuard { _generalApproveMax(filterAdmin, _DODO_APPROVE_, receivedFragAmount); + require(isWhiteListed[dodoProxy], "Not Whitelist Proxy Contract"); (bool success, ) = dodoProxy.call(dodoSwapData); require(success, "API_SWAP_FAILED"); @@ -245,6 +249,10 @@ contract DODONFTPoolProxy is InitializableOwnable, ReentrancyGuard { emit SetFilterTemplate(idx, newFilterTemplate); } + function changeWhiteList(address contractAddr, bool isWhiteListed) external onlyOwner { + isWhiteListed[contractAddr] = isWhiteListed; + emit ChangeWhiteList(contractAddr, isWhiteListed); + } //======================= Internal ===================== function _generalApproveMax( From 7b526b8208ec172b4ccfabbf22b39edcbe284561 Mon Sep 17 00:00:00 2001 From: owen05 Date: Tue, 28 Sep 2021 14:38:53 +0800 Subject: [PATCH 37/42] fix --- contracts/NFTPool/impl/FilterERC1155V1.sol | 10 +++++----- contracts/SmartRoute/proxies/DODONFTPoolProxy.sol | 8 ++++---- truffle-test.sh | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index 642026c..87943de 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -68,7 +68,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { uint256 tokenId = tokenIds[i]; require(isNFTIDValid(tokenId), "NFT_ID_NOT_SUPPORT"); uint256 inAmount = _maintainERC1155In(tokenId); - totalAmount += inAmount; + totalAmount = totalAmount.add(inAmount); emit NftIn(tokenId, inAmount); } require(totalAmount <= avaliableNFTInAmount, "EXCEDD_IN_AMOUNT"); @@ -90,7 +90,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { uint256 totalAmount = 0; for (uint256 i = 0; i < tokenIds.length; i++) { - totalAmount += amounts[i]; + totalAmount = totalAmount.add(amounts[i]); _transferOutERC1155(to, tokenIds[i], amounts[i]); emit TargetOut(tokenIds[i], amounts[i]); } @@ -116,7 +116,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { uint256 sum; for (uint256 j = 0; j < _NFT_IDS_.length; j++) { uint256 tokenId = _NFT_IDS_[j]; - sum += _NFT_RESERVE_[tokenId]; + sum = sum.add(_NFT_RESERVE_[tokenId]); if (sum >= randomNum) { _transferOutERC1155(to, tokenId, 1); emit RandomOut(tokenId, 1); @@ -190,7 +190,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { uint256 currentAmount = IERC1155(_NFT_COLLECTION_).balanceOf(address(this), tokenId); uint256 outAmount = _NFT_RESERVE_[tokenId].sub(currentAmount); _NFT_RESERVE_[tokenId] = currentAmount; - _TOTAL_NFT_AMOUNT_ -= outAmount; + _TOTAL_NFT_AMOUNT_ = _TOTAL_NFT_AMOUNT_.sub(outAmount); if (currentAmount == 0) { uint256 index = _TOKENID_IDX_[tokenId] - 1; if(index != _NFT_IDS_.length - 1) { @@ -211,7 +211,7 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { _TOKENID_IDX_[tokenId] = _NFT_IDS_.length; } _NFT_RESERVE_[tokenId] = currentAmount; - _TOTAL_NFT_AMOUNT_ += inAmount; + _TOTAL_NFT_AMOUNT_ = _TOTAL_NFT_AMOUNT_.add(inAmount); } // ============ Support ============ diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index 351e518..ad47e13 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -45,7 +45,7 @@ contract DODONFTPoolProxy is InitializableOwnable, ReentrancyGuard { event ChangeMaintainer(address newMaintainer); event ChangeContoller(address newController); event ChangeFilterAdminTemplate(address newFilterAdminTemplate); - event ChangeWhiteList(address contractAddr, bool isWhiteListed); + event ChangeWhiteList(address contractAddr, bool isAllowed); constructor( address cloneFactory, @@ -249,9 +249,9 @@ contract DODONFTPoolProxy is InitializableOwnable, ReentrancyGuard { emit SetFilterTemplate(idx, newFilterTemplate); } - function changeWhiteList(address contractAddr, bool isWhiteListed) external onlyOwner { - isWhiteListed[contractAddr] = isWhiteListed; - emit ChangeWhiteList(contractAddr, isWhiteListed); + function changeWhiteList(address contractAddr, bool isAllowed) external onlyOwner { + isWhiteListed[contractAddr] = isAllowed; + emit ChangeWhiteList(contractAddr, isAllowed); } //======================= Internal ===================== diff --git a/truffle-test.sh b/truffle-test.sh index ab848ec..4df5dc4 100644 --- a/truffle-test.sh +++ b/truffle-test.sh @@ -1,5 +1,5 @@ #!/bin/bash -# truffle compile --all +truffle compile --all if [ "$1"x = "proxy-dpp"x ] then From bbfe01231f3e0400c1545245c89ad82fcc15510a Mon Sep 17 00:00:00 2001 From: owen05 Date: Sat, 9 Oct 2021 10:30:43 +0800 Subject: [PATCH 38/42] fix --- contracts/Factory/DPPFactory.sol | 38 +++++++++++++++++++++++++++++++- migrations/2_deploy_v2.js | 5 +++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/contracts/Factory/DPPFactory.sol b/contracts/Factory/DPPFactory.sol index 912a234..51912e2 100644 --- a/contracts/Factory/DPPFactory.sol +++ b/contracts/Factory/DPPFactory.sol @@ -30,6 +30,8 @@ contract DPPFactory is InitializableOwnable { address public _DPP_TEMPLATE_; address public _DPP_ADMIN_TEMPLATE_; + mapping (address => bool) public isAdminListed; + // ============ Registry ============ // base -> quote -> DPP address list @@ -46,9 +48,11 @@ contract DPPFactory is InitializableOwnable { address dpp ); - event RemoveDPP(address dpp); + event addAdmin(address admin); + event removeAdmin(address admin); + constructor( address cloneFactory, address dppTemplate, @@ -81,6 +85,7 @@ contract DPPFactory is InitializableOwnable { uint256 i, bool isOpenTwap ) external { + require(isAdminListed[msg.sender], "ACCESS_DENIED"); { address _dppAddress = dppAddress; address adminModel = _createDPPAdminModel( @@ -131,6 +136,16 @@ contract DPPFactory is InitializableOwnable { _DPP_TEMPLATE_ = _newDPPTemplate; } + function addAdminList (address contractAddr) external onlyOwner { + isAdminListed[contractAddr] = true; + emit addAdmin(contractAddr); + } + + function removeAdminList (address contractAddr) external onlyOwner { + isAdminListed[contractAddr] = false; + emit removeAdmin(contractAddr); + } + function addPoolByAdmin( address creator, address baseToken, @@ -142,6 +157,27 @@ contract DPPFactory is InitializableOwnable { emit NewDPP(baseToken, quoteToken, creator, pool); } + function batchAddPoolByAdmin( + address[] memory creators, + address[] memory baseTokens, + address[] memory quoteTokens, + address[] memory pools + ) external onlyOwner { + require(creators.length == baseTokens.length,"PARAMS_INVALID"); + require(creators.length == quoteTokens.length,"PARAMS_INVALID"); + require(creators.length == pools.length,"PARAMS_INVALID"); + for(uint256 i = 0; i < creators.length; i++) { + address creator = creators[i]; + address baseToken = baseTokens[i]; + address quoteToken = quoteTokens[i]; + address pool = pools[i]; + + _REGISTRY_[baseToken][quoteToken].push(pool); + _USER_REGISTRY_[creator].push(pool); + emit NewDPP(baseToken, quoteToken, creator, pool); + } + } + function removePoolByAdmin( address creator, address baseToken, diff --git a/migrations/2_deploy_v2.js b/migrations/2_deploy_v2.js index 4d89033..b5aa361 100644 --- a/migrations/2_deploy_v2.js +++ b/migrations/2_deploy_v2.js @@ -492,6 +492,11 @@ module.exports = async (deployer, network, accounts) => { const dodoMineV3RegistryInstance = await DODOMineV3Registry.at(DODOMineV3RegistryAddress); var tx = await dodoMineV3RegistryInstance.addAdminList(DODOMineV3ProxyAddress); logger.log("DODOMineV3RegistryAddress Init tx: ", tx.tx); + + //DPPFactory add DODProxy as admin + const dppFactoryInstance = await DPPFactory.at(DppFactoryAddress); + var tx = await dppFactoryInstance.addAdminList(DODOV2ProxyAddress); + logger.log("DPPFactory Init tx: ", tx.tx); } } From 484cadbe95c641634f0291cfe928185bb8833a7b Mon Sep 17 00:00:00 2001 From: owen05 Date: Mon, 11 Oct 2021 15:47:30 +0800 Subject: [PATCH 39/42] ing --- config/boba-test-config.js | 68 ++++++++ configAdapter.js | 6 + contracts/SmartRoute/DODOV2Proxy02.sol | 156 +++++++++--------- contracts/SmartRoute/intf/IDODOV2Proxy01.sol | 40 ++--- contracts/SmartRoute/proxies/DODODppProxy.sol | 121 ++++++++++++-- migrations/2_deploy_v2.js | 2 +- truffle-config.js | 19 +-- 7 files changed, 282 insertions(+), 130 deletions(-) create mode 100644 config/boba-test-config.js diff --git a/config/boba-test-config.js b/config/boba-test-config.js new file mode 100644 index 0000000..3077b92 --- /dev/null +++ b/config/boba-test-config.js @@ -0,0 +1,68 @@ +module.exports = { + BOBA_TEST_CONFIG: { + //TOKEN + WETH: "", + CHI: "0x0000000000000000000000000000000000000000", + DODO: "", + + //Helper + //MultiCall: "", + //DODOSwapCalcHelper: "", + //DODOZoo: "", + //DODO: "", + //ERC20Helper: "", + DODOSellHelper: "", + DODOCalleeHelper: "", + DODOV1PmmHelper: "", + DODOV2RouteHelper: "", + CurveSample: "", + + //Template + CloneFactory: "", + FeeRateModel: "", + //FeeRateDIP3: "", + PermissionManager: "", + DVM: "", + DPP: "", + DSP: "", + DPPAdmin: "", + CP: "", + //ERC20MineV2: "", + ERC20MineV3: "", + + ERC20: "", + CustomERC20: "", + + + //Factory + DVMFactory: "", + DPPFactory: "", + DSPFactory: "", + UpCpFactory: "", + CrowdPoolingFactory: "", + ERC20V2Factory: "", + //DODOMineV2Factory: "", + DODOMineV3Registry: "", + + //Approve + DODOApprove: "", + DODOApproveProxy: "", + + //Adapter + DODOV1Adapter: "", + DODOV2Adapter: "", + UniAdapter: "", + CurveAdapter: "", + + //Proxy + DODOV2Proxy: "", + DSPProxy: "", + CpProxy: "", + RouteProxy: "", + DODOMineV3Proxy: "", + + //Account + multiSigAddress: "0x7e83d9d94837eE82F0cc18a691da6f42F03F1d86", + defaultMaintainer: "0x7e83d9d94837eE82F0cc18a691da6f42F03F1d86", + } +} \ No newline at end of file diff --git a/configAdapter.js b/configAdapter.js index 0df241f..ed6698b 100644 --- a/configAdapter.js +++ b/configAdapter.js @@ -8,6 +8,7 @@ const { RINKEBY_CONFIG } = require("./config/rinkeby-config"); const { OK_CONFIG } = require("./config/ok-config"); const { NEON_TEST_CONFIG } = require("./config/neon-test-config"); const { MOONRIVER_CONFIG } = require("./config/moonriver-config"); +const { BOBA_TEST_CONFIG } = require("./config/boba-test-config"); exports.GetConfig = function (network, accounts) { var CONFIG = {} @@ -49,6 +50,11 @@ exports.GetConfig = function (network, accounts) { CONFIG.multiSigAddress = accounts[0] CONFIG.defaultMaintainer = accounts[0] break; + case "boba_test": + CONFIG = BOBA_TEST_CONFIG + CONFIG.multiSigAddress = accounts[0] + CONFIG.defaultMaintainer = accounts[0] + break; } return CONFIG } diff --git a/contracts/SmartRoute/DODOV2Proxy02.sol b/contracts/SmartRoute/DODOV2Proxy02.sol index 8aebf26..6c0d697 100644 --- a/contracts/SmartRoute/DODOV2Proxy02.sol +++ b/contracts/SmartRoute/DODOV2Proxy02.sol @@ -210,90 +210,90 @@ contract DODOV2Proxy02 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable // ============ DPP Functions (create & reset) ============ - function createDODOPrivatePool( - address baseToken, - address quoteToken, - uint256 baseInAmount, - uint256 quoteInAmount, - uint256 lpFeeRate, - uint256 i, - uint256 k, - bool isOpenTwap, - uint256 deadLine - ) - external - override - payable - preventReentrant - judgeExpired(deadLine) - returns (address newPrivatePool) - { - newPrivatePool = IDODOV2(_DPP_FACTORY_).createDODOPrivatePool(); + // function createDODOPrivatePool( + // address baseToken, + // address quoteToken, + // uint256 baseInAmount, + // uint256 quoteInAmount, + // uint256 lpFeeRate, + // uint256 i, + // uint256 k, + // bool isOpenTwap, + // uint256 deadLine + // ) + // external + // override + // payable + // preventReentrant + // judgeExpired(deadLine) + // returns (address newPrivatePool) + // { + // newPrivatePool = IDODOV2(_DPP_FACTORY_).createDODOPrivatePool(); - address _baseToken = baseToken; - address _quoteToken = quoteToken; - _deposit(msg.sender, newPrivatePool, _baseToken, baseInAmount, _baseToken == _ETH_ADDRESS_); - _deposit( - msg.sender, - newPrivatePool, - _quoteToken, - quoteInAmount, - _quoteToken == _ETH_ADDRESS_ - ); + // address _baseToken = baseToken; + // address _quoteToken = quoteToken; + // _deposit(msg.sender, newPrivatePool, _baseToken, baseInAmount, _baseToken == _ETH_ADDRESS_); + // _deposit( + // msg.sender, + // newPrivatePool, + // _quoteToken, + // quoteInAmount, + // _quoteToken == _ETH_ADDRESS_ + // ); - if (_baseToken == _ETH_ADDRESS_) _baseToken = _WETH_; - if (_quoteToken == _ETH_ADDRESS_) _quoteToken = _WETH_; + // if (_baseToken == _ETH_ADDRESS_) _baseToken = _WETH_; + // if (_quoteToken == _ETH_ADDRESS_) _quoteToken = _WETH_; - IDODOV2(_DPP_FACTORY_).initDODOPrivatePool( - newPrivatePool, - msg.sender, - _baseToken, - _quoteToken, - lpFeeRate, - k, - i, - isOpenTwap - ); - } + // IDODOV2(_DPP_FACTORY_).initDODOPrivatePool( + // newPrivatePool, + // msg.sender, + // _baseToken, + // _quoteToken, + // lpFeeRate, + // k, + // i, + // isOpenTwap + // ); + // } - function resetDODOPrivatePool( - address dppAddress, - uint256[] memory paramList, //0 - newLpFeeRate, 1 - newI, 2 - newK - uint256[] memory amountList, //0 - baseInAmount, 1 - quoteInAmount, 2 - baseOutAmount, 3- quoteOutAmount - uint8 flag, // 0 - ERC20, 1 - baseInETH, 2 - quoteInETH, 3 - baseOutETH, 4 - quoteOutETH - uint256 minBaseReserve, - uint256 minQuoteReserve, - uint256 deadLine - ) external override payable preventReentrant judgeExpired(deadLine) { - _deposit( - msg.sender, - dppAddress, - IDODOV2(dppAddress)._BASE_TOKEN_(), - amountList[0], - flag == 1 - ); - _deposit( - msg.sender, - dppAddress, - IDODOV2(dppAddress)._QUOTE_TOKEN_(), - amountList[1], - flag == 2 - ); + // function resetDODOPrivatePool( + // address dppAddress, + // uint256[] memory paramList, //0 - newLpFeeRate, 1 - newI, 2 - newK + // uint256[] memory amountList, //0 - baseInAmount, 1 - quoteInAmount, 2 - baseOutAmount, 3- quoteOutAmount + // uint8 flag, // 0 - ERC20, 1 - baseInETH, 2 - quoteInETH, 3 - baseOutETH, 4 - quoteOutETH + // uint256 minBaseReserve, + // uint256 minQuoteReserve, + // uint256 deadLine + // ) external override payable preventReentrant judgeExpired(deadLine) { + // _deposit( + // msg.sender, + // dppAddress, + // IDODOV2(dppAddress)._BASE_TOKEN_(), + // amountList[0], + // flag == 1 + // ); + // _deposit( + // msg.sender, + // dppAddress, + // IDODOV2(dppAddress)._QUOTE_TOKEN_(), + // amountList[1], + // flag == 2 + // ); - require(IDODOV2(IDODOV2(dppAddress)._OWNER_()).reset( - msg.sender, - paramList[0], - paramList[1], - paramList[2], - amountList[2], - amountList[3], - minBaseReserve, - minQuoteReserve - ), "Reset Failed"); + // require(IDODOV2(IDODOV2(dppAddress)._OWNER_()).reset( + // msg.sender, + // paramList[0], + // paramList[1], + // paramList[2], + // amountList[2], + // amountList[3], + // minBaseReserve, + // minQuoteReserve + // ), "Reset Failed"); - _withdraw(msg.sender, IDODOV2(dppAddress)._BASE_TOKEN_(), amountList[2], flag == 3); - _withdraw(msg.sender, IDODOV2(dppAddress)._QUOTE_TOKEN_(), amountList[3], flag == 4); - } + // _withdraw(msg.sender, IDODOV2(dppAddress)._BASE_TOKEN_(), amountList[2], flag == 3); + // _withdraw(msg.sender, IDODOV2(dppAddress)._QUOTE_TOKEN_(), amountList[3], flag == 4); + // } // ============ Swap ============ diff --git a/contracts/SmartRoute/intf/IDODOV2Proxy01.sol b/contracts/SmartRoute/intf/IDODOV2Proxy01.sol index 5821138..806b34f 100644 --- a/contracts/SmartRoute/intf/IDODOV2Proxy01.sol +++ b/contracts/SmartRoute/intf/IDODOV2Proxy01.sol @@ -69,27 +69,27 @@ interface IDODOV2Proxy01 { uint256 quoteAdjustedInAmount ); - function createDODOPrivatePool( - address baseToken, - address quoteToken, - uint256 baseInAmount, - uint256 quoteInAmount, - uint256 lpFeeRate, - uint256 i, - uint256 k, - bool isOpenTwap, - uint256 deadLine - ) external payable returns (address newPrivatePool); + // function createDODOPrivatePool( + // address baseToken, + // address quoteToken, + // uint256 baseInAmount, + // uint256 quoteInAmount, + // uint256 lpFeeRate, + // uint256 i, + // uint256 k, + // bool isOpenTwap, + // uint256 deadLine + // ) external payable returns (address newPrivatePool); - function resetDODOPrivatePool( - address dppAddress, - uint256[] memory paramList, //0 - newLpFeeRate, 1 - newI, 2 - newK - uint256[] memory amountList, //0 - baseInAmount, 1 - quoteInAmount, 2 - baseOutAmount, 3 - quoteOutAmount - uint8 flag, // 0 - ERC20, 1 - baseInETH, 2 - quoteInETH, 3 - baseOutETH, 4 - quoteOutETH - uint256 minBaseReserve, - uint256 minQuoteReserve, - uint256 deadLine - ) external payable; + // function resetDODOPrivatePool( + // address dppAddress, + // uint256[] memory paramList, //0 - newLpFeeRate, 1 - newI, 2 - newK + // uint256[] memory amountList, //0 - baseInAmount, 1 - quoteInAmount, 2 - baseOutAmount, 3 - quoteOutAmount + // uint8 flag, // 0 - ERC20, 1 - baseInETH, 2 - quoteInETH, 3 - baseOutETH, 4 - quoteOutETH + // uint256 minBaseReserve, + // uint256 minQuoteReserve, + // uint256 deadLine + // ) external payable; function bid( diff --git a/contracts/SmartRoute/proxies/DODODppProxy.sol b/contracts/SmartRoute/proxies/DODODppProxy.sol index f839093..6d9a85c 100644 --- a/contracts/SmartRoute/proxies/DODODppProxy.sol +++ b/contracts/SmartRoute/proxies/DODODppProxy.sol @@ -14,17 +14,17 @@ import {SafeMath} from "../../lib/SafeMath.sol"; import {SafeERC20} from "../../lib/SafeERC20.sol"; import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; -interface IDPPOracle { - function reset( - address assetTo, - uint256 newLpFeeRate, - uint256 newK, - uint256 baseOutAmount, - uint256 quoteOutAmount, - uint256 minBaseReserve, - uint256 minQuoteReserve - ) external returns (bool); -} +// interface IDPPOracle { +// function reset( +// address assetTo, +// uint256 newLpFeeRate, +// uint256 newK, +// uint256 baseOutAmount, +// uint256 quoteOutAmount, +// uint256 minBaseReserve, +// uint256 minQuoteReserve +// ) external returns (bool); +// } /** @@ -42,6 +42,7 @@ contract DODODppProxy is ReentrancyGuard { address constant _ETH_ADDRESS_ = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; address public immutable _WETH_; address public immutable _DODO_APPROVE_PROXY_; + address public immutable _DPP_FACTORY_; // ============ Modifiers ============ @@ -56,18 +57,65 @@ contract DODODppProxy is ReentrancyGuard { constructor( address payable weth, - address dodoApproveProxy + address dodoApproveProxy, + address dppFactory ) public { _WETH_ = weth; _DODO_APPROVE_PROXY_ = dodoApproveProxy; + _DPP_FACTORY_ = dppFactory; + } + + + function createDODOPrivatePool( + address baseToken, + address quoteToken, + uint256 baseInAmount, + uint256 quoteInAmount, + uint256 lpFeeRate, + uint256 i, + uint256 k, + bool isOpenTwap, + uint256 deadLine + ) + external + payable + preventReentrant + judgeExpired(deadLine) + returns (address newPrivatePool) + { + newPrivatePool = IDODOV2(_DPP_FACTORY_).createDODOPrivatePool(); + + address _baseToken = baseToken; + address _quoteToken = quoteToken; + _deposit(msg.sender, newPrivatePool, _baseToken, baseInAmount, _baseToken == _ETH_ADDRESS_); + _deposit( + msg.sender, + newPrivatePool, + _quoteToken, + quoteInAmount, + _quoteToken == _ETH_ADDRESS_ + ); + + if (_baseToken == _ETH_ADDRESS_) _baseToken = _WETH_; + if (_quoteToken == _ETH_ADDRESS_) _quoteToken = _WETH_; + + IDODOV2(_DPP_FACTORY_).initDODOPrivatePool( + newPrivatePool, + msg.sender, + _baseToken, + _quoteToken, + lpFeeRate, + k, + i, + isOpenTwap + ); } - // DPPOracle function resetDODOPrivatePool( address dppAddress, - uint256[] memory paramList, //0 - newLpFeeRate, 1 - newK + uint256[] memory paramList, //0 - newLpFeeRate, 1 - newI, 2 - newK uint256[] memory amountList, //0 - baseInAmount, 1 - quoteInAmount, 2 - baseOutAmount, 3- quoteOutAmount - uint8 flag, //0 - ERC20, 1 - baseInETH, 2 - quoteInETH, 3 - baseOutETH, 4 - quoteOutETH + uint8 flag, // 0 - ERC20, 1 - baseInETH, 2 - quoteInETH, 3 - baseOutETH, 4 - quoteOutETH uint256 minBaseReserve, uint256 minQuoteReserve, uint256 deadLine @@ -87,10 +135,11 @@ contract DODODppProxy is ReentrancyGuard { flag == 2 ); - require(IDPPOracle(IDODOV2(dppAddress)._OWNER_()).reset( + require(IDODOV2(IDODOV2(dppAddress)._OWNER_()).reset( msg.sender, paramList[0], paramList[1], + paramList[2], amountList[2], amountList[3], minBaseReserve, @@ -101,6 +150,46 @@ contract DODODppProxy is ReentrancyGuard { _withdraw(msg.sender, IDODOV2(dppAddress)._QUOTE_TOKEN_(), amountList[3], flag == 4); } + + + // DPPOracle + // function resetDODOPrivatePool( + // address dppAddress, + // uint256[] memory paramList, //0 - newLpFeeRate, 1 - newK + // uint256[] memory amountList, //0 - baseInAmount, 1 - quoteInAmount, 2 - baseOutAmount, 3- quoteOutAmount + // uint8 flag, //0 - ERC20, 1 - baseInETH, 2 - quoteInETH, 3 - baseOutETH, 4 - quoteOutETH + // uint256 minBaseReserve, + // uint256 minQuoteReserve, + // uint256 deadLine + // ) external payable preventReentrant judgeExpired(deadLine) { + // _deposit( + // msg.sender, + // dppAddress, + // IDODOV2(dppAddress)._BASE_TOKEN_(), + // amountList[0], + // flag == 1 + // ); + // _deposit( + // msg.sender, + // dppAddress, + // IDODOV2(dppAddress)._QUOTE_TOKEN_(), + // amountList[1], + // flag == 2 + // ); + + // require(IDPPOracle(IDODOV2(dppAddress)._OWNER_()).reset( + // msg.sender, + // paramList[0], + // paramList[1], + // amountList[2], + // amountList[3], + // minBaseReserve, + // minQuoteReserve + // ), "Reset Failed"); + + // _withdraw(msg.sender, IDODOV2(dppAddress)._BASE_TOKEN_(), amountList[2], flag == 3); + // _withdraw(msg.sender, IDODOV2(dppAddress)._QUOTE_TOKEN_(), amountList[3], flag == 4); + // } //====================== internal ======================= diff --git a/migrations/2_deploy_v2.js b/migrations/2_deploy_v2.js index b5aa361..7e5bd17 100644 --- a/migrations/2_deploy_v2.js +++ b/migrations/2_deploy_v2.js @@ -461,7 +461,7 @@ module.exports = async (deployer, network, accounts) => { } - if (network == 'kovan' || network == 'rinkeby') { + if (network == 'kovan' || network == 'rinkeby' ||network == "boba_test") { var tx; //ApproveProxy init以及添加ProxyList const DODOApproveProxyInstance = await DODOApproveProxy.at(DODOApproveProxyAddress); diff --git a/truffle-config.js b/truffle-config.js index 2e2dbc7..ab31a09 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -40,7 +40,7 @@ module.exports = { */ deploySwitch: { DEPLOY_V1: false, - DEPLOY_V2: false, + DEPLOY_V2: true, MOCK_TOKEN: false, MOCK_V2_POOL: false, vDODOToken: false, @@ -166,12 +166,12 @@ module.exports = { skipDryRun: true }, - omgTest: { + boba_test: { networkCheckTimeout: 100000, provider: () => { - return new HDWalletProvider(privKey, 'https://rinkeby.omgx.network') + return new HDWalletProvider(privKey, 'https://rinkeby-v2.boba.network') }, - network_id: 28, + network_id: 420, gasPrice: 0, }, @@ -184,17 +184,6 @@ module.exports = { gasPrice: 0, }, - arbtest: { - provider: function () { - return wrapProvider( - new HDWalletProvider(privKey, "https://kovan4.arbitrum.io/rpc") - ) - }, - network_id: '212984383488152', - gas: 1000000000, - gasPrice: 0, - }, - arb: { networkCheckTimeout: 100000, // provider: function () { From 7034f43f34746614091020de84c9a3522c3f4df8 Mon Sep 17 00:00:00 2001 From: owen05 Date: Mon, 11 Oct 2021 23:42:56 +0800 Subject: [PATCH 40/42] add dppProxy --- config/arb-config.js | 5 +- config/boba-test-config.js | 1 + config/bsc-config.js | 5 +- config/eth-config.js | 5 +- config/heco-config.js | 5 +- config/matic-config.js | 5 +- config/moonriver-config.js | 5 +- config/neon-test-config.js | 68 -------------- config/ok-config.js | 5 +- config/rinkeby-config.js | 5 +- configAdapter.js | 6 -- contracts/SmartRoute/DODOV2Proxy02.sol | 90 ------------------- contracts/SmartRoute/proxies/DODODppProxy.sol | 2 +- migrations/2_deploy_v2.js | 20 ++++- 14 files changed, 42 insertions(+), 185 deletions(-) delete mode 100644 config/neon-test-config.js diff --git a/config/arb-config.js b/config/arb-config.js index c8bcead..1abdaa5 100644 --- a/config/arb-config.js +++ b/config/arb-config.js @@ -13,7 +13,7 @@ module.exports = { DODOSellHelper: "0xA5F36E822540eFD11FcD77ec46626b916B217c3e", DODOCalleeHelper: "0xe3B40F8D8346d428EAB28d9Fd672b784d921cfBD", DODOV1PmmHelper: "0x4EE6398898F7FC3e648b3f6bA458310ac29cD352", - DODOV2RouteHelper: "0xBE24a05a317ED701630EE503F0C59A01e4B0c88C", + DODOV2RouteHelper: "0x7737fd30535c69545deeEa54AB8Dd590ccaEBD3c",//0xBE24a05a317ED701630EE503F0C59A01e4B0c88C ERC20Helper: "0x7C062B9C584fA6eC2504270790D38240A2c5fE72", DODOSwapCalcHelper: "0xd7863Aee0B7A312F2c055B441253d66AFac8d144", MultiCall: "0xF718F2bd590E5621e53f7b89398e52f7Acced8ca", @@ -37,7 +37,7 @@ module.exports = { //Factory DVMFactory: "0xDa4c4411c55B0785e501332354A036c04833B72b", - DPPFactory: "0xDdB13e6dd168E1a68DC2285Cb212078ae10394A9", + DPPFactory: "0xa6Cf3d163358aF376ec5e8B7Cc5e102a05FdE63D",//0xDdB13e6dd168E1a68DC2285Cb212078ae10394A9 DSPFactory: "0xC8fE2440744dcd733246a4dB14093664DEFD5A53", UpCpFactory: "0xE92787F457F541a9D5Ae17C1dbdBffA8D3BA3C25", CrowdPoolingFactory: "0x6E5f239f729c83E976a7A280F81Dd1685dd89218", @@ -60,6 +60,7 @@ module.exports = { DODOV2Proxy: "0x88CBf433471A0CD8240D2a12354362988b4593E5", DSPProxy: "0x36E5238B4479d1ba0bFE47550B0B8e4f4f500EAA", CpProxy: "0x074890524059905096caA0D1A7B5715C6203c155", + DPPProxy: "0xE8C9A78725D0451FA19878D5f8A3dC0D55FECF25", RouteProxy: "0x3B6067D4CAa8A14c63fdBE6318F27A0bBc9F9237", DODOMineV3Proxy: "0x9A74B169798bE874EF1C23b4092e5689969eF45E", diff --git a/config/boba-test-config.js b/config/boba-test-config.js index 3077b92..2bdb2c0 100644 --- a/config/boba-test-config.js +++ b/config/boba-test-config.js @@ -58,6 +58,7 @@ module.exports = { DODOV2Proxy: "", DSPProxy: "", CpProxy: "", + DPPProxy: "", RouteProxy: "", DODOMineV3Proxy: "", diff --git a/config/bsc-config.js b/config/bsc-config.js index 6892343..a513605 100644 --- a/config/bsc-config.js +++ b/config/bsc-config.js @@ -9,7 +9,7 @@ module.exports = { DODOSellHelper: "0x0F859706AeE7FcF61D5A8939E8CB9dBB6c1EDA33", DODOCalleeHelper: "0x2673E5333620bb22BD1bFB3af9Fc7012008E3b4B", DODOV1PmmHelper: "0x2BBD66fC4898242BDBD2583BBe1d76E8b8f71445", - DODOV2RouteHelper: "0xC1CCE4C003B10052f168072A4c3c02051053d957", + DODOV2RouteHelper: "0xb48eE7B874Af8bC0e068036e55e33b5DC91C3a65",//0xC1CCE4C003B10052f168072A4c3c02051053d957 CurveSample: "", @@ -30,7 +30,7 @@ module.exports = { //Factory DVMFactory: "0x790B4A80Fb1094589A3c0eFC8740aA9b0C1733fB", - DPPFactory: "0xAfe0A75DFFb395eaaBd0a7E1BBbd0b11f8609eeF", + DPPFactory: "0xd9CAc3D964327e47399aebd8e1e6dCC4c251DaAE",//0xAfe0A75DFFb395eaaBd0a7E1BBbd0b11f8609eeF DSPFactory: "0x0fb9815938Ad069Bf90E14FE6C596c514BEDe767", UpCpFactory: "0x4F57F6929E58AE564F6AB090fE0AEEcb39B0f270", CrowdPoolingFactory: "0x778DF5B12170e8af8dF94356BfC864E57CE185DC", @@ -57,6 +57,7 @@ module.exports = { DODOV2Proxy: "", DSPProxy: "0x2442A8B5cdf1E659F3F949A7E454Caa554D4E65a", CpProxy: "0xA867241cDC8d3b0C07C85cC06F25a0cD3b5474d8", + DPPProxy: "0x624FC8368fE11BE00D8B2F3fE0B9D0053BEc21b9", RouteProxy: "0x6B3D817814eABc984d51896b1015C0b89E9737Ca", DODOMineV3Proxy: "0x3c39dCb3630D305530a30419b3DEEcea629597AC", diff --git a/config/eth-config.js b/config/eth-config.js index 4f539ac..60621cb 100644 --- a/config/eth-config.js +++ b/config/eth-config.js @@ -9,7 +9,7 @@ module.exports = { DODOSellHelper: "0x533da777aedce766ceae696bf90f8541a4ba80eb", DODOCalleeHelper: "0x45a7E2E9D780613E047f7e78a9d3902ff854B522", DODOV1PmmHelper: "0x6373ceB657C83C91088d328622573FB766064Ac4", - DODOV2RouteHelper: "0x0672952Fab6BD1336C57AE09E49DB6D3e78B1896", + DODOV2RouteHelper: "0x6e90797C1caaa81bAEc1cF3351d989A78b2D4E99",//0x0672952Fab6BD1336C57AE09E49DB6D3e78B1896 CurveSample: "0x5381382257C761DAc6F1509B1BA1B70dDaa6862a", //Template @@ -28,7 +28,7 @@ module.exports = { //Factory DVMFactory: "0x72d220cE168C4f361dD4deE5D826a01AD8598f6C", - DPPFactory: "0x6B4Fa0bc61Eddc928e0Df9c7f01e407BfcD3e5EF", + DPPFactory: "0x5336edE8F971339F6c0e304c66ba16F1296A2Fbe",//0x6B4Fa0bc61Eddc928e0Df9c7f01e407BfcD3e5EF DSPFactory: "0x6fdDB76c93299D985f4d3FC7ac468F9A168577A4", UpCpFactory: "0x78D338F9d54E9e41872E68cB1c01d9499d87eE52", CrowdPoolingFactory: "0xE8C9A78725D0451FA19878D5f8A3dC0D55FECF25", @@ -53,6 +53,7 @@ module.exports = { DODOV2Proxy: "", DSPProxy: "0x4599ed18F34cFE06820E3684bF0aACB8D75c644d", CpProxy: "0x048B8926bb0eE9c52e05D61fDffbCCffbeE06Fc2", + DPPProxy: "0xfF7C8F518e6f1435957ed3d3E0692C94676dAE7a", RouteProxy: "0xa2398842F37465f89540430bDC00219fA9E4D28a", DODOMineV3Proxy: "0x0d9685D4037580F68D9F77B08971f17E1000bBdc", diff --git a/config/heco-config.js b/config/heco-config.js index 0444d03..98c1d8e 100644 --- a/config/heco-config.js +++ b/config/heco-config.js @@ -9,7 +9,7 @@ module.exports = { DODOSellHelper: "0xA0Bb1FbC23a547a8D448C7c8a2336F69A9dBa1AF", DODOCalleeHelper: "0x684D9d0768761fF0659B9f55D98F57648302ed14", DODOV1PmmHelper: "0xFB973C79C665C0AC69E74C67be90D4C7A6f23c59", - DODOV2RouteHelper: "0x67166F14E9aCf43A822BE147eA59CdDd01A7C00d", + DODOV2RouteHelper: "0x075E8e0615F5c014e75Fc87C1B23E280005915Af",//0x67166F14E9aCf43A822BE147eA59CdDd01A7C00d CurveSample: "0xDfb423413f9Aa62Dd8e86a73d6e89918b4C22dd8", @@ -29,7 +29,7 @@ module.exports = { //Factory DVMFactory: "0xd8c77CF0F01222b07B8343A681C46eFA3faEa985", - DPPFactory: "0xF5420a3b91c457d336589217d45CcB8F7250eAed", + DPPFactory: "0x834235Cb6df67BAd0B7DDb612D316Ad8B77fAa6B",//0xF5420a3b91c457d336589217d45CcB8F7250eAed DSPFactory: "0xaEc55245AB0cDcc03Bc5f104dAdCd0a33Ef50714", UpCpFactory: "0xF7c3467786efAA9BD4022941ecd5a00C61b17FA9", CrowdPoolingFactory: "0x56133d0b63abf7A15D5697dD2dB9e04730f1A9C2", @@ -54,6 +54,7 @@ module.exports = { DODOV2Proxy: "0xAc7cC7d2374492De2D1ce21e2FEcA26EB0d113e7", DSPProxy: "0x9951CdEc21F42ab69D02daAAFEF4C3fc810B36FF", CpProxy: "0x8930101c6cFbe0f3cb31E7526a16E72255388E97", + DPPProxy: "0xa10b13b55da91F0505FEf91fB91C8a4900F43488", RouteProxy: "0xEc0fA5746E37dE75bDA9C1F874F2B75C12e505F6", DODOMineV3Proxy: "0x50832DEdCF8b390160D892654530Fb96042d674B", diff --git a/config/matic-config.js b/config/matic-config.js index aff2687..99d32d9 100644 --- a/config/matic-config.js +++ b/config/matic-config.js @@ -9,7 +9,7 @@ module.exports = { DODOSellHelper: "0xDfaf9584F5d229A9DBE5978523317820A8897C5A", DODOCalleeHelper: "0x261F6cF4dF0e5c1432739cDAFD9299150FEd3dFc", DODOV1PmmHelper: "0x18DFdE99F578A0735410797e949E8D3e2AFCB9D2", - DODOV2RouteHelper: "0x324c747885a88EA6f8115C46E0605C828ed527D3", + DODOV2RouteHelper: "0x6b0C1Ec661b776A819F5d5b2D0B622dE3419fDB0",//0x324c747885a88EA6f8115C46E0605C828ed527D3 CurveSample: "", @@ -30,7 +30,7 @@ module.exports = { //Factory DVMFactory: "0x79887f65f83bdf15Bcc8736b5e5BcDB48fb8fE13", - DPPFactory: "0x95E887aDF9EAa22cC1c6E3Cb7f07adC95b4b25a8", + DPPFactory: "0xd24153244066F0afA9415563bFC7Ba248bfB7a51",//0x95E887aDF9EAa22cC1c6E3Cb7f07adC95b4b25a8 DSPFactory: "0x43C49f8DD240e1545F147211Ec9f917376Ac1e87", UpCpFactory: "0x326c788c4C236f2bceC9476C66F8593Aa31be4Fc", CrowdPoolingFactory: "0x42ddEc68db70F5992eB7AB22dfaD8A57109841C9", @@ -53,6 +53,7 @@ module.exports = { DODOV2Proxy: "0x45894C062E6f4E58B257e0826675355305dfef0d", DSPProxy: "0xfDDCA6ffCE24dF5bE3e8AaD32081822f86178048", CpProxy: "0x5480B32c03647ff5E5A653F0465E798DBe558B57", + DPPProxy: "0xF6f1A1Ef2f5b56bb289993F75C12Eb41e4abC2f7", RouteProxy: "0x53eE28b9F0A6416857C1e7503032E27e80F52DA0", DODOMineV3Proxy: "0x47a65e74dd6b6B5E3243dBb01EDEd9D55ba234Ad", diff --git a/config/moonriver-config.js b/config/moonriver-config.js index bc51026..80557e9 100644 --- a/config/moonriver-config.js +++ b/config/moonriver-config.js @@ -14,7 +14,7 @@ module.exports = { DODOSellHelper: "0x5e84190a270333aCe5B9202a3F4ceBf11b81bB01", //not verify DODOCalleeHelper: "0xc4436fBAE6eBa5d95bf7d53Ae515F8A707Bd402A", DODOV1PmmHelper: "0x3CD6D7F5fF977bf8069548eA1F9441b061162b42", //not verify - DODOV2RouteHelper: "0x6B4Fa0bc61Eddc928e0Df9c7f01e407BfcD3e5EF", + DODOV2RouteHelper: "",//0x6B4Fa0bc61Eddc928e0Df9c7f01e407BfcD3e5EF CurveSample: "", //Template @@ -37,7 +37,7 @@ module.exports = { //not verify //Factory DVMFactory: "0x738Ebf387A0CE0eb46b0eF8Fa5DEa2EaE6B1Df51", - DPPFactory: "0xb57Dd5c265dBb13CA014F2332069E90CD0e22e65", + DPPFactory: "",//0xb57Dd5c265dBb13CA014F2332069E90CD0e22e65 DSPFactory: "0xB76de21f04F677f07D9881174a1D8E624276314C", UpCpFactory: "0x043957f7554275b90c5178872faE851dcfC1089D", CrowdPoolingFactory: "0x0596908263Ef2724fBfBcAfA1c983FCD7a629038", @@ -64,6 +64,7 @@ module.exports = { DODOV2Proxy: "0xd9deC7c3C06e62a4c1BeEB07CadF568f496b14c2", DSPProxy: "0x9f015aa5557ffBb47850c045Df67Bf229B07f2eA", CpProxy: "0x357c5E9cfA8B834EDcef7C7aAbD8F9Db09119d11", + DPPProxy: "", RouteProxy: "0x0125Cd41312F72a0774112Ca639D65A2C02e3627", DODOMineV3Proxy: "0xb159260989012fA98af560A3Fa6D9cd11a64cf6E", //not verify diff --git a/config/neon-test-config.js b/config/neon-test-config.js deleted file mode 100644 index 16c00ac..0000000 --- a/config/neon-test-config.js +++ /dev/null @@ -1,68 +0,0 @@ -module.exports = { - NEON_TEST_CONFIG: { - //DODOV1 - DODO: "", - DODOZoo: "", - - //TOKEN - WETH: "", - DODO: "", - - //Helper - DODOSellHelper: "", - DODOCalleeHelper: "", - DODOV1PmmHelper: "", - DODOV2RouteHelper: "", - ERC20Helper: "", - DODOSwapCalcHelper: "", - MultiCall: "", - CurveSample: "", - - //Template - CloneFactory: "", - FeeRateModel: "", - FeeRateDIP3: "", - PermissionManager: "", - DVM: "", - DPP: "", - DSP: "", - DPPAdmin: "", - CP: "", - ERC20MineV2: "", - ERC20MineV3: "", - ERC20: "", - CustomERC20: "", - - //Factory - DVMFactory: "", - DPPFactory: "", - DSPFactory: "", - UpCpFactory: "", - CrowdPoolingFactory: "", - ERC20V2Factory: "", - DODOMineV2Factory: "", - DODOMineV3Registry: "", - - //Approve - DODOApprove: "", - DODOApproveProxy: "", - - //Adapter - DODOV1Adapter: "", - DODOV2Adapter: "", - UniAdapter: "", - CurveAdapter: "", - - - //Proxy - DODOV2Proxy: "", - DSPProxy: "", - CpProxy: "", - RouteProxy: "", - DODOMineV3Proxy: "", - - //Account - multiSigAddress: "", - defaultMaintainer: "" - } -} \ No newline at end of file diff --git a/config/ok-config.js b/config/ok-config.js index f5aa653..854e242 100644 --- a/config/ok-config.js +++ b/config/ok-config.js @@ -9,7 +9,7 @@ module.exports = { DODOSellHelper: "0x6B208E08dcF6BD51F50C5Da09d15B2D8E5C46Cf2", DODOCalleeHelper: "0x3CD6D7F5fF977bf8069548eA1F9441b061162b42", DODOV1PmmHelper: "0xDfaf9584F5d229A9DBE5978523317820A8897C5A", - DODOV2RouteHelper: "0x9B64c81ba54eA51e1f6B7fefb3cfF8AA6F1e2A09", + DODOV2RouteHelper: "0xa3cA30a7D523959fDDF7C9800C7121211B559D24",//0x9B64c81ba54eA51e1f6B7fefb3cfF8AA6F1e2A09 CurveSample: "", @@ -30,7 +30,7 @@ module.exports = { //Factory DVMFactory: "0x9aE501385Bc7996A2A4a1FBb00c8d3820611BCB5", - DPPFactory: "0xbAb9F4ff4A19a0e8EEBC56b06750253228ffAc6E", + DPPFactory: "0xfdD51aAba2f949195a460121aA3f2D392d2524A9",//0xbAb9F4ff4A19a0e8EEBC56b06750253228ffAc6E DSPFactory: "0x44D5dF24d5Ef52A791D6436Fa45A8D426f6de34e", UpCpFactory: "0xE55154D09265b18aC7CDAC6E646672A5460389a1", CrowdPoolingFactory: "0x85351262f7474Ebe23FfAcD633cf20A491F1325D", @@ -54,6 +54,7 @@ module.exports = { DODOV2Proxy: "0x6B4Fa0bc61Eddc928e0Df9c7f01e407BfcD3e5EF", DSPProxy: "0x5515363c0412AdD5c72d3E302fE1bD7dCBCF93Fe", CpProxy: "0x335aC99bb3E51BDbF22025f092Ebc1Cf2c5cC619", + DPPProxy: "0xa222e6a71D1A1Dd5F279805fbe38d5329C1d0e70", RouteProxy: "0xd9deC7c3C06e62a4c1BeEB07CadF568f496b14c2", DODOMineV3Proxy: "0x958f79e2998DFe417208b9A07D799265B0298e58", diff --git a/config/rinkeby-config.js b/config/rinkeby-config.js index c2e7687..b130b15 100644 --- a/config/rinkeby-config.js +++ b/config/rinkeby-config.js @@ -9,7 +9,7 @@ module.exports = { DODOSellHelper: "0x4635FAc4471BC5B5839007f1EF65ff469ace333F", DODOCalleeHelper: "0x87C296100df953aFa324ABB870891baD9dDBf0fC", DODOV1PmmHelper: "0xEb06236b035f1Db7F1D181Efd635Edd621874472", - DODOV2RouteHelper: "0x80a70F228D8faBF20fdaf8274b9A7c4AE3551861", + DODOV2RouteHelper: "",//0x80a70F228D8faBF20fdaf8274b9A7c4AE3551861 //Template CloneFactory: "0x823ECBfCCD3e6Cb67d4c9334F743BEe0E60A7349", @@ -28,7 +28,7 @@ module.exports = { //Factory DVMFactory: "0x17DddEFA0c82E0c850a4Adac2aFE3F1fe977A242", - DPPFactory: "0x510b49803E356C750f3a93bA5508C0FFD9f71bDD", + DPPFactory: "",//0x510b49803E356C750f3a93bA5508C0FFD9f71bDD DSPFactory: "0xa1ab675cB49BA0DC3F39fA4C20E216572A8dD3c8", CrowdPoolingFactory: "0xDD43520779dDCfbDe373174Ee36aEaD39771cD4f", UpCpFactory: "0xb09E91505347234Cb722D67042290f50F1C13749", @@ -52,6 +52,7 @@ module.exports = { DODOV2Proxy: "0xba001E96AF87bF9d8D0BDA667067A9921FE6d294", DSPProxy: "0x0f6345D1d07C134BB0973AD102F38eA9195F6f78", CpProxy: "0x2E483CBb9e76fE6543168DEd698d9244EE1ED8Dd", + DPPProxy: "", RouteProxy: "0xe2b538a781eB5a115a1359B8f363B9703Fd19dE6", DODOMineV3Proxy: "0xcb15BBb59AC8a4B64A4db9B8d9F66c397d89Bd22", diff --git a/configAdapter.js b/configAdapter.js index ed6698b..6b358d2 100644 --- a/configAdapter.js +++ b/configAdapter.js @@ -6,7 +6,6 @@ const { MATIC_CONFIG } = require("./config/matic-config"); const { ARB_CONFIG } = require("./config/arb-config"); const { RINKEBY_CONFIG } = require("./config/rinkeby-config"); const { OK_CONFIG } = require("./config/ok-config"); -const { NEON_TEST_CONFIG } = require("./config/neon-test-config"); const { MOONRIVER_CONFIG } = require("./config/moonriver-config"); const { BOBA_TEST_CONFIG } = require("./config/boba-test-config"); @@ -45,11 +44,6 @@ exports.GetConfig = function (network, accounts) { CONFIG.multiSigAddress = accounts[0] CONFIG.defaultMaintainer = accounts[0] break; - case "neon_test": - CONFIG = NEON_TEST_CONFIG - CONFIG.multiSigAddress = accounts[0] - CONFIG.defaultMaintainer = accounts[0] - break; case "boba_test": CONFIG = BOBA_TEST_CONFIG CONFIG.multiSigAddress = accounts[0] diff --git a/contracts/SmartRoute/DODOV2Proxy02.sol b/contracts/SmartRoute/DODOV2Proxy02.sol index 6c0d697..84042cf 100644 --- a/contracts/SmartRoute/DODOV2Proxy02.sol +++ b/contracts/SmartRoute/DODOV2Proxy02.sol @@ -40,7 +40,6 @@ contract DODOV2Proxy02 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable address public immutable _DODO_APPROVE_PROXY_; address public immutable _DODO_SELL_HELPER_; address public immutable _DVM_FACTORY_; - address public immutable _DPP_FACTORY_; mapping (address => bool) public isWhiteListed; // ============ Events ============ @@ -66,13 +65,11 @@ contract DODOV2Proxy02 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable constructor( address dvmFactory, - address dppFactory, address payable weth, address dodoApproveProxy, address dodoSellHelper ) public { _DVM_FACTORY_ = dvmFactory; - _DPP_FACTORY_ = dppFactory; _WETH_ = weth; _DODO_APPROVE_PROXY_ = dodoApproveProxy; _DODO_SELL_HELPER_ = dodoSellHelper; @@ -208,93 +205,6 @@ contract DODOV2Proxy02 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable } } - // ============ DPP Functions (create & reset) ============ - - // function createDODOPrivatePool( - // address baseToken, - // address quoteToken, - // uint256 baseInAmount, - // uint256 quoteInAmount, - // uint256 lpFeeRate, - // uint256 i, - // uint256 k, - // bool isOpenTwap, - // uint256 deadLine - // ) - // external - // override - // payable - // preventReentrant - // judgeExpired(deadLine) - // returns (address newPrivatePool) - // { - // newPrivatePool = IDODOV2(_DPP_FACTORY_).createDODOPrivatePool(); - - // address _baseToken = baseToken; - // address _quoteToken = quoteToken; - // _deposit(msg.sender, newPrivatePool, _baseToken, baseInAmount, _baseToken == _ETH_ADDRESS_); - // _deposit( - // msg.sender, - // newPrivatePool, - // _quoteToken, - // quoteInAmount, - // _quoteToken == _ETH_ADDRESS_ - // ); - - // if (_baseToken == _ETH_ADDRESS_) _baseToken = _WETH_; - // if (_quoteToken == _ETH_ADDRESS_) _quoteToken = _WETH_; - - // IDODOV2(_DPP_FACTORY_).initDODOPrivatePool( - // newPrivatePool, - // msg.sender, - // _baseToken, - // _quoteToken, - // lpFeeRate, - // k, - // i, - // isOpenTwap - // ); - // } - - // function resetDODOPrivatePool( - // address dppAddress, - // uint256[] memory paramList, //0 - newLpFeeRate, 1 - newI, 2 - newK - // uint256[] memory amountList, //0 - baseInAmount, 1 - quoteInAmount, 2 - baseOutAmount, 3- quoteOutAmount - // uint8 flag, // 0 - ERC20, 1 - baseInETH, 2 - quoteInETH, 3 - baseOutETH, 4 - quoteOutETH - // uint256 minBaseReserve, - // uint256 minQuoteReserve, - // uint256 deadLine - // ) external override payable preventReentrant judgeExpired(deadLine) { - // _deposit( - // msg.sender, - // dppAddress, - // IDODOV2(dppAddress)._BASE_TOKEN_(), - // amountList[0], - // flag == 1 - // ); - // _deposit( - // msg.sender, - // dppAddress, - // IDODOV2(dppAddress)._QUOTE_TOKEN_(), - // amountList[1], - // flag == 2 - // ); - - // require(IDODOV2(IDODOV2(dppAddress)._OWNER_()).reset( - // msg.sender, - // paramList[0], - // paramList[1], - // paramList[2], - // amountList[2], - // amountList[3], - // minBaseReserve, - // minQuoteReserve - // ), "Reset Failed"); - - // _withdraw(msg.sender, IDODOV2(dppAddress)._BASE_TOKEN_(), amountList[2], flag == 3); - // _withdraw(msg.sender, IDODOV2(dppAddress)._QUOTE_TOKEN_(), amountList[3], flag == 4); - // } - // ============ Swap ============ function dodoSwapV2ETHToToken( diff --git a/contracts/SmartRoute/proxies/DODODppProxy.sol b/contracts/SmartRoute/proxies/DODODppProxy.sol index 6d9a85c..0b7cd55 100644 --- a/contracts/SmartRoute/proxies/DODODppProxy.sol +++ b/contracts/SmartRoute/proxies/DODODppProxy.sol @@ -47,7 +47,7 @@ contract DODODppProxy is ReentrancyGuard { // ============ Modifiers ============ modifier judgeExpired(uint256 deadLine) { - require(deadLine >= block.timestamp, "DODOCpProxy: EXPIRED"); + require(deadLine >= block.timestamp, "DPPProxy: EXPIRED"); _; } diff --git a/migrations/2_deploy_v2.js b/migrations/2_deploy_v2.js index 7e5bd17..d2414ce 100644 --- a/migrations/2_deploy_v2.js +++ b/migrations/2_deploy_v2.js @@ -40,6 +40,7 @@ const DODOApproveProxy = artifacts.require("DODOApproveProxy"); const DODODspProxy = artifacts.require("DODODspProxy"); const DODOCpProxy = artifacts.require("DODOCpProxy"); +const DODODppProxy = artifacts.require("DODODppProxy"); const DODORouteProxy = artifacts.require("DODORouteProxy"); const DODOMineV3Proxy = artifacts.require("DODOMineV3Proxy"); const DODOProxyV2 = artifacts.require("DODOV2Proxy02"); @@ -102,6 +103,7 @@ module.exports = async (deployer, network, accounts) => { let DODOV2ProxyAddress = CONFIG.DODOV2Proxy; let DODODspProxyAddress = CONFIG.DSPProxy; let DODOCpProxyAddress = CONFIG.CpProxy; + let DODODppProxyAddress = CONFIG.DPPProxy; let DODOMineV3ProxyAddress = CONFIG.DODOMineV3Proxy; let DODORouteProxyAddress = CONFIG.RouteProxy; @@ -399,7 +401,6 @@ module.exports = async (deployer, network, accounts) => { await deployer.deploy( DODOProxyV2, DvmFactoryAddress, - DppFactoryAddress, WETHAddress, DODOApproveProxyAddress, DODOSellHelperAddress @@ -434,6 +435,17 @@ module.exports = async (deployer, network, accounts) => { logger.log("CpProxy address: ", DODOCpProxy.address); } + if (DODODppProxyAddress == "") { + await deployer.deploy( + DODODppProxy, + WETHAddress, + DODOApproveProxyAddress, + DppFactoryAddress + ); + DODODppProxyAddress = DODODppProxy.address; + logger.log("DPPProxy address: ", DODODppProxy.address); + } + if (DODOMineV3ProxyAddress == "") { await deployer.deploy( DODOMineV3Proxy, @@ -465,7 +477,7 @@ module.exports = async (deployer, network, accounts) => { var tx; //ApproveProxy init以及添加ProxyList const DODOApproveProxyInstance = await DODOApproveProxy.at(DODOApproveProxyAddress); - tx = await DODOApproveProxyInstance.init(multiSigAddress, [DODOV2ProxyAddress, DODODspProxyAddress, DODOCpProxyAddress, DODOMineV3ProxyAddress, DODORouteProxyAddress]); + tx = await DODOApproveProxyInstance.init(multiSigAddress, [DODOV2ProxyAddress, DODODspProxyAddress, DODOCpProxyAddress, DODODppProxyAddress, DODOMineV3ProxyAddress, DODORouteProxyAddress]); logger.log("DODOApproveProxy Init tx: ", tx.tx); //Approve init @@ -495,8 +507,8 @@ module.exports = async (deployer, network, accounts) => { //DPPFactory add DODProxy as admin const dppFactoryInstance = await DPPFactory.at(DppFactoryAddress); - var tx = await dppFactoryInstance.addAdminList(DODOV2ProxyAddress); - logger.log("DPPFactory Init tx: ", tx.tx); + var tx = await dppFactoryInstance.addAdminList(DODODppProxyAddress); + logger.log("DPPFactory Add DPPProxy tx: ", tx.tx); } } From 1f18222950beff3ef3d5be1788bf52c307a8eb71 Mon Sep 17 00:00:00 2001 From: owen05 Date: Wed, 13 Oct 2021 22:19:27 +0800 Subject: [PATCH 41/42] deploy nftPool --- config/arb-config.js | 8 ++++++++ config/bsc-config.js | 10 +++++++++- config/eth-config.js | 8 ++++++++ config/heco-config.js | 8 ++++++++ config/matic-config.js | 8 ++++++++ config/ok-config.js | 17 +++++++---------- truffle-config.js | 2 +- 7 files changed, 49 insertions(+), 12 deletions(-) diff --git a/config/arb-config.js b/config/arb-config.js index 1abdaa5..c22c80a 100644 --- a/config/arb-config.js +++ b/config/arb-config.js @@ -79,5 +79,13 @@ module.exports = { DODONFTRegistry: "0x8327b093caf1a32985887CE89BD6c8F80A6B7535", DODONFTProxy: "0x6A51C8A1139B9233C31331D2Cf442E0652329f23", + + //=================== NFTPool ================== + DODONFTApprove: "0xaeB5CF31b97dce6134e416129845e01106fFB177", + DODONFTPoolProxy: "0xE55154D09265b18aC7CDAC6E646672A5460389a1", + FilterAdmin: "0x738Ebf387A0CE0eb46b0eF8Fa5DEa2EaE6B1Df51", + FilterERC721V1: "0xbAb9F4ff4A19a0e8EEBC56b06750253228ffAc6E", + FilterERC1155V1: "0xb57Dd5c265dBb13CA014F2332069E90CD0e22e65", + NFTPoolController: "0x9aE501385Bc7996A2A4a1FBb00c8d3820611BCB5" } } \ No newline at end of file diff --git a/config/bsc-config.js b/config/bsc-config.js index a513605..16077dd 100644 --- a/config/bsc-config.js +++ b/config/bsc-config.js @@ -100,6 +100,14 @@ module.exports = { //================= DropsV2 ================== DropsFeeModel: "0x6de96b1a41FF2E8264925B4C5C0564C46DC0C67d", - DropsProxy: "0xC05A30468d039381AaBaB6dcaC31078DB2C3323B" + DropsProxy: "0xC05A30468d039381AaBaB6dcaC31078DB2C3323B", + + //=================== NFTPool ================== + DODONFTApprove: "0x8f697865Fb43236683a174feE87fD84dB64C7A6c", + DODONFTPoolProxy: "0x509D3775F684fA4d19C9C99B961202e309B5B965", + FilterAdmin: "0xDe8A380e84998986A59bE6519FF172d40c0F9d41", + FilterERC721V1: "0x33364198d93648D3E976aA8625097567791c301F", + FilterERC1155V1: "0x7635694249B1bb39476a6aB28Cc6B17c1E3cAEe1", + NFTPoolController: "0x4187aab02f9E3AbdEb9a6cC71397a7A839113634" } } \ No newline at end of file diff --git a/config/eth-config.js b/config/eth-config.js index 60621cb..ebd1a83 100644 --- a/config/eth-config.js +++ b/config/eth-config.js @@ -78,5 +78,13 @@ module.exports = { DODONFTRegistry: "0xA7263eb38b9A61B72397c884b5f9bFb5C34A7840", DODONFTProxy: "0x987e2a3A65A526C66ac66B6F8c84120055363625", + + //=================== NFTPool ================== + DODONFTApprove: "0xf740253BDF035620E12f55D61C092aa294E2E5F3", + DODONFTPoolProxy: "0x9D4c342298Ba0C77E75CfD379a27F1420B3Ce45D", + FilterAdmin: "0xD4F77342a08EFF87B67D2F0Cfd34ed8c8E6Aa49e", + FilterERC721V1: "0x5f3178c155cB96f3Fd42E66933efF941a1122D79", + FilterERC1155V1: "0x5258Db198f6E39889bfCA6016786AF562Ab8bE91", + NFTPoolController: "0xfEdBea78dd3D1C301Be7ca7fdd5FB25A3b8C0b80" } } \ No newline at end of file diff --git a/config/heco-config.js b/config/heco-config.js index 98c1d8e..3c28da6 100644 --- a/config/heco-config.js +++ b/config/heco-config.js @@ -81,5 +81,13 @@ module.exports = { DODONFTRegistry: "0x77777FF74856716fd3FF89aD59fcABcCc1bE0522", DODONFTProxy: "0x181d1F15281E5475c517Fb840A6d31d32BbF65f3", + + //=================== NFTPool ================== + DODONFTApprove: "0x0d0463bCfAb32892F9dAf56f88d9671F913baB77", + DODONFTPoolProxy: "0xe5A12788A968671abD2ACce8FFD62c95314D3d90", + FilterAdmin: "0xf8d034B598E06A902f11CD6810926f9C1c1A1f0f", + FilterERC721V1: "0x48Adda32F7e77EdCE23F66497da16373dE20E922", + FilterERC1155V1: "0x2D11eFCd67782C153E16Af1B4B517136993008F0", + NFTPoolController: "0x2CEA100b9E842FFB67CaBebAdb73cE9D200E92dE" } } \ No newline at end of file diff --git a/config/matic-config.js b/config/matic-config.js index 99d32d9..5e567c3 100644 --- a/config/matic-config.js +++ b/config/matic-config.js @@ -74,5 +74,13 @@ module.exports = { DODONFTRegistry: "0x840135913a2527C3481DB29e323E05F301D33210", DODONFTProxy: "0xCb0A88A465Da7aB09C09B418F27bFf159A0FE09b", + + //=================== NFTPool ================== + DODONFTApprove: "0x91E1c84BA8786B1FaE2570202F0126C0b88F6Ec7", + DODONFTPoolProxy: "0x533AF8ad419fB3082df9C80BE2ad903912a817FB", + FilterAdmin: "0x50D148D0908C602A56884B8628A36470a875EEb2", + FilterERC721V1: "0x697F28107dF60BE8813Ce44103F15e51aEA1D61b", + FilterERC1155V1: "0x072ff3ed5F723FB4E9a83a76755ED5222ea99d7A", + NFTPoolController: "0xe6AafA1c45D9d0C64686c1f1D17B9fe9c7DAB05b" } } \ No newline at end of file diff --git a/config/ok-config.js b/config/ok-config.js index 854e242..fa14cfa 100644 --- a/config/ok-config.js +++ b/config/ok-config.js @@ -75,15 +75,12 @@ module.exports = { DODONFTRegistry: "0x327344B382EE1b44FB0a72945fCDCC7243200dD7", DODONFTProxy: "0x326c788c4C236f2bceC9476C66F8593Aa31be4Fc", - //================= DropsV1 ================= - MysteryBoxV1: "", - RandomGenerator: "", - RandomPool: [ - "", - ], - - //================= DropsV2 ================== - DropsFeeModel: "", - DropsProxy: "" + //=================== NFTPool ================== + DODONFTApprove: "0x0E3CA67AdB97E8FD07E516AFd869d1886E932F59", + DODONFTPoolProxy: "0x4599ed18F34cFE06820E3684bF0aACB8D75c644d", + FilterAdmin: "0x6fdDB76c93299D985f4d3FC7ac468F9A168577A4", + FilterERC721V1: "0xd0e1aA51dF0896c126Ce6F8A064E551e0DD3D39b", + FilterERC1155V1: "0x0672952Fab6BD1336C57AE09E49DB6D3e78B1896", + NFTPoolController: "0x8735AAd3BEae15487a017EE32cb11d8fd593e036" } } \ No newline at end of file diff --git a/truffle-config.js b/truffle-config.js index ab31a09..4892ba4 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -111,7 +111,7 @@ module.exports = { return new HDWalletProvider(privKey, "https://mainnet.infura.io/v3/" + infuraId); }, gas: 4000000, - gasPrice: 50000000000, + gasPrice: 65000000000, network_id: 1, skipDryRun: true }, From 5517f969e3bf2ae796267e976e7d25bea0f040cf Mon Sep 17 00:00:00 2001 From: owen05 Date: Mon, 18 Oct 2021 10:30:13 +0800 Subject: [PATCH 42/42] reset nft on rinkeby --- config/rinkeby-config.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/config/rinkeby-config.js b/config/rinkeby-config.js index b130b15..ce92f43 100644 --- a/config/rinkeby-config.js +++ b/config/rinkeby-config.js @@ -75,9 +75,9 @@ module.exports = { //================== NFT ==================== BuyoutModel: "0x1A18Ccf68040f660Ac7f83B4911d17398eDbC79f", - Fragment: "0xd0578C69e3455a7836456FfEEcd31934bda088d1", + Fragment: "0xe06ed1f2EBeD2b3c723c02C977c83b3150724f53", NFTCollateralVault: "0x9d9A560661eD783F99188EFC2cFd5F37bCC30609", - DODONFTRouteHelper: "0xf3194Dcbba7dDC1F8Cf1bC97AD502BC25581Ba0d", + DODONFTRouteHelper: "0xC08D918400859272442CC71fc8cC3b1a69835B4a", InitializableERC721: "0xC0ccfC832BD45Cd3A2d62e47FE92Fc50DD2210ac", InitializableERC1155: "0x9DC9086B65cCBec43F92bFa37Db81150Ed1DDDed", @@ -86,8 +86,8 @@ module.exports = { DodoNftErc721: "0x78B7AFf2E5fA95B1E7E16679645FB65a850ed6AB", DodoNftErc1155: "0x4C455532af01bc34a0Ec60fDd63c68FE41068c63", - DODONFTRegistry: "0x3DEf969E84FEAa9Dd2a29E1008D0426c8d89D5C5", - DODONFTProxy: "0xBF243C5626A0766031d57269c01F6eFd57B603fc", + DODONFTRegistry: "0xfa391c0Ed6898e0C6186605d69e877f1317Bb506", + DODONFTProxy: "0xc83c4aFdF216C7D0E15D50B9e1658298320A9551", //================= DropsV1 ================= MysteryBoxV1: "", @@ -103,11 +103,11 @@ module.exports = { // DropsERC1155: "0x3a8EcF30428bd4e33Cd7011533DFd596F7705c8F", //=================== NFTPool ================== - DODONFTApprove: "0x40F3bBe2f8C5F2E5f4adFf7d51f652F2B9F77315", + DODONFTApprove: "0x5a93021C4a2072E0F3daA646deBab0C9A8E3feE6", FilterAdmin: "0x729f7f44bf64Ce814716b6261e267DbE6cdf021c", FilterERC721V1: "0x47E2C563cDCd7F36B4E77cc33a6A5c152663f915", FilterERC1155V1: "0x55e2e1fe50FfaBd4fE3712Bd1aBfc9307a44c7F4", - DODONFTPoolProxy: "0xb23150F62D3089BB3Fa181ee6185324cdECE1942", + DODONFTPoolProxy: "0x81AD954B2Ed65d85d3023Eeb2D8DF6A512D4cd59", NFTPoolController: "0xf5d24499dD76C3791ee6D19aa206f55b72270415" }