From da5e9dd8e35c44e5a3894087c928364e17d0f893 Mon Sep 17 00:00:00 2001 From: owen05 Date: Sun, 4 Apr 2021 01:05:08 +0800 Subject: [PATCH] nftproxy&®ister&&fix --- .../impl/NFTCollateralVault.sol | 6 +- .../CollateralVault/intf/ICollateralVault.sol | 6 +- contracts/DODOFee/FeeDistributer.sol | 89 +++++--- contracts/Factory/FragmentFactory.sol | 97 --------- .../Factory/Registries/DODONFTRegistry.sol | 86 ++++++++ contracts/Factory/TokenFactory.sol | 85 ++++++++ .../{ => impl}/Fragment.sol | 34 +-- .../GeneralizedFragment/intf/IFragment.sol | 28 +++ .../SmartRoute/helper/DODOCalleeHelper.sol | 10 + contracts/SmartRoute/proxies/DODONFTProxy.sol | 195 ++++++++++++++++++ contracts/intf/IDODOCallee.sol | 6 + contracts/intf/IFeeDistributor.sol | 21 ++ 12 files changed, 515 insertions(+), 148 deletions(-) delete mode 100644 contracts/Factory/FragmentFactory.sol create mode 100644 contracts/Factory/Registries/DODONFTRegistry.sol create mode 100644 contracts/Factory/TokenFactory.sol rename contracts/GeneralizedFragment/{ => impl}/Fragment.sol (80%) create mode 100644 contracts/GeneralizedFragment/intf/IFragment.sol create mode 100644 contracts/SmartRoute/proxies/DODONFTProxy.sol create mode 100644 contracts/intf/IFeeDistributor.sol diff --git a/contracts/CollateralVault/impl/NFTCollateralVault.sol b/contracts/CollateralVault/impl/NFTCollateralVault.sol index ed776b1..a099585 100644 --- a/contracts/CollateralVault/impl/NFTCollateralVault.sol +++ b/contracts/CollateralVault/impl/NFTCollateralVault.sol @@ -27,7 +27,11 @@ contract NFTCollateralVault is InitializableOwnable, IERC721Receiver, IERC1155Re } NftInfo[] public nftInfos; - constructor (string memory _name) public { + function init( + address owner, + string memory _name + ) external { + initOwner(owner); name = _name; } diff --git a/contracts/CollateralVault/intf/ICollateralVault.sol b/contracts/CollateralVault/intf/ICollateralVault.sol index 582673b..3be1d60 100644 --- a/contracts/CollateralVault/intf/ICollateralVault.sol +++ b/contracts/CollateralVault/intf/ICollateralVault.sol @@ -6,10 +6,12 @@ */ pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; + interface ICollateralVault { function _OWNER_() external returns (address); - function transferOwner(address to) external; + function init(address owner, string memory name) external; + + function directTransferOwnership(address newOwner) external; } diff --git a/contracts/DODOFee/FeeDistributer.sol b/contracts/DODOFee/FeeDistributer.sol index 6128d86..aa7c0ca 100644 --- a/contracts/DODOFee/FeeDistributer.sol +++ b/contracts/DODOFee/FeeDistributer.sol @@ -11,10 +11,9 @@ import {SafeMath} from "../lib/SafeMath.sol"; import {DecimalMath} from "../lib/DecimalMath.sol"; import {IERC20} from "../intf/IERC20.sol"; import {SafeERC20} from "../lib/SafeERC20.sol"; -import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; import {Ownable} from "../lib/Ownable.sol"; -contract FeeDistributor is InitializableOwnable { +contract FeeDistributor { using SafeMath for uint256; using SafeERC20 for IERC20; @@ -24,46 +23,55 @@ contract FeeDistributor is InitializableOwnable { address public _QUOTE_TOKEN_; uint256 public _BASE_RESERVE_; uint256 public _QUOTE_RESERVE_; - uint256 public _BASE_REWARD_RATIO_; - uint256 public _QUOTE_REWARD_RATIO_; address public _STAKE_VAULT_; address public _STAKE_TOKEN_; uint256 public _STAKE_RESERVE_; - mapping(address => uint256) internal _BASE_DEBT_; - mapping(address => uint256) internal _QUOTE_DEBT_; - mapping(address => uint256) internal _SHARES_; + uint256 public _BASE_REWARD_RATIO_; + mapping(address => uint256) internal _USER_BASE_REWARDS_; + mapping(address => uint256) internal _USER_BASE_PER_SHARE_; + + uint256 public _QUOTE_REWARD_RATIO_; + mapping(address => uint256) internal _USER_QUOTE_REWARDS_; + mapping(address => uint256) internal _USER_QUOTE_PER_SHARE_; + + mapping(address => uint256) internal _SHARES_; + + bool internal _FEE_INITIALIZED_; function init( address baseToken, address quoteToken, address stakeToken ) external { + require(!_FEE_INITIALIZED_, "ALREADY_INITIALIZED"); + _FEE_INITIALIZED_ = true; + _BASE_TOKEN_ = baseToken; _QUOTE_TOKEN_ = quoteToken; _STAKE_TOKEN_ = stakeToken; - _BASE_REWARD_RATIO_ = DecimalMath.ONE; - _QUOTE_REWARD_RATIO_ = DecimalMath.ONE; _STAKE_VAULT_ = address(new StakeVault()); } - //TODO: 用户的手续费base or quote 直接转到该合约中,stakeVault保存的是? function stake(address to) external { - _accuReward(); + _updateGlobalState(); + _updateUserReward(to); uint256 stakeVault = IERC20(_STAKE_TOKEN_).balanceOf(_STAKE_VAULT_); uint256 stakeInput = stakeVault.sub(_STAKE_RESERVE_); _addShares(stakeInput, to); } function claim(address to) external { - _accuReward(); + _updateGlobalState(); + _updateUserReward(msg.sender); _claim(msg.sender, to); } function unstake(uint256 amount, address to, bool withClaim) external { require(_SHARES_[msg.sender]>=amount, "STAKE BALANCE ONT ENOUGH"); - _accuReward(); + _updateGlobalState(); + _updateUserReward(msg.sender); if (withClaim) { _claim(msg.sender, to); @@ -73,30 +81,18 @@ contract FeeDistributor is InitializableOwnable { StakeVault(_STAKE_VAULT_).transferOut(_STAKE_TOKEN_, amount, to); } + // ============ Internal ============ + function _claim(address sender, address to) internal { - uint256 allBase = DecimalMath.mulFloor(_SHARES_[sender], _BASE_REWARD_RATIO_); - uint256 allQuote = DecimalMath.mulFloor(_SHARES_[sender], _QUOTE_REWARD_RATIO_); - IERC20(_BASE_TOKEN_).safeTransfer(to, allBase.sub(_BASE_DEBT_[sender])); - IERC20(_QUOTE_TOKEN_).safeTransfer(to, allQuote.sub(_QUOTE_DEBT_[sender])); - _BASE_DEBT_[sender] = allBase; - _QUOTE_DEBT_[sender] = allQuote; + uint256 allBase = _USER_BASE_REWARDS_[sender]; + uint256 allQuote = _USER_QUOTE_REWARDS_[sender]; + IERC20(_BASE_TOKEN_).safeTransfer(to, allBase); + IERC20(_QUOTE_TOKEN_).safeTransfer(to, allQuote); + _USER_BASE_REWARDS_[sender] = 0; + _USER_BASE_REWARDS_[sender] = 0; } - function _addShares(uint256 amount, address to) internal { - _SHARES_[to] = _SHARES_[to].add(amount); - _BASE_DEBT_[to] = _BASE_DEBT_[to].add(DecimalMath.mulCeil(amount, _BASE_REWARD_RATIO_)); - _QUOTE_DEBT_[to] = _QUOTE_DEBT_[to].add(DecimalMath.mulCeil(amount, _QUOTE_REWARD_RATIO_)); - _STAKE_RESERVE_ = IERC20(_STAKE_TOKEN_).balanceOf(_STAKE_VAULT_); - } - - function _removeShares(uint256 amount, address from) internal { - _SHARES_[from] = _SHARES_[from].sub(amount); - _BASE_DEBT_[from] = _BASE_DEBT_[from].sub(DecimalMath.mulFloor(amount, _BASE_REWARD_RATIO_)); - _QUOTE_DEBT_[from] = _QUOTE_DEBT_[from].sub(DecimalMath.mulFloor(amount, _QUOTE_REWARD_RATIO_)); - _STAKE_RESERVE_ = IERC20(_STAKE_TOKEN_).balanceOf(_STAKE_VAULT_); - } - - function _accuReward() internal { + function _updateGlobalState() internal { uint256 baseInput = IERC20(_BASE_TOKEN_).balanceOf(address(this)).sub(_BASE_RESERVE_); uint256 quoteInput = IERC20(_QUOTE_TOKEN_).balanceOf(address(this)).sub(_QUOTE_RESERVE_); _BASE_REWARD_RATIO_ = _BASE_REWARD_RATIO_.add(DecimalMath.divFloor(baseInput, _STAKE_RESERVE_)); @@ -105,6 +101,31 @@ contract FeeDistributor is InitializableOwnable { _QUOTE_RESERVE_ = _QUOTE_RESERVE_.add(quoteInput); } + function _updateUserReward(address user) internal { + _USER_BASE_REWARDS_[user] = DecimalMath.mulFloor( + _SHARES_[user], + _BASE_REWARD_RATIO_.sub(_USER_BASE_PER_SHARE_[user]) + ).add(_USER_BASE_REWARDS_[user]); + + _USER_BASE_PER_SHARE_[user] = _BASE_REWARD_RATIO_; + + _USER_QUOTE_REWARDS_[user] = DecimalMath.mulFloor( + _SHARES_[user], + _QUOTE_REWARD_RATIO_.sub(_USER_QUOTE_PER_SHARE_[user]) + ).add(_USER_QUOTE_REWARDS_[user]); + + _USER_QUOTE_PER_SHARE_[user] = _QUOTE_REWARD_RATIO_; + } + + function _addShares(uint256 amount, address to) internal { + _SHARES_[to] = _SHARES_[to].add(amount); + _STAKE_RESERVE_ = IERC20(_STAKE_TOKEN_).balanceOf(_STAKE_VAULT_); + } + + function _removeShares(uint256 amount, address from) internal { + _SHARES_[from] = _SHARES_[from].sub(amount); + _STAKE_RESERVE_ = IERC20(_STAKE_TOKEN_).balanceOf(_STAKE_VAULT_); + } } contract StakeVault is Ownable { diff --git a/contracts/Factory/FragmentFactory.sol b/contracts/Factory/FragmentFactory.sol deleted file mode 100644 index 8857687..0000000 --- a/contracts/Factory/FragmentFactory.sol +++ /dev/null @@ -1,97 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; -import {ICloneFactory} from "../lib/CloneFactory.sol"; -import {IDVM} from "../DODOVendingMachine/intf/IDVM.sol"; - -// 这一部分最后写,先把前面的接口定下来 -// 业务流程(没有NFT的情况下) -// 1. 上传媒体,创建NFTCollateralVault -// 2. 把CollateralVault提供给FragmentFactory做碎片化 -// (已经有NFT的情况下) -// 1. 将NFT打包成NFTCollateralVault -// 2. 把CollateralVault提供给FragmentFactory做碎片化 -// 所以总体来说,factory只需要处理vault已经建立好之后的拼装工作即可 - -interface IFragmentFactory { - function createFragment() external returns (address newVendingMachine); -} - -contract FragmentFactory is InitializableOwnable { - // ============ Templates ============ - - address public immutable _CLONE_FACTORY_; - address public immutable _MT_FEE_RATE_MODEL_; - address public _DVM_TEMPLATE_; - address public _FEE_DISTRIBUTOR_TEMPLATE_; - address public _FRAGMENT_TEMPLATE_; - - // ============ Registry ============ - - // base -> quote -> DVM address list - mapping(address => mapping(address => address[])) public _REGISTRY_; - // creator -> DVM address list - mapping(address => address[]) public _USER_REGISTRY_; - - // ============ Functions ============ - - constructor( - address cloneFactory, - address dvmTemplate, - address feeDistributorTemplate, - address fragmentTemplate, - address mtFeeRateModel - ) public { - _CLONE_FACTORY_ = cloneFactory; - _DVM_TEMPLATE_ = dvmTemplate; - _FEE_DISTRIBUTOR_TEMPLATE_ = feeDistributorTemplate; - _FRAGMENT_TEMPLATE_ = fragmentTemplate; - _MT_FEE_RATE_MODEL_ = mtFeeRateModel; - } - - function createFragment( - address owner, - address vault, - address quoteToken, - uint256 mtFeeRate, - uint256 i, - uint256 k, - uint256 totalSupply, - uint256 ownerRatio, - uint256 buyoutTimestamp - ) external returns (address newFragment) { - // newFragment = ICloneFactory(_CLONE_FACTORY_).clone(_FRAGMENT_TEMPLATE_); - // newVendingMachine = ICloneFactory(_CLONE_FACTORY_).clone(_DVM_TEMPLATE_); - // newFeeDistributor = ICloneFactory(_CLONE_FACTORY_).clone(_FEE_DISTRIBUTOR_TEMPLATE_); - - // { - // IFeeDistributor(newFeeDistributor).init(newFragment, quoteToken, newFragment); - // } - - // { - // IDVM(newVendingMachine).init( - // newFeeDistributor, - // newFragment, - // quoteToken, - // 0, - // mtFeeRateModel, - // i, - // k, - // false - // ); - // IFeeRateRegistry(mtFeeRateModel).set(newVendingMachine, mtFeeRate); - // } - - // { - // IFragment(newFragment).init(owner, newVendingMachine, vault, totalSupply, ownerRatio, buyoutTimestamp); - // } - } -} diff --git a/contracts/Factory/Registries/DODONFTRegistry.sol b/contracts/Factory/Registries/DODONFTRegistry.sol new file mode 100644 index 0000000..3bac098 --- /dev/null +++ b/contracts/Factory/Registries/DODONFTRegistry.sol @@ -0,0 +1,86 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; + +interface IDODONFTRegistry { + function addRegistry( + address vault, + address fragment, + address feeDistributor, + address dvm + ) external; +} + +/** + * @title DODONFT Registry + * @author DODO Breeder + * + * @notice Register DODONFT Pools + */ +contract DODONFTRegistry is InitializableOwnable { + + mapping (address => bool) public isAdminListed; + + // ============ Registry ============ + + // Frag -> FeeDistributor + mapping(address => address) public _FRAG_FEE_REGISTRY_; + // DVM -> FeeDistributor + mapping(address => address) public _DVM_FEE_REGISTRY_; + // Vault -> Frag + mapping(address => address) public _VAULT_FRAG_REGISTRY_; + + // ============ Events ============ + + event NewRegistry( + address vault, + address fragment, + address feeDistributor, + address dvm + ); + + event RemoveRegistry(address fragment); + + + // ============ Admin Operation Functions ============ + + function addRegistry( + address vault, + address fragment, + address feeDistributor, + address dvm + ) external { + require(isAdminListed[msg.sender], "ACCESS_DENIED"); + _FRAG_FEE_REGISTRY_[fragment] = feeDistributor; + _DVM_FEE_REGISTRY_[dvm] = feeDistributor; + _VAULT_FRAG_REGISTRY_[vault] = fragment; + emit NewRegistry(vault, fragment, feeDistributor, dvm); + } + + function removeRegistry( + address vault, + address fragment, + address dvm + ) external onlyOwner { + _FRAG_FEE_REGISTRY_[fragment] = address(0); + _DVM_FEE_REGISTRY_[dvm] = address(0); + _VAULT_FRAG_REGISTRY_[vault] = address(0); + emit RemoveRegistry(fragment); + } + + function addAmindList (address contractAddr) public onlyOwner { + isAdminListed[contractAddr] = true; + } + + function removeWhiteList (address contractAddr) public onlyOwner { + isAdminListed[contractAddr] = false; + } +} diff --git a/contracts/Factory/TokenFactory.sol b/contracts/Factory/TokenFactory.sol new file mode 100644 index 0000000..f275767 --- /dev/null +++ b/contracts/Factory/TokenFactory.sol @@ -0,0 +1,85 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ +//TODO: +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {ICloneFactory} from "../lib/CloneFactory.sol"; +import {InitializableERC20} from "../external/ERC20/InitializableERC20.sol"; +import {InitializableMintableERC20} from "../external/ERC20/InitializableMintableERC20.sol"; + +/** + * @title DODO TokenFactory + * @author DODO Breeder + * + * @notice Help user to create erc20 && erc721 && erc1155 token + */ +contract TokenFacotry { + // ============ Templates ============ + + address public immutable _CLONE_FACTORY_; + address public immutable _ERC20_TEMPLATE_; + address public immutable _MINTABLE_ERC20_TEMPLATE_; + + // ============ Events ============ + + event NewERC20(address erc20, address creator, bool isMintable); + + // ============ Registry ============ + // creator -> token address list + mapping(address => address[]) public _USER_STD_REGISTRY_; + + // ============ Functions ============ + + constructor( + address cloneFactory, + address erc20Template, + address mintableErc20Template + ) public { + _CLONE_FACTORY_ = cloneFactory; + _ERC20_TEMPLATE_ = erc20Template; + _MINTABLE_ERC20_TEMPLATE_ = mintableErc20Template; + } + + function createStdERC20( + uint256 totalSupply, + string memory name, + string memory symbol, + uint256 decimals + ) external returns (address newERC20) { + newERC20 = ICloneFactory(_CLONE_FACTORY_).clone(_ERC20_TEMPLATE_); + InitializableERC20(newERC20).init(msg.sender, totalSupply, name, symbol, decimals); + _USER_STD_REGISTRY_[msg.sender].push(newERC20); + emit NewERC20(newERC20, msg.sender, false); + } + + function createMintableERC20( + uint256 initSupply, + string memory name, + string memory symbol, + uint256 decimals + ) external returns (address newMintableERC20) { + newMintableERC20 = ICloneFactory(_CLONE_FACTORY_).clone(_MINTABLE_ERC20_TEMPLATE_); + InitializableMintableERC20(newMintableERC20).init( + msg.sender, + initSupply, + name, + symbol, + decimals + ); + emit NewERC20(newMintableERC20, msg.sender, true); + } + + + function getTokenByUser(address user) + external + view + returns (address[] memory tokens) + { + return _USER_STD_REGISTRY_[user]; + } +} diff --git a/contracts/GeneralizedFragment/Fragment.sol b/contracts/GeneralizedFragment/impl/Fragment.sol similarity index 80% rename from contracts/GeneralizedFragment/Fragment.sol rename to contracts/GeneralizedFragment/impl/Fragment.sol index 73d3a85..62bc757 100644 --- a/contracts/GeneralizedFragment/Fragment.sol +++ b/contracts/GeneralizedFragment/impl/Fragment.sol @@ -7,17 +7,14 @@ pragma solidity 0.6.9; -import {SafeMath} from "../lib/SafeMath.sol"; -import {SafeERC20} from "../lib/SafeERC20.sol"; -import {DecimalMath} from "../lib/DecimalMath.sol"; -import {IDVM} from "../DODOVendingMachine/intf/IDVM.sol"; -import {IERC20} from "../intf/IERC20.sol"; -import {InitializableERC20} from "../external/ERC20/InitializableERC20.sol"; - -interface ICollateralVault { - function directTransferOwnership(address newOwner) external; -} - +import {SafeMath} from "../../lib/SafeMath.sol"; +import {SafeERC20} from "../../lib/SafeERC20.sol"; +import {DecimalMath} from "../../lib/DecimalMath.sol"; +import {IDVM} from "../../DODOVendingMachine/intf/IDVM.sol"; +import {IDODOCallee} from "../../intf/IDODOCallee.sol"; +import {IERC20} from "../../intf/IERC20.sol"; +import {InitializableERC20} from "../../external/ERC20/InitializableERC20.sol"; +import {ICollateralVault} from "../../CollateralVault/intf/ICollateralVault.sol"; contract Fragment is InitializableERC20 { using SafeMath for uint256; @@ -105,11 +102,20 @@ contract Fragment is InitializableERC20 { } - function redeem(address to) external { + function redeem(address to, bytes calldata data) external { require(_IS_BUYOUT_, "DODOFragment: NEED_BUYOUT"); - IERC20(_QUOTE_).safeTransfer(to, DecimalMath.mulFloor(_BUYOUT_PRICE_, balances[to])); - _clearBalance(to); + uint256 quoteAmount = DecimalMath.mulFloor(_BUYOUT_PRICE_, balances[msg.sender]); + IERC20(_QUOTE_).safeTransfer(to, quoteAmount); + _clearBalance(msg.sender); + + if (data.length > 0) { + IDODOCallee(to).NFTRedeemCall( + msg.sender, + quoteAmount, + data + ); + } } function getBuyoutRequirement() external view returns (uint256 requireQuote){ diff --git a/contracts/GeneralizedFragment/intf/IFragment.sol b/contracts/GeneralizedFragment/intf/IFragment.sol new file mode 100644 index 0000000..e752ead --- /dev/null +++ b/contracts/GeneralizedFragment/intf/IFragment.sol @@ -0,0 +1,28 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + + +interface IFragment { + + function init( + address dvm, + address vaultPreOwner, + address collateralVault, + uint256 totalSupply, + uint256 ownerRatio, + uint256 buyoutTimestamp, + bool isOpenBuyout + ) external; + + function buyout(address newVaultOwner) external; + + function redeem(address to) external; + + function _QUOTE_() external view returns (address); +} diff --git a/contracts/SmartRoute/helper/DODOCalleeHelper.sol b/contracts/SmartRoute/helper/DODOCalleeHelper.sol index 5ea225f..48dad66 100644 --- a/contracts/SmartRoute/helper/DODOCalleeHelper.sol +++ b/contracts/SmartRoute/helper/DODOCalleeHelper.sol @@ -9,6 +9,7 @@ pragma solidity 0.6.9; pragma experimental ABIEncoderV2; import {IDODOV2} from "../intf/IDODOV2.sol"; +import {IFragment} from "../../GeneralizedFragment/intf/IFragment.sol"; import {IERC20} from "../../intf/IERC20.sol"; import {IWETH} from "../../intf/IWETH.sol"; import {SafeERC20} from "../../lib/SafeERC20.sol"; @@ -64,6 +65,15 @@ contract DODOCalleeHelper is ReentrancyGuard { _withdraw(assetTo, _quoteToken, quoteAmount, _quoteToken == _WETH_); } + function NFTRedeemCall( + address payable assetTo, + uint256 quoteAmount, + bytes calldata + ) external preventReentrant { + address _quoteToken = IFragment(msg.sender)._QUOTE_(); + _withdraw(assetTo, _quoteToken, quoteAmount, _quoteToken == _WETH_); + } + function _withdraw( address payable to, address token, diff --git a/contracts/SmartRoute/proxies/DODONFTProxy.sol b/contracts/SmartRoute/proxies/DODONFTProxy.sol new file mode 100644 index 0000000..1f30a73 --- /dev/null +++ b/contracts/SmartRoute/proxies/DODONFTProxy.sol @@ -0,0 +1,195 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +import {IDODOApproveProxy} from "../DODOApproveProxy.sol"; +import {ICloneFactory} from "../../lib/CloneFactory.sol"; +import {IERC20} from "../../intf/IERC20.sol"; +import {IWETH} from "../../intf/IWETH.sol"; +import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; +import {ICollateralVault} from "../../CollateralVault/intf/ICollateralVault.sol"; +import {IDODOV2} from "../intf/IDODOV2.sol"; +import {IFragment} from "../../GeneralizedFragment/intf/IFragment.sol"; +import {IFeeDistributor} from "../../intf/IFeeDistributor.sol"; +import {IDODONFTRegistry} from "../../Factory/Registries/DODONFTRegistry.sol"; +import {SafeMath} from "../../lib/SafeMath.sol"; +import {SafeERC20} from "../../lib/SafeERC20.sol"; +import {DecimalMath} from "../../lib/DecimalMath.sol"; +import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; + + +/** + * @title DODONFTProxy + * @author DODO Breeder + * + * @notice Entrance of NFT in DODO platform + */ +contract DODONFTProxy is ReentrancyGuard, InitializableOwnable { + using SafeMath for uint256; + + + // ============ Storage ============ + + address constant _ETH_ADDRESS_ = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address public immutable _WETH_; + address public immutable _DODO_APPROVE_PROXY_; + address public immutable _CLONE_FACTORY_; + address public immutable _DVM_FACTORY_; + address public immutable _NFT_REGISTY_; + + address public _VAULT_TEMPLATE_; + address public _FRAG_TEMPLATE_; + address public _FEE_TEMPLATE_; + + // ============ Events ============ + event ChangeVaultTemplate(address newVaultTemplate); + event ChangeFragTemplate(address newFragTemplate); + event ChangeFeeTemplate(address newFeeTemplate); + event CreateNFTCollateralVault(address creator, address vault); + event CreateFragment(address vault, address fragment, address dvm, address feeDistributor); + event Buyout(address from, address fragment, uint256 amount); + event Stake(address from, address feeDistributor, uint256 amount); + + + fallback() external payable {} + + receive() external payable {} + + constructor( + address cloneFactory, + address payable weth, + address dodoApproveProxy, + address dvmFactory, + address vaultTemplate, + address fragTemplate, + address feeTemplate, + address nftRegistry + ) public { + _CLONE_FACTORY_ = cloneFactory; + _WETH_ = weth; + _DODO_APPROVE_PROXY_ = dodoApproveProxy; + _DVM_FACTORY_ = dvmFactory; + _VAULT_TEMPLATE_ = vaultTemplate; + _FRAG_TEMPLATE_ = fragTemplate; + _FEE_TEMPLATE_ = feeTemplate; + _NFT_REGISTY_ = nftRegistry; + } + + function createNFTCollateralVault(string memory name) external returns (address newVault) { + newVault = ICloneFactory(_CLONE_FACTORY_).clone(_VAULT_TEMPLATE_); + ICollateralVault(newVault).init(msg.sender, name); + emit CreateNFTCollateralVault(msg.sender, newVault); + } + + function createFragment( + address quoteToken, + address collateralVault, + address vaultPreOwner, + address stakeToken, + uint256[] calldata dvmParams, //0 - lpFeeRate, 1 - I, 2 - K + uint256[] calldata fragParams, //0 - totalSupply, 1 - ownerRatio, 2 - buyoutTimestamp + bool isOpenBuyout + ) external returns (address newFragment, address newDvm, address newFeeDistributor) { + require(msg.sender == collateralVault, "NEED_BE_CALLED_BY_VAULT"); + + newFragment = ICloneFactory(_CLONE_FACTORY_).clone(_FRAG_TEMPLATE_); + address _quoteToken = quoteToken == _ETH_ADDRESS_ ? _WETH_ : quoteToken; + + { + uint256[] memory _dvmParams = dvmParams; + uint256[] memory _fragParams = fragParams; + + newDvm = IDODOV2(_DVM_FACTORY_).createDODOVendingMachine( + newFragment, + _quoteToken, + _dvmParams[0], + _dvmParams[1], + _dvmParams[2], + false + ); + + IFragment(newFragment).init( + newDvm, + vaultPreOwner, + msg.sender, + _fragParams[0], + _fragParams[1], + _fragParams[2], + isOpenBuyout + ); + } + + ICollateralVault(msg.sender).directTransferOwnership(newFragment); + + + if(stakeToken == address(0)) { + newFeeDistributor = address(0); + } else { + newFeeDistributor = ICloneFactory(_CLONE_FACTORY_).clone(_FEE_TEMPLATE_); + IFeeDistributor(newFeeDistributor).init(newFragment, _quoteToken, stakeToken); + } + + IDODONFTRegistry(_NFT_REGISTY_).addRegistry(msg.sender, newFragment, newFeeDistributor, newDvm); + + emit CreateFragment(msg.sender, newFragment, newDvm, newFeeDistributor); + } + + function buyout( + address fragment, + uint256 quoteAmount, + uint8 flag // 0 - ERC20, 1 - quoteInETH + ) external payable preventReentrant { + _deposit(msg.sender, fragment, IFragment(fragment)._QUOTE_(), quoteAmount, flag == 1); + IFragment(fragment).buyout(msg.sender); + emit Buyout(msg.sender, fragment, quoteAmount); + } + + function stakeToFeeDistributor( + address feeDistributor, + uint256 stakeAmount, + uint8 flag // 0 - ERC20, 1 - ETH + ) external payable preventReentrant { + _deposit(msg.sender, feeDistributor, IFeeDistributor(feeDistributor)._STAKE_TOKEN_(), stakeAmount, flag == 1); + IFeeDistributor(feeDistributor).stake(msg.sender); + emit Stake(msg.sender, feeDistributor, stakeAmount); + } + + //============= Owner =================== + function updateVaultTemplate(address newVaultTemplate) external onlyOwner { + _VAULT_TEMPLATE_ = newVaultTemplate; + emit ChangeVaultTemplate(newVaultTemplate); + } + + function updateFragTemplate(address newFragTemplate) external onlyOwner { + _FRAG_TEMPLATE_ = newFragTemplate; + emit ChangeFragTemplate(newFragTemplate); + } + + function updateFeeTemplate(address newFeeTemplate) external onlyOwner { + _FEE_TEMPLATE_ = newFeeTemplate; + emit ChangeFeeTemplate(newFeeTemplate); + } + + + function _deposit( + address from, + address to, + address token, + uint256 amount, + bool isETH + ) internal { + if (isETH) { + if (amount > 0) { + IWETH(_WETH_).deposit{value: amount}(); + if (to != address(this)) SafeERC20.safeTransfer(IERC20(_WETH_), to, amount); + } + } else { + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(token, from, to, amount); + } + } +} diff --git a/contracts/intf/IDODOCallee.sol b/contracts/intf/IDODOCallee.sol index a668917..bec83c6 100644 --- a/contracts/intf/IDODOCallee.sol +++ b/contracts/intf/IDODOCallee.sol @@ -50,4 +50,10 @@ interface IDODOCallee { uint256 quoteAmount, bytes calldata data ) external; + + function NFTRedeemCall( + address payable assetTo, + uint256 quoteAmount, + bytes calldata + ) external; } diff --git a/contracts/intf/IFeeDistributor.sol b/contracts/intf/IFeeDistributor.sol new file mode 100644 index 0000000..891683f --- /dev/null +++ b/contracts/intf/IFeeDistributor.sol @@ -0,0 +1,21 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +interface IFeeDistributor { + function init( + address baseToken, + address quoteToken, + address stakeToken + ) external; + + function stake(address to) external; + + function _STAKE_TOKEN_() external view returns(address); + +}