This commit is contained in:
owen05
2021-09-09 15:53:53 +08:00
parent 66891972b1
commit aff42e7108
8 changed files with 225 additions and 118 deletions

View File

@@ -4,7 +4,7 @@
- contracts/NFTPool/impl/FilterModel01.sol
- contracts/NFTPool/impl/NFTPoolFeeModel.sol
- contracts/NFTPool/impl/ControllerModel.sol
- contracts/external/ERC20/InitializableInternalMintableERC20.sol

View File

@@ -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];
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}
}

View File

@@ -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_;
}
}
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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));
}
}
}