From e8182dd1a13c5505e3edef7f4efe8becbcaab12b Mon Sep 17 00:00:00 2001 From: mingda Date: Fri, 23 Oct 2020 01:16:52 +0800 Subject: [PATCH] restruct contract repo --- contracts/DODOEthProxy.sol | 303 --------------- contracts/{token => DODOToken}/DODOMine.sol | 0 .../{token => DODOToken}/DODORewardVault.sol | 0 contracts/{token => DODOToken}/DODOToken.sol | 0 .../{token => DODOToken}/LockedTokenVault.sol | 0 contracts/DODOVenderMachine/DVMFactory.sol | 51 +++ contracts/DODOVenderMachine/impl/DVMAdmin.sol | 19 + .../DODOVenderMachine/impl/DVMController.sol | 38 ++ .../DODOVenderMachine/impl/DVMFunding.sol | 69 ++++ .../DODOVenderMachine/impl/DVMStorage.sol | 75 ++++ .../DODOVenderMachine/impl/DVMTrader.sol | 69 ++++ contracts/DODOVenderMachine/impl/DVMVault.sol | 205 +++++++++++ contracts/DODOZoo.sol | 134 ------- contracts/dodo.sol | 74 ---- .../ERC20/NormalERC20.sol} | 2 +- .../{helper => external/ERC20}/TestWETH.sol | 0 .../uniswap}/UniswapV2.sol | 8 +- .../helper/BandBNBBUSDPriceOracleProxy.sol | 30 -- .../ChainlinkCOMPUSDCPriceOracleProxy.sol | 25 -- .../ChainlinkEthUSDCPriceOracleProxy.sol | 25 -- .../ChainlinkEthUSDTPriceOracleProxy.sol | 25 -- .../ChainlinkLENDUSDCPriceOracleProxy.sol | 25 -- .../ChainlinkLINKUSDPriceOracleProxy.sol | 25 -- .../ChainlinkSNXUSDPriceOracleProxy.sol | 25 -- .../ChainlinkWBTCUSDCPriceOracleProxy.sol | 25 -- .../ChainlinkYFIUSDCPriceOracleProxy.sol | 32 -- contracts/helper/ConstFeeRateModel.sol | 23 ++ contracts/helper/ConstOracle.sol | 22 -- contracts/helper/MinimumOracle.sol | 56 --- contracts/helper/NaiveFeeRateModel.sol | 28 ++ contracts/helper/NaiveOracle.sol | 25 -- contracts/helper/UniswapArbitrageur.sol | 165 --------- contracts/impl/Admin.sol | 122 ------- contracts/impl/DODOLpToken.sol | 134 ------- contracts/impl/LiquidityProvider.sol | 345 ------------------ contracts/impl/Pricing.sol | 198 ---------- contracts/impl/Settlement.sol | 163 --------- contracts/impl/Storage.sol | 118 ------ contracts/impl/Trader.sol | 274 -------------- contracts/intf/IDODO.sol | 67 ---- contracts/intf/IDODOCallee.sol | 18 - contracts/intf/IDODOLpToken.sol | 20 - contracts/intf/IERC20.sol | 2 + .../{lib/Types.sol => intf/IFeeRateModel.sol} | 5 +- contracts/intf/IVault.sol | 15 + contracts/{helper => lib}/CloneFactory.sol | 0 contracts/lib/DecimalMath.sol | 4 + contracts/lib/InitializableOwnable.sol | 15 +- contracts/lib/PermissionManager.sol | 53 +++ .../MultiSigWalletWithTimelock.sol} | 0 contracts/token/DODOMineReader.sol | 44 --- test/Rebalance.test.ts | 101 +++++ test/utils/Contracts.ts | 49 +-- 53 files changed, 775 insertions(+), 2575 deletions(-) delete mode 100644 contracts/DODOEthProxy.sol rename contracts/{token => DODOToken}/DODOMine.sol (100%) rename contracts/{token => DODOToken}/DODORewardVault.sol (100%) rename contracts/{token => DODOToken}/DODOToken.sol (100%) rename contracts/{token => DODOToken}/LockedTokenVault.sol (100%) create mode 100644 contracts/DODOVenderMachine/DVMFactory.sol create mode 100644 contracts/DODOVenderMachine/impl/DVMAdmin.sol create mode 100644 contracts/DODOVenderMachine/impl/DVMController.sol create mode 100644 contracts/DODOVenderMachine/impl/DVMFunding.sol create mode 100644 contracts/DODOVenderMachine/impl/DVMStorage.sol create mode 100644 contracts/DODOVenderMachine/impl/DVMTrader.sol create mode 100644 contracts/DODOVenderMachine/impl/DVMVault.sol delete mode 100644 contracts/DODOZoo.sol delete mode 100644 contracts/dodo.sol rename contracts/{helper/TestERC20.sol => external/ERC20/NormalERC20.sol} (97%) rename contracts/{helper => external/ERC20}/TestWETH.sol (100%) rename contracts/{helper => external/uniswap}/UniswapV2.sol (98%) delete mode 100644 contracts/helper/BandBNBBUSDPriceOracleProxy.sol delete mode 100644 contracts/helper/ChainlinkCOMPUSDCPriceOracleProxy.sol delete mode 100644 contracts/helper/ChainlinkEthUSDCPriceOracleProxy.sol delete mode 100644 contracts/helper/ChainlinkEthUSDTPriceOracleProxy.sol delete mode 100644 contracts/helper/ChainlinkLENDUSDCPriceOracleProxy.sol delete mode 100644 contracts/helper/ChainlinkLINKUSDPriceOracleProxy.sol delete mode 100644 contracts/helper/ChainlinkSNXUSDPriceOracleProxy.sol delete mode 100644 contracts/helper/ChainlinkWBTCUSDCPriceOracleProxy.sol delete mode 100644 contracts/helper/ChainlinkYFIUSDCPriceOracleProxy.sol create mode 100644 contracts/helper/ConstFeeRateModel.sol delete mode 100644 contracts/helper/ConstOracle.sol delete mode 100644 contracts/helper/MinimumOracle.sol create mode 100644 contracts/helper/NaiveFeeRateModel.sol delete mode 100644 contracts/helper/NaiveOracle.sol delete mode 100644 contracts/helper/UniswapArbitrageur.sol delete mode 100644 contracts/impl/Admin.sol delete mode 100644 contracts/impl/DODOLpToken.sol delete mode 100644 contracts/impl/LiquidityProvider.sol delete mode 100644 contracts/impl/Pricing.sol delete mode 100644 contracts/impl/Settlement.sol delete mode 100644 contracts/impl/Storage.sol delete mode 100644 contracts/impl/Trader.sol delete mode 100644 contracts/intf/IDODO.sol delete mode 100644 contracts/intf/IDODOCallee.sol delete mode 100644 contracts/intf/IDODOLpToken.sol rename contracts/{lib/Types.sol => intf/IFeeRateModel.sol} (57%) create mode 100644 contracts/intf/IVault.sol rename contracts/{helper => lib}/CloneFactory.sol (100%) create mode 100644 contracts/lib/PermissionManager.sol rename contracts/{helper/MultiSig.sol => multisig/MultiSigWalletWithTimelock.sol} (100%) delete mode 100644 contracts/token/DODOMineReader.sol create mode 100644 test/Rebalance.test.ts diff --git a/contracts/DODOEthProxy.sol b/contracts/DODOEthProxy.sol deleted file mode 100644 index aebaa52..0000000 --- a/contracts/DODOEthProxy.sol +++ /dev/null @@ -1,303 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -import {ReentrancyGuard} from "./lib/ReentrancyGuard.sol"; -import {SafeERC20} from "./lib/SafeERC20.sol"; -import {SafeMath} from "./lib/SafeMath.sol"; -import {IDODO} from "./intf/IDODO.sol"; -import {IERC20} from "./intf/IERC20.sol"; -import {IWETH} from "./intf/IWETH.sol"; - -interface IDODOZoo { - function getDODO(address baseToken, address quoteToken) external view returns (address); -} - -/** - * @title DODO Eth Proxy - * @author DODO Breeder - * - * @notice Handle ETH-WETH converting for users. - */ -contract DODOEthProxy is ReentrancyGuard { - using SafeERC20 for IERC20; - using SafeMath for uint256; - - address public _DODO_ZOO_; - address payable public _WETH_; - - // ============ Events ============ - - event ProxySellEthToToken( - address indexed seller, - address indexed quoteToken, - uint256 payEth, - uint256 receiveToken - ); - - event ProxyBuyEthWithToken( - address indexed buyer, - address indexed quoteToken, - uint256 receiveEth, - uint256 payToken - ); - - event ProxySellTokenToEth( - address indexed seller, - address indexed baseToken, - uint256 payToken, - uint256 receiveEth - ); - - event ProxyBuyTokenWithEth( - address indexed buyer, - address indexed baseToken, - uint256 receiveToken, - uint256 payEth - ); - - event ProxyDepositEthAsBase(address indexed lp, address indexed DODO, uint256 ethAmount); - - event ProxyWithdrawEthAsBase(address indexed lp, address indexed DODO, uint256 ethAmount); - - event ProxyDepositEthAsQuote(address indexed lp, address indexed DODO, uint256 ethAmount); - - event ProxyWithdrawEthAsQuote(address indexed lp, address indexed DODO, uint256 ethAmount); - - // ============ Functions ============ - - constructor(address dodoZoo, address payable weth) public { - _DODO_ZOO_ = dodoZoo; - _WETH_ = weth; - } - - fallback() external payable { - require(msg.sender == _WETH_, "WE_SAVED_YOUR_ETH_:)"); - } - - receive() external payable { - require(msg.sender == _WETH_, "WE_SAVED_YOUR_ETH_:)"); - } - - function sellEthToToken( - address quoteTokenAddress, - uint256 ethAmount, - uint256 minReceiveTokenAmount - ) external payable preventReentrant returns (uint256 receiveTokenAmount) { - require(msg.value == ethAmount, "ETH_AMOUNT_NOT_MATCH"); - address DODO = IDODOZoo(_DODO_ZOO_).getDODO(_WETH_, quoteTokenAddress); - require(DODO != address(0), "DODO_NOT_EXIST"); - IWETH(_WETH_).deposit{value: ethAmount}(); - IWETH(_WETH_).approve(DODO, ethAmount); - receiveTokenAmount = IDODO(DODO).sellBaseToken(ethAmount, minReceiveTokenAmount, ""); - _transferOut(quoteTokenAddress, msg.sender, receiveTokenAmount); - emit ProxySellEthToToken(msg.sender, quoteTokenAddress, ethAmount, receiveTokenAmount); - return receiveTokenAmount; - } - - function buyEthWithToken( - address quoteTokenAddress, - uint256 ethAmount, - uint256 maxPayTokenAmount - ) external preventReentrant returns (uint256 payTokenAmount) { - address DODO = IDODOZoo(_DODO_ZOO_).getDODO(_WETH_, quoteTokenAddress); - require(DODO != address(0), "DODO_NOT_EXIST"); - payTokenAmount = IDODO(DODO).queryBuyBaseToken(ethAmount); - _transferIn(quoteTokenAddress, msg.sender, payTokenAmount); - IERC20(quoteTokenAddress).safeApprove(DODO, payTokenAmount); - IDODO(DODO).buyBaseToken(ethAmount, maxPayTokenAmount, ""); - IWETH(_WETH_).withdraw(ethAmount); - msg.sender.transfer(ethAmount); - emit ProxyBuyEthWithToken(msg.sender, quoteTokenAddress, ethAmount, payTokenAmount); - return payTokenAmount; - } - - function sellTokenToEth( - address baseTokenAddress, - uint256 tokenAmount, - uint256 minReceiveEthAmount - ) external preventReentrant returns (uint256 receiveEthAmount) { - address DODO = IDODOZoo(_DODO_ZOO_).getDODO(baseTokenAddress, _WETH_); - require(DODO != address(0), "DODO_NOT_EXIST"); - IERC20(baseTokenAddress).safeApprove(DODO, tokenAmount); - _transferIn(baseTokenAddress, msg.sender, tokenAmount); - receiveEthAmount = IDODO(DODO).sellBaseToken(tokenAmount, minReceiveEthAmount, ""); - IWETH(_WETH_).withdraw(receiveEthAmount); - msg.sender.transfer(receiveEthAmount); - emit ProxySellTokenToEth(msg.sender, baseTokenAddress, tokenAmount, receiveEthAmount); - return receiveEthAmount; - } - - function buyTokenWithEth( - address baseTokenAddress, - uint256 tokenAmount, - uint256 maxPayEthAmount - ) external payable preventReentrant returns (uint256 payEthAmount) { - require(msg.value == maxPayEthAmount, "ETH_AMOUNT_NOT_MATCH"); - address DODO = IDODOZoo(_DODO_ZOO_).getDODO(baseTokenAddress, _WETH_); - require(DODO != address(0), "DODO_NOT_EXIST"); - payEthAmount = IDODO(DODO).queryBuyBaseToken(tokenAmount); - IWETH(_WETH_).deposit{value: payEthAmount}(); - IWETH(_WETH_).approve(DODO, payEthAmount); - IDODO(DODO).buyBaseToken(tokenAmount, maxPayEthAmount, ""); - _transferOut(baseTokenAddress, msg.sender, tokenAmount); - uint256 refund = maxPayEthAmount.sub(payEthAmount); - if (refund > 0) { - msg.sender.transfer(refund); - } - emit ProxyBuyTokenWithEth(msg.sender, baseTokenAddress, tokenAmount, payEthAmount); - return payEthAmount; - } - - function depositEthAsBase(uint256 ethAmount, address quoteTokenAddress) - external - payable - preventReentrant - { - require(msg.value == ethAmount, "ETH_AMOUNT_NOT_MATCH"); - address DODO = IDODOZoo(_DODO_ZOO_).getDODO(_WETH_, quoteTokenAddress); - require(DODO != address(0), "DODO_NOT_EXIST"); - IWETH(_WETH_).deposit{value: ethAmount}(); - IWETH(_WETH_).approve(DODO, ethAmount); - IDODO(DODO).depositBaseTo(msg.sender, ethAmount); - emit ProxyDepositEthAsBase(msg.sender, DODO, ethAmount); - } - - function withdrawEthAsBase(uint256 ethAmount, address quoteTokenAddress) - external - preventReentrant - returns (uint256 withdrawAmount) - { - address DODO = IDODOZoo(_DODO_ZOO_).getDODO(_WETH_, quoteTokenAddress); - require(DODO != address(0), "DODO_NOT_EXIST"); - address ethLpToken = IDODO(DODO)._BASE_CAPITAL_TOKEN_(); - - // transfer all pool shares to proxy - uint256 lpBalance = IERC20(ethLpToken).balanceOf(msg.sender); - IERC20(ethLpToken).transferFrom(msg.sender, address(this), lpBalance); - IDODO(DODO).withdrawBase(ethAmount); - - // transfer remain shares back to msg.sender - lpBalance = IERC20(ethLpToken).balanceOf(address(this)); - IERC20(ethLpToken).transfer(msg.sender, lpBalance); - - // because of withdraw penalty, withdrawAmount may not equal to ethAmount - // query weth amount first and than transfer ETH to msg.sender - uint256 wethAmount = IERC20(_WETH_).balanceOf(address(this)); - IWETH(_WETH_).withdraw(wethAmount); - msg.sender.transfer(wethAmount); - emit ProxyWithdrawEthAsBase(msg.sender, DODO, wethAmount); - return wethAmount; - } - - function withdrawAllEthAsBase(address quoteTokenAddress) - external - preventReentrant - returns (uint256 withdrawAmount) - { - address DODO = IDODOZoo(_DODO_ZOO_).getDODO(_WETH_, quoteTokenAddress); - require(DODO != address(0), "DODO_NOT_EXIST"); - address ethLpToken = IDODO(DODO)._BASE_CAPITAL_TOKEN_(); - - // transfer all pool shares to proxy - uint256 lpBalance = IERC20(ethLpToken).balanceOf(msg.sender); - IERC20(ethLpToken).transferFrom(msg.sender, address(this), lpBalance); - IDODO(DODO).withdrawAllBase(); - - // because of withdraw penalty, withdrawAmount may not equal to ethAmount - // query weth amount first and than transfer ETH to msg.sender - uint256 wethAmount = IERC20(_WETH_).balanceOf(address(this)); - IWETH(_WETH_).withdraw(wethAmount); - msg.sender.transfer(wethAmount); - emit ProxyWithdrawEthAsBase(msg.sender, DODO, wethAmount); - return wethAmount; - } - - function depositEthAsQuote(uint256 ethAmount, address baseTokenAddress) - external - payable - preventReentrant - { - require(msg.value == ethAmount, "ETH_AMOUNT_NOT_MATCH"); - address DODO = IDODOZoo(_DODO_ZOO_).getDODO(baseTokenAddress, _WETH_); - require(DODO != address(0), "DODO_NOT_EXIST"); - IWETH(_WETH_).deposit{value: ethAmount}(); - IWETH(_WETH_).approve(DODO, ethAmount); - IDODO(DODO).depositQuoteTo(msg.sender, ethAmount); - emit ProxyDepositEthAsQuote(msg.sender, DODO, ethAmount); - } - - function withdrawEthAsQuote(uint256 ethAmount, address baseTokenAddress) - external - preventReentrant - returns (uint256 withdrawAmount) - { - address DODO = IDODOZoo(_DODO_ZOO_).getDODO(baseTokenAddress, _WETH_); - require(DODO != address(0), "DODO_NOT_EXIST"); - address ethLpToken = IDODO(DODO)._QUOTE_CAPITAL_TOKEN_(); - - // transfer all pool shares to proxy - uint256 lpBalance = IERC20(ethLpToken).balanceOf(msg.sender); - IERC20(ethLpToken).transferFrom(msg.sender, address(this), lpBalance); - IDODO(DODO).withdrawQuote(ethAmount); - - // transfer remain shares back to msg.sender - lpBalance = IERC20(ethLpToken).balanceOf(address(this)); - IERC20(ethLpToken).transfer(msg.sender, lpBalance); - - // because of withdraw penalty, withdrawAmount may not equal to ethAmount - // query weth amount first and than transfer ETH to msg.sender - uint256 wethAmount = IERC20(_WETH_).balanceOf(address(this)); - IWETH(_WETH_).withdraw(wethAmount); - msg.sender.transfer(wethAmount); - emit ProxyWithdrawEthAsQuote(msg.sender, DODO, wethAmount); - return wethAmount; - } - - function withdrawAllEthAsQuote(address baseTokenAddress) - external - preventReentrant - returns (uint256 withdrawAmount) - { - address DODO = IDODOZoo(_DODO_ZOO_).getDODO(baseTokenAddress, _WETH_); - require(DODO != address(0), "DODO_NOT_EXIST"); - address ethLpToken = IDODO(DODO)._QUOTE_CAPITAL_TOKEN_(); - - // transfer all pool shares to proxy - uint256 lpBalance = IERC20(ethLpToken).balanceOf(msg.sender); - IERC20(ethLpToken).transferFrom(msg.sender, address(this), lpBalance); - IDODO(DODO).withdrawAllQuote(); - - // because of withdraw penalty, withdrawAmount may not equal to ethAmount - // query weth amount first and than transfer ETH to msg.sender - uint256 wethAmount = IERC20(_WETH_).balanceOf(address(this)); - IWETH(_WETH_).withdraw(wethAmount); - msg.sender.transfer(wethAmount); - emit ProxyWithdrawEthAsQuote(msg.sender, DODO, wethAmount); - return wethAmount; - } - - // ============ Helper Functions ============ - - function _transferIn( - address tokenAddress, - address from, - uint256 amount - ) internal { - IERC20(tokenAddress).safeTransferFrom(from, address(this), amount); - } - - function _transferOut( - address tokenAddress, - address to, - uint256 amount - ) internal { - IERC20(tokenAddress).safeTransfer(to, amount); - } -} diff --git a/contracts/token/DODOMine.sol b/contracts/DODOToken/DODOMine.sol similarity index 100% rename from contracts/token/DODOMine.sol rename to contracts/DODOToken/DODOMine.sol diff --git a/contracts/token/DODORewardVault.sol b/contracts/DODOToken/DODORewardVault.sol similarity index 100% rename from contracts/token/DODORewardVault.sol rename to contracts/DODOToken/DODORewardVault.sol diff --git a/contracts/token/DODOToken.sol b/contracts/DODOToken/DODOToken.sol similarity index 100% rename from contracts/token/DODOToken.sol rename to contracts/DODOToken/DODOToken.sol diff --git a/contracts/token/LockedTokenVault.sol b/contracts/DODOToken/LockedTokenVault.sol similarity index 100% rename from contracts/token/LockedTokenVault.sol rename to contracts/DODOToken/LockedTokenVault.sol diff --git a/contracts/DODOVenderMachine/DVMFactory.sol b/contracts/DODOVenderMachine/DVMFactory.sol new file mode 100644 index 0000000..178ca7e --- /dev/null +++ b/contracts/DODOVenderMachine/DVMFactory.sol @@ -0,0 +1,51 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {Ownable} from "../lib/Ownable.sol"; +import {ICloneFactory} from "../lib/CloneFactory.sol"; +import {DVMVault} from "./impl/DVMVault.sol"; +import {DVMController} from "./impl/DVMController.sol"; + +contract DVMFactory is Ownable { + address public _CLONE_FACTORY_; + address public _VAULT_TEMPLATE_; + address public _CONTROLLER_TEMPLATE_; + + function createDODOVenderMachine( + address maintainer, + address baseToken, + address quoteToken, + address lpFeeRateModel, + address mtFeeRateModel, + uint256 i, + uint256 k, + uint256 gasPriceLimit + ) external returns (address newVenderMachine) { + DVMController controller = DVMController( + ICloneFactory(_CLONE_FACTORY_).clone(_CONTROLLER_TEMPLATE_) + ); + DVMVault vault = DVMVault(ICloneFactory(_CLONE_FACTORY_).clone(_VAULT_TEMPLATE_)); + vault.init(address(controller), baseToken, quoteToken); // vault owner is controller + + controller.init( + msg.sender, + maintainer, + address(vault), + lpFeeRateModel, + mtFeeRateModel, + i, + k, + gasPriceLimit + ); + + newVenderMachine = address(controller); + return newVenderMachine; + } +} diff --git a/contracts/DODOVenderMachine/impl/DVMAdmin.sol b/contracts/DODOVenderMachine/impl/DVMAdmin.sol new file mode 100644 index 0000000..3738c41 --- /dev/null +++ b/contracts/DODOVenderMachine/impl/DVMAdmin.sol @@ -0,0 +1,19 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {DVMStorage} from "./DVMStorage.sol"; + +contract DVMAdmin is DVMStorage{ + + function setI(uint256 newI) external onlyOwner{} + + function setK(uint256 newK) external onlyOwner{} + +} \ No newline at end of file diff --git a/contracts/DODOVenderMachine/impl/DVMController.sol b/contracts/DODOVenderMachine/impl/DVMController.sol new file mode 100644 index 0000000..26b4920 --- /dev/null +++ b/contracts/DODOVenderMachine/impl/DVMController.sol @@ -0,0 +1,38 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {DVMTrader} from "./DVMTrader.sol"; +import {DVMFunding} from "./DVMFunding.sol"; +import {DVMAdmin} from "./DVMAdmin.sol"; +import {DVMVault} from "./DVMVault.sol"; +import {IFeeRateModel} from "../../intf/IFeeRateModel.sol"; + +contract DVMController is DVMTrader, DVMFunding, DVMAdmin { + function init( + address owner, + address maintainer, + address vault, + address lpFeeRateModel, + address mtFeeRateModel, + uint256 i, + uint256 k, + uint256 gasPriceLimit + ) external { + initOwner(owner); + _MAINTAINER_ = maintainer; + _BASE_TOKEN_ = DVMVault(vault)._BASE_TOKEN_(); + _QUOTE_TOKEN_ = DVMVault(vault)._QUOTE_TOKEN_(); + _LP_FEE_RATE_MODEL_ = IFeeRateModel(lpFeeRateModel); + _MT_FEE_RATE_MODEL_ = IFeeRateModel(mtFeeRateModel); + _I_ = i; + _K_ = k; + _GAS_PRICE_LIMIT_ = gasPriceLimit; + } +} diff --git a/contracts/DODOVenderMachine/impl/DVMFunding.sol b/contracts/DODOVenderMachine/impl/DVMFunding.sol new file mode 100644 index 0000000..34d8f9a --- /dev/null +++ b/contracts/DODOVenderMachine/impl/DVMFunding.sol @@ -0,0 +1,69 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {DVMStorage} from "./DVMStorage.sol"; +import {DecimalMath} from "../../lib/DecimalMath.sol"; + +contract DVMFunding is DVMStorage { + function buyShares(address account) external returns (uint256) { + uint256 baseInput = _VAULT_.getBaseInput(); + uint256 quoteInput = _VAULT_.getQuoteInput(); + + require(baseInput > 0, "NO_BASE_INPUT"); + + uint256 baseReserve = _VAULT_._BASE_RESERVE_(); + uint256 quoteReserve = _VAULT_._QUOTE_RESERVE_(); + uint256 mintAmount; + + // case 1. initial supply + if (baseReserve == 0 && quoteReserve == 0) { + mintAmount = baseInput; + } + + // case 2. supply when quote reserve is 0 + if (baseReserve > 0 && quoteReserve == 0) { + uint256 mintRatio = DecimalMath.divFloor(baseInput, baseReserve); + mintAmount = DecimalMath.mulFloor(_VAULT_.totalSupply(), mintRatio); + } + + // case 3. normal case + if (baseReserve > 0 && quoteReserve > 0) { + uint256 baseInputRatio = DecimalMath.divFloor(baseInput, baseReserve); + uint256 quoteInputRatio = DecimalMath.divFloor(quoteInput, quoteReserve); + uint256 mintRatio = baseInputRatio > quoteInputRatio ? quoteInputRatio : baseInputRatio; + // 在提币的时候向下取整。因此永远不会出现,balance为0但totalsupply不为0的情况 + // 但有可能出现,reserve>0但totalSupply=0的场景 + uint256 totalShare = _VAULT_.totalSupply(); + if (totalShare > 0) { + mintAmount = DecimalMath.mulFloor(totalShare, mintRatio); + } else { + mintAmount = baseInput; + } + } + + _VAULT_.mint(account, mintAmount); + _VAULT_.sync(); + } + + function sellShares( + address account, + address to, + uint256 amount + ) external returns (uint256) { + require(msg.sender == account, "PERMISSION_DENY"); + require(_VAULT_.balanceOf(account) >= amount, "SHARES_NOT_ENOUGH"); + (uint256 baseBalance, uint256 quoteBalance) = _VAULT_.getVaultBalance(); + uint256 totalShares = _VAULT_.totalSupply(); + _VAULT_.burn(account, amount); + _VAULT_.transferBaseOut(to, baseBalance.mul(amount).div(totalShares)); + _VAULT_.transferQuoteOut(to, quoteBalance.mul(amount).div(totalShares)); + _VAULT_.sync(); + } +} diff --git a/contracts/DODOVenderMachine/impl/DVMStorage.sol b/contracts/DODOVenderMachine/impl/DVMStorage.sol new file mode 100644 index 0000000..7be080d --- /dev/null +++ b/contracts/DODOVenderMachine/impl/DVMStorage.sol @@ -0,0 +1,75 @@ +/* + + 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 {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; +import {SafeMath} from "../../lib/SafeMath.sol"; +import {DODOMath} from "../../lib/DODOMath.sol"; +import {DecimalMath} from "../../lib/DecimalMath.sol"; +import {PermissionManager} from "../../lib/PermissionManager.sol"; +import {IFeeRateModel} from "../../intf/IFeeRateModel.sol"; +import {DVMVault} from "./DVMVault.sol"; + +contract DVMStorage is InitializableOwnable, ReentrancyGuard { + using SafeMath for uint256; + + // ============ Variables for Control ============ + + bool public _CLOSED_; + uint256 public _GAS_PRICE_LIMIT_; + + // ============ Advanced Controls ============ + + bool public _BUYING_ALLOWED_; + bool public _SELLING_ALLOWED_; + + PermissionManager public _TRADE_PERMISSION_; + PermissionManager public _FUNDING_PERMISSION_; + + // ============ Core Address ============ + + address public _MAINTAINER_; // collect maintainer fee + + address public _BASE_TOKEN_; + address public _QUOTE_TOKEN_; + + // ============ Variables for Pricing ============ + + IFeeRateModel public _LP_FEE_RATE_MODEL_; + IFeeRateModel public _MT_FEE_RATE_MODEL_; + uint256 public _K_; + uint256 public _I_; + uint256 public _BASE0_; + + DVMVault public _VAULT_; + DVMVault public _PROTECTION_VAULT_; + + // ============ Modifiers ============ + + modifier notClosed() { + require(!_CLOSED_, "DODO_CLOSED"); + _; + } + + // ============ Helper Functions ============ + function _updateBase0() internal { + uint256 fairAmount = DecimalMath.divFloor(_VAULT_._QUOTE_RESERVE_(), _I_); + _BASE0_ = DODOMath._SolveQuadraticFunctionForTarget( + _VAULT_._BASE_RESERVE_(), + _K_, + fairAmount + ); + } + + // ============ Version Control ============ + function version() external pure returns (uint256) { + return 101; // 1.0.1 + } +} diff --git a/contracts/DODOVenderMachine/impl/DVMTrader.sol b/contracts/DODOVenderMachine/impl/DVMTrader.sol new file mode 100644 index 0000000..9a6aea9 --- /dev/null +++ b/contracts/DODOVenderMachine/impl/DVMTrader.sol @@ -0,0 +1,69 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {DVMStorage} from "./DVMStorage.sol"; +import {SafeMath} from "../../lib/SafeMath.sol"; +import {DecimalMath} from "../../lib/DecimalMath.sol"; +import {DODOMath} from "../../lib/DODOMath.sol"; + +contract DVMTrader is DVMStorage { + using SafeMath for uint256; + + function sellBase (address to) external returns(uint256 receiveQuoteAmount){ + uint256 baseInput = _VAULT_.getBaseInput(); + uint256 mtFee; + (receiveQuoteAmount, mtFee) = querySellBase(baseInput); + _VAULT_.transferQuoteOut(to, receiveQuoteAmount); + if (mtFee>0){ + _VAULT_.transferQuoteOut(_MAINTAINER_, mtFee); + } + _VAULT_.sync(); + _updateBase0(); // 这里需要想想,原则上不需要update B0. 但精度问题,或者用户往合约里充值,可能导致需要updateBase0 + return receiveQuoteAmount; + } + + function sellQuote(address to) external returns(uint256 receiveBaseAmount){ + uint256 quoteInput = _VAULT_.getQuoteInput(); + uint256 mtFee; + (receiveBaseAmount, mtFee) = querySellQuote(quoteInput); + _VAULT_.transferBaseOut(to, receiveBaseAmount); + if (mtFee>0){ + _VAULT_.transferBaseOut(_MAINTAINER_, mtFee); + } + _VAULT_.sync(); + _updateBase0(); + return receiveBaseAmount; + } + + function querySellBase(uint256 payBaseAmount) public view returns(uint256 receiveQuoteAmount, uint256 mtFee){ + uint256 B2 = _VAULT_._BASE_RESERVE_(); + uint256 B1 = B2.add(payBaseAmount); + require(_BASE0_>=B1, "DODO_BASE_BALANCE_NOT_ENOUGH"); + uint256 Q = DODOMath._GeneralIntegrate(_BASE0_, B1, B2, _I_, _K_); + uint256 lpFeeRate = _LP_FEE_RATE_MODEL_.getFeeRate(Q); + uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(Q); + mtFee = DecimalMath.mulCeil(Q, mtFeeRate); + receiveQuoteAmount = Q.sub(mtFee).sub(DecimalMath.mulCeil(Q, lpFeeRate)); + return (receiveQuoteAmount, mtFee); + } + + function querySellQuote(uint256 payQuoteAmount) public view returns(uint256 receiveBaseAmount, uint256 mtFee){ + uint256 B1 = _VAULT_._BASE_RESERVE_(); + uint256 fairAmount = DecimalMath.divFloor(payQuoteAmount, _I_); + uint256 newBaseReserve = DODOMath._SolveQuadraticFunctionForTrade(_BASE0_,B1,fairAmount,false, _K_); + uint256 deltaBase = B1.sub(newBaseReserve); + uint256 lpFeeRate = _LP_FEE_RATE_MODEL_.getFeeRate(payQuoteAmount); + uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(payQuoteAmount); + mtFee = DecimalMath.mulCeil(deltaBase, mtFeeRate); + receiveBaseAmount = deltaBase.sub(mtFee).sub(DecimalMath.mulCeil(deltaBase, lpFeeRate)); + return (receiveBaseAmount, mtFee); + } + +} \ No newline at end of file diff --git a/contracts/DODOVenderMachine/impl/DVMVault.sol b/contracts/DODOVenderMachine/impl/DVMVault.sol new file mode 100644 index 0000000..0461d82 --- /dev/null +++ b/contracts/DODOVenderMachine/impl/DVMVault.sol @@ -0,0 +1,205 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {IERC20} from "../../intf/IERC20.sol"; +import {SafeMath} from "../../lib/SafeMath.sol"; +import {DecimalMath} from "../../lib/DecimalMath.sol"; +import {SafeERC20} from "../../lib/SafeERC20.sol"; +import {Ownable} from "../../lib/Ownable.sol"; +import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; + +contract DVMVault is InitializableOwnable { + using SafeMath for uint256; + using SafeERC20 for IERC20; + + address public _BASE_TOKEN_; + address public _QUOTE_TOKEN_; + + uint256 public _BASE_RESERVE_; + uint256 public _QUOTE_RESERVE_; + + string public symbol; + uint256 public decimals; + string public name; + + uint256 public totalSupply; + mapping(address => uint256) internal _SHARES_; + mapping(address => mapping(address => uint256)) internal _ALLOWED_; + + // ============ Events ============ + + event Transfer(address indexed from, address indexed to, uint256 amount); + + event Approval(address indexed owner, address indexed spender, uint256 amount); + + event Mint(address indexed user, uint256 value); + + event Burn(address indexed user, uint256 value); + + // init functions + function init( + address owner, + address _baseToken, + address _quoteToken + ) public notInitialized { + initOwner(owner); + string memory connect = "_"; + string memory suffix = "DLP"; + string memory uid = string(abi.encodePacked(address(this))); + name = string( + abi.encodePacked( + suffix, + connect, + IERC20(_baseToken).symbol(), + connect, + IERC20(_quoteToken).symbol(), + connect, + uid + ) + ); + symbol = "DLP"; + decimals = IERC20(_baseToken).decimals(); + } + + // Vault related + + function getVaultBalance() public view returns (uint256 baseBalance, uint256 quoteBalance) { + return ( + IERC20(_BASE_TOKEN_).balanceOf(address(this)), + IERC20(_QUOTE_TOKEN_).balanceOf(address(this)) + ); + } + + function getBaseBalance() public view returns (uint256 baseBalance) { + return IERC20(_BASE_TOKEN_).balanceOf(address(this)); + } + + function getQuoteBalance() public view returns (uint256 quoteBalance) { + return IERC20(_QUOTE_TOKEN_).balanceOf(address(this)); + } + + function getBaseInput() public view returns (uint256 input) { + return IERC20(_BASE_TOKEN_).balanceOf(address(this)).sub(_BASE_RESERVE_); + } + + function getQuoteInput() public view returns (uint256 input) { + return IERC20(_QUOTE_TOKEN_).balanceOf(address(this)).sub(_QUOTE_RESERVE_); + } + + function sync() public onlyOwner { + (uint256 baseBalance, uint256 quoteBalance) = getVaultBalance(); + if (baseBalance != _BASE_RESERVE_) { + _BASE_RESERVE_ = baseBalance; + } + if (quoteBalance != _QUOTE_RESERVE_) { + _QUOTE_RESERVE_ = quoteBalance; + } + } + + function transferOut( + address token, + address to, + uint256 amount + ) public onlyOwner { + IERC20(token).safeTransfer(to, amount); + } + + function transferBaseOut(address to, uint256 amount) public onlyOwner { + IERC20(_BASE_TOKEN_).safeTransfer(to, amount); + } + + function transferQuoteOut(address to, uint256 amount) public onlyOwner { + IERC20(_QUOTE_TOKEN_).safeTransfer(to, amount); + } + + // Shares related + /** + * @dev transfer token for a specified address + * @param to The address to transfer to. + * @param amount The amount to be transferred. + */ + function transfer(address to, uint256 amount) public returns (bool) { + require(amount <= _SHARES_[msg.sender], "BALANCE_NOT_ENOUGH"); + + _SHARES_[msg.sender] = _SHARES_[msg.sender].sub(amount); + _SHARES_[to] = _SHARES_[to].add(amount); + emit Transfer(msg.sender, to, amount); + return true; + } + + /** + * @dev Gets the balance of the specified address. + * @param owner The address to query the the balance of. + * @return balance An uint256 representing the amount owned by the passed address. + */ + function balanceOf(address owner) external view returns (uint256 balance) { + return _SHARES_[owner]; + } + + function shareRatioOf(address owner) external view returns (uint256 shareRatio) { + return DecimalMath.divFloor(_SHARES_[owner], totalSupply); + } + + /** + * @dev Transfer tokens from one address to another + * @param from address The address which you want to send tokens from + * @param to address The address which you want to transfer to + * @param amount uint256 the amount of tokens to be transferred + */ + function transferFrom( + address from, + address to, + uint256 amount + ) public returns (bool) { + require(amount <= _SHARES_[from], "BALANCE_NOT_ENOUGH"); + require(amount <= _ALLOWED_[from][msg.sender], "ALLOWANCE_NOT_ENOUGH"); + + _SHARES_[from] = _SHARES_[from].sub(amount); + _SHARES_[to] = _SHARES_[to].add(amount); + _ALLOWED_[from][msg.sender] = _ALLOWED_[from][msg.sender].sub(amount); + emit Transfer(from, to, amount); + return true; + } + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. + * @param spender The address which will spend the funds. + * @param amount The amount of tokens to be spent. + */ + function approve(address spender, uint256 amount) public returns (bool) { + _ALLOWED_[msg.sender][spender] = amount; + emit Approval(msg.sender, spender, amount); + return true; + } + + /** + * @dev Function to check the amount of tokens that an owner _ALLOWED_ to a spender. + * @param owner address The address which owns the funds. + * @param spender address The address which will spend the funds. + * @return A uint256 specifying the amount of tokens still available for the spender. + */ + function allowance(address owner, address spender) public view returns (uint256) { + return _ALLOWED_[owner][spender]; + } + + function mint(address user, uint256 value) external onlyOwner { + _SHARES_[user] = _SHARES_[user].add(value); + totalSupply = totalSupply.add(value); + emit Mint(user, value); + emit Transfer(address(0), user, value); + } + + function burn(address user, uint256 value) external onlyOwner { + _SHARES_[user] = _SHARES_[user].sub(value); + totalSupply = totalSupply.sub(value); + emit Burn(user, value); + emit Transfer(user, address(0), value); + } +} diff --git a/contracts/DODOZoo.sol b/contracts/DODOZoo.sol deleted file mode 100644 index c47cc10..0000000 --- a/contracts/DODOZoo.sol +++ /dev/null @@ -1,134 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -import {Ownable} from "./lib/Ownable.sol"; -import {IDODO} from "./intf/IDODO.sol"; -import {ICloneFactory} from "./helper/CloneFactory.sol"; - - -/** - * @title DODOZoo - * @author DODO Breeder - * - * @notice Register of All DODO - */ -contract DODOZoo is Ownable { - address public _DODO_LOGIC_; - address public _CLONE_FACTORY_; - - address public _DEFAULT_SUPERVISOR_; - - mapping(address => mapping(address => address)) internal _DODO_REGISTER_; - address[] public _DODOs; - - // ============ Events ============ - - event DODOBirth(address newBorn, address baseToken, address quoteToken); - - // ============ Constructor Function ============ - - constructor( - address _dodoLogic, - address _cloneFactory, - address _defaultSupervisor - ) public { - _DODO_LOGIC_ = _dodoLogic; - _CLONE_FACTORY_ = _cloneFactory; - _DEFAULT_SUPERVISOR_ = _defaultSupervisor; - } - - // ============ Admin Function ============ - - function setDODOLogic(address _dodoLogic) external onlyOwner { - _DODO_LOGIC_ = _dodoLogic; - } - - function setCloneFactory(address _cloneFactory) external onlyOwner { - _CLONE_FACTORY_ = _cloneFactory; - } - - function setDefaultSupervisor(address _defaultSupervisor) external onlyOwner { - _DEFAULT_SUPERVISOR_ = _defaultSupervisor; - } - - function removeDODO(address dodo) external onlyOwner { - address baseToken = IDODO(dodo)._BASE_TOKEN_(); - address quoteToken = IDODO(dodo)._QUOTE_TOKEN_(); - require(isDODORegistered(baseToken, quoteToken), "DODO_NOT_REGISTERED"); - _DODO_REGISTER_[baseToken][quoteToken] = address(0); - for (uint256 i = 0; i <= _DODOs.length - 1; i++) { - if (_DODOs[i] == dodo) { - _DODOs[i] = _DODOs[_DODOs.length - 1]; - _DODOs.pop(); - break; - } - } - } - - function addDODO(address dodo) public onlyOwner { - address baseToken = IDODO(dodo)._BASE_TOKEN_(); - address quoteToken = IDODO(dodo)._QUOTE_TOKEN_(); - require(!isDODORegistered(baseToken, quoteToken), "DODO_REGISTERED"); - _DODO_REGISTER_[baseToken][quoteToken] = dodo; - _DODOs.push(dodo); - } - - // ============ Breed DODO Function ============ - - function breedDODO( - address maintainer, - address baseToken, - address quoteToken, - address oracle, - uint256 lpFeeRate, - uint256 mtFeeRate, - uint256 k, - uint256 gasPriceLimit - ) external onlyOwner returns (address newBornDODO) { - require(!isDODORegistered(baseToken, quoteToken), "DODO_REGISTERED"); - newBornDODO = ICloneFactory(_CLONE_FACTORY_).clone(_DODO_LOGIC_); - IDODO(newBornDODO).init( - _OWNER_, - _DEFAULT_SUPERVISOR_, - maintainer, - baseToken, - quoteToken, - oracle, - lpFeeRate, - mtFeeRate, - k, - gasPriceLimit - ); - addDODO(newBornDODO); - emit DODOBirth(newBornDODO, baseToken, quoteToken); - return newBornDODO; - } - - // ============ View Functions ============ - - function isDODORegistered(address baseToken, address quoteToken) public view returns (bool) { - if ( - _DODO_REGISTER_[baseToken][quoteToken] == address(0) && - _DODO_REGISTER_[quoteToken][baseToken] == address(0) - ) { - return false; - } else { - return true; - } - } - - function getDODO(address baseToken, address quoteToken) external view returns (address) { - return _DODO_REGISTER_[baseToken][quoteToken]; - } - - function getDODOs() external view returns (address[] memory) { - return _DODOs; - } -} diff --git a/contracts/dodo.sol b/contracts/dodo.sol deleted file mode 100644 index 8e63036..0000000 --- a/contracts/dodo.sol +++ /dev/null @@ -1,74 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -import {Types} from "./lib/Types.sol"; -import {IERC20} from "./intf/IERC20.sol"; -import {Storage} from "./impl/Storage.sol"; -import {Trader} from "./impl/Trader.sol"; -import {LiquidityProvider} from "./impl/LiquidityProvider.sol"; -import {Admin} from "./impl/Admin.sol"; -import {DODOLpToken} from "./impl/DODOLpToken.sol"; - - -/** - * @title DODO - * @author DODO Breeder - * - * @notice Entrance for users - */ -contract DODO is Admin, Trader, LiquidityProvider { - function init( - address owner, - address supervisor, - address maintainer, - address baseToken, - address quoteToken, - address oracle, - uint256 lpFeeRate, - uint256 mtFeeRate, - uint256 k, - uint256 gasPriceLimit - ) external { - require(!_INITIALIZED_, "DODO_INITIALIZED"); - _INITIALIZED_ = true; - - // constructor - _OWNER_ = owner; - emit OwnershipTransferred(address(0), _OWNER_); - - _SUPERVISOR_ = supervisor; - _MAINTAINER_ = maintainer; - _BASE_TOKEN_ = baseToken; - _QUOTE_TOKEN_ = quoteToken; - _ORACLE_ = oracle; - - _DEPOSIT_BASE_ALLOWED_ = false; - _DEPOSIT_QUOTE_ALLOWED_ = false; - _TRADE_ALLOWED_ = false; - _GAS_PRICE_LIMIT_ = gasPriceLimit; - - // Advanced controls are disabled by default - _BUYING_ALLOWED_ = true; - _SELLING_ALLOWED_ = true; - uint256 MAX_INT = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - _BASE_BALANCE_LIMIT_ = MAX_INT; - _QUOTE_BALANCE_LIMIT_ = MAX_INT; - - _LP_FEE_RATE_ = lpFeeRate; - _MT_FEE_RATE_ = mtFeeRate; - _K_ = k; - _R_STATUS_ = Types.RStatus.ONE; - - _BASE_CAPITAL_TOKEN_ = address(new DODOLpToken(_BASE_TOKEN_)); - _QUOTE_CAPITAL_TOKEN_ = address(new DODOLpToken(_QUOTE_TOKEN_)); - - _checkDODOParameters(); - } -} diff --git a/contracts/helper/TestERC20.sol b/contracts/external/ERC20/NormalERC20.sol similarity index 97% rename from contracts/helper/TestERC20.sol rename to contracts/external/ERC20/NormalERC20.sol index 5d02d72..416b1e8 100644 --- a/contracts/helper/TestERC20.sol +++ b/contracts/external/ERC20/NormalERC20.sol @@ -7,7 +7,7 @@ pragma solidity 0.6.9; -import {SafeMath} from "../lib/SafeMath.sol"; +import {SafeMath} from "../../lib/SafeMath.sol"; contract TestERC20 { using SafeMath for uint256; diff --git a/contracts/helper/TestWETH.sol b/contracts/external/ERC20/TestWETH.sol similarity index 100% rename from contracts/helper/TestWETH.sol rename to contracts/external/ERC20/TestWETH.sol diff --git a/contracts/helper/UniswapV2.sol b/contracts/external/uniswap/UniswapV2.sol similarity index 98% rename from contracts/helper/UniswapV2.sol rename to contracts/external/uniswap/UniswapV2.sol index 282d07b..6e1b28c 100644 --- a/contracts/helper/UniswapV2.sol +++ b/contracts/external/uniswap/UniswapV2.sol @@ -1,5 +1,6 @@ -/** - *Submitted for verification at Etherscan.io on 2020-05-05 +/* + Submitted for verification at Etherscan.io on 2020-05-05 + SPDX-License-Identifier: Apache-2.0 */ // File: contracts/interfaces/IUniswapV2Pair.sol @@ -184,7 +185,8 @@ contract UniswapV2ERC20 { bytes32 public DOMAIN_SEPARATOR; // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); - bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; + bytes32 + public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; mapping(address => uint256) public nonces; event Approval(address indexed owner, address indexed spender, uint256 value); diff --git a/contracts/helper/BandBNBBUSDPriceOracleProxy.sol b/contracts/helper/BandBNBBUSDPriceOracleProxy.sol deleted file mode 100644 index 681f31a..0000000 --- a/contracts/helper/BandBNBBUSDPriceOracleProxy.sol +++ /dev/null @@ -1,30 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - - -interface IBandOracleAggregator { - function getReferenceData(string memory base, string memory quote) - external - view - returns (uint256); -} - - -contract BandBNBBUSDPriceOracleProxy { - IBandOracleAggregator public aggregator; - - constructor(IBandOracleAggregator _aggregator) public { - aggregator = _aggregator; - } - - function getPrice() public view returns (uint256) { - return aggregator.getReferenceData("BNB", "USD"); - } -} diff --git a/contracts/helper/ChainlinkCOMPUSDCPriceOracleProxy.sol b/contracts/helper/ChainlinkCOMPUSDCPriceOracleProxy.sol deleted file mode 100644 index 6f9bce8..0000000 --- a/contracts/helper/ChainlinkCOMPUSDCPriceOracleProxy.sol +++ /dev/null @@ -1,25 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - - -interface IChainlink { - function latestAnswer() external view returns (uint256); -} - - -// for COMP-USDC(decimals=6) price convert - -contract ChainlinkCOMPUSDCPriceOracleProxy { - address public chainlink = 0xdbd020CAeF83eFd542f4De03e3cF0C28A4428bd5; - - function getPrice() external view returns (uint256) { - return IChainlink(chainlink).latestAnswer() / 100; - } -} diff --git a/contracts/helper/ChainlinkEthUSDCPriceOracleProxy.sol b/contracts/helper/ChainlinkEthUSDCPriceOracleProxy.sol deleted file mode 100644 index 53036a7..0000000 --- a/contracts/helper/ChainlinkEthUSDCPriceOracleProxy.sol +++ /dev/null @@ -1,25 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - - -interface IChainlink { - function latestAnswer() external view returns (uint256); -} - - -// for WETH-USDC(decimals=6) price convert - -contract ChainlinkETHPriceOracleProxy { - address public chainlink = 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419; - - function getPrice() external view returns (uint256) { - return IChainlink(chainlink).latestAnswer() / 100; - } -} diff --git a/contracts/helper/ChainlinkEthUSDTPriceOracleProxy.sol b/contracts/helper/ChainlinkEthUSDTPriceOracleProxy.sol deleted file mode 100644 index 6d27f7a..0000000 --- a/contracts/helper/ChainlinkEthUSDTPriceOracleProxy.sol +++ /dev/null @@ -1,25 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - - -interface IChainlink { - function latestAnswer() external view returns (uint256); -} - - -// for WETH-USDT(decimals=6) price convert - -contract ChainlinkETHUSDTPriceOracleProxy { - address public chainlink = 0xEe9F2375b4bdF6387aa8265dD4FB8F16512A1d46; - - function getPrice() external view returns (uint256) { - return 10**24 / IChainlink(chainlink).latestAnswer(); - } -} diff --git a/contracts/helper/ChainlinkLENDUSDCPriceOracleProxy.sol b/contracts/helper/ChainlinkLENDUSDCPriceOracleProxy.sol deleted file mode 100644 index c338136..0000000 --- a/contracts/helper/ChainlinkLENDUSDCPriceOracleProxy.sol +++ /dev/null @@ -1,25 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - - -interface IChainlink { - function latestAnswer() external view returns (uint256); -} - - -// for LEND-USDC(decimals=6) price convert - -contract ChainlinkLENDUSDCPriceOracleProxy { - address public chainlink = 0x4aB81192BB75474Cf203B56c36D6a13623270A67; - - function getPrice() external view returns (uint256) { - return IChainlink(chainlink).latestAnswer() / 100; - } -} diff --git a/contracts/helper/ChainlinkLINKUSDPriceOracleProxy.sol b/contracts/helper/ChainlinkLINKUSDPriceOracleProxy.sol deleted file mode 100644 index fe6615d..0000000 --- a/contracts/helper/ChainlinkLINKUSDPriceOracleProxy.sol +++ /dev/null @@ -1,25 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - - -interface IChainlink { - function latestAnswer() external view returns (uint256); -} - - -// for LINK-USDC(decimals=6) price convert - -contract ChainlinkLINKUSDCPriceOracleProxy { - address public chainlink = 0x2c1d072e956AFFC0D435Cb7AC38EF18d24d9127c; - - function getPrice() external view returns (uint256) { - return IChainlink(chainlink).latestAnswer() / 100; - } -} diff --git a/contracts/helper/ChainlinkSNXUSDPriceOracleProxy.sol b/contracts/helper/ChainlinkSNXUSDPriceOracleProxy.sol deleted file mode 100644 index e3d478c..0000000 --- a/contracts/helper/ChainlinkSNXUSDPriceOracleProxy.sol +++ /dev/null @@ -1,25 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - - -interface IChainlink { - function latestAnswer() external view returns (uint256); -} - - -// for SNX-USDC(decimals=6) price convert - -contract ChainlinkSNXUSDCPriceOracleProxy { - address public chainlink = 0xDC3EA94CD0AC27d9A86C180091e7f78C683d3699; - - function getPrice() external view returns (uint256) { - return IChainlink(chainlink).latestAnswer() / 100; - } -} diff --git a/contracts/helper/ChainlinkWBTCUSDCPriceOracleProxy.sol b/contracts/helper/ChainlinkWBTCUSDCPriceOracleProxy.sol deleted file mode 100644 index ae78ebd..0000000 --- a/contracts/helper/ChainlinkWBTCUSDCPriceOracleProxy.sol +++ /dev/null @@ -1,25 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - - -interface IChainlink { - function latestAnswer() external view returns (uint256); -} - - -// for WBTC(decimals=8)-USDC(decimals=6) price convert - -contract ChainlinkWBTCUSDCPriceOracleProxy { - address public chainlink = 0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c; - - function getPrice() external view returns (uint256) { - return IChainlink(chainlink).latestAnswer() * (10**8); - } -} diff --git a/contracts/helper/ChainlinkYFIUSDCPriceOracleProxy.sol b/contracts/helper/ChainlinkYFIUSDCPriceOracleProxy.sol deleted file mode 100644 index b2d071d..0000000 --- a/contracts/helper/ChainlinkYFIUSDCPriceOracleProxy.sol +++ /dev/null @@ -1,32 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -import {SafeMath} from "../lib/SafeMath.sol"; - - -interface IChainlink { - function latestAnswer() external view returns (uint256); -} - - -// for YFI-USDC(decimals=6) price convert - -contract ChainlinkYFIUSDCPriceOracleProxy { - using SafeMath for uint256; - - address public yfiEth = 0x7c5d4F8345e66f68099581Db340cd65B078C41f4; - address public EthUsd = 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419; - - function getPrice() external view returns (uint256) { - uint256 yfiEthPrice = IChainlink(yfiEth).latestAnswer(); - uint256 EthUsdPrice = IChainlink(EthUsd).latestAnswer(); - return yfiEthPrice.mul(EthUsdPrice).div(10**20); - } -} diff --git a/contracts/helper/ConstFeeRateModel.sol b/contracts/helper/ConstFeeRateModel.sol new file mode 100644 index 0000000..a3afd17 --- /dev/null +++ b/contracts/helper/ConstFeeRateModel.sol @@ -0,0 +1,23 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {IFeeRateModel} from "../intf/IFeeRateModel.sol"; + +contract ConstFeeRateModel is IFeeRateModel { + uint256 public _FEE_RATE_; + + constructor(uint256 feeRate) public { + feeRate = _FEE_RATE_; + } + + function getFeeRate(uint256) external override view returns (uint256) { + return _FEE_RATE_; + } +} diff --git a/contracts/helper/ConstOracle.sol b/contracts/helper/ConstOracle.sol deleted file mode 100644 index 87181e8..0000000 --- a/contracts/helper/ConstOracle.sol +++ /dev/null @@ -1,22 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - - -contract ConstOracle { - uint256 public tokenPrice; - - constructor(uint256 _price) public { - tokenPrice = _price; - } - - function getPrice() external view returns (uint256) { - return tokenPrice; - } -} diff --git a/contracts/helper/MinimumOracle.sol b/contracts/helper/MinimumOracle.sol deleted file mode 100644 index 0056add..0000000 --- a/contracts/helper/MinimumOracle.sol +++ /dev/null @@ -1,56 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - - -interface IMinimumOracle { - function getPrice() external view returns (uint256); - - function setPrice(uint256 newPrice) external; - - function transferOwnership(address newOwner) external; -} - - -contract MinimumOracle { - address public _OWNER_; - uint256 public tokenPrice; - - // ============ Events ============ - - event OwnershipTransfer(address indexed previousOwner, address indexed newOwner); - - // ============ Modifiers ============ - - modifier onlyOwner() { - require(msg.sender == _OWNER_, "NOT_OWNER"); - _; - } - - // ============ Functions ============ - - constructor() public { - _OWNER_ = msg.sender; - emit OwnershipTransfer(address(0), _OWNER_); - } - - function transferOwnership(address newOwner) external onlyOwner { - require(newOwner != address(0), "INVALID_OWNER"); - emit OwnershipTransfer(_OWNER_, newOwner); - _OWNER_ = newOwner; - } - - function setPrice(uint256 newPrice) external onlyOwner { - tokenPrice = newPrice; - } - - function getPrice() external view returns (uint256) { - return tokenPrice; - } -} diff --git a/contracts/helper/NaiveFeeRateModel.sol b/contracts/helper/NaiveFeeRateModel.sol new file mode 100644 index 0000000..1a244e1 --- /dev/null +++ b/contracts/helper/NaiveFeeRateModel.sol @@ -0,0 +1,28 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {IFeeRateModel} from "../intf/IFeeRateModel.sol"; +import {Ownable} from "../lib/Ownable.sol"; + +contract ConstFeeRateModel is Ownable, IFeeRateModel { + uint256 public _FEE_RATE_; + + constructor(uint256 feeRate) public { + feeRate = _FEE_RATE_; + } + + function setFeeRate(uint256 newFeeRate) external { + _FEE_RATE_ = newFeeRate; + } + + function getFeeRate(uint256) external override view returns (uint256) { + return _FEE_RATE_; + } +} diff --git a/contracts/helper/NaiveOracle.sol b/contracts/helper/NaiveOracle.sol deleted file mode 100644 index b472960..0000000 --- a/contracts/helper/NaiveOracle.sol +++ /dev/null @@ -1,25 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -import {Ownable} from "../lib/Ownable.sol"; - - -// Oracle only for test -contract NaiveOracle is Ownable { - uint256 public tokenPrice; - - function setPrice(uint256 newPrice) external onlyOwner { - tokenPrice = newPrice; - } - - function getPrice() external view returns (uint256) { - return tokenPrice; - } -} diff --git a/contracts/helper/UniswapArbitrageur.sol b/contracts/helper/UniswapArbitrageur.sol deleted file mode 100644 index a67433e..0000000 --- a/contracts/helper/UniswapArbitrageur.sol +++ /dev/null @@ -1,165 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -import {Ownable} from "../lib/Ownable.sol"; -import {IDODO} from "../intf/IDODO.sol"; -import {IERC20} from "../intf/IERC20.sol"; -import {SafeERC20} from "../lib/SafeERC20.sol"; -import {SafeMath} from "../lib/SafeMath.sol"; - -interface IUniswapV2Pair { - function token0() external view returns (address); - - function token1() external view returns (address); - - function getReserves() - external - view - returns ( - uint112 reserve0, - uint112 reserve1, - uint32 blockTimestampLast - ); - - function swap( - uint256 amount0Out, - uint256 amount1Out, - address to, - bytes calldata data - ) external; -} - -contract UniswapArbitrageur { - using SafeMath for uint256; - using SafeERC20 for IERC20; - - address public _UNISWAP_; - address public _DODO_; - address public _BASE_; - address public _QUOTE_; - - bool public _REVERSE_; // true if dodo.baseToken=uniswap.token0 - - constructor(address _uniswap, address _dodo) public { - _UNISWAP_ = _uniswap; - _DODO_ = _dodo; - - _BASE_ = IDODO(_DODO_)._BASE_TOKEN_(); - _QUOTE_ = IDODO(_DODO_)._QUOTE_TOKEN_(); - - address token0 = IUniswapV2Pair(_UNISWAP_).token0(); - address token1 = IUniswapV2Pair(_UNISWAP_).token1(); - - if (token0 == _BASE_ && token1 == _QUOTE_) { - _REVERSE_ = false; - } else if (token0 == _QUOTE_ && token1 == _BASE_) { - _REVERSE_ = true; - } else { - require(true, "DODO_UNISWAP_NOT_MATCH"); - } - - IERC20(_BASE_).approve(_DODO_, uint256(-1)); - IERC20(_QUOTE_).approve(_DODO_, uint256(-1)); - } - - function executeBuyArbitrage(uint256 baseAmount) external returns (uint256 quoteProfit) { - IDODO(_DODO_).buyBaseToken(baseAmount, uint256(-1), "0xd"); - quoteProfit = IERC20(_QUOTE_).balanceOf(address(this)); - IERC20(_QUOTE_).transfer(msg.sender, quoteProfit); - return quoteProfit; - } - - function executeSellArbitrage(uint256 baseAmount) external returns (uint256 baseProfit) { - IDODO(_DODO_).sellBaseToken(baseAmount, 0, "0xd"); - baseProfit = IERC20(_BASE_).balanceOf(address(this)); - IERC20(_BASE_).transfer(msg.sender, baseProfit); - return baseProfit; - } - - function dodoCall( - bool isDODOBuy, - uint256 baseAmount, - uint256 quoteAmount, - bytes calldata - ) external { - require(msg.sender == _DODO_, "WRONG_DODO"); - if (_REVERSE_) { - _inverseArbitrage(isDODOBuy, baseAmount, quoteAmount); - } else { - _arbitrage(isDODOBuy, baseAmount, quoteAmount); - } - } - - function _inverseArbitrage( - bool isDODOBuy, - uint256 baseAmount, - uint256 quoteAmount - ) internal { - (uint112 _reserve0, uint112 _reserve1, ) = IUniswapV2Pair(_UNISWAP_).getReserves(); - uint256 token0Balance = uint256(_reserve0); - uint256 token1Balance = uint256(_reserve1); - uint256 token0Amount; - uint256 token1Amount; - if (isDODOBuy) { - IERC20(_BASE_).transfer(_UNISWAP_, baseAmount); - // transfer token1 into uniswap - uint256 newToken0Balance = token0Balance.mul(token1Balance).div( - token1Balance.add(baseAmount) - ); - token0Amount = token0Balance.sub(newToken0Balance).mul(9969).div(10000); // mul 0.9969 - require(token0Amount > quoteAmount, "NOT_PROFITABLE"); - IUniswapV2Pair(_UNISWAP_).swap(token0Amount, token1Amount, address(this), ""); - } else { - IERC20(_QUOTE_).transfer(_UNISWAP_, quoteAmount); - // transfer token0 into uniswap - uint256 newToken1Balance = token0Balance.mul(token1Balance).div( - token0Balance.add(quoteAmount) - ); - token1Amount = token1Balance.sub(newToken1Balance).mul(9969).div(10000); // mul 0.9969 - require(token1Amount > baseAmount, "NOT_PROFITABLE"); - IUniswapV2Pair(_UNISWAP_).swap(token0Amount, token1Amount, address(this), ""); - } - } - - function _arbitrage( - bool isDODOBuy, - uint256 baseAmount, - uint256 quoteAmount - ) internal { - (uint112 _reserve0, uint112 _reserve1, ) = IUniswapV2Pair(_UNISWAP_).getReserves(); - uint256 token0Balance = uint256(_reserve0); - uint256 token1Balance = uint256(_reserve1); - uint256 token0Amount; - uint256 token1Amount; - if (isDODOBuy) { - IERC20(_BASE_).transfer(_UNISWAP_, baseAmount); - // transfer token0 into uniswap - uint256 newToken1Balance = token1Balance.mul(token0Balance).div( - token0Balance.add(baseAmount) - ); - token1Amount = token1Balance.sub(newToken1Balance).mul(9969).div(10000); // mul 0.9969 - require(token1Amount > quoteAmount, "NOT_PROFITABLE"); - IUniswapV2Pair(_UNISWAP_).swap(token0Amount, token1Amount, address(this), ""); - } else { - IERC20(_QUOTE_).transfer(_UNISWAP_, quoteAmount); - // transfer token1 into uniswap - uint256 newToken0Balance = token1Balance.mul(token0Balance).div( - token1Balance.add(quoteAmount) - ); - token0Amount = token0Balance.sub(newToken0Balance).mul(9969).div(10000); // mul 0.9969 - require(token0Amount > baseAmount, "NOT_PROFITABLE"); - IUniswapV2Pair(_UNISWAP_).swap(token0Amount, token1Amount, address(this), ""); - } - } - - function retrieve(address token, uint256 amount) external { - IERC20(token).safeTransfer(msg.sender, amount); - } -} diff --git a/contracts/impl/Admin.sol b/contracts/impl/Admin.sol deleted file mode 100644 index 2e5edf7..0000000 --- a/contracts/impl/Admin.sol +++ /dev/null @@ -1,122 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -import {Storage} from "./Storage.sol"; - - -/** - * @title Admin - * @author DODO Breeder - * - * @notice Functions for admin operations - */ -contract Admin is Storage { - // ============ Events ============ - - event UpdateGasPriceLimit(uint256 oldGasPriceLimit, uint256 newGasPriceLimit); - - event UpdateLiquidityProviderFeeRate( - uint256 oldLiquidityProviderFeeRate, - uint256 newLiquidityProviderFeeRate - ); - - event UpdateMaintainerFeeRate(uint256 oldMaintainerFeeRate, uint256 newMaintainerFeeRate); - - event UpdateK(uint256 oldK, uint256 newK); - - // ============ Params Setting Functions ============ - - function setOracle(address newOracle) external onlyOwner { - _ORACLE_ = newOracle; - } - - function setSupervisor(address newSupervisor) external onlyOwner { - _SUPERVISOR_ = newSupervisor; - } - - function setMaintainer(address newMaintainer) external onlyOwner { - _MAINTAINER_ = newMaintainer; - } - - function setLiquidityProviderFeeRate(uint256 newLiquidityPorviderFeeRate) external onlyOwner { - emit UpdateLiquidityProviderFeeRate(_LP_FEE_RATE_, newLiquidityPorviderFeeRate); - _LP_FEE_RATE_ = newLiquidityPorviderFeeRate; - _checkDODOParameters(); - } - - function setMaintainerFeeRate(uint256 newMaintainerFeeRate) external onlyOwner { - emit UpdateMaintainerFeeRate(_MT_FEE_RATE_, newMaintainerFeeRate); - _MT_FEE_RATE_ = newMaintainerFeeRate; - _checkDODOParameters(); - } - - function setK(uint256 newK) external onlyOwner { - emit UpdateK(_K_, newK); - _K_ = newK; - _checkDODOParameters(); - } - - function setGasPriceLimit(uint256 newGasPriceLimit) external onlySupervisorOrOwner { - emit UpdateGasPriceLimit(_GAS_PRICE_LIMIT_, newGasPriceLimit); - _GAS_PRICE_LIMIT_ = newGasPriceLimit; - } - - // ============ System Control Functions ============ - - function disableTrading() external onlySupervisorOrOwner { - _TRADE_ALLOWED_ = false; - } - - function enableTrading() external onlyOwner notClosed { - _TRADE_ALLOWED_ = true; - } - - function disableQuoteDeposit() external onlySupervisorOrOwner { - _DEPOSIT_QUOTE_ALLOWED_ = false; - } - - function enableQuoteDeposit() external onlyOwner notClosed { - _DEPOSIT_QUOTE_ALLOWED_ = true; - } - - function disableBaseDeposit() external onlySupervisorOrOwner { - _DEPOSIT_BASE_ALLOWED_ = false; - } - - function enableBaseDeposit() external onlyOwner notClosed { - _DEPOSIT_BASE_ALLOWED_ = true; - } - - // ============ Advanced Control Functions ============ - - function disableBuying() external onlySupervisorOrOwner { - _BUYING_ALLOWED_ = false; - } - - function enableBuying() external onlyOwner notClosed { - _BUYING_ALLOWED_ = true; - } - - function disableSelling() external onlySupervisorOrOwner { - _SELLING_ALLOWED_ = false; - } - - function enableSelling() external onlyOwner notClosed { - _SELLING_ALLOWED_ = true; - } - - function setBaseBalanceLimit(uint256 newBaseBalanceLimit) external onlyOwner notClosed { - _BASE_BALANCE_LIMIT_ = newBaseBalanceLimit; - } - - function setQuoteBalanceLimit(uint256 newQuoteBalanceLimit) external onlyOwner notClosed { - _QUOTE_BALANCE_LIMIT_ = newQuoteBalanceLimit; - } -} diff --git a/contracts/impl/DODOLpToken.sol b/contracts/impl/DODOLpToken.sol deleted file mode 100644 index 33e47b7..0000000 --- a/contracts/impl/DODOLpToken.sol +++ /dev/null @@ -1,134 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -import {IERC20} from "../intf/IERC20.sol"; -import {SafeMath} from "../lib/SafeMath.sol"; -import {Ownable} from "../lib/Ownable.sol"; - -/** - * @title DODOLpToken - * @author DODO Breeder - * - * @notice Tokenize liquidity pool assets. An ordinary ERC20 contract with mint and burn functions - */ -contract DODOLpToken is Ownable { - using SafeMath for uint256; - - string public symbol = "DLP"; - address public originToken; - - uint256 public totalSupply; - mapping(address => uint256) internal balances; - mapping(address => mapping(address => uint256)) internal allowed; - - // ============ Events ============ - - event Transfer(address indexed from, address indexed to, uint256 amount); - - event Approval(address indexed owner, address indexed spender, uint256 amount); - - event Mint(address indexed user, uint256 value); - - event Burn(address indexed user, uint256 value); - - // ============ Functions ============ - - constructor(address _originToken) public { - originToken = _originToken; - } - - function name() public view returns (string memory) { - string memory lpTokenSuffix = "_DODO_LP_TOKEN_"; - return string(abi.encodePacked(IERC20(originToken).name(), lpTokenSuffix)); - } - - function decimals() public view returns (uint8) { - return IERC20(originToken).decimals(); - } - - /** - * @dev transfer token for a specified address - * @param to The address to transfer to. - * @param amount The amount to be transferred. - */ - function transfer(address to, uint256 amount) public returns (bool) { - require(amount <= balances[msg.sender], "BALANCE_NOT_ENOUGH"); - - balances[msg.sender] = balances[msg.sender].sub(amount); - balances[to] = balances[to].add(amount); - emit Transfer(msg.sender, to, amount); - return true; - } - - /** - * @dev Gets the balance of the specified address. - * @param owner The address to query the the balance of. - * @return balance An uint256 representing the amount owned by the passed address. - */ - function balanceOf(address owner) external view returns (uint256 balance) { - return balances[owner]; - } - - /** - * @dev Transfer tokens from one address to another - * @param from address The address which you want to send tokens from - * @param to address The address which you want to transfer to - * @param amount uint256 the amount of tokens to be transferred - */ - function transferFrom( - address from, - address to, - uint256 amount - ) public returns (bool) { - require(amount <= balances[from], "BALANCE_NOT_ENOUGH"); - require(amount <= allowed[from][msg.sender], "ALLOWANCE_NOT_ENOUGH"); - - balances[from] = balances[from].sub(amount); - balances[to] = balances[to].add(amount); - allowed[from][msg.sender] = allowed[from][msg.sender].sub(amount); - emit Transfer(from, to, amount); - return true; - } - - /** - * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. - * @param spender The address which will spend the funds. - * @param amount The amount of tokens to be spent. - */ - function approve(address spender, uint256 amount) public returns (bool) { - allowed[msg.sender][spender] = amount; - emit Approval(msg.sender, spender, amount); - return true; - } - - /** - * @dev Function to check the amount of tokens that an owner allowed to a spender. - * @param owner address The address which owns the funds. - * @param spender address The address which will spend the funds. - * @return A uint256 specifying the amount of tokens still available for the spender. - */ - function allowance(address owner, address spender) public view returns (uint256) { - return allowed[owner][spender]; - } - - function mint(address user, uint256 value) external onlyOwner { - balances[user] = balances[user].add(value); - totalSupply = totalSupply.add(value); - emit Mint(user, value); - emit Transfer(address(0), user, value); - } - - function burn(address user, uint256 value) external onlyOwner { - balances[user] = balances[user].sub(value); - totalSupply = totalSupply.sub(value); - emit Burn(user, value); - emit Transfer(user, address(0), value); - } -} diff --git a/contracts/impl/LiquidityProvider.sol b/contracts/impl/LiquidityProvider.sol deleted file mode 100644 index 237209d..0000000 --- a/contracts/impl/LiquidityProvider.sol +++ /dev/null @@ -1,345 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -import {SafeMath} from "../lib/SafeMath.sol"; -import {DecimalMath} from "../lib/DecimalMath.sol"; -import {DODOMath} from "../lib/DODOMath.sol"; -import {Types} from "../lib/Types.sol"; -import {IDODOLpToken} from "../intf/IDODOLpToken.sol"; -import {Storage} from "./Storage.sol"; -import {Settlement} from "./Settlement.sol"; -import {Pricing} from "./Pricing.sol"; - - -/** - * @title LiquidityProvider - * @author DODO Breeder - * - * @notice Functions for liquidity provider operations - */ -contract LiquidityProvider is Storage, Pricing, Settlement { - using SafeMath for uint256; - - // ============ Events ============ - - event Deposit( - address indexed payer, - address indexed receiver, - bool isBaseToken, - uint256 amount, - uint256 lpTokenAmount - ); - - event Withdraw( - address indexed payer, - address indexed receiver, - bool isBaseToken, - uint256 amount, - uint256 lpTokenAmount - ); - - event ChargePenalty(address indexed payer, bool isBaseToken, uint256 amount); - - // ============ Modifiers ============ - - modifier depositQuoteAllowed() { - require(_DEPOSIT_QUOTE_ALLOWED_, "DEPOSIT_QUOTE_NOT_ALLOWED"); - _; - } - - modifier depositBaseAllowed() { - require(_DEPOSIT_BASE_ALLOWED_, "DEPOSIT_BASE_NOT_ALLOWED"); - _; - } - - modifier dodoNotClosed() { - require(!_CLOSED_, "DODO_CLOSED"); - _; - } - - // ============ Routine Functions ============ - - function withdrawBase(uint256 amount) external returns (uint256) { - return withdrawBaseTo(msg.sender, amount); - } - - function depositBase(uint256 amount) external returns (uint256) { - return depositBaseTo(msg.sender, amount); - } - - function withdrawQuote(uint256 amount) external returns (uint256) { - return withdrawQuoteTo(msg.sender, amount); - } - - function depositQuote(uint256 amount) external returns (uint256) { - return depositQuoteTo(msg.sender, amount); - } - - function withdrawAllBase() external returns (uint256) { - return withdrawAllBaseTo(msg.sender); - } - - function withdrawAllQuote() external returns (uint256) { - return withdrawAllQuoteTo(msg.sender); - } - - // ============ Deposit Functions ============ - - function depositQuoteTo(address to, uint256 amount) - public - preventReentrant - depositQuoteAllowed - returns (uint256) - { - (, uint256 quoteTarget) = getExpectedTarget(); - uint256 totalQuoteCapital = getTotalQuoteCapital(); - uint256 capital = amount; - if (totalQuoteCapital == 0) { - // give remaining quote token to lp as a gift - capital = amount.add(quoteTarget); - } else if (quoteTarget > 0) { - capital = amount.mul(totalQuoteCapital).div(quoteTarget); - } - - // settlement - _quoteTokenTransferIn(msg.sender, amount); - _mintQuoteCapital(to, capital); - _TARGET_QUOTE_TOKEN_AMOUNT_ = _TARGET_QUOTE_TOKEN_AMOUNT_.add(amount); - - emit Deposit(msg.sender, to, false, amount, capital); - return capital; - } - - function depositBaseTo(address to, uint256 amount) - public - preventReentrant - depositBaseAllowed - returns (uint256) - { - (uint256 baseTarget, ) = getExpectedTarget(); - uint256 totalBaseCapital = getTotalBaseCapital(); - uint256 capital = amount; - if (totalBaseCapital == 0) { - // give remaining base token to lp as a gift - capital = amount.add(baseTarget); - } else if (baseTarget > 0) { - capital = amount.mul(totalBaseCapital).div(baseTarget); - } - - // settlement - _baseTokenTransferIn(msg.sender, amount); - _mintBaseCapital(to, capital); - _TARGET_BASE_TOKEN_AMOUNT_ = _TARGET_BASE_TOKEN_AMOUNT_.add(amount); - - emit Deposit(msg.sender, to, true, amount, capital); - return capital; - } - - // ============ Withdraw Functions ============ - - function withdrawQuoteTo(address to, uint256 amount) - public - preventReentrant - dodoNotClosed - returns (uint256) - { - // calculate capital - (, uint256 quoteTarget) = getExpectedTarget(); - uint256 totalQuoteCapital = getTotalQuoteCapital(); - require(totalQuoteCapital > 0, "NO_QUOTE_LP"); - - uint256 requireQuoteCapital = amount.mul(totalQuoteCapital).divCeil(quoteTarget); - require( - requireQuoteCapital <= getQuoteCapitalBalanceOf(msg.sender), - "LP_QUOTE_CAPITAL_BALANCE_NOT_ENOUGH" - ); - - // handle penalty, penalty may exceed amount - uint256 penalty = getWithdrawQuotePenalty(amount); - require(penalty < amount, "PENALTY_EXCEED"); - - // settlement - _TARGET_QUOTE_TOKEN_AMOUNT_ = _TARGET_QUOTE_TOKEN_AMOUNT_.sub(amount); - _burnQuoteCapital(msg.sender, requireQuoteCapital); - _quoteTokenTransferOut(to, amount.sub(penalty)); - _donateQuoteToken(penalty); - - emit Withdraw(msg.sender, to, false, amount.sub(penalty), requireQuoteCapital); - emit ChargePenalty(msg.sender, false, penalty); - - return amount.sub(penalty); - } - - function withdrawBaseTo(address to, uint256 amount) - public - preventReentrant - dodoNotClosed - returns (uint256) - { - // calculate capital - (uint256 baseTarget, ) = getExpectedTarget(); - uint256 totalBaseCapital = getTotalBaseCapital(); - require(totalBaseCapital > 0, "NO_BASE_LP"); - - uint256 requireBaseCapital = amount.mul(totalBaseCapital).divCeil(baseTarget); - require( - requireBaseCapital <= getBaseCapitalBalanceOf(msg.sender), - "LP_BASE_CAPITAL_BALANCE_NOT_ENOUGH" - ); - - // handle penalty, penalty may exceed amount - uint256 penalty = getWithdrawBasePenalty(amount); - require(penalty <= amount, "PENALTY_EXCEED"); - - // settlement - _TARGET_BASE_TOKEN_AMOUNT_ = _TARGET_BASE_TOKEN_AMOUNT_.sub(amount); - _burnBaseCapital(msg.sender, requireBaseCapital); - _baseTokenTransferOut(to, amount.sub(penalty)); - _donateBaseToken(penalty); - - emit Withdraw(msg.sender, to, true, amount.sub(penalty), requireBaseCapital); - emit ChargePenalty(msg.sender, true, penalty); - - return amount.sub(penalty); - } - - // ============ Withdraw all Functions ============ - - function withdrawAllQuoteTo(address to) - public - preventReentrant - dodoNotClosed - returns (uint256) - { - uint256 withdrawAmount = getLpQuoteBalance(msg.sender); - uint256 capital = getQuoteCapitalBalanceOf(msg.sender); - - // handle penalty, penalty may exceed amount - uint256 penalty = getWithdrawQuotePenalty(withdrawAmount); - require(penalty <= withdrawAmount, "PENALTY_EXCEED"); - - // settlement - _TARGET_QUOTE_TOKEN_AMOUNT_ = _TARGET_QUOTE_TOKEN_AMOUNT_.sub(withdrawAmount); - _burnQuoteCapital(msg.sender, capital); - _quoteTokenTransferOut(to, withdrawAmount.sub(penalty)); - _donateQuoteToken(penalty); - - emit Withdraw(msg.sender, to, false, withdrawAmount, capital); - emit ChargePenalty(msg.sender, false, penalty); - - return withdrawAmount.sub(penalty); - } - - function withdrawAllBaseTo(address to) public preventReentrant dodoNotClosed returns (uint256) { - uint256 withdrawAmount = getLpBaseBalance(msg.sender); - uint256 capital = getBaseCapitalBalanceOf(msg.sender); - - // handle penalty, penalty may exceed amount - uint256 penalty = getWithdrawBasePenalty(withdrawAmount); - require(penalty <= withdrawAmount, "PENALTY_EXCEED"); - - // settlement - _TARGET_BASE_TOKEN_AMOUNT_ = _TARGET_BASE_TOKEN_AMOUNT_.sub(withdrawAmount); - _burnBaseCapital(msg.sender, capital); - _baseTokenTransferOut(to, withdrawAmount.sub(penalty)); - _donateBaseToken(penalty); - - emit Withdraw(msg.sender, to, true, withdrawAmount, capital); - emit ChargePenalty(msg.sender, true, penalty); - - return withdrawAmount.sub(penalty); - } - - // ============ Helper Functions ============ - - function _mintBaseCapital(address user, uint256 amount) internal { - IDODOLpToken(_BASE_CAPITAL_TOKEN_).mint(user, amount); - } - - function _mintQuoteCapital(address user, uint256 amount) internal { - IDODOLpToken(_QUOTE_CAPITAL_TOKEN_).mint(user, amount); - } - - function _burnBaseCapital(address user, uint256 amount) internal { - IDODOLpToken(_BASE_CAPITAL_TOKEN_).burn(user, amount); - } - - function _burnQuoteCapital(address user, uint256 amount) internal { - IDODOLpToken(_QUOTE_CAPITAL_TOKEN_).burn(user, amount); - } - - // ============ Getter Functions ============ - - function getLpBaseBalance(address lp) public view returns (uint256 lpBalance) { - uint256 totalBaseCapital = getTotalBaseCapital(); - (uint256 baseTarget, ) = getExpectedTarget(); - if (totalBaseCapital == 0) { - return 0; - } - lpBalance = getBaseCapitalBalanceOf(lp).mul(baseTarget).div(totalBaseCapital); - return lpBalance; - } - - function getLpQuoteBalance(address lp) public view returns (uint256 lpBalance) { - uint256 totalQuoteCapital = getTotalQuoteCapital(); - (, uint256 quoteTarget) = getExpectedTarget(); - if (totalQuoteCapital == 0) { - return 0; - } - lpBalance = getQuoteCapitalBalanceOf(lp).mul(quoteTarget).div(totalQuoteCapital); - return lpBalance; - } - - function getWithdrawQuotePenalty(uint256 amount) public view returns (uint256 penalty) { - require(amount <= _QUOTE_BALANCE_, "DODO_QUOTE_BALANCE_NOT_ENOUGH"); - if (_R_STATUS_ == Types.RStatus.BELOW_ONE) { - uint256 spareBase = _BASE_BALANCE_.sub(_TARGET_BASE_TOKEN_AMOUNT_); - uint256 price = getOraclePrice(); - uint256 fairAmount = DecimalMath.mul(spareBase, price); - uint256 targetQuote = DODOMath._SolveQuadraticFunctionForTarget( - _QUOTE_BALANCE_, - _K_, - fairAmount - ); - // if amount = _QUOTE_BALANCE_, div error - uint256 targetQuoteWithWithdraw = DODOMath._SolveQuadraticFunctionForTarget( - _QUOTE_BALANCE_.sub(amount), - _K_, - fairAmount - ); - return targetQuote.sub(targetQuoteWithWithdraw.add(amount)); - } else { - return 0; - } - } - - function getWithdrawBasePenalty(uint256 amount) public view returns (uint256 penalty) { - require(amount <= _BASE_BALANCE_, "DODO_BASE_BALANCE_NOT_ENOUGH"); - if (_R_STATUS_ == Types.RStatus.ABOVE_ONE) { - uint256 spareQuote = _QUOTE_BALANCE_.sub(_TARGET_QUOTE_TOKEN_AMOUNT_); - uint256 price = getOraclePrice(); - uint256 fairAmount = DecimalMath.divFloor(spareQuote, price); - uint256 targetBase = DODOMath._SolveQuadraticFunctionForTarget( - _BASE_BALANCE_, - _K_, - fairAmount - ); - // if amount = _BASE_BALANCE_, div error - uint256 targetBaseWithWithdraw = DODOMath._SolveQuadraticFunctionForTarget( - _BASE_BALANCE_.sub(amount), - _K_, - fairAmount - ); - return targetBase.sub(targetBaseWithWithdraw.add(amount)); - } else { - return 0; - } - } -} diff --git a/contracts/impl/Pricing.sol b/contracts/impl/Pricing.sol deleted file mode 100644 index ce45235..0000000 --- a/contracts/impl/Pricing.sol +++ /dev/null @@ -1,198 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -import {SafeMath} from "../lib/SafeMath.sol"; -import {DecimalMath} from "../lib/DecimalMath.sol"; -import {DODOMath} from "../lib/DODOMath.sol"; -import {Types} from "../lib/Types.sol"; -import {Storage} from "./Storage.sol"; - - -/** - * @title Pricing - * @author DODO Breeder - * - * @notice DODO Pricing model - */ -contract Pricing is Storage { - using SafeMath for uint256; - - // ============ R = 1 cases ============ - - function _ROneSellBaseToken(uint256 amount, uint256 targetQuoteTokenAmount) - internal - view - returns (uint256 receiveQuoteToken) - { - uint256 i = getOraclePrice(); - uint256 Q2 = DODOMath._SolveQuadraticFunctionForTrade( - targetQuoteTokenAmount, - targetQuoteTokenAmount, - DecimalMath.mul(i, amount), - false, - _K_ - ); - // in theory Q2 <= targetQuoteTokenAmount - // however when amount is close to 0, precision problems may cause Q2 > targetQuoteTokenAmount - return targetQuoteTokenAmount.sub(Q2); - } - - function _ROneBuyBaseToken(uint256 amount, uint256 targetBaseTokenAmount) - internal - view - returns (uint256 payQuoteToken) - { - require(amount < targetBaseTokenAmount, "DODO_BASE_BALANCE_NOT_ENOUGH"); - uint256 B2 = targetBaseTokenAmount.sub(amount); - payQuoteToken = _RAboveIntegrate(targetBaseTokenAmount, targetBaseTokenAmount, B2); - return payQuoteToken; - } - - // ============ R < 1 cases ============ - - function _RBelowSellBaseToken( - uint256 amount, - uint256 quoteBalance, - uint256 targetQuoteAmount - ) internal view returns (uint256 receieQuoteToken) { - uint256 i = getOraclePrice(); - uint256 Q2 = DODOMath._SolveQuadraticFunctionForTrade( - targetQuoteAmount, - quoteBalance, - DecimalMath.mul(i, amount), - false, - _K_ - ); - return quoteBalance.sub(Q2); - } - - function _RBelowBuyBaseToken( - uint256 amount, - uint256 quoteBalance, - uint256 targetQuoteAmount - ) internal view returns (uint256 payQuoteToken) { - // Here we don't require amount less than some value - // Because it is limited at upper function - // See Trader.queryBuyBaseToken - uint256 i = getOraclePrice(); - uint256 Q2 = DODOMath._SolveQuadraticFunctionForTrade( - targetQuoteAmount, - quoteBalance, - DecimalMath.mulCeil(i, amount), - true, - _K_ - ); - return Q2.sub(quoteBalance); - } - - function _RBelowBackToOne() internal view returns (uint256 payQuoteToken) { - // important: carefully design the system to make sure spareBase always greater than or equal to 0 - uint256 spareBase = _BASE_BALANCE_.sub(_TARGET_BASE_TOKEN_AMOUNT_); - uint256 price = getOraclePrice(); - uint256 fairAmount = DecimalMath.mul(spareBase, price); - uint256 newTargetQuote = DODOMath._SolveQuadraticFunctionForTarget( - _QUOTE_BALANCE_, - _K_, - fairAmount - ); - return newTargetQuote.sub(_QUOTE_BALANCE_); - } - - // ============ R > 1 cases ============ - - function _RAboveBuyBaseToken( - uint256 amount, - uint256 baseBalance, - uint256 targetBaseAmount - ) internal view returns (uint256 payQuoteToken) { - require(amount < baseBalance, "DODO_BASE_BALANCE_NOT_ENOUGH"); - uint256 B2 = baseBalance.sub(amount); - return _RAboveIntegrate(targetBaseAmount, baseBalance, B2); - } - - function _RAboveSellBaseToken( - uint256 amount, - uint256 baseBalance, - uint256 targetBaseAmount - ) internal view returns (uint256 receiveQuoteToken) { - // here we don't require B1 <= targetBaseAmount - // Because it is limited at upper function - // See Trader.querySellBaseToken - uint256 B1 = baseBalance.add(amount); - return _RAboveIntegrate(targetBaseAmount, B1, baseBalance); - } - - function _RAboveBackToOne() internal view returns (uint256 payBaseToken) { - // important: carefully design the system to make sure spareBase always greater than or equal to 0 - uint256 spareQuote = _QUOTE_BALANCE_.sub(_TARGET_QUOTE_TOKEN_AMOUNT_); - uint256 price = getOraclePrice(); - uint256 fairAmount = DecimalMath.divFloor(spareQuote, price); - uint256 newTargetBase = DODOMath._SolveQuadraticFunctionForTarget( - _BASE_BALANCE_, - _K_, - fairAmount - ); - return newTargetBase.sub(_BASE_BALANCE_); - } - - // ============ Helper functions ============ - - function getExpectedTarget() public view returns (uint256 baseTarget, uint256 quoteTarget) { - uint256 Q = _QUOTE_BALANCE_; - uint256 B = _BASE_BALANCE_; - if (_R_STATUS_ == Types.RStatus.ONE) { - return (_TARGET_BASE_TOKEN_AMOUNT_, _TARGET_QUOTE_TOKEN_AMOUNT_); - } else if (_R_STATUS_ == Types.RStatus.BELOW_ONE) { - uint256 payQuoteToken = _RBelowBackToOne(); - return (_TARGET_BASE_TOKEN_AMOUNT_, Q.add(payQuoteToken)); - } else if (_R_STATUS_ == Types.RStatus.ABOVE_ONE) { - uint256 payBaseToken = _RAboveBackToOne(); - return (B.add(payBaseToken), _TARGET_QUOTE_TOKEN_AMOUNT_); - } - } - - function getMidPrice() public view returns (uint256 midPrice) { - (uint256 baseTarget, uint256 quoteTarget) = getExpectedTarget(); - if (_R_STATUS_ == Types.RStatus.BELOW_ONE) { - uint256 R = DecimalMath.divFloor( - quoteTarget.mul(quoteTarget).div(_QUOTE_BALANCE_), - _QUOTE_BALANCE_ - ); - R = DecimalMath.ONE.sub(_K_).add(DecimalMath.mul(_K_, R)); - return DecimalMath.divFloor(getOraclePrice(), R); - } else { - uint256 R = DecimalMath.divFloor( - baseTarget.mul(baseTarget).div(_BASE_BALANCE_), - _BASE_BALANCE_ - ); - R = DecimalMath.ONE.sub(_K_).add(DecimalMath.mul(_K_, R)); - return DecimalMath.mul(getOraclePrice(), R); - } - } - - function _RAboveIntegrate( - uint256 B0, - uint256 B1, - uint256 B2 - ) internal view returns (uint256) { - uint256 i = getOraclePrice(); - return DODOMath._GeneralIntegrate(B0, B1, B2, i, _K_); - } - - // function _RBelowIntegrate( - // uint256 Q0, - // uint256 Q1, - // uint256 Q2 - // ) internal view returns (uint256) { - // uint256 i = getOraclePrice(); - // i = DecimalMath.divFloor(DecimalMath.ONE, i); // 1/i - // return DODOMath._GeneralIntegrate(Q0, Q1, Q2, i, _K_); - // } -} diff --git a/contracts/impl/Settlement.sol b/contracts/impl/Settlement.sol deleted file mode 100644 index ac6e2bd..0000000 --- a/contracts/impl/Settlement.sol +++ /dev/null @@ -1,163 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -import {SafeMath} from "../lib/SafeMath.sol"; -import {SafeERC20} from "../lib/SafeERC20.sol"; -import {DecimalMath} from "../lib/DecimalMath.sol"; -import {Types} from "../lib/Types.sol"; -import {IDODOLpToken} from "../intf/IDODOLpToken.sol"; -import {IERC20} from "../intf/IERC20.sol"; -import {Storage} from "./Storage.sol"; - - -/** - * @title Settlement - * @author DODO Breeder - * - * @notice Functions for assets settlement - */ -contract Settlement is Storage { - using SafeMath for uint256; - using SafeERC20 for IERC20; - - // ============ Events ============ - - event Donate(uint256 amount, bool isBaseToken); - - event ClaimAssets(address indexed user, uint256 baseTokenAmount, uint256 quoteTokenAmount); - - // ============ Assets IN/OUT Functions ============ - - function _baseTokenTransferIn(address from, uint256 amount) internal { - require(_BASE_BALANCE_.add(amount) <= _BASE_BALANCE_LIMIT_, "BASE_BALANCE_LIMIT_EXCEEDED"); - IERC20(_BASE_TOKEN_).safeTransferFrom(from, address(this), amount); - _BASE_BALANCE_ = _BASE_BALANCE_.add(amount); - } - - function _quoteTokenTransferIn(address from, uint256 amount) internal { - require( - _QUOTE_BALANCE_.add(amount) <= _QUOTE_BALANCE_LIMIT_, - "QUOTE_BALANCE_LIMIT_EXCEEDED" - ); - IERC20(_QUOTE_TOKEN_).safeTransferFrom(from, address(this), amount); - _QUOTE_BALANCE_ = _QUOTE_BALANCE_.add(amount); - } - - function _baseTokenTransferOut(address to, uint256 amount) internal { - IERC20(_BASE_TOKEN_).safeTransfer(to, amount); - _BASE_BALANCE_ = _BASE_BALANCE_.sub(amount); - } - - function _quoteTokenTransferOut(address to, uint256 amount) internal { - IERC20(_QUOTE_TOKEN_).safeTransfer(to, amount); - _QUOTE_BALANCE_ = _QUOTE_BALANCE_.sub(amount); - } - - // ============ Donate to Liquidity Pool Functions ============ - - function _donateBaseToken(uint256 amount) internal { - _TARGET_BASE_TOKEN_AMOUNT_ = _TARGET_BASE_TOKEN_AMOUNT_.add(amount); - emit Donate(amount, true); - } - - function _donateQuoteToken(uint256 amount) internal { - _TARGET_QUOTE_TOKEN_AMOUNT_ = _TARGET_QUOTE_TOKEN_AMOUNT_.add(amount); - emit Donate(amount, false); - } - - function donateBaseToken(uint256 amount) external preventReentrant { - _baseTokenTransferIn(msg.sender, amount); - _donateBaseToken(amount); - } - - function donateQuoteToken(uint256 amount) external preventReentrant { - _quoteTokenTransferIn(msg.sender, amount); - _donateQuoteToken(amount); - } - - // ============ Final Settlement Functions ============ - - // last step to shut down dodo - function finalSettlement() external onlyOwner notClosed { - _CLOSED_ = true; - _DEPOSIT_QUOTE_ALLOWED_ = false; - _DEPOSIT_BASE_ALLOWED_ = false; - _TRADE_ALLOWED_ = false; - uint256 totalBaseCapital = getTotalBaseCapital(); - uint256 totalQuoteCapital = getTotalQuoteCapital(); - - if (_QUOTE_BALANCE_ > _TARGET_QUOTE_TOKEN_AMOUNT_) { - uint256 spareQuote = _QUOTE_BALANCE_.sub(_TARGET_QUOTE_TOKEN_AMOUNT_); - _BASE_CAPITAL_RECEIVE_QUOTE_ = DecimalMath.divFloor(spareQuote, totalBaseCapital); - } else { - _TARGET_QUOTE_TOKEN_AMOUNT_ = _QUOTE_BALANCE_; - } - - if (_BASE_BALANCE_ > _TARGET_BASE_TOKEN_AMOUNT_) { - uint256 spareBase = _BASE_BALANCE_.sub(_TARGET_BASE_TOKEN_AMOUNT_); - _QUOTE_CAPITAL_RECEIVE_BASE_ = DecimalMath.divFloor(spareBase, totalQuoteCapital); - } else { - _TARGET_BASE_TOKEN_AMOUNT_ = _BASE_BALANCE_; - } - - _R_STATUS_ = Types.RStatus.ONE; - } - - // claim remaining assets after final settlement - function claimAssets() external preventReentrant { - require(_CLOSED_, "DODO_NOT_CLOSED"); - require(!_CLAIMED_[msg.sender], "ALREADY_CLAIMED"); - _CLAIMED_[msg.sender] = true; - - uint256 quoteCapital = getQuoteCapitalBalanceOf(msg.sender); - uint256 baseCapital = getBaseCapitalBalanceOf(msg.sender); - - uint256 quoteAmount = 0; - if (quoteCapital > 0) { - quoteAmount = _TARGET_QUOTE_TOKEN_AMOUNT_.mul(quoteCapital).div(getTotalQuoteCapital()); - } - uint256 baseAmount = 0; - if (baseCapital > 0) { - baseAmount = _TARGET_BASE_TOKEN_AMOUNT_.mul(baseCapital).div(getTotalBaseCapital()); - } - - _TARGET_QUOTE_TOKEN_AMOUNT_ = _TARGET_QUOTE_TOKEN_AMOUNT_.sub(quoteAmount); - _TARGET_BASE_TOKEN_AMOUNT_ = _TARGET_BASE_TOKEN_AMOUNT_.sub(baseAmount); - - quoteAmount = quoteAmount.add(DecimalMath.mul(baseCapital, _BASE_CAPITAL_RECEIVE_QUOTE_)); - baseAmount = baseAmount.add(DecimalMath.mul(quoteCapital, _QUOTE_CAPITAL_RECEIVE_BASE_)); - - _baseTokenTransferOut(msg.sender, baseAmount); - _quoteTokenTransferOut(msg.sender, quoteAmount); - - IDODOLpToken(_BASE_CAPITAL_TOKEN_).burn(msg.sender, baseCapital); - IDODOLpToken(_QUOTE_CAPITAL_TOKEN_).burn(msg.sender, quoteCapital); - - emit ClaimAssets(msg.sender, baseAmount, quoteAmount); - return; - } - - // in case someone transfer to contract directly - function retrieve(address token, uint256 amount) external onlyOwner { - if (token == _BASE_TOKEN_) { - require( - IERC20(_BASE_TOKEN_).balanceOf(address(this)) >= _BASE_BALANCE_.add(amount), - "DODO_BASE_BALANCE_NOT_ENOUGH" - ); - } - if (token == _QUOTE_TOKEN_) { - require( - IERC20(_QUOTE_TOKEN_).balanceOf(address(this)) >= _QUOTE_BALANCE_.add(amount), - "DODO_QUOTE_BALANCE_NOT_ENOUGH" - ); - } - IERC20(token).safeTransfer(msg.sender, amount); - } -} diff --git a/contracts/impl/Storage.sol b/contracts/impl/Storage.sol deleted file mode 100644 index 7828bde..0000000 --- a/contracts/impl/Storage.sol +++ /dev/null @@ -1,118 +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 {SafeMath} from "../lib/SafeMath.sol"; -import {DecimalMath} from "../lib/DecimalMath.sol"; -import {ReentrancyGuard} from "../lib/ReentrancyGuard.sol"; -import {IOracle} from "../intf/IOracle.sol"; -import {IDODOLpToken} from "../intf/IDODOLpToken.sol"; -import {Types} from "../lib/Types.sol"; - - -/** - * @title Storage - * @author DODO Breeder - * - * @notice Local Variables - */ -contract Storage is InitializableOwnable, ReentrancyGuard { - using SafeMath for uint256; - - // ============ Variables for Control ============ - - bool internal _INITIALIZED_; - bool public _CLOSED_; - bool public _DEPOSIT_QUOTE_ALLOWED_; - bool public _DEPOSIT_BASE_ALLOWED_; - bool public _TRADE_ALLOWED_; - uint256 public _GAS_PRICE_LIMIT_; - - // ============ Advanced Controls ============ - bool public _BUYING_ALLOWED_; - bool public _SELLING_ALLOWED_; - uint256 public _BASE_BALANCE_LIMIT_; - uint256 public _QUOTE_BALANCE_LIMIT_; - - // ============ Core Address ============ - - address public _SUPERVISOR_; // could freeze system in emergency - address public _MAINTAINER_; // collect maintainer fee to buy food for DODO - - address public _BASE_TOKEN_; - address public _QUOTE_TOKEN_; - address public _ORACLE_; - - // ============ Variables for PMM Algorithm ============ - - uint256 public _LP_FEE_RATE_; - uint256 public _MT_FEE_RATE_; - uint256 public _K_; - - Types.RStatus public _R_STATUS_; - uint256 public _TARGET_BASE_TOKEN_AMOUNT_; - uint256 public _TARGET_QUOTE_TOKEN_AMOUNT_; - uint256 public _BASE_BALANCE_; - uint256 public _QUOTE_BALANCE_; - - address public _BASE_CAPITAL_TOKEN_; - address public _QUOTE_CAPITAL_TOKEN_; - - // ============ Variables for Final Settlement ============ - - uint256 public _BASE_CAPITAL_RECEIVE_QUOTE_; - uint256 public _QUOTE_CAPITAL_RECEIVE_BASE_; - mapping(address => bool) public _CLAIMED_; - - // ============ Modifiers ============ - - modifier onlySupervisorOrOwner() { - require(msg.sender == _SUPERVISOR_ || msg.sender == _OWNER_, "NOT_SUPERVISOR_OR_OWNER"); - _; - } - - modifier notClosed() { - require(!_CLOSED_, "DODO_CLOSED"); - _; - } - - // ============ Helper Functions ============ - - function _checkDODOParameters() internal view returns (uint256) { - require(_K_ < DecimalMath.ONE, "K>=1"); - require(_K_ > 0, "K=0"); - require(_LP_FEE_RATE_.add(_MT_FEE_RATE_) < DecimalMath.ONE, "FEE_RATE>=1"); - } - - function getOraclePrice() public view returns (uint256) { - return IOracle(_ORACLE_).getPrice(); - } - - function getBaseCapitalBalanceOf(address lp) public view returns (uint256) { - return IDODOLpToken(_BASE_CAPITAL_TOKEN_).balanceOf(lp); - } - - function getTotalBaseCapital() public view returns (uint256) { - return IDODOLpToken(_BASE_CAPITAL_TOKEN_).totalSupply(); - } - - function getQuoteCapitalBalanceOf(address lp) public view returns (uint256) { - return IDODOLpToken(_QUOTE_CAPITAL_TOKEN_).balanceOf(lp); - } - - function getTotalQuoteCapital() public view returns (uint256) { - return IDODOLpToken(_QUOTE_CAPITAL_TOKEN_).totalSupply(); - } - - // ============ Version Control ============ - function version() external pure returns (uint256) { - return 101; // 1.0.1 - } -} diff --git a/contracts/impl/Trader.sol b/contracts/impl/Trader.sol deleted file mode 100644 index 36d00d9..0000000 --- a/contracts/impl/Trader.sol +++ /dev/null @@ -1,274 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -import {SafeMath} from "../lib/SafeMath.sol"; -import {DecimalMath} from "../lib/DecimalMath.sol"; -import {Types} from "../lib/Types.sol"; -import {IDODOCallee} from "../intf/IDODOCallee.sol"; -import {Storage} from "./Storage.sol"; -import {Pricing} from "./Pricing.sol"; -import {Settlement} from "./Settlement.sol"; - - -/** - * @title Trader - * @author DODO Breeder - * - * @notice Functions for trader operations - */ -contract Trader is Storage, Pricing, Settlement { - using SafeMath for uint256; - - // ============ Events ============ - - event SellBaseToken(address indexed seller, uint256 payBase, uint256 receiveQuote); - - event BuyBaseToken(address indexed buyer, uint256 receiveBase, uint256 payQuote); - - event ChargeMaintainerFee(address indexed maintainer, bool isBaseToken, uint256 amount); - - // ============ Modifiers ============ - - modifier tradeAllowed() { - require(_TRADE_ALLOWED_, "TRADE_NOT_ALLOWED"); - _; - } - - modifier buyingAllowed() { - require(_BUYING_ALLOWED_, "BUYING_NOT_ALLOWED"); - _; - } - - modifier sellingAllowed() { - require(_SELLING_ALLOWED_, "SELLING_NOT_ALLOWED"); - _; - } - - modifier gasPriceLimit() { - require(tx.gasprice <= _GAS_PRICE_LIMIT_, "GAS_PRICE_EXCEED"); - _; - } - - // ============ Trade Functions ============ - - function sellBaseToken( - uint256 amount, - uint256 minReceiveQuote, - bytes calldata data - ) external tradeAllowed sellingAllowed gasPriceLimit preventReentrant returns (uint256) { - // query price - ( - uint256 receiveQuote, - uint256 lpFeeQuote, - uint256 mtFeeQuote, - Types.RStatus newRStatus, - uint256 newQuoteTarget, - uint256 newBaseTarget - ) = _querySellBaseToken(amount); - require(receiveQuote >= minReceiveQuote, "SELL_BASE_RECEIVE_NOT_ENOUGH"); - - // settle assets - _quoteTokenTransferOut(msg.sender, receiveQuote); - if (data.length > 0) { - IDODOCallee(msg.sender).dodoCall(false, amount, receiveQuote, data); - } - _baseTokenTransferIn(msg.sender, amount); - if (mtFeeQuote != 0) { - _quoteTokenTransferOut(_MAINTAINER_, mtFeeQuote); - emit ChargeMaintainerFee(_MAINTAINER_, false, mtFeeQuote); - } - - // update TARGET - if (_TARGET_QUOTE_TOKEN_AMOUNT_ != newQuoteTarget) { - _TARGET_QUOTE_TOKEN_AMOUNT_ = newQuoteTarget; - } - if (_TARGET_BASE_TOKEN_AMOUNT_ != newBaseTarget) { - _TARGET_BASE_TOKEN_AMOUNT_ = newBaseTarget; - } - if (_R_STATUS_ != newRStatus) { - _R_STATUS_ = newRStatus; - } - - _donateQuoteToken(lpFeeQuote); - emit SellBaseToken(msg.sender, amount, receiveQuote); - - return receiveQuote; - } - - function buyBaseToken( - uint256 amount, - uint256 maxPayQuote, - bytes calldata data - ) external tradeAllowed buyingAllowed gasPriceLimit preventReentrant returns (uint256) { - // query price - ( - uint256 payQuote, - uint256 lpFeeBase, - uint256 mtFeeBase, - Types.RStatus newRStatus, - uint256 newQuoteTarget, - uint256 newBaseTarget - ) = _queryBuyBaseToken(amount); - require(payQuote <= maxPayQuote, "BUY_BASE_COST_TOO_MUCH"); - - // settle assets - _baseTokenTransferOut(msg.sender, amount); - if (data.length > 0) { - IDODOCallee(msg.sender).dodoCall(true, amount, payQuote, data); - } - _quoteTokenTransferIn(msg.sender, payQuote); - if (mtFeeBase != 0) { - _baseTokenTransferOut(_MAINTAINER_, mtFeeBase); - emit ChargeMaintainerFee(_MAINTAINER_, true, mtFeeBase); - } - - // update TARGET - if (_TARGET_QUOTE_TOKEN_AMOUNT_ != newQuoteTarget) { - _TARGET_QUOTE_TOKEN_AMOUNT_ = newQuoteTarget; - } - if (_TARGET_BASE_TOKEN_AMOUNT_ != newBaseTarget) { - _TARGET_BASE_TOKEN_AMOUNT_ = newBaseTarget; - } - if (_R_STATUS_ != newRStatus) { - _R_STATUS_ = newRStatus; - } - - _donateBaseToken(lpFeeBase); - emit BuyBaseToken(msg.sender, amount, payQuote); - - return payQuote; - } - - // ============ Query Functions ============ - - function querySellBaseToken(uint256 amount) external view returns (uint256 receiveQuote) { - (receiveQuote, , , , , ) = _querySellBaseToken(amount); - return receiveQuote; - } - - function queryBuyBaseToken(uint256 amount) external view returns (uint256 payQuote) { - (payQuote, , , , , ) = _queryBuyBaseToken(amount); - return payQuote; - } - - function _querySellBaseToken(uint256 amount) - internal - view - returns ( - uint256 receiveQuote, - uint256 lpFeeQuote, - uint256 mtFeeQuote, - Types.RStatus newRStatus, - uint256 newQuoteTarget, - uint256 newBaseTarget - ) - { - (newBaseTarget, newQuoteTarget) = getExpectedTarget(); - - uint256 sellBaseAmount = amount; - - if (_R_STATUS_ == Types.RStatus.ONE) { - // case 1: R=1 - // R falls below one - receiveQuote = _ROneSellBaseToken(sellBaseAmount, newQuoteTarget); - newRStatus = Types.RStatus.BELOW_ONE; - } else if (_R_STATUS_ == Types.RStatus.ABOVE_ONE) { - uint256 backToOnePayBase = newBaseTarget.sub(_BASE_BALANCE_); - uint256 backToOneReceiveQuote = _QUOTE_BALANCE_.sub(newQuoteTarget); - // case 2: R>1 - // complex case, R status depends on trading amount - if (sellBaseAmount < backToOnePayBase) { - // case 2.1: R status do not change - receiveQuote = _RAboveSellBaseToken(sellBaseAmount, _BASE_BALANCE_, newBaseTarget); - newRStatus = Types.RStatus.ABOVE_ONE; - if (receiveQuote > backToOneReceiveQuote) { - // [Important corner case!] may enter this branch when some precision problem happens. And consequently contribute to negative spare quote amount - // to make sure spare quote>=0, mannually set receiveQuote=backToOneReceiveQuote - receiveQuote = backToOneReceiveQuote; - } - } else if (sellBaseAmount == backToOnePayBase) { - // case 2.2: R status changes to ONE - receiveQuote = backToOneReceiveQuote; - newRStatus = Types.RStatus.ONE; - } else { - // case 2.3: R status changes to BELOW_ONE - receiveQuote = backToOneReceiveQuote.add( - _ROneSellBaseToken(sellBaseAmount.sub(backToOnePayBase), newQuoteTarget) - ); - newRStatus = Types.RStatus.BELOW_ONE; - } - } else { - // _R_STATUS_ == Types.RStatus.BELOW_ONE - // case 3: R<1 - receiveQuote = _RBelowSellBaseToken(sellBaseAmount, _QUOTE_BALANCE_, newQuoteTarget); - newRStatus = Types.RStatus.BELOW_ONE; - } - - // count fees - lpFeeQuote = DecimalMath.mul(receiveQuote, _LP_FEE_RATE_); - mtFeeQuote = DecimalMath.mul(receiveQuote, _MT_FEE_RATE_); - receiveQuote = receiveQuote.sub(lpFeeQuote).sub(mtFeeQuote); - - return (receiveQuote, lpFeeQuote, mtFeeQuote, newRStatus, newQuoteTarget, newBaseTarget); - } - - function _queryBuyBaseToken(uint256 amount) - internal - view - returns ( - uint256 payQuote, - uint256 lpFeeBase, - uint256 mtFeeBase, - Types.RStatus newRStatus, - uint256 newQuoteTarget, - uint256 newBaseTarget - ) - { - (newBaseTarget, newQuoteTarget) = getExpectedTarget(); - - // charge fee from user receive amount - lpFeeBase = DecimalMath.mul(amount, _LP_FEE_RATE_); - mtFeeBase = DecimalMath.mul(amount, _MT_FEE_RATE_); - uint256 buyBaseAmount = amount.add(lpFeeBase).add(mtFeeBase); - - if (_R_STATUS_ == Types.RStatus.ONE) { - // case 1: R=1 - payQuote = _ROneBuyBaseToken(buyBaseAmount, newBaseTarget); - newRStatus = Types.RStatus.ABOVE_ONE; - } else if (_R_STATUS_ == Types.RStatus.ABOVE_ONE) { - // case 2: R>1 - payQuote = _RAboveBuyBaseToken(buyBaseAmount, _BASE_BALANCE_, newBaseTarget); - newRStatus = Types.RStatus.ABOVE_ONE; - } else if (_R_STATUS_ == Types.RStatus.BELOW_ONE) { - uint256 backToOnePayQuote = newQuoteTarget.sub(_QUOTE_BALANCE_); - uint256 backToOneReceiveBase = _BASE_BALANCE_.sub(newBaseTarget); - // case 3: R<1 - // complex case, R status may change - if (buyBaseAmount < backToOneReceiveBase) { - // case 3.1: R status do not change - // no need to check payQuote because spare base token must be greater than zero - payQuote = _RBelowBuyBaseToken(buyBaseAmount, _QUOTE_BALANCE_, newQuoteTarget); - newRStatus = Types.RStatus.BELOW_ONE; - } else if (buyBaseAmount == backToOneReceiveBase) { - // case 3.2: R status changes to ONE - payQuote = backToOnePayQuote; - newRStatus = Types.RStatus.ONE; - } else { - // case 3.3: R status changes to ABOVE_ONE - payQuote = backToOnePayQuote.add( - _ROneBuyBaseToken(buyBaseAmount.sub(backToOneReceiveBase), newBaseTarget) - ); - newRStatus = Types.RStatus.ABOVE_ONE; - } - } - - return (payQuote, lpFeeBase, mtFeeBase, newRStatus, newQuoteTarget, newBaseTarget); - } -} diff --git a/contracts/intf/IDODO.sol b/contracts/intf/IDODO.sol deleted file mode 100644 index 4b1f6cc..0000000 --- a/contracts/intf/IDODO.sol +++ /dev/null @@ -1,67 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - - -interface IDODO { - function init( - address owner, - address supervisor, - address maintainer, - address baseToken, - address quoteToken, - address oracle, - uint256 lpFeeRate, - uint256 mtFeeRate, - uint256 k, - uint256 gasPriceLimit - ) external; - - function transferOwnership(address newOwner) external; - - function claimOwnership() external; - - function sellBaseToken( - uint256 amount, - uint256 minReceiveQuote, - bytes calldata data - ) external returns (uint256); - - function buyBaseToken( - uint256 amount, - uint256 maxPayQuote, - bytes calldata data - ) external returns (uint256); - - function querySellBaseToken(uint256 amount) external view returns (uint256 receiveQuote); - - function queryBuyBaseToken(uint256 amount) external view returns (uint256 payQuote); - - function getExpectedTarget() external view returns (uint256 baseTarget, uint256 quoteTarget); - - function depositBaseTo(address to, uint256 amount) external returns (uint256); - - function withdrawBase(uint256 amount) external returns (uint256); - - function withdrawAllBase() external returns (uint256); - - function depositQuoteTo(address to, uint256 amount) external returns (uint256); - - function withdrawQuote(uint256 amount) external returns (uint256); - - function withdrawAllQuote() external returns (uint256); - - function _BASE_CAPITAL_TOKEN_() external view returns (address); - - function _QUOTE_CAPITAL_TOKEN_() external view returns (address); - - function _BASE_TOKEN_() external returns (address); - - function _QUOTE_TOKEN_() external returns (address); -} diff --git a/contracts/intf/IDODOCallee.sol b/contracts/intf/IDODOCallee.sol deleted file mode 100644 index 3b9b70d..0000000 --- a/contracts/intf/IDODOCallee.sol +++ /dev/null @@ -1,18 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -interface IDODOCallee { - function dodoCall( - bool isBuyBaseToken, - uint256 baseAmount, - uint256 quoteAmount, - bytes calldata data - ) external; -} diff --git a/contracts/intf/IDODOLpToken.sol b/contracts/intf/IDODOLpToken.sol deleted file mode 100644 index f1b5383..0000000 --- a/contracts/intf/IDODOLpToken.sol +++ /dev/null @@ -1,20 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity ^0.6.9; -pragma experimental ABIEncoderV2; - - -interface IDODOLpToken { - function mint(address user, uint256 value) external; - - function burn(address user, uint256 value) external; - - function balanceOf(address owner) external view returns (uint256); - - function totalSupply() external view returns (uint256); -} diff --git a/contracts/intf/IERC20.sol b/contracts/intf/IERC20.sol index 252e1f5..c1a98ed 100644 --- a/contracts/intf/IERC20.sol +++ b/contracts/intf/IERC20.sol @@ -17,6 +17,8 @@ interface IERC20 { function name() external view returns (string memory); + function symbol() external view returns (string memory); + /** * @dev Returns the amount of tokens owned by `account`. */ diff --git a/contracts/lib/Types.sol b/contracts/intf/IFeeRateModel.sol similarity index 57% rename from contracts/lib/Types.sol rename to contracts/intf/IFeeRateModel.sol index 0892400..81c3fdd 100644 --- a/contracts/lib/Types.sol +++ b/contracts/intf/IFeeRateModel.sol @@ -8,6 +8,7 @@ pragma solidity 0.6.9; pragma experimental ABIEncoderV2; -library Types { - enum RStatus {ONE, ABOVE_ONE, BELOW_ONE} + +interface IFeeRateModel { + function getFeeRate(uint256 amount) external view returns (uint256); } diff --git a/contracts/intf/IVault.sol b/contracts/intf/IVault.sol new file mode 100644 index 0000000..f616820 --- /dev/null +++ b/contracts/intf/IVault.sol @@ -0,0 +1,15 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +interface IBasicVault { + function controller() external view returns(address); + function getReserve(address token) external view returns(address); + function transferOut(address token, address to, uint256 amount) external; +} \ No newline at end of file diff --git a/contracts/helper/CloneFactory.sol b/contracts/lib/CloneFactory.sol similarity index 100% rename from contracts/helper/CloneFactory.sol rename to contracts/lib/CloneFactory.sol diff --git a/contracts/lib/DecimalMath.sol b/contracts/lib/DecimalMath.sol index 99debb1..0a406a4 100644 --- a/contracts/lib/DecimalMath.sol +++ b/contracts/lib/DecimalMath.sol @@ -26,6 +26,10 @@ library DecimalMath { return target.mul(d) / ONE; } + function mulFloor(uint256 target, uint256 d) internal pure returns (uint256) { + return target.mul(d) / ONE; + } + function mulCeil(uint256 target, uint256 d) internal pure returns (uint256) { return target.mul(d).divCeil(ONE); } diff --git a/contracts/lib/InitializableOwnable.sol b/contracts/lib/InitializableOwnable.sol index c90dde0..7d70838 100644 --- a/contracts/lib/InitializableOwnable.sol +++ b/contracts/lib/InitializableOwnable.sol @@ -17,6 +17,7 @@ pragma experimental ABIEncoderV2; contract InitializableOwnable { address public _OWNER_; address public _NEW_OWNER_; + bool internal _INITIALIZED_; // ============ Events ============ @@ -26,6 +27,11 @@ contract InitializableOwnable { // ============ Modifiers ============ + modifier notInitialized() { + require(!_INITIALIZED_, "DODO_INITIALIZED"); + _; + } + modifier onlyOwner() { require(msg.sender == _OWNER_, "NOT_OWNER"); _; @@ -33,13 +39,18 @@ contract InitializableOwnable { // ============ Functions ============ - function transferOwnership(address newOwner) external onlyOwner { + function initOwner(address newOwner) public notInitialized{ + _INITIALIZED_ = true; + _OWNER_ = newOwner; + } + + function transferOwnership(address newOwner) public onlyOwner { require(newOwner != address(0), "INVALID_OWNER"); emit OwnershipTransferPrepared(_OWNER_, newOwner); _NEW_OWNER_ = newOwner; } - function claimOwnership() external { + function claimOwnership() public { require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM"); emit OwnershipTransferred(_OWNER_, _NEW_OWNER_); _OWNER_ = _NEW_OWNER_; diff --git a/contracts/lib/PermissionManager.sol b/contracts/lib/PermissionManager.sol new file mode 100644 index 0000000..e813039 --- /dev/null +++ b/contracts/lib/PermissionManager.sol @@ -0,0 +1,53 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {InitializableOwnable} from "./InitializableOwnable.sol"; + +contract PermissionManager is InitializableOwnable { + + bool public _BLACKLIST_MODE_ON_; + + mapping(address => bool) internal _whitelist_; + mapping(address => bool) internal _blacklist_; + + function isAllowed(address account) external view returns(bool){ + if (_BLACKLIST_MODE_ON_) { + return !_blacklist_[account]; + } else { + return _whitelist_[account]; + } + } + + function openBlacklist() external onlyOwner { + _BLACKLIST_MODE_ON_ = true; + } + + function openWhitelist() external onlyOwner { + _BLACKLIST_MODE_ON_ = true; + + } + + function addToWhitelist(address account) external onlyOwner{ + _whitelist_[account] = true; + } + + function removeFromWhitelist(address account) external onlyOwner{ + _whitelist_[account] = false; + } + + function addToBlacklist(address account) external onlyOwner{ + _blacklist_[account] = true; + } + + function removeFromBlacklist(address account) external onlyOwner{ + _blacklist_[account] = false; + } + +} \ No newline at end of file diff --git a/contracts/helper/MultiSig.sol b/contracts/multisig/MultiSigWalletWithTimelock.sol similarity index 100% rename from contracts/helper/MultiSig.sol rename to contracts/multisig/MultiSigWalletWithTimelock.sol diff --git a/contracts/token/DODOMineReader.sol b/contracts/token/DODOMineReader.sol deleted file mode 100644 index f5ee9a4..0000000 --- a/contracts/token/DODOMineReader.sol +++ /dev/null @@ -1,44 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -import {IDODO} from "../intf/IDODO.sol"; -import {IERC20} from "../intf/IERC20.sol"; -import {SafeMath} from "../lib/SafeMath.sol"; - - -interface IDODOMine { - function getUserLpBalance(address _lpToken, address _user) external view returns (uint256); -} - - -contract DODOMineReader { - using SafeMath for uint256; - - function getUserStakedBalance( - address _dodoMine, - address _dodo, - address _user - ) external view returns (uint256 baseBalance, uint256 quoteBalance) { - address baseLpToken = IDODO(_dodo)._BASE_CAPITAL_TOKEN_(); - address quoteLpToken = IDODO(_dodo)._QUOTE_CAPITAL_TOKEN_(); - - uint256 baseLpBalance = IDODOMine(_dodoMine).getUserLpBalance(baseLpToken, _user); - uint256 quoteLpBalance = IDODOMine(_dodoMine).getUserLpBalance(quoteLpToken, _user); - - uint256 baseLpTotalSupply = IERC20(baseLpToken).totalSupply(); - uint256 quoteLpTotalSupply = IERC20(quoteLpToken).totalSupply(); - - (uint256 baseTarget, uint256 quoteTarget) = IDODO(_dodo).getExpectedTarget(); - baseBalance = baseTarget.mul(baseLpBalance).div(baseLpTotalSupply); - quoteBalance = quoteTarget.mul(quoteLpBalance).div(quoteLpTotalSupply); - - return (baseBalance, quoteBalance); - } -} diff --git a/test/Rebalance.test.ts b/test/Rebalance.test.ts new file mode 100644 index 0000000..a99b4ce --- /dev/null +++ b/test/Rebalance.test.ts @@ -0,0 +1,101 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +import * as assert from 'assert'; +import { Contract } from 'web3-eth-contract'; + +import { DODOContext, getDODOContext } from './utils/Context'; +import { DODO_REBALANCER_NAME, newContract } from './utils/Contracts'; +import { decimalStr } from './utils/Converter'; + +let lp: string; +let trader: string; +let rebalancer: Contract; + +async function init(ctx: DODOContext): Promise { + await ctx.setOraclePrice(decimalStr("100")); + + lp = ctx.spareAccounts[0]; + trader = ctx.spareAccounts[1]; + await ctx.approveDODO(lp); + await ctx.approveDODO(trader); + + await ctx.mintTestToken(lp, decimalStr("10"), decimalStr("1000")); + await ctx.mintTestToken(trader, decimalStr("10"), decimalStr("1000")); + + await ctx.DODO.methods + .depositBaseTo(lp, decimalStr("10")) + .send(ctx.sendParam(lp)); + await ctx.DODO.methods + .depositQuoteTo(lp, decimalStr("1000")) + .send(ctx.sendParam(lp)); + + rebalancer = await newContract(DODO_REBALANCER_NAME) +} + +describe("Trader", () => { + let snapshotId: string; + let ctx: DODOContext; + + before(async () => { + ctx = await getDODOContext(); + await init(ctx); + }); + + beforeEach(async () => { + snapshotId = await ctx.EVM.snapshot(); + }); + + afterEach(async () => { + await ctx.EVM.reset(snapshotId); + }); + + describe("rebalance", () => { + it("R above ONE rebalance", async () => { + await ctx.DODO.methods.buyBaseToken(decimalStr("1"), decimalStr("110"), "0x").send(ctx.sendParam(trader)) + await ctx.DODO.methods.disableTrading().send(ctx.sendParam(ctx.Deployer)) + await ctx.DODO.methods.transferOwnership(rebalancer.options.address).send(ctx.sendParam(ctx.Deployer)) + await rebalancer.methods.claimOwnership(ctx.DODO.options.address).send(ctx.sendParam(ctx.Deployer)) + + await ctx.BASE.methods.transfer(rebalancer.options.address, decimalStr("2")).send(ctx.sendParam(trader)) + await rebalancer.methods.rebalance(ctx.DODO.options.address).send(ctx.sendParam(ctx.Deployer)); + + assert.equal(await ctx.DODO.methods.getMidPrice().call(), await ctx.DODO.methods.getOraclePrice().call()) + + await rebalancer.methods.transferOwnership(ctx.DODO.options.address, ctx.Deployer).send(ctx.sendParam(ctx.Deployer)) + await ctx.DODO.methods.claimOwnership().send(ctx.sendParam(ctx.Deployer)) + + await rebalancer.methods.retrieve(ctx.BASE.options.address).send(ctx.sendParam(ctx.Deployer)) + await rebalancer.methods.retrieve(ctx.QUOTE.options.address).send(ctx.sendParam(ctx.Deployer)) + + assert.equal(await ctx.BASE.methods.balanceOf(ctx.Deployer).call(), "996997569110682237") + assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.Deployer).call(), "101113906016449927750") + }); + + it("R below ONE rebalance", async () => { + await ctx.DODO.methods.sellBaseToken(decimalStr("1"), decimalStr("90"), "0x").send(ctx.sendParam(trader)) + await ctx.DODO.methods.disableTrading().send(ctx.sendParam(ctx.Deployer)) + await ctx.DODO.methods.transferOwnership(rebalancer.options.address).send(ctx.sendParam(ctx.Deployer)) + await rebalancer.methods.claimOwnership(ctx.DODO.options.address).send(ctx.sendParam(ctx.Deployer)) + + await ctx.QUOTE.methods.transfer(rebalancer.options.address, decimalStr("200")).send(ctx.sendParam(trader)) + await rebalancer.methods.rebalance(ctx.DODO.options.address).send(ctx.sendParam(ctx.Deployer)); + + assert.equal(await ctx.DODO.methods.getMidPrice().call(), await ctx.DODO.methods.getOraclePrice().call()) + + await rebalancer.methods.transferOwnership(ctx.DODO.options.address, ctx.Deployer).send(ctx.sendParam(ctx.Deployer)) + await ctx.DODO.methods.claimOwnership().send(ctx.sendParam(ctx.Deployer)) + + await rebalancer.methods.retrieve(ctx.BASE.options.address).send(ctx.sendParam(ctx.Deployer)) + await rebalancer.methods.retrieve(ctx.QUOTE.options.address).send(ctx.sendParam(ctx.Deployer)) + + assert.equal(await ctx.BASE.methods.balanceOf(ctx.Deployer).call(), "997008973080757726") + assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.Deployer).call(), "101085569972088780856") + }); + + }); +}); diff --git a/test/utils/Contracts.ts b/test/utils/Contracts.ts index d89d28c..cc68264 100644 --- a/test/utils/Contracts.ts +++ b/test/utils/Contracts.ts @@ -10,56 +10,9 @@ if (process.env["COVERAGE"]) { jsonPath = "../../.coverage_artifacts/contracts/" } -const CloneFactory = require(`${jsonPath}CloneFactory.json`) -const DODO = require(`${jsonPath}DODO.json`) -const DODOZoo = require(`${jsonPath}DODOZoo.json`) -const DODOEthProxy = require(`${jsonPath}DODOEthProxy.json`) -const WETH = require(`${jsonPath}WETH9.json`) -const TestERC20 = require(`${jsonPath}TestERC20.json`) -const NaiveOracle = require(`${jsonPath}NaiveOracle.json`) -const DODOLpToken = require(`${jsonPath}DODOLpToken.json`) -const Uniswap = require(`${jsonPath}UniswapV2Pair.json`) -const UniswapArbitrageur = require(`${jsonPath}UniswapArbitrageur.json`) -const DODOToken = require(`${jsonPath}DODOToken.json`) -const DODOMine = require(`${jsonPath}DODOMine.json`) -const DODOMineReader = require(`${jsonPath}DODOMineReader.json`) -const LockedTokenVault = require(`${jsonPath}LockedTokenVault.json`) - import { getDefaultWeb3 } from './EVM'; import { Contract } from 'web3-eth-contract'; -export const CLONE_FACTORY_CONTRACT_NAME = "CloneFactory" -export const DODO_CONTRACT_NAME = "DODO" -export const TEST_ERC20_CONTRACT_NAME = "TestERC20" -export const NAIVE_ORACLE_CONTRACT_NAME = "NaiveOracle" -export const DODO_LP_TOKEN_CONTRACT_NAME = "DODOLpToken" -export const DODO_ZOO_CONTRACT_NAME = "DOOZoo" -export const DODO_WILD_CONTRACT_NAME = "DOOWild" -export const DODO_ETH_PROXY_CONTRACT_NAME = "DODOEthProxy" -export const WETH_CONTRACT_NAME = "WETH" -export const UNISWAP_CONTRACT_NAME = "Uniswap" -export const UNISWAP_ARBITRAGEUR_CONTRACT_NAME = "UniswapArbitrageur" -export const DODO_TOKEN_CONTRACT_NAME = "DODOToken" -export const LOCKED_TOKEN_VAULT_CONTRACT_NAME = "LockedTokenVault" -export const DODO_MINE_NAME = "DODOMine" -export const DODO_MINE_READER_NAME = "DODOMineReader" - -var contractMap: { [name: string]: any } = {} -contractMap[CLONE_FACTORY_CONTRACT_NAME] = CloneFactory -contractMap[DODO_CONTRACT_NAME] = DODO -contractMap[TEST_ERC20_CONTRACT_NAME] = TestERC20 -contractMap[NAIVE_ORACLE_CONTRACT_NAME] = NaiveOracle -contractMap[DODO_LP_TOKEN_CONTRACT_NAME] = DODOLpToken -contractMap[DODO_ZOO_CONTRACT_NAME] = DODOZoo -contractMap[DODO_ETH_PROXY_CONTRACT_NAME] = DODOEthProxy -contractMap[WETH_CONTRACT_NAME] = WETH -contractMap[UNISWAP_CONTRACT_NAME] = Uniswap -contractMap[UNISWAP_ARBITRAGEUR_CONTRACT_NAME] = UniswapArbitrageur -contractMap[DODO_TOKEN_CONTRACT_NAME] = DODOToken -contractMap[LOCKED_TOKEN_VAULT_CONTRACT_NAME] = LockedTokenVault -contractMap[DODO_MINE_NAME] = DODOMine -contractMap[DODO_MINE_READER_NAME] = DODOMineReader - interface ContractJson { abi: any; networks: { [network: number]: any }; @@ -67,7 +20,7 @@ interface ContractJson { } export function getContractJSON(contractName: string): ContractJson { - var info = contractMap[contractName] + var info = require(`${jsonPath}${contractName}.json`) return { abi: info.abi, networks: info.networks,