diff --git a/contracts/SmartRoute/proxies/DODORouteProxy.sol b/contracts/SmartRoute/proxies/DODORouteProxy.sol new file mode 100644 index 0000000..38a3785 --- /dev/null +++ b/contracts/SmartRoute/proxies/DODORouteProxy.sol @@ -0,0 +1,164 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {IDODOApproveProxy} from "../DODOApproveProxy.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 {SafeERC20} from "../../lib/SafeERC20.sol"; +import {IDODOAdapter} from "../intf/IDODOAdapter.sol"; + + +/** + * @title DODORouteProxy + * @author DODO Breeder + * + * @notice Entrance of Split trading in DODO platform + */ +contract DODORouteProxy { + using SafeMath for uint256; + using UniversalERC20 for IERC20; + + // ============ Storage ============ + + address constant _ETH_ADDRESS_ = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address public immutable _WETH_; + address public immutable _DODO_APPROVE_PROXY_; + + // ============ Events ============ + + event OrderHistory( + address fromToken, + address toToken, + address sender, + uint256 fromAmount, + uint256 returnAmount + ); + + // ============ Modifiers ============ + + modifier judgeExpired(uint256 deadLine) { + require(deadLine >= block.timestamp, "DODORouteProxy: EXPIRED"); + _; + } + + fallback() external payable {} + + receive() external payable {} + + constructor ( + address payable weth, + address dodoApproveProxy + ) public { + _WETH_ = weth; + _DODO_APPROVE_PROXY_ = dodoApproveProxy; + } + + function dodoMutliSwap( + uint256 fromTokenAmount, + uint256 minReturnAmount, + uint256[] memory totalWeight, + uint256[] memory splitNumber, + address[] memory midToken, + address[] memory assetFrom, + bytes[] memory sequence, + uint256 deadLine + ) external payable judgeExpired(deadLine) returns (uint256 returnAmount) { + require(assetFrom.length == splitNumber.length, 'DODORouteProxy: PAIR_ASSETTO_NOT_MATCH'); + require(minReturnAmount > 0, "DODORouteProxy: RETURN_AMOUNT_ZERO"); + + uint256 _fromTokenAmount = fromTokenAmount; + address fromToken = midToken[0]; + address toToken = midToken[midToken.length - 1]; + + uint256 toTokenOriginBalance = IERC20(toToken).universalBalanceOf(msg.sender); + _deposit(msg.sender, assetFrom[0], fromToken, _fromTokenAmount, fromToken == _ETH_ADDRESS_); + + _multiSwap(totalWeight, midToken, splitNumber, sequence, assetFrom); + + if(toToken == _ETH_ADDRESS_) { + returnAmount = IWETH(_WETH_).balanceOf(address(this)); + IWETH(_WETH_).withdraw(returnAmount); + msg.sender.transfer(returnAmount); + }else { + returnAmount = IERC20(toToken).tokenBalanceOf(msg.sender).sub(toTokenOriginBalance); + } + + require(returnAmount >= minReturnAmount, "DODORouteProxy: Return amount is not enough"); + + emit OrderHistory( + fromToken, + toToken, + msg.sender, + _fromTokenAmount, + returnAmount + ); + } + + + //====================== internal ======================= + + function _multiSwap( + uint256[] memory totalWeight, + address[] memory midToken, + uint256[] memory splitNumber, + bytes[] memory swapSequence, + address[] memory assetFrom + ) internal { + for(uint256 i = 1; i < splitNumber.length; i++) { + // define midtoken address, ETH -> WETH address + uint256 curTotalAmount = IERC20(midToken[i]).tokenBalanceOf(assetFrom[i-1]); + uint256 curTotalWeight = totalWeight[i-1]; + + for(uint256 j = splitNumber[i-1]; j < splitNumber[i]; j++) { + (address pool, address adapter, uint256 mixPara) = abi.decode(swapSequence[j], (address, address, uint256)); + uint256 direction = mixPara >> 17; + uint256 weight = (0xffff & mixPara) >> 9; + uint256 poolEdition = (0xff & mixPara); + + if(assetFrom[i-1] == address(this)) { + uint256 curAmount = curTotalAmount.div(curTotalWeight).mul(weight); + + if(poolEdition == 1) { + //For using transferFrom pool (like dodoV1) + IERC20(midToken[i]).transfer(adapter, curAmount); + } else { + //For using transfer pool (like dodoV2) + IERC20(midToken[i]).transfer(pool, curAmount); + } + } + + if(direction == 0) { + IDODOAdapter(adapter).sellBase(assetFrom[i], pool); + } else { + IDODOAdapter(adapter).sellQuote(assetFrom[i], pool); + } + } + } + } + + function _deposit( + address from, + address to, + address token, + uint256 amount, + bool isETH + ) internal { + if (isETH) { + if (amount > 0) { + IWETH(_WETH_).deposit{value: amount}(); + if (to != address(this)) SafeERC20.safeTransfer(IERC20(_WETH_), to, amount); + } + } else { + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(token, from, to, amount); + } + } +} \ No newline at end of file diff --git a/deploy-detail-periphery.txt b/deploy-detail-periphery.txt index 6d18582..4bfb577 100644 --- a/deploy-detail-periphery.txt +++ b/deploy-detail-periphery.txt @@ -392,3 +392,8 @@ DspFactoryAddress: 0xaEc55245AB0cDcc03Bc5f104dAdCd0a33Ef50714 Init DspFactory Tx: 0xa62cc600cdfe2378dc159eb18cdcff06a677804086354c185ee304ee90dc550e DODOV2RouteHelper Address: 0x67166F14E9aCf43A822BE147eA59CdDd01A7C00d DODODspProxy Address: 0x9951CdEc21F42ab69D02daAAFEF4C3fc810B36FF +==================================================== +network type: heco +Deploy time: 2021/4/20 下午12:41:59 +Deploy type: DODORouteProxy +DODORouteProxy Address: 0x8D3102e4fe76B5E1a896D8BCBC1BA2643D8Bea1a diff --git a/migrations/4_deploy_periphery.js b/migrations/4_deploy_periphery.js index 1044d17..7176093 100644 --- a/migrations/4_deploy_periphery.js +++ b/migrations/4_deploy_periphery.js @@ -17,6 +17,7 @@ const UpCrowdPoolingFactory = artifacts.require("UpCrowdPoolingFactory"); const CpFactory = artifacts.require("CrowdPoolingFactory"); const MultiCall = artifacts.require("Multicall"); const LockedTokenVault = artifacts.require("LockedTokenVault"); +const DODORouteProxy = artifacts.require("DODORouteProxy"); const DspTemplate = artifacts.require("DSP"); const DspFactory = artifacts.require("DSPFactory"); @@ -279,6 +280,21 @@ module.exports = async (deployer, network, accounts) => { logger.log("Init DODORechargeAddress Tx:", tx.tx); } + if (deploySwitch.MULTIHOP) { + logger.log("===================================================="); + logger.log("network type: " + network); + logger.log("Deploy time: " + new Date().toLocaleString()); + logger.log("Deploy type: DODORouteProxy"); + + await deployer.deploy( + DODORouteProxy, + WETHAddress, + DODOApproveProxyAddress + ); + + logger.log("DODORouteProxy Address: ", DODORouteProxy.address); + } + if (deploySwitch.vDODOToken) { logger.log("===================================================="); diff --git a/truffle-config.js b/truffle-config.js index 2044cdd..cffe8c5 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -58,6 +58,7 @@ module.exports = { DSP: false, LockedVault: false, ERC20Mine: false, + MULTIHOP: true }, networks: {