This commit is contained in:
owen05
2021-09-09 22:38:19 +08:00
parent b652974a60
commit e617e61fe6
9 changed files with 379 additions and 227 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 {

View File

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