This commit is contained in:
owen05
2021-09-08 23:37:40 +08:00
parent 6a659a4b82
commit 7c34e0ef5d
13 changed files with 707 additions and 520 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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