From 34d9ccf668ed02865c8a0620800dcb13c8c6d65d Mon Sep 17 00:00:00 2001 From: owen05 Date: Fri, 8 Jan 2021 17:19:23 +0800 Subject: [PATCH] annotation && v2proxy add gasreturn and trade incentive && proxyV2 incentive test --- contracts/CrowdPooling/impl/CP.sol | 6 + contracts/CrowdPooling/impl/CPFunding.sol | 13 +- contracts/CrowdPooling/impl/CPStorage.sol | 2 + contracts/CrowdPooling/impl/CPVesting.sol | 2 + contracts/CrowdPooling/intf/ICP.sol | 5 - contracts/DODOPrivatePool/impl/DPP.sol | 6 + contracts/DODOPrivatePool/impl/DPPAdmin.sol | 9 +- contracts/DODOPrivatePool/impl/DPPStorage.sol | 6 - contracts/DODOPrivatePool/intf/IDPP.sol | 2 - contracts/DODOVendingMachine/impl/DVM.sol | 7 + .../DODOVendingMachine/impl/DVMFunding.sol | 7 +- .../DODOVendingMachine/impl/DVMTrader.sol | 2 +- .../DODOVendingMachine/impl/DVMVault.sol | 2 +- contracts/Factory/CrowdPoolingFactory.sol | 13 +- contracts/Factory/DPPFactory.sol | 10 +- contracts/Factory/DVMFactory.sol | 7 + contracts/Factory/ERC20Factory.sol | 7 + contracts/SmartRoute/DODOIncentive.sol | 14 +- contracts/SmartRoute/DODOV2Proxy01.sol | 123 ++++++-- .../SmartRoute/helper/DODOSellHelper.sol | 6 - contracts/SmartRoute/intf/IDODOV2Proxy01.sol | 7 +- kovan-mock-v2.0.txt | 12 + test/{Route => V1Proxy}/Incentive.test.ts | 0 test/{Route => V1Proxy}/Route.test.ts | 0 .../proxy.classical.test.ts | 3 +- test/{Proxy => V2Proxy}/proxy.cp.test.ts | 0 test/{Proxy => V2Proxy}/proxy.dpp.test.ts | 10 + test/{Proxy => V2Proxy}/proxy.dvm.test.ts | 10 + test/V2Proxy/proxy.incentive.test.ts | 264 ++++++++++++++++++ test/{Proxy => V2Proxy}/proxy.mix.test.ts | 3 + test/utils/Contracts.ts | 1 + test/utils/ProxyContextV2.ts | 41 ++- truffle-test.sh | 27 +- 33 files changed, 537 insertions(+), 90 deletions(-) rename test/{Route => V1Proxy}/Incentive.test.ts (100%) rename test/{Route => V1Proxy}/Route.test.ts (100%) rename test/{Proxy => V2Proxy}/proxy.classical.test.ts (99%) rename test/{Proxy => V2Proxy}/proxy.cp.test.ts (100%) rename test/{Proxy => V2Proxy}/proxy.dpp.test.ts (99%) rename test/{Proxy => V2Proxy}/proxy.dvm.test.ts (99%) create mode 100644 test/V2Proxy/proxy.incentive.test.ts rename test/{Proxy => V2Proxy}/proxy.mix.test.ts (99%) diff --git a/contracts/CrowdPooling/impl/CP.sol b/contracts/CrowdPooling/impl/CP.sol index db40ec3..94d2553 100644 --- a/contracts/CrowdPooling/impl/CP.sol +++ b/contracts/CrowdPooling/impl/CP.sol @@ -14,6 +14,12 @@ import {IPermissionManager} from "../../lib/PermissionManager.sol"; import {IFeeRateModel} from "../../lib/FeeRateModel.sol"; import {SafeMath} from "../../lib/SafeMath.sol"; +/** + * @title DODO CrowdPooling + * @author DODO Breeder + * + * @notice CrowdPooling initialization + */ contract CP is CPVesting { using SafeMath for uint256; diff --git a/contracts/CrowdPooling/impl/CPFunding.sol b/contracts/CrowdPooling/impl/CPFunding.sol index b15b4a2..4d39cbe 100644 --- a/contracts/CrowdPooling/impl/CPFunding.sol +++ b/contracts/CrowdPooling/impl/CPFunding.sol @@ -20,12 +20,14 @@ import {IDODOCallee} from "../../intf/IDODOCallee.sol"; contract CPFunding is CPStorage { using SafeERC20 for IERC20; + // ============ Events ============ + event Bid(address to, uint256 amount, uint256 fee); event Cancel(address to,uint256 amount); // ============ BID & CALM PHASE ============ - + modifier isBidderAllow(address bidder) { require(_BIDDER_PERMISSION_.isAllowed(bidder), "BIDDER_NOT_ALLOWED"); _; @@ -72,9 +74,10 @@ contract CPFunding is CPStorage { _UNUSED_QUOTE_ = _QUOTE_TOKEN_.balanceOf(address(this)).sub(poolQuote); _UNUSED_BASE_ = _BASE_TOKEN_.balanceOf(address(this)).sub(poolBase); - // 这里的目的是让midPrice尽量等于avgPrice - // 我们统一设定k=1,如果quote和base不平衡,就必然要截断一边 - // DVM截断了quote,所以如果进入池子的quote很多,就要把quote设置成DVM的base + // Try to make midPrice equal to avgPrice + // k=1, If quote and base are not balanced, one side must be cut off + // DVM truncated quote, but if more quote than base entering the pool, we need set the quote to the base + // m = avgPrice // i = m (1-quote/(m*base)) // if quote = m*base i = 1 @@ -199,8 +202,6 @@ contract CPFunding is CPStorage { } } - // ============ Asset Out ============ - function getShares(address user) external view returns (uint256) { return _SHARES_[user]; } diff --git a/contracts/CrowdPooling/impl/CPStorage.sol b/contracts/CrowdPooling/impl/CPStorage.sol index 7be9580..a788e84 100644 --- a/contracts/CrowdPooling/impl/CPStorage.sol +++ b/contracts/CrowdPooling/impl/CPStorage.sol @@ -18,6 +18,8 @@ import {IERC20} from "../../intf/IERC20.sol"; contract CPStorage is InitializableOwnable, ReentrancyGuard { using SafeMath for uint256; + // ============ Constant ============ + uint256 internal constant _SETTLEMENT_EXPIRE_ = 86400 * 7; uint256 internal constant _SETTEL_FUND_ = 200 finney; diff --git a/contracts/CrowdPooling/impl/CPVesting.sol b/contracts/CrowdPooling/impl/CPVesting.sol index fb3bb57..6810ccf 100644 --- a/contracts/CrowdPooling/impl/CPVesting.sol +++ b/contracts/CrowdPooling/impl/CPVesting.sol @@ -27,6 +27,8 @@ contract CPVesting is CPFunding { using SafeMath for uint256; using SafeERC20 for IERC20; + // ================ Modifiers ================ + modifier afterSettlement() { require(_SETTLED_, "NOT_SETTLED"); _; diff --git a/contracts/CrowdPooling/intf/ICP.sol b/contracts/CrowdPooling/intf/ICP.sol index b1193ba..86d6aa8 100644 --- a/contracts/CrowdPooling/intf/ICP.sol +++ b/contracts/CrowdPooling/intf/ICP.sol @@ -15,11 +15,6 @@ interface ICP { uint256[] calldata valueList ) external; - //============================== - - - //============================== - function bid(address to) external; function cancel(address assetTo, uint256 amount) external; diff --git a/contracts/DODOPrivatePool/impl/DPP.sol b/contracts/DODOPrivatePool/impl/DPP.sol index ddd7d16..dee0850 100644 --- a/contracts/DODOPrivatePool/impl/DPP.sol +++ b/contracts/DODOPrivatePool/impl/DPP.sol @@ -12,6 +12,12 @@ import {IFeeRateModel} from "../../lib/FeeRateModel.sol"; import {IERC20} from "../../intf/IERC20.sol"; import {DPPTrader} from "./DPPTrader.sol"; +/** + * @title DODO PrivatePool + * @author DODO Breeder + * + * @notice DODOPrivatePool initialization + */ contract DPP is DPPTrader { function init( address owner, diff --git a/contracts/DODOPrivatePool/impl/DPPAdmin.sol b/contracts/DODOPrivatePool/impl/DPPAdmin.sol index 506ba2c..24d26cb 100644 --- a/contracts/DODOPrivatePool/impl/DPPAdmin.sol +++ b/contracts/DODOPrivatePool/impl/DPPAdmin.sol @@ -12,13 +12,19 @@ import {IDPP} from "../intf/IDPP.sol"; import {IDODOApprove} from "../../intf/IDODOApprove.sol"; import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; +/** + * @title DPPAdmin + * @author DODO Breeder + * + * @notice Admin of DODOPrivatePool + */ contract DPPAdmin is InitializableOwnable { address public _DPP_; address public _OPERATOR_; address public _DODO_APPROVE_; - uint256 public _FREEZE_TIMESTAMP_; + modifier notFreezed() { require(block.timestamp >= _FREEZE_TIMESTAMP_, "ADMIN_FREEZED"); _; @@ -36,7 +42,6 @@ contract DPPAdmin is InitializableOwnable { _DODO_APPROVE_ = dodoApprove; } - //For Rebase Token function sync() external notFreezed onlyOwner { IDPP(_DPP_).ratioSync(); } diff --git a/contracts/DODOPrivatePool/impl/DPPStorage.sol b/contracts/DODOPrivatePool/impl/DPPStorage.sol index 4336d85..17754ca 100644 --- a/contracts/DODOPrivatePool/impl/DPPStorage.sol +++ b/contracts/DODOPrivatePool/impl/DPPStorage.sol @@ -16,12 +16,6 @@ import {IFeeRateModel} from "../../lib/FeeRateModel.sol"; import {IERC20} from "../../intf/IERC20.sol"; import {PMMPricing} from "../../lib/PMMPricing.sol"; -/** - * @title Storage - * @author DODO Breeder - * - * @notice Local Variables - */ contract DPPStorage is InitializableOwnable, ReentrancyGuard { using SafeMath for uint256; diff --git a/contracts/DODOPrivatePool/intf/IDPP.sol b/contracts/DODOPrivatePool/intf/IDPP.sol index 1d3383e..70eda0c 100644 --- a/contracts/DODOPrivatePool/intf/IDPP.sol +++ b/contracts/DODOPrivatePool/intf/IDPP.sol @@ -25,8 +25,6 @@ interface IDPP { //=========== admin ========== function ratioSync() external; - //============================== - function retrieve( address payable to, address token, diff --git a/contracts/DODOVendingMachine/impl/DVM.sol b/contracts/DODOVendingMachine/impl/DVM.sol index d1134a9..5aa1c0b 100644 --- a/contracts/DODOVendingMachine/impl/DVM.sol +++ b/contracts/DODOVendingMachine/impl/DVM.sol @@ -14,6 +14,12 @@ import {DVMTrader} from "./DVMTrader.sol"; import {DVMFunding} from "./DVMFunding.sol"; import {DVMVault} from "./DVMVault.sol"; +/** + * @title DODO VendingMachine + * @author DODO Breeder + * + * @notice DODOVendingMachine initialization + */ contract DVM is DVMTrader, DVMFunding { function init( address maintainer, @@ -76,6 +82,7 @@ contract DVM is DVMTrader, DVMFunding { } // ============ Version Control ============ + function version() external pure returns (string memory) { return "DVM 1.0.0"; } diff --git a/contracts/DODOVendingMachine/impl/DVMFunding.sol b/contracts/DODOVendingMachine/impl/DVMFunding.sol index 2ecbf64..c4d9630 100644 --- a/contracts/DODOVendingMachine/impl/DVMFunding.sol +++ b/contracts/DODOVendingMachine/impl/DVMFunding.sol @@ -40,11 +40,10 @@ contract DVMFunding is DVMVault { quoteInput = quoteBalance.sub(quoteReserve); require(baseInput > 0, "NO_BASE_INPUT"); - // case 1. initial supply - // 包含了 baseReserve == 0 && quoteReserve == 0 的情况 - // 在提币的时候向下取整。因此永远不会出现,balance为0但totalsupply不为0的情况 - // 但有可能出现,reserve>0但totalSupply=0的场景 + // Round down when withdrawing. Therefore, never be a situation occuring balance is 0 but totalsupply is not 0 + // But May Happen,reserve >0 But totalSupply = 0 if (totalSupply == 0) { + // case 1. initial supply require(baseBalance >= 10**3, "INSUFFICIENT_LIQUIDITY_MINED"); shares = baseBalance; // 以免出现balance很大但shares很小的情况 } else if (baseReserve > 0 && quoteReserve == 0) { diff --git a/contracts/DODOVendingMachine/impl/DVMTrader.sol b/contracts/DODOVendingMachine/impl/DVMTrader.sol index 06722c2..98728e1 100644 --- a/contracts/DODOVendingMachine/impl/DVMTrader.sol +++ b/contracts/DODOVendingMachine/impl/DVMTrader.sol @@ -35,7 +35,7 @@ contract DVMTrader is DVMVault { uint256 quoteAmount ); - // ============ Execute ============ + // ============ Trade Functions ============ function sellBase(address to) external diff --git a/contracts/DODOVendingMachine/impl/DVMVault.sol b/contracts/DODOVendingMachine/impl/DVMVault.sol index 8246df4..80e3f77 100644 --- a/contracts/DODOVendingMachine/impl/DVMVault.sol +++ b/contracts/DODOVendingMachine/impl/DVMVault.sol @@ -179,6 +179,7 @@ contract DVMVault is DVMStorage { } // ============================ Permit ====================================== + function permit( address owner, address spender, @@ -205,5 +206,4 @@ contract DVMVault is DVMStorage { ); _approve(owner, spender, value); } - // =========================================================================== } diff --git a/contracts/Factory/CrowdPoolingFactory.sol b/contracts/Factory/CrowdPoolingFactory.sol index 566b00a..b30fd23 100644 --- a/contracts/Factory/CrowdPoolingFactory.sol +++ b/contracts/Factory/CrowdPoolingFactory.sol @@ -15,6 +15,12 @@ import {SafeMath} from "../lib/SafeMath.sol"; import {IERC20} from "../intf/IERC20.sol"; import {DecimalMath} from "../lib/DecimalMath.sol"; +/** + * @title CrowdPoolingFacotry + * @author DODO Breeder + * + * @notice Create And Register CP Pools + */ contract CrowdPoolingFactory is InitializableOwnable { using SafeMath for uint256; // ============ Templates ============ @@ -27,6 +33,7 @@ contract CrowdPoolingFactory is InitializableOwnable { address public _CP_TEMPLATE_; // ============ Settings ============= + uint256 public _CAP_RATIO_ = 50; uint256 public _FREEZE_DURATION_ = 30 days; uint256 public _CALM_DURATION_ = 0; @@ -43,6 +50,7 @@ contract CrowdPoolingFactory is InitializableOwnable { mapping(address => address[]) public _USER_REGISTRY_; // ============ modifiers =========== + modifier valueCheck( address cpAddress, address baseToken, @@ -69,8 +77,6 @@ contract CrowdPoolingFactory is InitializableOwnable { address cp ); - // ============ Functions ============ - constructor( address cloneFactory, address cpTemplate, @@ -87,6 +93,8 @@ contract CrowdPoolingFactory is InitializableOwnable { _DEFAULT_PERMISSION_MANAGER_ = defaultPermissionManager; } + // ============ Functions ============ + function createCrowdPooling() external returns (address newCrowdPooling) { newCrowdPooling = ICloneFactory(_CLONE_FACTORY_).clone(_CP_TEMPLATE_); } @@ -149,6 +157,7 @@ contract CrowdPoolingFactory is InitializableOwnable { } // ============ Owner Functions ============ + function updateCPTemplate(address _newCPTemplate) external onlyOwner { _CP_TEMPLATE_ = _newCPTemplate; } diff --git a/contracts/Factory/DPPFactory.sol b/contracts/Factory/DPPFactory.sol index f1891ca..0f32f2f 100644 --- a/contracts/Factory/DPPFactory.sol +++ b/contracts/Factory/DPPFactory.sol @@ -14,6 +14,12 @@ import {IFeeRateModel} from "../lib/FeeRateModel.sol"; import {IDPP} from "../DODOPrivatePool/intf/IDPP.sol"; import {IDPPAdmin} from "../DODOPrivatePool/intf/IDPPAdmin.sol"; +/** + * @title DODO PrivatePool Factory + * @author DODO Breeder + * + * @notice Create And Register DPP Pools + */ contract DPPFactory is InitializableOwnable { // ============ Templates ============ @@ -43,8 +49,6 @@ contract DPPFactory is InitializableOwnable { event RemoveDPP(address dpp); - // ============ Functions ============ - constructor( address cloneFactory, address dppTemplate, @@ -61,6 +65,8 @@ contract DPPFactory is InitializableOwnable { _DODO_APPROVE_ = dodoApprove; } + // ============ Functions ============ + function createDODOPrivatePool() external returns (address newPrivatePool) { newPrivatePool = ICloneFactory(_CLONE_FACTORY_).clone(_DPP_TEMPLATE_); } diff --git a/contracts/Factory/DVMFactory.sol b/contracts/Factory/DVMFactory.sol index 2b091df..25b4d8a 100644 --- a/contracts/Factory/DVMFactory.sol +++ b/contracts/Factory/DVMFactory.sol @@ -22,6 +22,13 @@ interface IDVMFactory { ) external returns (address newVendingMachine); } + +/** + * @title DODO VendingMachine Factory + * @author DODO Breeder + * + * @notice Create And Register DVM Pools + */ contract DVMFactory is InitializableOwnable { // ============ Templates ============ diff --git a/contracts/Factory/ERC20Factory.sol b/contracts/Factory/ERC20Factory.sol index 7d409ef..cf6a0ab 100644 --- a/contracts/Factory/ERC20Factory.sol +++ b/contracts/Factory/ERC20Factory.sol @@ -12,6 +12,13 @@ import {ICloneFactory} from "../lib/CloneFactory.sol"; import {InitializableERC20} from "../external/ERC20/InitializableERC20.sol"; import {InitializableMintableERC20} from "../external/ERC20/InitializableMintableERC20.sol"; + +/** + * @title DODO ERC20Factory + * @author DODO Breeder + * + * @notice Help user to create erc20 token + */ contract ERC20Factory { // ============ Templates ============ diff --git a/contracts/SmartRoute/DODOIncentive.sol b/contracts/SmartRoute/DODOIncentive.sol index 4d40b05..3669aaf 100644 --- a/contracts/SmartRoute/DODOIncentive.sol +++ b/contracts/SmartRoute/DODOIncentive.sol @@ -40,10 +40,6 @@ contract DODOIncentive is InitializableOwnable { uint112 public totalReward; uint112 public totalDistribution; - constructor(address _dodoToken) public { - _DODO_TOKEN_ = _dodoToken; - } - // ============ Events ============ event SetBoost(address token, uint256 boostRate); @@ -53,6 +49,10 @@ contract DODOIncentive is InitializableOwnable { event SetDefaultRate(uint256 defaultRate); event Incentive(address user,uint256 reward); + constructor(address _dodoToken) public { + _DODO_TOKEN_ = _dodoToken; + } + // ============ Ownable ============ function switchIncentive(uint256 _startBlock) public onlyOwner { @@ -98,6 +98,7 @@ contract DODOIncentive is InitializableOwnable { // ============ Incentive function============ + function triggerIncentive(address fromToken,address toToken, address assetTo) external { require(msg.sender == _DODO_PROXY_, "DODOIncentive:Access restricted"); uint256 _startBlock = startBlock; @@ -106,14 +107,10 @@ contract DODOIncentive is InitializableOwnable { uint256 curTotalDistribution = totalDistribution; uint256 fromRate = boosts[fromToken]; uint256 toRate = boosts[toToken]; - // uint256 rate = (fromRate >= toRate ? fromRate : toRate).add(defaultRate); uint256 rate = (fromRate >= toRate ? fromRate : toRate) + defaultRate; - // uint256 _totalReward = uint256(totalReward).add(block.number.sub(uint256(lastRewardBlock)).mul(dodoPerBlock)); uint256 _totalReward = totalReward + (block.number - lastRewardBlock) * dodoPerBlock; - // uint256 reward = _totalReward.sub(curTotalDistribution).mul(rate).div(1000); uint256 reward = (_totalReward - curTotalDistribution) * rate / 1000; - // uint256 _totalDistribution = curTotalDistribution.add(reward); uint256 _totalDistribution = curTotalDistribution + reward; _update(_totalReward,_totalDistribution); @@ -125,7 +122,6 @@ contract DODOIncentive is InitializableOwnable { function _update(uint256 _totalReward, uint256 _totalDistribution) internal { if(_totalReward == 0) - // _totalReward = uint256(totalReward).add(block.number.sub(uint256(lastRewardBlock)).mul(dodoPerBlock)); _totalReward = totalReward + (block.number - lastRewardBlock) * dodoPerBlock; require(_totalReward < uint112(-1) && _totalDistribution < uint112(-1) && block.number < uint32(-1), "OVERFLOW"); lastRewardBlock = uint32(block.number); diff --git a/contracts/SmartRoute/DODOV2Proxy01.sol b/contracts/SmartRoute/DODOV2Proxy01.sol index cf784cb..1c20e6f 100644 --- a/contracts/SmartRoute/DODOV2Proxy01.sol +++ b/contracts/SmartRoute/DODOV2Proxy01.sol @@ -15,14 +15,21 @@ import {IDODOSellHelper} from "./helper/DODOSellHelper.sol"; import {IERC20} from "../intf/IERC20.sol"; import {IWETH} from "../intf/IWETH.sol"; import {IUni} from "./intf/IUni.sol"; +import {IChi} from "./intf/IChi.sol"; import {SafeMath} from "../lib/SafeMath.sol"; import {UniversalERC20} from "./lib/UniversalERC20.sol"; import {SafeERC20} from "../lib/SafeERC20.sol"; import {DecimalMath} from "../lib/DecimalMath.sol"; import {ReentrancyGuard} from "../lib/ReentrancyGuard.sol"; import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; +import {IDODOIncentive} from "./DODOIncentive.sol"; -//TODO: add gas return && trade incentive && replace DODOV1Proxy02 +/** + * @title DODOV2Proxy01 + * @author DODO Breeder + * + * @notice Entrance of trading in DODO platform + */ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable { using SafeMath for uint256; using UniversalERC20 for IERC20; @@ -36,6 +43,10 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable address public immutable _DVM_FACTORY_; address public immutable _DPP_FACTORY_; address public immutable _CP_FACTORY_; + address public immutable _DODO_INCENTIVE_; + address public immutable _CHI_TOKEN_; + uint256 public _GAS_DODO_MAX_RETURN_ = 0; + uint256 public _GAS_EXTERNAL_RETURN_ = 0; mapping (address => bool) public isWhiteListed; // ============ Events ============ @@ -65,7 +76,9 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable address cpFactory, address payable weth, address dodoApprove, - address dodoSellHelper + address dodoSellHelper, + address chiToken, + address dodoIncentive ) public { _DVM_FACTORY_ = dvmFactory; _DPP_FACTORY_ = dppFactory; @@ -73,6 +86,8 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable _WETH_ = weth; _DODO_APPROVE_ = dodoApprove; _DODO_SELL_HELPER_ = dodoSellHelper; + _CHI_TOKEN_ = chiToken; + _DODO_INCENTIVE_ = dodoIncentive; } function addWhiteList (address contractAddr) public onlyOwner { @@ -83,6 +98,11 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable isWhiteListed[contractAddr] = false; } + function updateGasReturn(uint256 newDodoGasReturn, uint256 newExternalGasReturn) public onlyOwner { + _GAS_DODO_MAX_RETURN_ = newDodoGasReturn; + _GAS_EXTERNAL_RETURN_ = newExternalGasReturn; + } + // ============ DVM Functions (create & add liquidity) ============ function createDODOVendingMachine( @@ -295,6 +315,7 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable uint256 minReturnAmount, address[] memory dodoPairs, uint256 directions, + bool isIncentive, uint256 deadLine ) external @@ -305,6 +326,7 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable { require(dodoPairs.length > 0, "DODOV2Proxy01: PAIRS_EMPTY"); require(minReturnAmount > 0, "DODOV2Proxy01: RETURN_AMOUNT_ZERO"); + uint256 originGas = gasleft(); uint256 originToTokenBalance = IERC20(toToken).balanceOf(msg.sender); IWETH(_WETH_).deposit{value: msg.value}(); @@ -329,6 +351,13 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable returnAmount = IERC20(toToken).balanceOf(msg.sender).sub(originToTokenBalance); require(returnAmount >= minReturnAmount, "DODOV2Proxy01: Return amount is not enough"); + + _dodoGasReturn(originGas); + + if(isIncentive) { + IDODOIncentive(_DODO_INCENTIVE_).triggerIncentive(_ETH_ADDRESS_,toToken,msg.sender); + } + emit OrderHistory( _ETH_ADDRESS_, toToken, @@ -344,6 +373,7 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable uint256 minReturnAmount, address[] memory dodoPairs, uint256 directions, + bool isIncentive, uint256 deadLine ) external @@ -353,6 +383,7 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable { require(dodoPairs.length > 0, "DODOV2Proxy01: PAIRS_EMPTY"); require(minReturnAmount > 0, "DODOV2Proxy01: RETURN_AMOUNT_ZERO"); + uint256 originGas = gasleft(); IDODOApprove(_DODO_APPROVE_).claimTokens(fromToken, msg.sender, dodoPairs[0], fromTokenAmount); @@ -376,6 +407,13 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable require(returnAmount >= minReturnAmount, "DODOV2Proxy01: Return amount is not enough"); IWETH(_WETH_).withdraw(returnAmount); msg.sender.transfer(returnAmount); + + _dodoGasReturn(originGas); + + if(isIncentive) { + IDODOIncentive(_DODO_INCENTIVE_).triggerIncentive(fromToken,_ETH_ADDRESS_,msg.sender); + } + emit OrderHistory( fromToken, _ETH_ADDRESS_, @@ -392,6 +430,7 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable uint256 minReturnAmount, address[] memory dodoPairs, uint256 directions, + bool isIncentive, uint256 deadLine ) external @@ -401,6 +440,7 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable { require(dodoPairs.length > 0, "DODOV2Proxy01: PAIRS_EMPTY"); require(minReturnAmount > 0, "DODOV2Proxy01: RETURN_AMOUNT_ZERO"); + uint256 originGas = gasleft(); uint256 originToTokenBalance = IERC20(toToken).balanceOf(msg.sender); IDODOApprove(_DODO_APPROVE_).claimTokens(fromToken, msg.sender, dodoPairs[0], fromTokenAmount); @@ -423,6 +463,13 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable } returnAmount = IERC20(toToken).balanceOf(msg.sender).sub(originToTokenBalance); require(returnAmount >= minReturnAmount, "DODOV2Proxy01: Return amount is not enough"); + + _dodoGasReturn(originGas); + + if(isIncentive) { + IDODOIncentive(_DODO_INCENTIVE_).triggerIncentive(fromToken,toToken,msg.sender); + } + emit OrderHistory( fromToken, toToken, @@ -440,6 +487,7 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable uint256 fromTokenAmount, uint256 minReturnAmount, bytes memory callDataConcat, + bool isIncentive, uint256 deadLine ) external @@ -474,6 +522,12 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable returnAmount = IERC20(toToken).universalBalanceOf(msg.sender).sub(toTokenOriginBalance); require(returnAmount >= minReturnAmount, "DODOV2Proxy01: Return amount is not enough"); + _externalGasReturn(); + + if(isIncentive) { + IDODOIncentive(_DODO_INCENTIVE_).triggerIncentive(fromToken,toToken,msg.sender); + } + emit OrderHistory( fromToken, toToken, @@ -489,7 +543,8 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable uint256 fromTokenAmount, uint256 minReturnAmount, address[] memory dodoPairs, - uint8[] memory directions, + uint256 directions, + bool isIncentive, uint256 deadLine ) external @@ -498,12 +553,18 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable judgeExpired(deadLine) returns (uint256 returnAmount) { - require(dodoPairs.length == directions.length, "DODOV2Proxy01: PARAMS_LENGTH_NOT_MATCH"); - _deposit(msg.sender, address(this), fromToken, fromTokenAmount, fromToken == _ETH_ADDRESS_); + require(dodoPairs.length > 0, "DODOV2Proxy01: PAIRS_EMPTY"); + require(minReturnAmount > 0, "DODOV2Proxy01: RETURN_AMOUNT_ZERO"); + uint256 originGas = gasleft(); + + address _fromToken = fromToken; + address _toToken = toToken; + + _deposit(msg.sender, address(this), _fromToken, fromTokenAmount, _fromToken == _ETH_ADDRESS_); for (uint256 i = 0; i < dodoPairs.length; i++) { address curDodoPair = dodoPairs[i]; - if (directions[i] == 0) { + if (directions & 1 == 0) { address curDodoBase = IDODOV1(curDodoPair)._BASE_TOKEN_(); uint256 curAmountIn = IERC20(curDodoBase).balanceOf(address(this)); IERC20(curDodoBase).universalApproveMax(curDodoPair, curAmountIn); @@ -518,19 +579,27 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable ); IDODOV1(curDodoPair).buyBaseToken(canBuyBaseAmount, curAmountIn, ""); } + directions = directions >> 1; } + - if (toToken == _ETH_ADDRESS_) { + if (_toToken == _ETH_ADDRESS_) { returnAmount = IWETH(_WETH_).balanceOf(address(this)); IWETH(_WETH_).withdraw(returnAmount); } else { - returnAmount = IERC20(toToken).tokenBalanceOf(address(this)); + returnAmount = IERC20(_toToken).tokenBalanceOf(address(this)); } require(returnAmount >= minReturnAmount, "DODOV2Proxy01: Return amount is not enough"); - IERC20(toToken).universalTransfer(msg.sender, returnAmount); + IERC20(_toToken).universalTransfer(msg.sender, returnAmount); - emit OrderHistory(fromToken, toToken, msg.sender, fromTokenAmount, returnAmount); + _dodoGasReturn(originGas); + + if(isIncentive) { + IDODOIncentive(_DODO_INCENTIVE_).triggerIncentive(_fromToken,_toToken,msg.sender); + } + + emit OrderHistory(_fromToken, _toToken, msg.sender, fromTokenAmount, returnAmount); } function mixSwapV1( @@ -539,11 +608,15 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable uint256 fromTokenAmount, uint256 minReturnAmount, address[] memory mixPairs, - uint8[] memory directions, + uint256[] memory directions, address[] memory portionPath, + bool isIncentive, uint256 deadLine ) external override payable judgeExpired(deadLine) returns (uint256 returnAmount) { require(mixPairs.length == directions.length, "DODOV2Proxy01: PARAMS_LENGTH_NOT_MATCH"); + require(mixPairs.length > 0, "DODOV2Proxy01: PAIRS_EMPTY"); + require(minReturnAmount > 0, "DODOV2Proxy01: RETURN_AMOUNT_ZERO"); + uint256 toTokenOriginBalance = IERC20(toToken).universalBalanceOf(msg.sender); address _fromToken = fromToken; @@ -573,11 +646,6 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable } } - IERC20(_fromToken).universalTransfer( - msg.sender, - IERC20(_fromToken).universalBalanceOf(address(this)) - ); - IERC20(_toToken).universalTransfer( msg.sender, IERC20(_toToken).universalBalanceOf(address(this)) @@ -586,6 +654,11 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable returnAmount = IERC20(_toToken).universalBalanceOf(msg.sender).sub(toTokenOriginBalance); require(returnAmount >= minReturnAmount, "DODOV2Proxy01: Return amount is not enough"); + _externalGasReturn(); + + if(isIncentive) { + IDODOIncentive(_DODO_INCENTIVE_).triggerIncentive(_fromToken,_toToken,msg.sender); + } emit OrderHistory(_fromToken, _toToken, msg.sender, fromTokenAmount, returnAmount); } @@ -696,4 +769,22 @@ contract DODOV2Proxy01 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable SafeERC20.safeTransfer(IERC20(token), to, amount); } } + + function _dodoGasReturn(uint256 originGas) internal { + uint256 _gasDodoMaxReturn = _GAS_DODO_MAX_RETURN_; + if(_gasDodoMaxReturn > 0) { + uint256 calcGasTokenBurn = originGas.sub(gasleft()) / 65000; + uint256 gasTokenBurn = calcGasTokenBurn > _gasDodoMaxReturn ? _gasDodoMaxReturn : calcGasTokenBurn; + if(gasTokenBurn >= 3 && gasleft() > 27710 + gasTokenBurn * 6080) + IChi(_CHI_TOKEN_).freeUpTo(gasTokenBurn); + } + } + + function _externalGasReturn() internal { + uint256 _gasExternalReturn = _GAS_EXTERNAL_RETURN_; + if(_gasExternalReturn > 0) { + if(gasleft() > 27710 + _gasExternalReturn * 6080) + IChi(_CHI_TOKEN_).freeUpTo(_gasExternalReturn); + } + } } diff --git a/contracts/SmartRoute/helper/DODOSellHelper.sol b/contracts/SmartRoute/helper/DODOSellHelper.sol index f517f72..622de36 100644 --- a/contracts/SmartRoute/helper/DODOSellHelper.sol +++ b/contracts/SmartRoute/helper/DODOSellHelper.sol @@ -1,9 +1,3 @@ -/** - *Submitted for verification at Etherscan.io on 2020-10-10 - */ - -// File: contracts/intf/IDODO.sol - /* Copyright 2020 DODO ZOO. diff --git a/contracts/SmartRoute/intf/IDODOV2Proxy01.sol b/contracts/SmartRoute/intf/IDODOV2Proxy01.sol index 3228c09..d59d37c 100644 --- a/contracts/SmartRoute/intf/IDODOV2Proxy01.sol +++ b/contracts/SmartRoute/intf/IDODOV2Proxy01.sol @@ -8,14 +8,15 @@ pragma solidity 0.6.9; pragma experimental ABIEncoderV2; -import {IDODOV1Proxy01} from "./IDODOV1Proxy01.sol"; +import {IDODOV1Proxy02} from "./IDODOV1Proxy02.sol"; -interface IDODOV2Proxy01 is IDODOV1Proxy01 { +interface IDODOV2Proxy01 is IDODOV1Proxy02 { function dodoSwapV2ETHToToken( address toToken, uint256 minReturnAmount, address[] memory dodoPairs, uint256 directions, + bool isIncentive, uint256 deadLine ) external payable returns (uint256 returnAmount); @@ -25,6 +26,7 @@ interface IDODOV2Proxy01 is IDODOV1Proxy01 { uint256 minReturnAmount, address[] memory dodoPairs, uint256 directions, + bool isIncentive, uint256 deadLine ) external returns (uint256 returnAmount); @@ -35,6 +37,7 @@ interface IDODOV2Proxy01 is IDODOV1Proxy01 { uint256 minReturnAmount, address[] memory dodoPairs, uint256 directions, + bool isIncentive, uint256 deadLine ) external returns (uint256 returnAmount); diff --git a/kovan-mock-v2.0.txt b/kovan-mock-v2.0.txt index 44b0fa7..a8039ee 100644 --- a/kovan-mock-v2.0.txt +++ b/kovan-mock-v2.0.txt @@ -255,3 +255,15 @@ Create DVM: 0xd8C30a4E866B188F16aD266dC3333BD47F34ebaE-0x5eca15b12d959dfcf9c71c5 Create DVM: 0xd7f02D1b4F9495B549787808503Ecfd231C3fbDA-0x5eca15b12d959dfcf9c71c59f8b467eb8c6efd0b Pool:0xF2A7675f42F286214550c76c02D79efAd4A6D7ce Tx: 0xd6605ad6f00b8f01d2d0514e833dd1b559550cc9c61d90056930f9ca48d07f65 Create DPP: 0xd8C30a4E866B188F16aD266dC3333BD47F34ebaE-0x5eca15b12d959dfcf9c71c59f8b467eb8c6efd0b Pool:0x50CA4A1714bE260B8aB8980c1649943b392d1cDf Tx: 0x8308da29d498548347380e73ad2daab06b4950db2d6e5e4766d858b4eea50eed Create DPP: 0xd7f02D1b4F9495B549787808503Ecfd231C3fbDA-0x5eca15b12d959dfcf9c71c59f8b467eb8c6efd0b Pool:0x2f487299a7f5aEe132348dC306b51816F4af17c2 Tx: 0x02fab2ac1c39494536d78004dd9de93e20609ea9a61f9aa9d1ddc1b0554071b2 +==================================================== +network type: kovan +Deploy time: 2021/1/8 上午9:29:18 +Mock TOKEN Tx: V2 +ERC20 address: 0xA22E0FdA630101d752C748DC213B36bE74A54cf7; Symbol:WOO0 +==================================================== +network type: kovan +Deploy time: 2021/1/8 上午9:37:13 +Mock POOL Tx: V2 +Approve:0xA22E0FdA630101d752C748DC213B36bE74A54cf7 Tx: 0xb1ad2f3b3782f3a09f75d951073ba7a1a07cde1696427f6383d8dcfcf471d0d6 +Approve:0x43688f367eb83697c3ca5d03c5055b6bd6f6ac4b Tx: 0x11b190fa1dd1232a7ae8e17540c5af4c1b81ad0f20b5e19b28d410b84c8f02bb +Create DPP: 0xA22E0FdA630101d752C748DC213B36bE74A54cf7-0x43688f367eb83697c3ca5d03c5055b6bd6f6ac4b Pool:0xCc50cC29769A296CB828496830664F747711F450 Tx: 0xf04ddff31fce21efe7f37a419ff15fb37e823abfe0cd2e44b47a52e0306879c1 \ No newline at end of file diff --git a/test/Route/Incentive.test.ts b/test/V1Proxy/Incentive.test.ts similarity index 100% rename from test/Route/Incentive.test.ts rename to test/V1Proxy/Incentive.test.ts diff --git a/test/Route/Route.test.ts b/test/V1Proxy/Route.test.ts similarity index 100% rename from test/Route/Route.test.ts rename to test/V1Proxy/Route.test.ts diff --git a/test/Proxy/proxy.classical.test.ts b/test/V2Proxy/proxy.classical.test.ts similarity index 99% rename from test/Proxy/proxy.classical.test.ts rename to test/V2Proxy/proxy.classical.test.ts index 074ea92..a6660cd 100644 --- a/test/Proxy/proxy.classical.test.ts +++ b/test/V2Proxy/proxy.classical.test.ts @@ -119,7 +119,8 @@ async function calcRoute(ctx: ProxyContext, fromTokenAmount: string, slippage: n fromTokenAmount, toAmount, dodoPairs, - tmpDirections, + parseInt(strDirections,2), + false, deadline ) } diff --git a/test/Proxy/proxy.cp.test.ts b/test/V2Proxy/proxy.cp.test.ts similarity index 100% rename from test/Proxy/proxy.cp.test.ts rename to test/V2Proxy/proxy.cp.test.ts diff --git a/test/Proxy/proxy.dpp.test.ts b/test/V2Proxy/proxy.dpp.test.ts similarity index 99% rename from test/Proxy/proxy.dpp.test.ts rename to test/V2Proxy/proxy.dpp.test.ts index e456563..23ebe58 100644 --- a/test/Proxy/proxy.dpp.test.ts +++ b/test/V2Proxy/proxy.dpp.test.ts @@ -242,6 +242,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader), "swap - one jump first"); var a_DOOD = await ctx.DODO.methods.balanceOf(trader).call(); @@ -257,6 +258,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader), "swap - one jump second"); }); @@ -277,6 +279,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader), "swap - two jump first"); var a_DOOD = await ctx.DODO.methods.balanceOf(trader).call(); @@ -292,6 +295,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader), "swap - two jump second"); }); @@ -310,6 +314,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader, "1"), "swap - two jump - inETH first"); var a_DOOD = await ctx.DODO.methods.balanceOf(trader).call(); @@ -324,6 +329,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader, "1"), "swap - two jump - inETH second"); }); @@ -345,6 +351,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader), "swap - two jump - outETH - first"); var a_DOOD = await ctx.DODO.methods.balanceOf(trader).call(); @@ -364,6 +371,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader), "swap - two jump - outETH - second"); }); @@ -386,6 +394,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader), "swap - three jump first"); var a_DOOD = await ctx.DODO.methods.balanceOf(trader).call(); @@ -401,6 +410,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader), "swap - three jump second"); }); diff --git a/test/Proxy/proxy.dvm.test.ts b/test/V2Proxy/proxy.dvm.test.ts similarity index 99% rename from test/Proxy/proxy.dvm.test.ts rename to test/V2Proxy/proxy.dvm.test.ts index f398c6b..ef5f2e6 100644 --- a/test/Proxy/proxy.dvm.test.ts +++ b/test/V2Proxy/proxy.dvm.test.ts @@ -285,6 +285,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader), "swap - one jump first"); var a_DOOD = await ctx.DODO.methods.balanceOf(trader).call(); @@ -300,6 +301,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader), "swap - one jump second"); }); @@ -321,6 +323,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader), "swap - two jump first"); var a_DOOD = await ctx.DODO.methods.balanceOf(trader).call(); @@ -336,6 +339,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader), "swap - two jump second"); }); @@ -354,6 +358,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader, "1"), "swap - two jump - inETH - first"); var a_DOOD = await ctx.DODO.methods.balanceOf(trader).call(); @@ -368,6 +373,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader, "1"), "swap - two jump - inETH - second"); }); @@ -386,6 +392,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader), "swap - two jump - outETH first"); var a_DOOD = await ctx.DODO.methods.balanceOf(trader).call(); @@ -400,6 +407,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader), "swap - two jump - outETH second"); }); @@ -419,6 +427,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader), "swap - three jump first"); var a_DOOD = await ctx.DODO.methods.balanceOf(trader).call(); @@ -432,6 +441,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader), "swap - three jump second"); }); diff --git a/test/V2Proxy/proxy.incentive.test.ts b/test/V2Proxy/proxy.incentive.test.ts new file mode 100644 index 0000000..696d2a8 --- /dev/null +++ b/test/V2Proxy/proxy.incentive.test.ts @@ -0,0 +1,264 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +import { decimalStr, mweiStr } from '../utils/Converter'; +import { logGas } from '../utils/Log'; +import { ProxyContext, getProxyContext } from '../utils/ProxyContextV2'; +import { assert } from 'chai'; +import * as contracts from '../utils/Contracts'; + +let lp: string; +let project: string; +let trader: string; + +let config = { + lpFeeRate: decimalStr("0.002"), + mtFeeRate: decimalStr("0.001"), + k: decimalStr("0.1"), + i: decimalStr("100"), +}; + +async function init(ctx: ProxyContext): Promise { + lp = ctx.SpareAccounts[0]; + project = ctx.SpareAccounts[1]; + trader = ctx.SpareAccounts[2]; + + await ctx.mintTestToken(lp, ctx.DODO, decimalStr("1000000")); + await ctx.mintTestToken(project, ctx.DODO, decimalStr("1000000")); + + await ctx.mintTestToken(lp, ctx.USDT, mweiStr("1000000")); + await ctx.mintTestToken(project, ctx.USDT, mweiStr("1000000")); + + await ctx.approveProxy(lp); + await ctx.approveProxy(project); + await ctx.approveProxy(trader); +} + + +async function initCreateDPP(ctx: ProxyContext, token0: string, token1: string, token0Amount: string, token1Amount: string, ethValue: string, i: string): Promise { + let PROXY = ctx.DODOProxyV2; + await PROXY.methods.createDODOPrivatePool( + token0, + token1, + token0Amount, + token1Amount, + config.lpFeeRate, + i, + config.k, + Math.floor(new Date().getTime() / 1000 + 60 * 10) + ).send(ctx.sendParam(project, ethValue)); + if (token0 == '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE') token0 = ctx.WETH.options.address; + if (token1 == '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE') token1 = ctx.WETH.options.address; + var addr = await ctx.DPPFactory.methods._REGISTRY_(token0, token1, 0).call(); + return addr; +} + +async function initCreateDVM(ctx: ProxyContext, token0: string, token1: string, token0Amount: string, token1Amount: string, ethValue: string, i: string): Promise { + let PROXY = ctx.DODOProxyV2; + await PROXY.methods.createDODOVendingMachine( + token0, + token1, + token0Amount, + token1Amount, + config.lpFeeRate, + i, + config.k, + Math.floor(new Date().getTime() / 1000 + 60 * 10) + ).send(ctx.sendParam(project, ethValue)); + if (token0 == '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE') token0 = ctx.WETH.options.address; + if (token1 == '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE') token1 = ctx.WETH.options.address; + var addr = await ctx.DVMFactory.methods._REGISTRY_(token0, token1, 0).call(); + return addr; +} + +async function initIncentive(ctx: ProxyContext): Promise { + var blockNum = await ctx.Web3.eth.getBlockNumber(); + await ctx.DODOIncentive.methods.switchIncentive(blockNum + 1).send(ctx.sendParam(ctx.Deployer)); + await ctx.mintTestToken(ctx.DODOIncentive.options.address, ctx.DODO, decimalStr("1000000")); +} + + +describe("DODOProxyV2.0", () => { + let snapshotId: string; + let ctx: ProxyContext; + let dpp_DODO_USDT: string; + let dvm_WETH_USDT: string; + + before(async () => { + let ETH = await contracts.newContract( + contracts.WETH_CONTRACT_NAME + ); + ctx = await getProxyContext(ETH.options.address); + await init(ctx); + dpp_DODO_USDT = await initCreateDPP(ctx, ctx.DODO.options.address, ctx.USDT.options.address, decimalStr("100000"), mweiStr("20000"), "0", mweiStr("0.2")); + dvm_WETH_USDT = await initCreateDVM(ctx, '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', ctx.USDT.options.address, decimalStr("5"), mweiStr("3000"), "5", mweiStr("600")); + console.log("dpp_DODO_USDT:", dpp_DODO_USDT); + console.log("dvm_WETH_USDT:", dvm_WETH_USDT); + await initIncentive(ctx); + }); + + beforeEach(async () => { + snapshotId = await ctx.EVM.snapshot(); + }); + + afterEach(async () => { + await ctx.EVM.reset(snapshotId); + }); + + describe("DODOIncentive", () => { + + it("incentive-switch with trade", async () => { + await ctx.DODOIncentive.methods.changePerReward(decimalStr("10")).send(ctx.sendParam(ctx.Deployer)); + var totalReward = await ctx.DODOIncentive.methods.totalReward().call(); + var totalDistribution = await ctx.DODOIncentive.methods.totalDistribution().call(); + var blockNum = await ctx.Web3.eth.getBlockNumber(); + console.log("Init - Total Reward:" + totalReward + "; Total distribution:" + totalDistribution + "; BlockNumber:" + blockNum); + + //Aim to increase block + await ctx.mintTestToken(lp, ctx.DODO, decimalStr("1000")); + await ctx.mintTestToken(lp, ctx.DODO, decimalStr("1000")); + await ctx.mintTestToken(lp, ctx.DODO, decimalStr("1000")); + blockNum = await ctx.Web3.eth.getBlockNumber(); + console.log("Close BlockNumber:", blockNum + 1) + await ctx.DODOIncentive.methods.switchIncentive(0).send(ctx.sendParam(ctx.Deployer)); + totalReward = await ctx.DODOIncentive.methods.totalReward().call(); + totalDistribution = await ctx.DODOIncentive.methods.totalDistribution().call(); + blockNum = await ctx.Web3.eth.getBlockNumber(); + console.log("Close incentive - Total Reward:" + totalReward + "; Total distribution:" + totalDistribution + "; BlockNumber:" + blockNum); + + //Aim to increase block + await ctx.mintTestToken(lp, ctx.DODO, decimalStr("1000")); + await ctx.mintTestToken(lp, ctx.DODO, decimalStr("1000")); + await ctx.mintTestToken(lp, ctx.DODO, decimalStr("1000")); + + blockNum = await ctx.Web3.eth.getBlockNumber(); + await ctx.DODOIncentive.methods.switchIncentive(blockNum + 1).send(ctx.sendParam(ctx.Deployer)); + console.log("Open BlockNumber:", blockNum + 1) + //Aim to increase block + await ctx.mintTestToken(lp, ctx.DODO, decimalStr("1000")); + await ctx.mintTestToken(lp, ctx.DODO, decimalStr("1000")); + await ctx.mintTestToken(lp, ctx.DODO, decimalStr("1000")); + await ctx.DODOIncentive.methods.changePerReward(decimalStr("10")).send(ctx.sendParam(ctx.Deployer)); + totalReward = await ctx.DODOIncentive.methods.totalReward().call(); + totalDistribution = await ctx.DODOIncentive.methods.totalDistribution().call(); + blockNum = await ctx.Web3.eth.getBlockNumber(); + console.log("End incentive - Total Reward:" + totalReward + "; Total distribution:" + totalDistribution + "; BlockNumber:" + blockNum); + assert(totalReward, decimalStr("100")); + }); + + it("incentive-changeBoost with trade", async () => { + await ctx.DODOIncentive.methods.changePerReward(decimalStr("10")).send(ctx.sendParam(ctx.Deployer)); + var totalReward = await ctx.DODOIncentive.methods.totalReward().call(); + var totalDistribution = await ctx.DODOIncentive.methods.totalDistribution().call(); + var blockNum = await ctx.Web3.eth.getBlockNumber(); + console.log("Init - Total Reward:" + totalReward + "; Total distribution:" + totalDistribution + "; BlockNumber:" + blockNum); + + //Aim to increase block + await ctx.mintTestToken(lp, ctx.DODO, decimalStr("1000")); + await ctx.mintTestToken(lp, ctx.DODO, decimalStr("1000")); + await ctx.mintTestToken(lp, ctx.DODO, decimalStr("1000")); + + blockNum = await ctx.Web3.eth.getBlockNumber(); + console.log("Change BlockNumber:", blockNum + 1) + await ctx.DODOIncentive.methods.changePerReward(decimalStr("20")).send(ctx.sendParam(ctx.Deployer)); + totalReward = await ctx.DODOIncentive.methods.totalReward().call(); + totalDistribution = await ctx.DODOIncentive.methods.totalDistribution().call(); + blockNum = await ctx.Web3.eth.getBlockNumber(); + console.log("change incentive - Total Reward:" + totalReward + "; Total distribution:" + totalDistribution + "; BlockNumber:" + blockNum); + + //Aim to increase block + await ctx.mintTestToken(lp, ctx.DODO, decimalStr("1000")); + await ctx.mintTestToken(lp, ctx.DODO, decimalStr("1000")); + await ctx.mintTestToken(lp, ctx.DODO, decimalStr("1000")); + await ctx.DODOIncentive.methods.changePerReward(decimalStr("10")).send(ctx.sendParam(ctx.Deployer)); + totalReward = await ctx.DODOIncentive.methods.totalReward().call(); + totalDistribution = await ctx.DODOIncentive.methods.totalDistribution().call(); + blockNum = await ctx.Web3.eth.getBlockNumber(); + console.log("End incentive - Total Reward:" + totalReward + "; Total distribution:" + totalDistribution + "; BlockNumber:" + blockNum); + + assert(totalReward, decimalStr("140")); + }); + + it("tigger - incentive", async () => { + await ctx.mintTestToken(trader, ctx.DODO, decimalStr("2000")); + var b_DODO = await ctx.DODO.methods.balanceOf(trader).call() + var b_USDT = await ctx.USDT.methods.balanceOf(trader).call() + console.log("Before DODO:" + b_DODO + "; USDT:" + b_USDT); + + var b_totalReward = await ctx.DODOIncentive.methods.totalReward().call(); + var b_totalDistribution = await ctx.DODOIncentive.methods.totalDistribution().call(); + console.log("Before Total Reward:" + b_totalReward + "; Total distribution:" + b_totalDistribution) + + var a_DODO = await ctx.DODO.methods.balanceOf(trader).call() + var a_USDT = await ctx.USDT.methods.balanceOf(trader).call() + console.log("After No Incentive DODO:" + a_DODO + "; USDT:" + a_USDT); + + var dodoPairs = [ + dpp_DODO_USDT + ] + var directions = 0 + + + await logGas(await ctx.DODOProxyV2.methods.dodoSwapV2TokenToToken( + ctx.DODO.options.address, + ctx.USDT.options.address, + decimalStr("500"), + 1, + dodoPairs, + directions, + false, + Math.floor(new Date().getTime() / 1000 + 60 * 10) + ), ctx.sendParam(trader), "swap without incentive first"); + + await logGas(await ctx.DODOProxyV2.methods.dodoSwapV2TokenToToken( + ctx.DODO.options.address, + ctx.USDT.options.address, + decimalStr("500"), + 1, + dodoPairs, + directions, + false, + Math.floor(new Date().getTime() / 1000 + 60 * 10) + ), ctx.sendParam(trader), "swap without incentive second"); + + var a_DODO = await ctx.DODO.methods.balanceOf(trader).call() + var a_USDT = await ctx.USDT.methods.balanceOf(trader).call() + console.log("After No Incentive DODO:" + a_DODO + "; USDT:" + a_USDT); + + await logGas(await ctx.DODOProxyV2.methods.dodoSwapV2TokenToToken( + ctx.DODO.options.address, + ctx.USDT.options.address, + decimalStr("500"), + 1, + dodoPairs, + directions, + true, + Math.floor(new Date().getTime() / 1000 + 60 * 10) + ), ctx.sendParam(trader), "swap with incentive first"); + + await logGas(await ctx.DODOProxyV2.methods.dodoSwapV2TokenToToken( + ctx.DODO.options.address, + ctx.USDT.options.address, + decimalStr("500"), + 1, + dodoPairs, + directions, + true, + Math.floor(new Date().getTime() / 1000 + 60 * 10) + ), ctx.sendParam(trader), "swap with incentive second"); + + var a_totalReward = await ctx.DODOIncentive.methods.totalReward().call(); + var a_totalDistribution = await ctx.DODOIncentive.methods.totalDistribution().call(); + console.log("After Total Reward:" + a_totalReward + "; Total distribution:" + a_totalDistribution) + + a_DODO = await ctx.DODO.methods.balanceOf(trader).call() + a_USDT = await ctx.USDT.methods.balanceOf(trader).call() + console.log("After Incentive DODO:" + a_DODO + "; USDT:" + a_USDT); + assert(a_DODO, "1095000000000000000"); + }); + }); +}); diff --git a/test/Proxy/proxy.mix.test.ts b/test/V2Proxy/proxy.mix.test.ts similarity index 99% rename from test/Proxy/proxy.mix.test.ts rename to test/V2Proxy/proxy.mix.test.ts index 513b660..94fa6b3 100644 --- a/test/Proxy/proxy.mix.test.ts +++ b/test/V2Proxy/proxy.mix.test.ts @@ -119,6 +119,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader), "swap - two jump"); var a_DOOD = await ctx.DODO.methods.balanceOf(trader).call(); @@ -143,6 +144,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader, "1"), "swap - two jump - inETH"); var a_DOOD = await ctx.DODO.methods.balanceOf(trader).call(); @@ -171,6 +173,7 @@ describe("DODOProxyV2.0", () => { 1, dodoPairs, directions, + false, Math.floor(new Date().getTime() / 1000 + 60 * 10) ), ctx.sendParam(trader), "swap - two jump - outETH"); var a_DOOD = await ctx.DODO.methods.balanceOf(trader).call(); diff --git a/test/utils/Contracts.ts b/test/utils/Contracts.ts index 42cc308..f5bd3e0 100644 --- a/test/utils/Contracts.ts +++ b/test/utils/Contracts.ts @@ -45,6 +45,7 @@ export const DPP_ADMIN_NAME = "DPPAdmin" export const DODO_CALLEE_HELPER_NAME = "DODOCalleeHelper" export const CROWD_POOLING_NAME = "CP" export const CROWD_POOLING_FACTORY = "CrowdPoolingFactory" +export const DODO_INCENTIVE = "DODOIncentive" interface ContractJson { abi: any; diff --git a/test/utils/ProxyContextV2.ts b/test/utils/ProxyContextV2.ts index a8444d3..b2d178a 100644 --- a/test/utils/ProxyContextV2.ts +++ b/test/utils/ProxyContextV2.ts @@ -37,6 +37,9 @@ export class ProxyContext { USDT: Contract; WETH: Contract; + //Functions + DODOIncentive: Contract; + Deployer: string; Maintainer: string; SpareAccounts: string[]; @@ -53,6 +56,19 @@ export class ProxyContext { this.WETH = contracts.getContractWithAddress(contracts.WETH_CONTRACT_NAME, weth); + this.DODO = await contracts.newContract( + contracts.MINTABLE_ERC20_CONTRACT_NAME, + ["DODO Token", "DODO", 18] + ); + this.USDT = await contracts.newContract( + contracts.MINTABLE_ERC20_CONTRACT_NAME, + ["USDT Token", "USDT", 6] + ); + this.USDC = await contracts.newContract( + contracts.MINTABLE_ERC20_CONTRACT_NAME, + ["USDC Token", "USDC", 6] + ); + var cloneFactory = await contracts.newContract( contracts.CLONE_FACTORY_CONTRACT_NAME ); @@ -78,6 +94,11 @@ export class ProxyContext { contracts.SMART_APPROVE ); + //DODO Incentive + this.DODOIncentive = await contracts.newContract( + contracts.DODO_INCENTIVE, + [this.DODO.options.address] + ) this.DPPFactory = await contracts.newContract(contracts.DPP_FACTORY_NAME, [ @@ -112,25 +133,17 @@ export class ProxyContext { this.CPFactory.options.address, this.WETH.options.address, this.DODOApprove.options.address, - this.DODOSellHelper.options.address + this.DODOSellHelper.options.address, + "0x0000000000000000000000000000000000000000", + this.DODOIncentive.options.address ] ); await this.DODOProxyV2.methods.initOwner(this.Deployer).send(this.sendParam(this.Deployer)); await this.DODOApprove.methods.init(this.Deployer,this.DODOProxyV2.options.address).send(this.sendParam(this.Deployer)); - - this.DODO = await contracts.newContract( - contracts.MINTABLE_ERC20_CONTRACT_NAME, - ["DODO Token", "DODO", 18] - ); - this.USDT = await contracts.newContract( - contracts.MINTABLE_ERC20_CONTRACT_NAME, - ["USDT Token", "USDT", 6] - ); - this.USDC = await contracts.newContract( - contracts.MINTABLE_ERC20_CONTRACT_NAME, - ["USDC Token", "USDC", 6] - ); + await this.DODOIncentive.methods.initOwner(this.Deployer).send(this.sendParam(this.Deployer)); + + await this.DODOIncentive.methods.changeDODOProxy(this.DODOProxyV2.options.address).send(this.sendParam(this.Deployer)); this.DODOCalleeHelper = await contracts.newContract( contracts.DODO_CALLEE_HELPER_NAME, diff --git a/truffle-test.sh b/truffle-test.sh index 7783009..0f64f7d 100644 --- a/truffle-test.sh +++ b/truffle-test.sh @@ -3,35 +3,40 @@ truffle compile --all if [ "$1"x = "proxy-dpp"x ] then - truffle test ./test/Proxy/proxy.dpp.test.ts + truffle test ./test/V2Proxy/proxy.dpp.test.ts fi if [ "$1"x = "proxy-dvm"x ] then - truffle test ./test/Proxy/proxy.dvm.test.ts + truffle test ./test/V2Proxy/proxy.dvm.test.ts fi if [ "$1"x = "proxy-mix"x ] then - truffle test ./test/Proxy/proxy.mix.test.ts + truffle test ./test/V2Proxy/proxy.mix.test.ts fi if [ "$1"x = "proxy-classical"x ] then - truffle test ./test/Proxy/proxy.classical.test.ts + truffle test ./test/V2Proxy/proxy.classical.test.ts fi if [ "$1"x = "proxy-cp"x ] then - truffle test ./test/Proxy/proxy.cp.test.ts + truffle test ./test/V2Proxy/proxy.cp.test.ts fi -if [ "$1"x = "route-incentive"x ] +if [ "$1"x = "proxy-incentive"x ] then - truffle test ./test/Route/Incentive.test.ts + truffle test ./test/V2Proxy/proxy.incentive.test.ts fi -if [ "$1"x = "route"x ] -then - truffle test ./test/Route/route.test.ts -fi \ No newline at end of file +# if [ "$1"x = "route-incentive"x ] +# then +# truffle test ./test/Route/Incentive.test.ts +# fi + +# if [ "$1"x = "route"x ] +# then +# truffle test ./test/Route/route.test.ts +# fi \ No newline at end of file