From b0a0ecd190f0f91ceb4e457ab33074c9c03a5e7c Mon Sep 17 00:00:00 2001 From: owen05 Date: Wed, 20 Jan 2021 11:18:10 +0800 Subject: [PATCH] proxy fix --- contracts/SmartRoute/DODOV1Proxy03.sol | 283 +++++++++++++++++++++++++ contracts/SmartRoute/DODOV2Proxy01.sol | 9 + deploy-detail-v1.5.txt | 6 + migrations/2_deploy_v1.5.js | 12 +- truffle-config.js | 4 +- truffle-test.sh | 2 +- 6 files changed, 307 insertions(+), 9 deletions(-) create mode 100644 contracts/SmartRoute/DODOV1Proxy03.sol diff --git a/contracts/SmartRoute/DODOV1Proxy03.sol b/contracts/SmartRoute/DODOV1Proxy03.sol new file mode 100644 index 0000000..c8b2d75 --- /dev/null +++ b/contracts/SmartRoute/DODOV1Proxy03.sol @@ -0,0 +1,283 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +import {IERC20} from "../intf/IERC20.sol"; +import {UniversalERC20} from "./lib/UniversalERC20.sol"; +import {SafeMath} from "../lib/SafeMath.sol"; +import {IDODOV1} from "./intf/IDODOV1.sol"; +import {IDODOSellHelper} from "./helper/DODOSellHelper.sol"; +import {IWETH} from "../intf/IWETH.sol"; +import {IChi} from "./intf/IChi.sol"; +import {IUni} from "./intf/IUni.sol"; +import {IDODOApprove} from "../intf/IDODOApprove.sol"; +import {IDODOV1Proxy02} from "./intf/IDODOV1Proxy02.sol"; +import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; + +/** + * @title DODOV1Proxy03 + * @author DODO Breeder + * + * @notice Entrance of trading in DODO platform + */ +contract DODOV1Proxy03 is IDODOV1Proxy02, InitializableOwnable { + using SafeMath for uint256; + using UniversalERC20 for IERC20; + + // ============ Storage ============ + + address constant _ETH_ADDRESS_ = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address public immutable _DODO_APPROVE_; + address public immutable _DODO_SELL_HELPER_; + address public immutable _WETH_; + address public immutable _CHI_TOKEN_; + uint256 public _GAS_DODO_MAX_RETURN_ = 10; + uint256 public _GAS_EXTERNAL_RETURN_ = 5; + mapping (address => bool) public isWhiteListed; + + // ============ Events ============ + + event OrderHistory( + address indexed fromToken, + address indexed toToken, + address indexed sender, + uint256 fromAmount, + uint256 returnAmount + ); + + // ============ Modifiers ============ + + modifier judgeExpired(uint256 deadLine) { + require(deadLine >= block.timestamp, "DODOV1Proxy03: EXPIRED"); + _; + } + + constructor( + address dodoApporve, + address dodoSellHelper, + address weth, + address chiToken + ) public { + _DODO_APPROVE_ = dodoApporve; + _DODO_SELL_HELPER_ = dodoSellHelper; + _WETH_ = weth; + _CHI_TOKEN_ = chiToken; + } + + fallback() external payable {} + + receive() external payable {} + + function updateGasReturn(uint256 newDodoGasReturn, uint256 newExternalGasReturn) public onlyOwner { + _GAS_DODO_MAX_RETURN_ = newDodoGasReturn; + _GAS_EXTERNAL_RETURN_ = newExternalGasReturn; + } + + function addWhiteList (address contractAddr) public onlyOwner { + isWhiteListed[contractAddr] = true; + } + + function removeWhiteList (address contractAddr) public onlyOwner { + isWhiteListed[contractAddr] = false; + } + + function dodoSwapV1( + address fromToken, + address toToken, + uint256 fromTokenAmount, + uint256 minReturnAmount, + address[] memory dodoPairs, + uint256 directions, + uint256 deadLine + ) external override payable judgeExpired(deadLine) returns (uint256 returnAmount) { + require(dodoPairs.length > 0, "DODOV1Proxy03: PAIRS_EMPTY"); + require(minReturnAmount > 0, "DODOV1Proxy03: RETURN_AMOUNT_ZERO"); + require(fromToken != _CHI_TOKEN_, "DODOV1Proxy03: NOT_SUPPORT_SELL_CHI"); + require(toToken != _CHI_TOKEN_, "DODOV1Proxy03: NOT_SUPPORT_BUY_CHI"); + + uint256 originGas = gasleft(); + + if (fromToken != _ETH_ADDRESS_) { + IDODOApprove(_DODO_APPROVE_).claimTokens( + fromToken, + msg.sender, + address(this), + fromTokenAmount + ); + } else { + require(msg.value == fromTokenAmount, "DODOV1Proxy03: ETH_AMOUNT_NOT_MATCH"); + IWETH(_WETH_).deposit{value: fromTokenAmount}(); + } + + for (uint256 i = 0; i < dodoPairs.length; i++) { + address curDodoPair = dodoPairs[i]; + if (directions & 1 == 0) { + address curDodoBase = IDODOV1(curDodoPair)._BASE_TOKEN_(); + require(curDodoBase != _CHI_TOKEN_, "DODOV1Proxy03: NOT_SUPPORT_CHI"); + uint256 curAmountIn = IERC20(curDodoBase).balanceOf(address(this)); + IERC20(curDodoBase).universalApproveMax(curDodoPair, curAmountIn); + IDODOV1(curDodoPair).sellBaseToken(curAmountIn, 0, ""); + } else { + address curDodoQuote = IDODOV1(curDodoPair)._QUOTE_TOKEN_(); + require(curDodoQuote != _CHI_TOKEN_, "DODOV1Proxy03: NOT_SUPPORT_CHI"); + uint256 curAmountIn = IERC20(curDodoQuote).balanceOf(address(this)); + IERC20(curDodoQuote).universalApproveMax(curDodoPair, curAmountIn); + uint256 canBuyBaseAmount = IDODOSellHelper(_DODO_SELL_HELPER_).querySellQuoteToken( + curDodoPair, + curAmountIn + ); + IDODOV1(curDodoPair).buyBaseToken(canBuyBaseAmount, curAmountIn, ""); + } + directions = directions >> 1; + } + + if (toToken == _ETH_ADDRESS_) { + returnAmount = IWETH(_WETH_).balanceOf(address(this)); + IWETH(_WETH_).withdraw(returnAmount); + } else { + returnAmount = IERC20(toToken).tokenBalanceOf(address(this)); + } + + require(returnAmount >= minReturnAmount, "DODOV1Proxy03: Return amount is not enough"); + IERC20(toToken).universalTransfer(msg.sender, returnAmount); + + emit OrderHistory(fromToken, toToken, msg.sender, fromTokenAmount, returnAmount); + + uint256 _gasDodoMaxReturn = _GAS_DODO_MAX_RETURN_; + if(_gasDodoMaxReturn > 0) { + uint256 calcGasTokenBurn = originGas.sub(gasleft()) / 65000; + uint256 gasTokenBurn = calcGasTokenBurn > _gasDodoMaxReturn ? _gasDodoMaxReturn : calcGasTokenBurn; + if(gasleft() > 27710 + gasTokenBurn * 6080) + IChi(_CHI_TOKEN_).freeUpTo(gasTokenBurn); + } + } + + function externalSwap( + address fromToken, + address toToken, + address approveTarget, + address swapTarget, + uint256 fromTokenAmount, + uint256 minReturnAmount, + bytes memory callDataConcat, + uint256 deadLine + ) external override payable judgeExpired(deadLine) returns (uint256 returnAmount) { + require(minReturnAmount > 0, "DODOV1Proxy03: RETURN_AMOUNT_ZERO"); + require(fromToken != _CHI_TOKEN_, "DODOV1Proxy03: NOT_SUPPORT_SELL_CHI"); + require(toToken != _CHI_TOKEN_, "DODOV1Proxy03: NOT_SUPPORT_BUY_CHI"); + + address _fromToken = fromToken; + address _toToken = toToken; + + uint256 toTokenOriginBalance = IERC20(_toToken).universalBalanceOf(msg.sender); + + if (_fromToken != _ETH_ADDRESS_) { + IDODOApprove(_DODO_APPROVE_).claimTokens( + _fromToken, + msg.sender, + address(this), + fromTokenAmount + ); + IERC20(_fromToken).universalApproveMax(approveTarget, fromTokenAmount); + } + + require(isWhiteListed[swapTarget], "DODOV1Proxy03: Not Whitelist Contract"); + (bool success, ) = swapTarget.call{value: _fromToken == _ETH_ADDRESS_ ? msg.value : 0}(callDataConcat); + + require(success, "DODOV1Proxy03: External Swap execution Failed"); + + IERC20(_toToken).universalTransfer( + msg.sender, + IERC20(_toToken).universalBalanceOf(address(this)) + ); + returnAmount = IERC20(_toToken).universalBalanceOf(msg.sender).sub(toTokenOriginBalance); + require(returnAmount >= minReturnAmount, "DODOV1Proxy03: Return amount is not enough"); + + emit OrderHistory(_fromToken, _toToken, msg.sender, fromTokenAmount, returnAmount); + + uint256 _gasExternalReturn = _GAS_EXTERNAL_RETURN_; + if(_gasExternalReturn > 0) { + if(gasleft() > 27710 + _gasExternalReturn * 6080) + IChi(_CHI_TOKEN_).freeUpTo(_gasExternalReturn); + } + } + + + function mixSwapV1( + address fromToken, + address toToken, + uint256 fromTokenAmount, + uint256 minReturnAmount, + address[] memory mixPairs, + uint256[] memory directions, + address[] memory portionPath, + uint256 deadLine + ) external override payable judgeExpired(deadLine) returns (uint256 returnAmount) { + require(mixPairs.length == directions.length, "DODOV1Proxy03: PARAMS_LENGTH_NOT_MATCH"); + require(mixPairs.length > 0, "DODOV1Proxy03: PAIRS_EMPTY"); + require(minReturnAmount > 0, "DODOV1Proxy03: RETURN_AMOUNT_ZERO"); + require(fromToken != _CHI_TOKEN_, "DODOV1Proxy03: NOT_SUPPORT_SELL_CHI"); + require(toToken != _CHI_TOKEN_, "DODOV1Proxy03: NOT_SUPPORT_BUY_CHI"); + + uint256 toTokenOriginBalance = IERC20(toToken).universalBalanceOf(msg.sender); + + if (fromToken != _ETH_ADDRESS_) { + IDODOApprove(_DODO_APPROVE_).claimTokens( + fromToken, + msg.sender, + address(this), + fromTokenAmount + ); + } else { + require(msg.value == fromTokenAmount, "DODOV1Proxy03: ETH_AMOUNT_NOT_MATCH"); + IWETH(_WETH_).deposit{value: fromTokenAmount}(); + } + + for (uint256 i = 0; i < mixPairs.length; i++) { + address curPair = mixPairs[i]; + if (directions[i] == 0) { + address curDodoBase = IDODOV1(curPair)._BASE_TOKEN_(); + require(curDodoBase != _CHI_TOKEN_, "DODOV1Proxy03: NOT_SUPPORT_CHI"); + uint256 curAmountIn = IERC20(curDodoBase).balanceOf(address(this)); + IERC20(curDodoBase).universalApproveMax(curPair, curAmountIn); + IDODOV1(curPair).sellBaseToken(curAmountIn, 0, ""); + } else if(directions[i] == 1){ + address curDodoQuote = IDODOV1(curPair)._QUOTE_TOKEN_(); + require(curDodoQuote != _CHI_TOKEN_, "DODOV1Proxy03: NOT_SUPPORT_CHI"); + uint256 curAmountIn = IERC20(curDodoQuote).balanceOf(address(this)); + IERC20(curDodoQuote).universalApproveMax(curPair, curAmountIn); + uint256 canBuyBaseAmount = IDODOSellHelper(_DODO_SELL_HELPER_).querySellQuoteToken( + curPair, + curAmountIn + ); + IDODOV1(curPair).buyBaseToken(canBuyBaseAmount, curAmountIn, ""); + } else { + require(portionPath[0] != _CHI_TOKEN_, "DODOV1Proxy03: NOT_SUPPORT_CHI"); + uint256 curAmountIn = IERC20(portionPath[0]).balanceOf(address(this)); + IERC20(portionPath[0]).universalApproveMax(curPair, curAmountIn); + IUni(curPair).swapExactTokensForTokens(curAmountIn,0,portionPath,address(this),deadLine); + } + } + + IERC20(toToken).universalTransfer( + msg.sender, + IERC20(toToken).universalBalanceOf(address(this)) + ); + + returnAmount = IERC20(toToken).universalBalanceOf(msg.sender).sub(toTokenOriginBalance); + require(returnAmount >= minReturnAmount, "DODOV1Proxy03: Return amount is not enough"); + + emit OrderHistory(fromToken, toToken, msg.sender, fromTokenAmount, returnAmount); + + uint256 _gasExternalReturn = _GAS_EXTERNAL_RETURN_; + if(_gasExternalReturn > 0) { + if(gasleft() > 27710 + _gasExternalReturn * 6080) + IChi(_CHI_TOKEN_).freeUpTo(_gasExternalReturn); + } + } +} diff --git a/contracts/SmartRoute/DODOV2Proxy01.sol b/contracts/SmartRoute/DODOV2Proxy01.sol index 171077a..9277e3a 100644 --- a/contracts/SmartRoute/DODOV2Proxy01.sol +++ b/contracts/SmartRoute/DODOV2Proxy01.sol @@ -496,6 +496,8 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable returns (uint256 returnAmount) { require(minReturnAmount > 0, "DODOV2Proxy01: RETURN_AMOUNT_ZERO"); + require(fromToken != _CHI_TOKEN_, "DODOV2Proxy01: NOT_SUPPORT_SELL_CHI"); + require(toToken != _CHI_TOKEN_, "DODOV2Proxy01: NOT_SUPPORT_BUY_CHI"); uint256 toTokenOriginBalance = IERC20(toToken).universalBalanceOf(msg.sender); if (fromToken != _ETH_ADDRESS_) { @@ -552,6 +554,9 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable { require(dodoPairs.length > 0, "DODOV2Proxy01: PAIRS_EMPTY"); require(minReturnAmount > 0, "DODOV2Proxy01: RETURN_AMOUNT_ZERO"); + require(fromToken != _CHI_TOKEN_, "DODOV2Proxy01: NOT_SUPPORT_SELL_CHI"); + require(toToken != _CHI_TOKEN_, "DODOV2Proxy01: NOT_SUPPORT_BUY_CHI"); + uint256 originGas = gasleft(); address _fromToken = fromToken; @@ -563,11 +568,13 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable address curDodoPair = dodoPairs[i]; if (directions & 1 == 0) { address curDodoBase = IDODOV1(curDodoPair)._BASE_TOKEN_(); + require(curDodoBase != _CHI_TOKEN_, "DODOV2Proxy01: NOT_SUPPORT_CHI"); uint256 curAmountIn = IERC20(curDodoBase).balanceOf(address(this)); IERC20(curDodoBase).universalApproveMax(curDodoPair, curAmountIn); IDODOV1(curDodoPair).sellBaseToken(curAmountIn, 0, ""); } else { address curDodoQuote = IDODOV1(curDodoPair)._QUOTE_TOKEN_(); + require(curDodoQuote != _CHI_TOKEN_, "DODOV2Proxy01: NOT_SUPPORT_CHI"); uint256 curAmountIn = IERC20(curDodoQuote).balanceOf(address(this)); IERC20(curDodoQuote).universalApproveMax(curDodoPair, curAmountIn); uint256 canBuyBaseAmount = IDODOSellHelper(_DODO_SELL_HELPER_).querySellQuoteToken( @@ -614,6 +621,8 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable require(mixPairs.length == mixAdapters.length, "DODOV2Proxy01: PAIR_ADAPTER_NOT_MATCH"); require(mixPairs.length == assetTo.length - 1, "DODOV2Proxy01: PAIR_ASSETTO_NOT_MATCH"); require(minReturnAmount > 0, "DODOV2Proxy01: RETURN_AMOUNT_ZERO"); + require(fromToken != _CHI_TOKEN_, "DODOV2Proxy01: NOT_SUPPORT_SELL_CHI"); + require(toToken != _CHI_TOKEN_, "DODOV2Proxy01: NOT_SUPPORT_BUY_CHI"); uint256 toTokenOriginBalance = IERC20(toToken).universalBalanceOf(msg.sender); diff --git a/deploy-detail-v1.5.txt b/deploy-detail-v1.5.txt index 6853957..9d412e0 100644 --- a/deploy-detail-v1.5.txt +++ b/deploy-detail-v1.5.txt @@ -94,3 +94,9 @@ Deploy time: 2021/1/15 上午12:16:50 Deploy type: Proxy DODOProxyV2 Address: 0x66EEdd38f757d8F9A0b7B4f47d3f74A09C7a1F37 Set DODOProxyV2 Owner tx: 0x832e2f880fc6798d87ea1aa8f56fd8f6cdd14a9dde62278fd5a1716c073dcb7a +==================================================== +network type: live +Deploy time: 2021/1/20 上午10:58:55 +Deploy type: Proxy +DODOV1Proxy03 Address: 0x9A9942458754bDf65dCbCd0B6B4B842a7D4031AB +Set DODOProxy Owner tx: 0x2fd5f1abb016536e4557d609bdf52e2b790cbac85dead5097128e77b4357252d diff --git a/migrations/2_deploy_v1.5.js b/migrations/2_deploy_v1.5.js index 483fe3d..3004e16 100644 --- a/migrations/2_deploy_v1.5.js +++ b/migrations/2_deploy_v1.5.js @@ -4,7 +4,7 @@ const file = fs.createWriteStream("../deploy-detail-v1.5.txt", { 'flags': 'a' }) let logger = new console.Console(file, file); const DODOApprove = artifacts.require("DODOApprove"); -const DODOProxyV2 = artifacts.require("DODOV1Proxy02"); +const DODOV1Proxy03 = artifacts.require("DODOV1Proxy03"); const DODOSellHelper = artifacts.require("DODOSellHelper"); const DODOSwapCalcHelper = artifacts.require("DODOSwapCalcHelper"); @@ -62,16 +62,16 @@ module.exports = async (deployer, network, accounts) => { } await deployer.deploy( - DODOProxyV2, + DODOV1Proxy03, DODOApproveAddress, DODOSellHelperAddress, WETHAddress, chiAddress ); - logger.log("DODOProxyV2 Address: ", DODOProxyV2.address); - const DODOProxyV2Instance = await DODOProxyV2.at(DODOProxyV2.address); - tx = await DODOProxyV2Instance.initOwner(ownerAddress); - logger.log("Set DODOProxyV2 Owner tx: ", tx.tx); + logger.log("DODOV1Proxy03 Address: ", DODOV1Proxy03.address); + const DODOProxyInstance = await DODOV1Proxy03.at(DODOV1Proxy03.address); + tx = await DODOProxyInstance.initOwner(ownerAddress); + logger.log("Set DODOProxy Owner tx: ", tx.tx); // const DODOApproveInstance = await DODOApprove.at(DODOApproveAddress); // tx = await DODOApproveInstance.init(ownerAddress, DODOProxyV1.address); diff --git a/truffle-config.js b/truffle-config.js index 89eb1df..5acf54f 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -38,7 +38,7 @@ module.exports = { * $ truffle test --network */ deploySwitch: { - DEPLOY_V1: false, + DEPLOY_V1: true, DEPLOY_V2: false, ADAPTER: false, MOCK_TOKEN: false, @@ -79,7 +79,7 @@ module.exports = { return new HDWalletProvider(privKey, "https://mainnet.infura.io/v3/" + infuraId); }, gas: 3000000, - gasPrice: 100000000000, + gasPrice: 75000000000, network_id: 1, skipDryRun: true }, diff --git a/truffle-test.sh b/truffle-test.sh index b3c15f4..24e566e 100644 --- a/truffle-test.sh +++ b/truffle-test.sh @@ -1,5 +1,5 @@ #!/bin/bash -truffle compile --all +# truffle compile --all if [ "$1"x = "proxy-dpp"x ] then