diff --git a/contracts/DODOPrivatePool/impl/DPP.sol b/contracts/DODOPrivatePool/impl/DPP.sol index 94b1273..53ded34 100644 --- a/contracts/DODOPrivatePool/impl/DPP.sol +++ b/contracts/DODOPrivatePool/impl/DPP.sol @@ -13,7 +13,6 @@ import {IPermissionManager} from "../../lib/PermissionManager.sol"; import {IExternalValue} from "../../lib/ExternalValue.sol"; import {IERC20} from "../../intf/IERC20.sol"; import {DPPTrader} from "./DPPTrader.sol"; -import {ISmartApprove} from "../../intf/ISmartApprove.sol"; contract DPP is DPPTrader { @@ -31,22 +30,19 @@ contract DPP is DPPTrader { address kSource, address iSource, address gasPriceSource, - address tradePermissionManager, - address iSmartApprove + address tradePermissionManager ) external { require(msg.sender == _FACTORY_, 'INIT FORBIDDEN'); initOwner(owner); _MAINTAINER_ = maintainer; _BASE_TOKEN_ = IERC20(baseTokenAddress); _QUOTE_TOKEN_ = IERC20(quoteTokenAddress); - _DODO_SMART_APPROVE_ = ISmartApprove(iSmartApprove); _LP_FEE_RATE_MODEL_ = IFeeRateModel(lpFeeRateModel); _MT_FEE_RATE_MODEL_ = IFeeRateModel(mtFeeRateModel); _I_ = IExternalValue(iSource); _K_ = IExternalValue(kSource); _GAS_PRICE_LIMIT_ = IExternalValue(gasPriceSource); _TRADE_PERMISSION_ = IPermissionManager(tradePermissionManager); - _resetTargetAndReserve(); } // ============ Version Control ============ diff --git a/contracts/DODOPrivatePool/impl/DPPStorage.sol b/contracts/DODOPrivatePool/impl/DPPStorage.sol index 7f2c4b3..2f51e33 100644 --- a/contracts/DODOPrivatePool/impl/DPPStorage.sol +++ b/contracts/DODOPrivatePool/impl/DPPStorage.sol @@ -15,7 +15,6 @@ import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; import {IPermissionManager} from "../../lib/PermissionManager.sol"; import {IExternalValue} from "../../lib/ExternalValue.sol"; import {IFeeRateModel} from "../../lib/FeeRateModel.sol"; -import {ISmartApprove} from "../../intf/ISmartApprove.sol"; import {IERC20} from "../../intf/IERC20.sol"; import {PMMPricing} from "../../lib/PMMPricing.sol"; @@ -29,7 +28,6 @@ contract DPPStorage is InitializableOwnable, ReentrancyGuard { using SafeMath for uint256; address public _FACTORY_; - ISmartApprove public _DODO_SMART_APPROVE_; // ============ Variables for Control ============ diff --git a/contracts/DODOPrivatePool/impl/DPPVault.sol b/contracts/DODOPrivatePool/impl/DPPVault.sol index 758777e..a6f12c2 100644 --- a/contracts/DODOPrivatePool/impl/DPPVault.sol +++ b/contracts/DODOPrivatePool/impl/DPPVault.sol @@ -37,6 +37,11 @@ contract DPPVault is DPPStorage { return _QUOTE_TOKEN_.balanceOf(address(this)).sub(_QUOTE_RESERVE_); } + // ============ Vault Related + function getVaultReserve() public view returns (uint256 baseReserve, uint256 quoteReserve) { + return (_BASE_RESERVE_, _QUOTE_RESERVE_); + } + // ============ Set Status ============ function setTarget(uint256 baseTarget, uint256 quoteTarget) public onlyOwner { @@ -50,6 +55,12 @@ contract DPPVault is DPPStorage { _QUOTE_RESERVE_ = _QUOTE_TOKEN_.balanceOf(address(this)); } + //TODO: + function initTargetAndReserve() public { + require(tx.origin == _OWNER_, "INIT FORBIDDEN!"); + _resetTargetAndReserve(); + } + function _resetTargetAndReserve() internal { _BASE_TARGET_ = _BASE_TOKEN_.balanceOf(address(this)); _QUOTE_TARGET_ = _QUOTE_TOKEN_.balanceOf(address(this)); @@ -63,14 +74,13 @@ contract DPPVault is DPPStorage { uint256 newI, uint256 newK, uint256 baseOutAmount, - uint256 quoteOutAmount, - address to + uint256 quoteOutAmount ) public { //TODO: owner 权限可以是operator - require(msg.sender == _DODO_SMART_APPROVE_.getSmartSwap() || msg.sender == _OWNER_, "RESET FORBIDDEN!"); + require(tx.origin == _OWNER_, "RESET FORBIDDEN!"); require(newK > 0 && newK <= 10**18, "K OUT OF RANGE!"); - if(baseOutAmount > 0) _transferBaseOut(to, baseOutAmount); - if(quoteOutAmount > 0) _transferQuoteOut(to, quoteOutAmount); + if(baseOutAmount > 0) _transferBaseOut(tx.origin, baseOutAmount); + if(quoteOutAmount > 0) _transferQuoteOut(tx.origin, quoteOutAmount); _resetTargetAndReserve(); _LP_FEE_RATE_MODEL_.setFeeRate(newLpFeeRate); _MT_FEE_RATE_MODEL_.setFeeRate(newMtFeeRate); diff --git a/contracts/DODOPrivatePool/intf/IDPP.sol b/contracts/DODOPrivatePool/intf/IDPP.sol index 8a35e94..41df191 100644 --- a/contracts/DODOPrivatePool/intf/IDPP.sol +++ b/contracts/DODOPrivatePool/intf/IDPP.sol @@ -19,7 +19,6 @@ interface IDPP { address kSource, address iSource, address gasPriceSource, - address tradePermissionManager, - address iSmartApprove + address tradePermissionManager ) external; } diff --git a/contracts/DODOVendingMachine/impl/DVMFunding.sol b/contracts/DODOVendingMachine/impl/DVMFunding.sol index 4718fd3..e2887d4 100644 --- a/contracts/DODOVendingMachine/impl/DVMFunding.sol +++ b/contracts/DODOVendingMachine/impl/DVMFunding.sol @@ -13,7 +13,7 @@ import {DecimalMath} from "../../lib/DecimalMath.sol"; import {IDODOCallee} from "../../intf/IDODOCallee.sol"; contract DVMFunding is DVMVault { - function buyShares(address to) external preventReentrant returns (uint256) { + function buyShares(address to) external preventReentrant returns (uint256 shares,uint256 baseAmount,uint256 quoteAmount) { uint256 baseInput = getBaseInput(); uint256 quoteInput = getQuoteInput(); require(baseInput > 0, "NO_BASE_INPUT"); @@ -33,7 +33,14 @@ contract DVMFunding is DVMVault { if (baseReserve > 0 && quoteReserve > 0) { uint256 baseInputRatio = DecimalMath.divFloor(baseInput, baseReserve); uint256 quoteInputRatio = DecimalMath.divFloor(quoteInput, quoteReserve); - uint256 mintRatio = baseInputRatio > quoteInputRatio ? quoteInputRatio : baseInputRatio; + uint256 mintRatio; + if(baseInputRatio > quoteInputRatio){ + mintRatio = quoteInputRatio; + baseInput = DecimalMath.mulFloor(baseInput, mintRatio); + }else { + mintRatio = baseInputRatio; + quoteInput = DecimalMath.mulFloor(quoteInput, mintRatio); + } // 在提币的时候向下取整。因此永远不会出现,balance为0但totalsupply不为0的情况 // 但有可能出现,reserve>0但totalSupply=0的场景 uint256 totalShare = totalSupply; @@ -45,32 +52,19 @@ contract DVMFunding is DVMVault { } _mint(to, mintAmount); _sync(); + return (mintAmount,baseInput,quoteInput); } - //TODO:Router unwrap WETH - function sellShares( - address to - // uint256 shareAmount, - // bytes calldata data - ) external preventReentrant returns (uint256) { - // require(_SHARES_[msg.sender] >= shareAmount, "SHARES_NOT_ENOUGH"); + function sellShares(address to) external preventReentrant returns (uint256 baseAmount,uint256 quoteAmount) { (uint256 baseBalance, uint256 quoteBalance) = getVaultBalance(); uint256 totalShares = totalSupply; uint256 shareAmount = _SHARES_[address(this)]; - uint256 baseAmount = baseBalance.mul(shareAmount).div(totalShares); - uint256 quoteAmount = quoteBalance.mul(shareAmount).div(totalShares); + baseAmount = baseBalance.mul(shareAmount).div(totalShares); + quoteAmount = quoteBalance.mul(shareAmount).div(totalShares); require(baseAmount > 0 && quoteAmount > 0, 'NO_DLP_INPUT'); _burn(address(this), shareAmount); _transferBaseOut(to, baseAmount); _transferQuoteOut(to, quoteAmount); _sync(); - // if (data.length > 0) - // IDODOCallee(msg.sender).DVMSellShareCall( - // to, - // shareAmount, - // baseAmount, - // quoteAmount, - // data - // ); } } diff --git a/contracts/Factory/DPPFactory.sol b/contracts/Factory/DPPFactory.sol index a200b87..396c785 100644 --- a/contracts/Factory/DPPFactory.sol +++ b/contracts/Factory/DPPFactory.sol @@ -17,7 +17,6 @@ import {IPermissionManager} from "../lib/PermissionManager.sol"; contract DPPFactory is Ownable { address public _CLONE_FACTORY_; - address public _DODO_SMART_APPROVE_; address public _DPP_TEMPLATE_; address public _FEE_RATE_MODEL_TEMPLATE_; address public _PERMISSION_MANAGER_TEMPLATE_; @@ -43,21 +42,19 @@ contract DPPFactory is Ownable { constructor( address cloneFactory, - address dodoSmartApprove, address dppTemplate, address defautFeeRateModelTemplate, address defaultPermissionManagerTemplate, address defaultExternalValueTemplate ) public { _CLONE_FACTORY_ = cloneFactory; - _DODO_SMART_APPROVE_ = dodoSmartApprove; _DPP_TEMPLATE_ = dppTemplate; _FEE_RATE_MODEL_TEMPLATE_ = defautFeeRateModelTemplate; _PERMISSION_MANAGER_TEMPLATE_ = defaultPermissionManagerTemplate; _VALUE_SOURCE_ = defaultExternalValueTemplate; } - function createStandardDODOPrivatePool( + function createDODOPrivatePool( address baseToken, address quoteToken, //TODO: tag 粒度 @@ -73,14 +70,13 @@ contract DPPFactory is Ownable { msg.sender, baseToken, quoteToken, - (valueTemplates[0] == address(0) ? createFeeRateModel(newPrivatePool, values[0]) : valueTemplates[0]), - (valueTemplates[1] == address(0) ? createFeeRateModel(newPrivatePool, values[1]) : valueTemplates[1]), - (valueTemplates[2] == address(0) ? createExternalValueModel(newPrivatePool, values[2]) : valueTemplates[2]), - (valueTemplates[3] == address(0) ? createExternalValueModel(newPrivatePool, values[3]) : valueTemplates[3]), + (valueTemplates[0] == address(0) ? _createFeeRateModel(newPrivatePool, values[0]) : valueTemplates[0]), + (valueTemplates[1] == address(0) ? _createFeeRateModel(newPrivatePool, values[1]) : valueTemplates[1]), + (valueTemplates[2] == address(0) ? _createExternalValueModel(newPrivatePool, values[2]) : valueTemplates[2]), + (valueTemplates[3] == address(0) ? _createExternalValueModel(newPrivatePool, values[3]) : valueTemplates[3]), //hardcode - createExternalValueModel(msg.sender, 10**22), - createPermissionManager(msg.sender), - _DODO_SMART_APPROVE_ + _createExternalValueModel(msg.sender, 10**22), + _createPermissionManager(msg.sender) ); _REGISTRY_[baseToken][quoteToken].push(newPrivatePool); @@ -95,25 +91,19 @@ contract DPPFactory is Ownable { return newPrivatePool; } - function createFeeRateModel(address owner, uint256 feeRate) - public - returns (address feeRateModel) - { + function _createFeeRateModel(address owner, uint256 feeRate) internal returns (address feeRateModel){ feeRateModel = ICloneFactory(_CLONE_FACTORY_).clone(_FEE_RATE_MODEL_TEMPLATE_); IFeeRateModel(feeRateModel).init(owner, feeRate); return feeRateModel; } - function createPermissionManager(address owner) public returns (address permissionManager) { + function _createPermissionManager(address owner) internal returns (address permissionManager) { permissionManager = ICloneFactory(_CLONE_FACTORY_).clone(_PERMISSION_MANAGER_TEMPLATE_); IPermissionManager(permissionManager).initOwner(owner); return permissionManager; } - function createExternalValueModel(address owner, uint256 value) - public - returns (address valueModel) - { + function _createExternalValueModel(address owner, uint256 value) internal returns (address valueModel) { valueModel = ICloneFactory(_CLONE_FACTORY_).clone(_VALUE_SOURCE_); IExternalValue(valueModel).init(owner, value); return valueModel; diff --git a/contracts/Factory/DVMFactory.sol b/contracts/Factory/DVMFactory.sol index 9472dac..4153f85 100644 --- a/contracts/Factory/DVMFactory.sol +++ b/contracts/Factory/DVMFactory.sol @@ -50,7 +50,7 @@ contract DVMFactory is Ownable { _DEFAULT_GAS_PRICE_SOURCE_ = defaultGasPriceSource; } - function createStandardDODOVendingMachine( + function createDODOVendingMachine( address baseToken, address quoteToken, uint256 lpFeeRate, @@ -66,9 +66,10 @@ contract DVMFactory is Ownable { msg.sender, baseToken, quoteToken, - createConstFeeRateModel(newVendingMachine, lpFeeRate), - createConstFeeRateModel(newVendingMachine, mtFeeRate), - createPermissionManager(msg.sender), + //TODO:标准库 统一的feeRateModel,owner归平台控制 + _createConstFeeRateModel(newVendingMachine, lpFeeRate), + _createConstFeeRateModel(newVendingMachine, mtFeeRate), + _createPermissionManager(msg.sender), _DEFAULT_GAS_PRICE_SOURCE_, i, k @@ -86,16 +87,13 @@ contract DVMFactory is Ownable { return newVendingMachine; } - function createConstFeeRateModel(address owner, uint256 feeRate) - public - returns (address feeRateModel) - { + function _createConstFeeRateModel(address owner, uint256 feeRate) internal returns (address feeRateModel) { feeRateModel = ICloneFactory(_CLONE_FACTORY_).clone(_FEE_RATE_MODEL_TEMPLATE_); IConstFeeRateModel(feeRateModel).init(owner, feeRate); return feeRateModel; } - function createPermissionManager(address owner) public returns (address permissionManager) { + function _createPermissionManager(address owner) internal returns (address permissionManager) { permissionManager = ICloneFactory(_CLONE_FACTORY_).clone(_PERMISSION_MANAGER_TEMPLATE_); IPermissionManager(permissionManager).initOwner(owner); return permissionManager; diff --git a/contracts/SmartRoute/DODOV2Proxy01.sol b/contracts/SmartRoute/DODOV2Proxy01.sol new file mode 100644 index 0000000..dbb36d4 --- /dev/null +++ b/contracts/SmartRoute/DODOV2Proxy01.sol @@ -0,0 +1,348 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +import {IDODOV2Proxy01} from "../intf/IDODOV2Proxy01.sol"; +import {IDODOV2} from "../intf/IDODOV2.sol"; +import {IERC20} from "../intf/IERC20.sol"; +import {IWETH} from "../intf/IWETH.sol"; +import {SafeMath} from "../lib/SafeMath.sol"; +import {UniversalERC20} from "../lib/UniversalERC20.sol"; +import {DecimalMath} from "../lib/DecimalMath.sol"; + +contract DODOV2Proxy01 is IDODOV2Proxy01 { + using SafeMath for uint256; + using UniversalERC20 for IERC20; + + address constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address payable public _WETH_; + address public smartApprove; + address public dodoSellHelper; + address public dvmFactory; + address public dppFactory; + + modifier judgeExpired(uint256 deadline) { + require(deadline >= block.timestamp, 'DODOV2Proxy01: EXPIRED'); + _; + } + + //============================== events ================================== + event OrderHistory( + address indexed fromToken, + address indexed toToken, + address indexed sender, + uint256 fromAmount, + uint256 returnAmount, + uint256 timeStamp + ); + + event ExternalRecord(address indexed to, address indexed sender); + //======================================================================== + + + + constructor( + address _dvmFactory, + address _dppFactory, + address payable _weth, + address _smartApprove, + address _dodoSellHelper + ) public { + dvmFactory = _dvmFactory; + dppFactory = _dppFactory; + _WETH_ = _weth; + smartApprove = _smartApprove; + dodoSellHelper = _dodoSellHelper; + } + + //TODO:ETH + function createDODOVendingMachine( + address baseToken, + address quoteToken, + uint256 baseInAmount, + uint256 quoteInAmount, + uint256 lpFeeRate, + uint256 mtFeeRate, + uint256 i, + uint256 k, + uint256 deadline + ) external virtual override payable judgeExpired(deadline) returns (address newVendingMachine,uint256 shares) { + require(k > 0 && k<= 10**18, "DODOV2Proxy01: K OUT OF RANGE"); + newVendingMachine = IDODOV2(dvmFactory).createDODOVendingMachine(baseToken,quoteToken,lpFeeRate,mtFeeRate,i,k); + if(baseInAmount > 0) + IDODOV2(smartApprove).claimTokens(baseToken, msg.sender, newVendingMachine, baseInAmount); + if(quoteInAmount > 0) + IDODOV2(smartApprove).claimTokens(quoteToken, msg.sender, newVendingMachine, quoteInAmount); + (shares,,) = IDODOV2(newVendingMachine).buyShares(msg.sender); + } + + + function _addDVMLiquidity( + address DVMAddress, + uint256 baseInAmount, + uint256 quoteInAmount + ) internal virtual view returns (uint baseAdjustedInAmount, uint quoteAdjustedInAmount) { + (uint256 baseReserve, uint256 quoteReserve) = IDODOV2(DVMAddress).getVaultReserve(); + if (quoteReserve == 0 && baseReserve == 0) { + baseAdjustedInAmount = baseInAmount; + quoteAdjustedInAmount = quoteInAmount; + } + //TODO: 若init时候 baseInAmount > 0 quoteReserve = 0 之后没法添加quote? + if (quoteReserve == 0 && baseReserve > 0) { + baseAdjustedInAmount = baseInAmount; + quoteAdjustedInAmount = 0; + } + if (quoteReserve > 0 && baseReserve > 0) { + uint256 baseIncreaseRatio = DecimalMath.divFloor(baseInAmount, baseReserve); + uint256 quoteIncreaseRatio = DecimalMath.divFloor(quoteInAmount, quoteReserve); + if (baseIncreaseRatio <= quoteIncreaseRatio) { + baseAdjustedInAmount = baseInAmount; + quoteAdjustedInAmount = DecimalMath.mulFloor(quoteReserve, baseIncreaseRatio); + } else { + quoteAdjustedInAmount = quoteInAmount; + baseAdjustedInAmount = DecimalMath.mulFloor(baseReserve, quoteIncreaseRatio); + } + } + } + + //TODO:ETH + function addDVMLiquidity( + address DVMAddress, + address to, + uint256 baseInAmount, + uint256 quoteInAmount, + uint256 baseMinAmount, + uint256 quoteMinAmount, + uint256 deadline + ) external virtual override payable judgeExpired(deadline) returns (uint256 shares,uint256 baseActualInAmount,uint256 quoteActualInAmount) { + (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount) = _addDVMLiquidity(DVMAddress,baseInAmount,quoteInAmount); + address _dvm = DVMAddress; + if(baseAdjustedInAmount > 0) + IDODOV2(smartApprove).claimTokens(IDODOV2(_dvm)._BASE_TOKEN_(), msg.sender, _dvm, baseAdjustedInAmount); + if(quoteAdjustedInAmount > 0) + IDODOV2(smartApprove).claimTokens(IDODOV2(_dvm)._QUOTE_TOKEN_(), msg.sender, _dvm, quoteAdjustedInAmount); + (shares,baseActualInAmount,quoteActualInAmount) = IDODOV2(_dvm).buyShares(to); + require(baseActualInAmount >= baseMinAmount && quoteActualInAmount >= quoteMinAmount, "DODOV2Proxy01: deposit amount is not enough"); + } + + //TODO:ETH + function removeDVMLiquidity( + address DVMAddress, + address to, + uint256 shares, + uint256 baseOutMinAmount, + uint256 quoteOutMinAmount, + uint256 deadline + ) external virtual override payable judgeExpired(deadline) returns (uint256 baseOutAmount,uint256 quoteOutAmount) { + require(shares > 0, "DODOV2Proxy01: Insufficient_Liquidity"); + IDODOV2(smartApprove).claimTokens(DVMAddress, msg.sender, DVMAddress, shares); + (baseOutAmount,quoteOutAmount) = IDODOV2(DVMAddress).sellShares(to); + require(baseOutAmount >= baseOutMinAmount && quoteOutAmount >= quoteOutMinAmount,"DODOV2Proxy01: withdraw amount is not enough"); + } + + //TODO:ETH + function createDODOPrivatePool( + address baseToken, + address quoteToken, + uint256 baseInAmount, + uint256 quoteInAmount, + address[] memory valueTemplates, //feeRateAddr,mtRateAddr,kAddr,iAddr + uint256[] memory values, // feeRate,mtRate,k,i + uint256 deadline + ) external virtual override payable judgeExpired(deadline) returns (address newPrivatePool) { + newPrivatePool = IDODOV2(dppFactory).createDODOPrivatePool(baseToken,quoteToken,valueTemplates,values); + if(baseInAmount > 0) + IDODOV2(smartApprove).claimTokens(baseToken, msg.sender, newPrivatePool, baseInAmount); + if(quoteInAmount > 0) + IDODOV2(smartApprove).claimTokens(quoteToken, msg.sender, newPrivatePool, quoteInAmount); + IDODOV2(newPrivatePool).initTargetAndReserve(); + } + + //TODO:ETH + function resetDODOPrivatePool( + address DPPAddress, + uint256 newLpFeeRate, + uint256 newMtFeeRate, + uint256 newI, + uint256 newK, + uint256 baseInAmount, + uint256 quoteInAmount, + uint256 baseOutAmount, + uint256 quoteOutAmount, + uint256 deadline + ) external virtual override payable judgeExpired(deadline) { + if(baseInAmount > 0) + IDODOV2(smartApprove).claimTokens(IDODOV2(DPPAddress)._BASE_TOKEN_(), msg.sender, DPPAddress, baseInAmount); + if(quoteInAmount > 0) + IDODOV2(smartApprove).claimTokens(IDODOV2(DPPAddress)._QUOTE_TOKEN_(), msg.sender, DPPAddress, quoteInAmount); + IDODOV2(DPPAddress).reset( + newLpFeeRate, + newMtFeeRate, + newI, + newK, + baseOutAmount, + quoteOutAmount + ); + } + + function dodoSwap( + address fromToken, + address toToken, + uint256 fromTokenAmount, + uint256 minReturnAmount, + address[] memory dodoPairs, + uint256[] memory directions, + uint256 deadline + ) external virtual override payable judgeExpired(deadline) returns (uint256 returnAmount) { + require(minReturnAmount > 0, "DODOV2Proxy01: Min return should be bigger than 0."); + require(dodoPairs.length > 0, "DODOV2Proxy01: pairs should exists."); + + if (fromToken != ETH_ADDRESS) { + IDODOV2(smartApprove).claimTokens(fromToken, msg.sender, dodoPairs[0], fromTokenAmount); + } else { + require(msg.value == fromTokenAmount, "DODOV2Proxy01: ETH_AMOUNT_NOT_MATCH"); + IWETH(_WETH_).deposit{value: fromTokenAmount}(); + IWETH(_WETH_).transfer(dodoPairs[0],IWETH(_WETH_).balanceOf(address(this))); + } + + for (uint256 i = 0; i < dodoPairs.length; i++) { + address curTo; + if(i == dodoPairs.length - 1){ + curTo = address(this); + } else { + curTo = dodoPairs[i+1]; + } + if (directions[i] == 0) { + IDODOV2(dodoPairs[i]).sellBase(curTo); + } else { + IDODOV2(dodoPairs[i]).sellQuote(curTo); + } + } + IERC20(fromToken).universalTransfer(msg.sender, IERC20(fromToken).universalBalanceOf(address(this))); + + if (toToken == ETH_ADDRESS) { + uint256 wethAmount = IWETH(_WETH_).balanceOf(address(this)); + IWETH(_WETH_).withdraw(wethAmount); + } + + returnAmount = IERC20(toToken).universalBalanceOf(address(this)); + + require(returnAmount >= minReturnAmount, "DODOV2Proxy01: Return amount is not enough"); + IERC20(toToken).universalTransfer(msg.sender, returnAmount); + emit OrderHistory(fromToken, toToken, msg.sender, fromTokenAmount, returnAmount, block.timestamp); + } + + function externalSwap( + address fromToken, + address toToken, + address approveTarget, + address to, + uint256 gasSwap, + uint256 fromTokenAmount, + uint256 minReturnAmount, + bytes memory callDataConcat, + uint256 deadline + ) external virtual override payable judgeExpired(deadline) returns (uint256 returnAmount) { + + require(minReturnAmount > 0, "DODOV2Proxy01: Min return should be bigger then 0."); + + if (fromToken != ETH_ADDRESS) { + IDODOV2(smartApprove).claimTokens(fromToken, msg.sender, address(this), fromTokenAmount); + IERC20(fromToken).universalApprove(approveTarget, fromTokenAmount); + } + + (bool success, ) = to.call{value: fromToken == ETH_ADDRESS ? msg.value : 0, gas: gasSwap}( + callDataConcat + ); + + require(success, "DODOV2Proxy01: Contract Swap execution Failed"); + + IERC20(fromToken).universalTransfer(msg.sender, IERC20(fromToken).universalBalanceOf(address(this))); + returnAmount = IERC20(toToken).universalBalanceOf(address(this)); + + require(returnAmount >= minReturnAmount, "DODOV2Proxy01: Return amount is not enough"); + IERC20(toToken).universalTransfer(msg.sender, returnAmount); + emit OrderHistory(fromToken, toToken, msg.sender, fromTokenAmount, returnAmount, block.timestamp); + emit ExternalRecord(to, msg.sender); + } + + //====================== temporary for test ====================== + // function sellBaseOnDVM( + // address DVMAddress, + // address to, + // uint256 baseAmount, + // uint256 minReceive + // ) public returns (uint256 receiveAmount) { + // IERC20(IDVM(DVMAddress)._BASE_TOKEN_()).safeTransferFrom( + // msg.sender, + // DVMAddress, + // baseAmount + // ); + // receiveAmount = IDVM(DVMAddress).sellBase(to); + // require(receiveAmount >= minReceive, "RECEIVE_NOT_ENOUGH"); + // return receiveAmount; + // } + + // function sellQuoteOnDVM( + // address DVMAddress, + // address to, + // uint256 quoteAmount, + // uint256 minReceive + // ) public returns (uint256 receiveAmount) { + // IERC20(IDVM(DVMAddress)._QUOTE_TOKEN_()).safeTransferFrom( + // msg.sender, + // DVMAddress, + // quoteAmount + // ); + // receiveAmount = IDVM(DVMAddress).sellQuote(to); + // require(receiveAmount >= minReceive, "RECEIVE_NOT_ENOUGU"); + // return receiveAmount; + // } + + // function depositToDVM( + // address DVMAddress, + // address to, + // uint256 baseAmount, + // uint256 quoteAmount + // ) public returns (uint256 shares) { + // uint256 adjustedBaseAmount; + // uint256 adjustedQuoteAmount; + // (uint256 baseReserve, uint256 quoteReserve) = IDVM(DVMAddress).getVaultReserve(); + // if (quoteReserve == 0 && baseReserve == 0) { + // adjustedBaseAmount = baseAmount; + // adjustedQuoteAmount = quoteAmount; + // } + // if (quoteReserve == 0 && baseReserve > 0) { + // adjustedBaseAmount = baseAmount; + // adjustedQuoteAmount = 0; + // } + // if (quoteReserve > 0 && baseReserve > 0) { + // uint256 baseIncreaseRatio = DecimalMath.divFloor(baseAmount, baseReserve); + // uint256 quoteIncreaseRatio = DecimalMath.divFloor(quoteAmount, quoteReserve); + // if (baseIncreaseRatio <= quoteIncreaseRatio) { + // adjustedBaseAmount = baseAmount; + // adjustedQuoteAmount = DecimalMath.mulFloor(quoteReserve, baseIncreaseRatio); + // } else { + // adjustedQuoteAmount = quoteAmount; + // adjustedBaseAmount = DecimalMath.mulFloor(baseReserve, quoteIncreaseRatio); + // } + // } + // IERC20(IDVM(DVMAddress)._BASE_TOKEN_()).safeTransferFrom( + // msg.sender, + // DVMAddress, + // adjustedBaseAmount + // ); + // IERC20(IDVM(DVMAddress)._QUOTE_TOKEN_()).safeTransferFrom( + // msg.sender, + // DVMAddress, + // adjustedQuoteAmount + // ); + // shares = IDVM(DVMAddress).buyShares(to); + // return shares; + // } +} diff --git a/contracts/SmartRoute/DVMProxy.sol b/contracts/SmartRoute/DVMProxy.sol deleted file mode 100644 index 6e4fc9c..0000000 --- a/contracts/SmartRoute/DVMProxy.sol +++ /dev/null @@ -1,117 +0,0 @@ -/* - - Copyright 2020 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; - -import {IDVM} from "../DODOVendingMachine/intf/IDVM.sol"; -import {IERC20} from "../intf/IERC20.sol"; -import {SafeERC20} from "../lib/SafeERC20.sol"; -import {SafeMath} from "../lib/SafeMath.sol"; -import {DecimalMath} from "../lib/DecimalMath.sol"; - -contract DVMProxy { - using SafeMath for uint256; - using SafeERC20 for IERC20; - - //TODO: dodoSwap - - //TODO: externalSwap - - //TODO: createDVM - - //TODO: addLiquidityToDVM - - //TODO: removeLiquidityToDVM(待定) - - //TODO: createDPP - - //TODO: resetDPP - - //TODO: addLiquidityToClassical - - //TODO: removeLiquidityToClassical - - function sellBaseOnDVM( - address DVMAddress, - address to, - uint256 baseAmount, - uint256 minReceive - ) public returns (uint256 receiveAmount) { - IERC20(IDVM(DVMAddress)._BASE_TOKEN_()).safeTransferFrom( - msg.sender, - DVMAddress, - baseAmount - ); - receiveAmount = IDVM(DVMAddress).sellBase(to); - require(receiveAmount >= minReceive, "RECEIVE_NOT_ENOUGH"); - return receiveAmount; - } - - function sellQuoteOnDVM( - address DVMAddress, - address to, - uint256 quoteAmount, - uint256 minReceive - ) public returns (uint256 receiveAmount) { - IERC20(IDVM(DVMAddress)._QUOTE_TOKEN_()).safeTransferFrom( - msg.sender, - DVMAddress, - quoteAmount - ); - receiveAmount = IDVM(DVMAddress).sellQuote(to); - require(receiveAmount >= minReceive, "RECEIVE_NOT_ENOUGU"); - return receiveAmount; - } - - function depositToDVM( - address DVMAddress, - address to, - uint256 baseAmount, - uint256 quoteAmount - ) public returns (uint256 shares) { - uint256 adjustedBaseAmount; - uint256 adjustedQuoteAmount; - (uint256 baseReserve, uint256 quoteReserve) = IDVM(DVMAddress).getVaultReserve(); - - if (quoteReserve == 0 && baseReserve == 0) { - adjustedBaseAmount = baseAmount; - adjustedQuoteAmount = quoteAmount; - } - - if (quoteReserve == 0 && baseReserve > 0) { - adjustedBaseAmount = baseAmount; - adjustedQuoteAmount = 0; - } - - if (quoteReserve > 0 && baseReserve > 0) { - uint256 baseIncreaseRatio = DecimalMath.divFloor(baseAmount, baseReserve); - uint256 quoteIncreaseRatio = DecimalMath.divFloor(quoteAmount, quoteReserve); - if (baseIncreaseRatio <= quoteIncreaseRatio) { - adjustedBaseAmount = baseAmount; - adjustedQuoteAmount = DecimalMath.mulFloor(quoteReserve, baseIncreaseRatio); - } else { - adjustedQuoteAmount = quoteAmount; - adjustedBaseAmount = DecimalMath.mulFloor(baseReserve, quoteIncreaseRatio); - } - } - - IERC20(IDVM(DVMAddress)._BASE_TOKEN_()).safeTransferFrom( - msg.sender, - DVMAddress, - adjustedBaseAmount - ); - IERC20(IDVM(DVMAddress)._QUOTE_TOKEN_()).safeTransferFrom( - msg.sender, - DVMAddress, - adjustedQuoteAmount - ); - - shares = IDVM(DVMAddress).buyShares(to); - - return shares; - } -} diff --git a/contracts/SmartRoute/SmartApprove.sol b/contracts/SmartRoute/SmartApprove.sol index a894b94..72a2abc 100644 --- a/contracts/SmartRoute/SmartApprove.sol +++ b/contracts/SmartRoute/SmartApprove.sol @@ -24,12 +24,12 @@ contract SmartApprove is Ownable { } function claimTokens( - IERC20 token, + address token, address who, address dest, uint256 amount ) external { require(msg.sender == smartSwap, "Not SmartSwap Address, Access restricted"); - token.safeTransferFrom(who, dest, amount); + IERC20(token).safeTransferFrom(who, dest, amount); } } diff --git a/contracts/SmartRoute/SmartSwap.sol b/contracts/SmartRoute/SmartSwap.sol index 80b8185..94a0601 100644 --- a/contracts/SmartRoute/SmartSwap.sol +++ b/contracts/SmartRoute/SmartSwap.sol @@ -20,9 +20,9 @@ contract SmartSwap is Ownable { using SafeMath for uint256; using UniversalERC20 for IERC20; - IERC20 constant ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); - ISmartApprove public smartApprove; - IDODOSellHelper public dodoSellHelper; + address constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address public smartApprove; + address public dodoSellHelper; address payable public _WETH_; @@ -32,8 +32,8 @@ contract SmartSwap is Ownable { } event OrderHistory( - IERC20 indexed fromToken, - IERC20 indexed toToken, + address indexed fromToken, + address indexed toToken, address indexed sender, uint256 fromAmount, uint256 returnAmount, @@ -47,8 +47,8 @@ contract SmartSwap is Ownable { address _dodoSellHelper, address payable _weth ) public { - smartApprove = ISmartApprove(_smartApprove); - dodoSellHelper = IDODOSellHelper(_dodoSellHelper); + smartApprove = _smartApprove; + dodoSellHelper = _dodoSellHelper; _WETH_ = _weth; } @@ -57,8 +57,8 @@ contract SmartSwap is Ownable { receive() external payable {} function dodoSwap( - IERC20 fromToken, - IERC20 toToken, + address fromToken, + address toToken, uint256 fromTokenAmount, uint256 minReturnAmount, address[] memory dodoPairs, @@ -69,7 +69,7 @@ contract SmartSwap is Ownable { require(dodoPairs.length > 0, "DODO SmartSwap: pairs should exists."); if (fromToken != ETH_ADDRESS) { - smartApprove.claimTokens(fromToken, msg.sender, address(this),fromTokenAmount); + ISmartApprove(smartApprove).claimTokens(fromToken, msg.sender, address(this),fromTokenAmount); } else { require(msg.value == fromTokenAmount, "DODO SmartSwap: ETH_AMOUNT_NOT_MATCH"); IWETH(_WETH_).deposit{value: fromTokenAmount}(); @@ -87,30 +87,30 @@ contract SmartSwap is Ownable { address curDodoQuote = IDODO(curDodoPair)._QUOTE_TOKEN_(); uint256 curAmountIn = IERC20(curDodoQuote).balanceOf(address(this)); IERC20(curDodoQuote).universalApprove(curDodoPair, curAmountIn); - uint256 canBuyBaseAmount = dodoSellHelper.querySellQuoteToken( + uint256 canBuyBaseAmount = IDODOSellHelper(dodoSellHelper).querySellQuoteToken( curDodoPair, curAmountIn ); IDODO(curDodoPair).buyBaseToken(canBuyBaseAmount, curAmountIn, ""); } } - fromToken.universalTransfer(msg.sender, fromToken.universalBalanceOf(address(this))); + IERC20(fromToken).universalTransfer(msg.sender, IERC20(fromToken).universalBalanceOf(address(this))); if (toToken == ETH_ADDRESS) { uint256 wethAmount = IWETH(_WETH_).balanceOf(address(this)); IWETH(_WETH_).withdraw(wethAmount); } - returnAmount = toToken.universalBalanceOf(address(this)); + returnAmount = IERC20(toToken).universalBalanceOf(address(this)); require(returnAmount >= minReturnAmount, "DODO SmartSwap: Return amount is not enough"); - toToken.universalTransfer(msg.sender, returnAmount); + IERC20(toToken).universalTransfer(msg.sender, returnAmount); emit OrderHistory(fromToken, toToken, msg.sender, fromTokenAmount, returnAmount, block.timestamp); } function externalSwap( - IERC20 fromToken, - IERC20 toToken, + address fromToken, + address toToken, address approveTarget, address to, uint256 gasSwap, @@ -123,8 +123,8 @@ contract SmartSwap is Ownable { require(minReturnAmount > 0, "DODO SmartSwap: Min return should be bigger then 0."); if (fromToken != ETH_ADDRESS) { - smartApprove.claimTokens(fromToken, msg.sender, address(this), fromTokenAmount); - fromToken.universalApprove(approveTarget, fromTokenAmount); + ISmartApprove(smartApprove).claimTokens(fromToken, msg.sender, address(this), fromTokenAmount); + IERC20(fromToken).universalApprove(approveTarget, fromTokenAmount); } (bool success, ) = to.call{value: fromToken == ETH_ADDRESS ? msg.value : 0, gas: gasSwap}( @@ -133,11 +133,11 @@ contract SmartSwap is Ownable { require(success, "DODO SmartSwap: Contract Swap execution Failed"); - fromToken.universalTransfer(msg.sender, fromToken.universalBalanceOf(address(this))); - returnAmount = toToken.universalBalanceOf(address(this)); + IERC20(fromToken).universalTransfer(msg.sender, IERC20(fromToken).universalBalanceOf(address(this))); + returnAmount = IERC20(toToken).universalBalanceOf(address(this)); require(returnAmount >= minReturnAmount, "DODO SmartSwap: Return amount is not enough"); - toToken.universalTransfer(msg.sender, returnAmount); + IERC20(toToken).universalTransfer(msg.sender, returnAmount); emit OrderHistory(fromToken, toToken, msg.sender, fromTokenAmount, returnAmount, block.timestamp); emit ExternalRecord(to, msg.sender); } diff --git a/contracts/intf/IDODOV2.sol b/contracts/intf/IDODOV2.sol new file mode 100644 index 0000000..c9c3e91 --- /dev/null +++ b/contracts/intf/IDODOV2.sol @@ -0,0 +1,69 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {IERC20} from "./IERC20.sol"; + +interface IDODOV2 { + function createDODOPrivatePool( + address baseToken, + address quoteToken, + address[] memory valueTemplates, //feeRateAddr,mtRateAddr,kAddr,iAddr + uint256[] memory values //feeRate,mtRate,k,i + ) external returns (address newPrivatePool); + + function createDODOVendingMachine( + address baseToken, + address quoteToken, + uint256 lpFeeRate, + uint256 mtFeeRate, + uint256 i, + uint256 k + ) external returns (address newVendingMachine); + + function sellBase(address to) external returns (uint256 receiveQuoteAmount); + + function sellQuote(address to) external returns (uint256 receiveBaseAmount); + + function buyShares(address to) external returns (uint256,uint256,uint256); + + function sellShares(address to) external returns (uint256,uint256); + + + function getVaultReserve() external view returns (uint256 baseReserve, uint256 quoteReserve); + + function _BASE_TOKEN_() external returns (address); + + function _QUOTE_TOKEN_() external returns (address); + + //========== DODOPrivatePool =========== + function initTargetAndReserve() external; + + function reset( + uint256 newLpFeeRate, + uint256 newMtFeeRate, + uint256 newI, + uint256 newK, + uint256 baseOutAmount, + uint256 quoteOutAmount + ) external; + + //========== DODOSellHelper ============ + + function querySellQuoteToken(address pair, uint256 quoteInAmount) external view returns (uint256 expectedReceiveBaseAmount); + + function querySellBaseToken(address pair, uint256 baseInAmount) external view returns (uint256 expectedReceiveQuoteAmount); + + //========== SmartApprove ============= + + function claimTokens(address token,address who,address dest,uint256 amount) external; + + function getSmartSwap() external view returns (address); + +} \ No newline at end of file diff --git a/contracts/intf/IDODOV2Proxy01.sol b/contracts/intf/IDODOV2Proxy01.sol new file mode 100644 index 0000000..7fad30d --- /dev/null +++ b/contracts/intf/IDODOV2Proxy01.sol @@ -0,0 +1,100 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {IERC20} from "./IERC20.sol"; + +interface IDODOV2Proxy01 { + function dodoSwap( + address fromToken, + address toToken, + uint256 fromTokenAmount, + uint256 minReturnAmount, + address[] memory dodoPairs, + uint256[] memory directions, + uint256 deadline + ) external payable returns (uint256 returnAmount); + + + function externalSwap( + address fromToken, + address toToken, + address approveTarget, + address to, + uint256 gasSwap, + uint256 fromTokenAmount, + uint256 minReturnAmount, + bytes memory callDataConcat, + uint256 deadline + ) external payable returns (uint256 returnAmount); + + + function createDODOVendingMachine( + address baseToken, + address quoteToken, + uint256 baseInAmount, + uint256 quoteInAmount, + uint256 lpFeeRate, + uint256 mtFeeRate, + uint256 i, + uint256 k, + uint256 deadline + ) external payable returns (address newVendingMachine,uint256 shares); + + + function addDVMLiquidity( + address DVMAddress, + address to, + uint256 baseInAmount, + uint256 quoteInAmount, + uint256 baseMinAmount, + uint256 quoteMinAmount, + uint256 deadline + ) external payable returns (uint256 shares,uint256 baseActualInAmount,uint256 quoteActualInAmount); + + + function removeDVMLiquidity( + address DVMAddress, + address to, + uint256 shares, + uint256 baseOutMinAmount, + uint256 quoteOutMinAmount, + uint256 deadline + ) external payable returns (uint256 baseOutAmount,uint256 quoteOutAmount); + + + function createDODOPrivatePool( + address baseToken, + address quoteToken, + uint256 baseInAmount, + uint256 quoteInAmount, + address[] memory valueTemplates, //feeRateAddr,mtRateAddr,kAddr,iAddr + uint256[] memory values, // feeRate,mtRate,k,i + uint256 deadline + ) external payable returns (address newPrivatePool); + + + function resetDODOPrivatePool( + address DPPAddress, + uint256 newLpFeeRate, + uint256 newMtFeeRate, + uint256 newI, + uint256 newK, + uint256 baseInAmount, + uint256 quoteInAmount, + uint256 baseOutAmount, + uint256 quoteOutAmount, + uint256 deadline + ) external payable; + + //TODO: addLiquidityToClassical + + //TODO: removeLiquidityToClassical + +} diff --git a/contracts/intf/ISmartApprove.sol b/contracts/intf/ISmartApprove.sol index 24631d7..60b70da 100644 --- a/contracts/intf/ISmartApprove.sol +++ b/contracts/intf/ISmartApprove.sol @@ -8,9 +8,7 @@ pragma solidity 0.6.9; pragma experimental ABIEncoderV2; -import {IERC20} from "./IERC20.sol"; - interface ISmartApprove { - function claimTokens(IERC20 token,address who,address dest,uint256 amount) external; + function claimTokens(address token,address who,address dest,uint256 amount) external; function getSmartSwap() external view returns (address); } \ No newline at end of file diff --git a/contracts/lib/DODOMath.sol b/contracts/lib/DODOMath.sol index e1844e2..89bebf5 100644 --- a/contracts/lib/DODOMath.sol +++ b/contracts/lib/DODOMath.sol @@ -68,7 +68,7 @@ library DODOMath { DecimalMath.ONE ); // V0 is greater than or equal to V1 according to the solution - return DecimalMath.mul(V1, DecimalMath.ONE.add(premium)); + return DecimalMath.mul(V1, premium); } /*