diff --git a/config/bsc-config.js b/config/bsc-config.js index 6031094..d59460d 100644 --- a/config/bsc-config.js +++ b/config/bsc-config.js @@ -9,13 +9,13 @@ module.exports = { DODOSellHelper: "0x0F859706AeE7FcF61D5A8939E8CB9dBB6c1EDA33", DODOCalleeHelper: "0xaaffAd1017D6a13E026A00121BF258C616B25f7C", DODOV1PmmHelper: "0x2BBD66fC4898242BDBD2583BBe1d76E8b8f71445", - DODOV2RouteHelper: "0x1dc8D1f1600B7C1D39e6b60FBC7b021Bc4F9C993", + DODOV2RouteHelper: "0xd72b354BD39f8F11D0cA07bD5724896Bb1a42707", //Template CloneFactory: "0x03E2427859119E497EB856a166F616a2Ce5f8c88", FeeRateModel: "0x18DFdE99F578A0735410797e949E8D3e2AFCB9D2", PermissionManager: "0x729f7f44bf64Ce814716b6261e267DbE6cdf021c", - DVM: "0xC3BeD579CaB3EC29B22D9AB99F4E586af42496b9", + DVM: "0x02607600407329389C2912F46DD357d7fa33d901", DPP: "0x85351262f7474Ebe23FfAcD633cf20A491F1325D", DPPAdmin: "0x44D5dF24d5Ef52A791D6436Fa45A8D426f6de34e", CP: "0x041ABa00c57Dd47abC37A2931dF569a2A2cc57Be", @@ -23,7 +23,7 @@ module.exports = { MintableERC20: "0x6373ceb657c83c91088d328622573fb766064ac4", //Factory - DVMFactory: "0x790B4A80Fb1094589A3c0eFC8740aA9b0C1733fB", + DVMFactory: "0xa1254eE5c6d6616904A82c55C6e134557096B6D4", DPPFactory: "0xAfe0A75DFFb395eaaBd0a7E1BBbd0b11f8609eeF", CrowdPoolingFactory: "0x778DF5B12170e8af8dF94356BfC864E57CE185DC", ERC20Factory: "0x5e84190a270333aCe5B9202a3F4ceBf11b81bB01", @@ -42,7 +42,7 @@ module.exports = { UniAdapter: "0x5e530cD60931d9DE694a86827e76BB24493A1520", //Proxy - DODOV2Proxy: "0x8F8Dd7DB1bDA5eD3da8C9daf3bfa471c12d58486", + DODOV2Proxy: "0x3a343F2e4e142412c5dD130359edb765a6054965", //vDODO DODOCirculationHelper: "", diff --git a/config/eth-config.js b/config/eth-config.js index 5544711..aa7b89c 100644 --- a/config/eth-config.js +++ b/config/eth-config.js @@ -9,13 +9,13 @@ module.exports = { DODOSellHelper: "0x533da777aedce766ceae696bf90f8541a4ba80eb", DODOCalleeHelper: "0xef49a6DBa1C8DF859E49c17E9A485B439c7689d3", DODOV1PmmHelper: "0x6373ceB657C83C91088d328622573FB766064Ac4", - DODOV2RouteHelper: "0xbe9a66e49503e84ae59a4d0545365AABedf33b40", + DODOV2RouteHelper: "0xeAB910bea37DD837dDCED91C8E99dBcC4DBcCc01", //Template CloneFactory: "0x5e5a7b76462e4bdf83aa98795644281bdba80b88", FeeRateModel: "0x5e84190a270333aCe5B9202a3F4ceBf11b81bB01", PermissionManager: "0x6B208E08dcF6BD51F50C5Da09d15B2D8E5C46Cf2", - DVM: "0x2BBD66fC4898242BDBD2583BBe1d76E8b8f71445", + DVM: "0x8a538751A501A9785F93727d4cB7b7827FAb1ad0", DPP: "0xB76de21f04F677f07D9881174a1D8E624276314C", DPPAdmin: "0x5515363c0412AdD5c72d3E302fE1bD7dCBCF93Fe", CP: "0x18b0bD918b55f995Fd404B872404378A62cb403b", @@ -23,7 +23,7 @@ module.exports = { MintableERC20: "0x0596908263ef2724fbfbcafa1c983fcd7a629038", //Factory - DVMFactory: "0x72d220cE168C4f361dD4deE5D826a01AD8598f6C", + DVMFactory: "0xc9eD9B18e447e600238fe50e944B9062B664DEa4", DPPFactory: "0x6B4Fa0bc61Eddc928e0Df9c7f01e407BfcD3e5EF", CrowdPoolingFactory: "0xE8C9A78725D0451FA19878D5f8A3dC0D55FECF25", ERC20Factory: "0x44D5dF24d5Ef52A791D6436Fa45A8D426f6de34e", @@ -41,7 +41,7 @@ module.exports = { UniAdapter: "0x043957f7554275b90c5178872faE851dcfC1089D", //Proxy - DODOV2Proxy: "0xa356867fDCEa8e71AEaF87805808803806231FdC", + DODOV2Proxy: "", //vDODO DODOCirculationHelper: "0x357c5e9cfa8b834edcef7c7aabd8f9db09119d11", diff --git a/contracts/DODOToken/DODOIncentiveBsc.sol b/contracts/DODOToken/DODOIncentiveBsc.sol new file mode 100644 index 0000000..9dd21a0 --- /dev/null +++ b/contracts/DODOToken/DODOIncentiveBsc.sol @@ -0,0 +1,99 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; +import {ILockedTokenVault02} from "../intf/ILockedTokenVault02.sol"; +import {SafeERC20} from "../lib/SafeERC20.sol"; +import {SafeMath} from "../lib/SafeMath.sol"; +import {IERC20} from "../intf/IERC20.sol"; + +/** + * @title DODOIncentiveBsc + * @author DODO Breeder + * + * @notice Trade Incentive in DODO platform + */ +contract DODOIncentiveBsc is InitializableOwnable { + using SafeMath for uint256; + using SafeERC20 for IERC20; + + // ============ Storage ============ + address public immutable _DODO_TOKEN_; + address public _DODO_PROXY_; + address public _LOCKED_VAULT_; + + mapping(address => bool) public stableList; + mapping(address => bool) public tokenList; //Not include stable tokens + uint256 public baseAmount = 1000; + uint256 public baseReward = 10**18; + + // ============ Events ============ + + event SetStableList(address token, bool isUse); + event SetTokenList(address token, bool isUse); + event SetBaseAmount(uint256 baseAmount); + event SetBaseReward(uint256 baseReward); + + event Incentive(address user, uint256 reward); + + constructor(address _dodoToken) public { + _DODO_TOKEN_ = _dodoToken; + } + + // ============ Ownable ============ + + function setContract(address dodoProxy,address lockedVault) external onlyOwner { + _DODO_PROXY_ = dodoProxy; + _LOCKED_VAULT_ = lockedVault; + } + + function setStableList(address token, bool isUse) external onlyOwner { + require(token != address(0)); + stableList[token] = isUse; + emit SetStableList(token, isUse); + } + + function setTokenList(address token, bool isUse) external onlyOwner { + require(token != address(0)); + tokenList[token] = isUse; + emit SetTokenList(token, isUse); + } + + function changeBaseAmount(uint256 newBaseAmount) external onlyOwner { + baseAmount = newBaseAmount; + emit SetBaseAmount(newBaseAmount); + } + + function changeBaseReward(uint256 newBaseReward) external onlyOwner { + baseReward = newBaseReward; + emit SetBaseAmount(newBaseReward); + } + + // ============ Incentive function ============ + + function triggerIncentive( + address fromToken, + address toToken, + uint256 fromAmount, + uint256 returnAmount, + address assetTo + ) external { + require(msg.sender == _DODO_PROXY_, "DODOIncentiveBsc:Access restricted"); + uint256 reward = 0; + if(stableList[fromToken] && tokenList[toToken]) { + reward = fromAmount.div(baseAmount); + } else if (stableList[toToken] && tokenList[fromToken]) { + reward = returnAmount.div(baseAmount); + } + if (reward != 0) { + ILockedTokenVault02(_LOCKED_VAULT_).tradeIncentive(assetTo,reward); + emit Incentive(assetTo, reward); + } + } +} diff --git a/contracts/DODOToken/LockedTokenVault02.sol b/contracts/DODOToken/LockedTokenVault02.sol new file mode 100644 index 0000000..c70d064 --- /dev/null +++ b/contracts/DODOToken/LockedTokenVault02.sol @@ -0,0 +1,196 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; +import {SafeMath} from "../lib/SafeMath.sol"; +import {DecimalMath} from "../lib/DecimalMath.sol"; +import {SafeERC20} from "../lib/SafeERC20.sol"; +import {IERC20} from "../intf/IERC20.sol"; + +/** + * @title LockedTokenVault02 + * @author DODO Breeder + * + * @notice Lock Token and release it linearly + */ + +contract LockedTokenVault02 is InitializableOwnable { + using SafeMath for uint256; + using SafeERC20 for IERC20; + + address _TOKEN_; + address public _DODO_INCENTIVE_; + + mapping(address => uint256) internal originBalances; + mapping(address => uint256) internal claimedBalances; + + // uint256 public _UNDISTRIBUTED_AMOUNT_; + uint256 public _START_RELEASE_TIME_; + uint256 public _RELEASE_DURATION_; + uint256 public _CLIFF_RATE_; + + bool public _DISTRIBUTE_FINISHED_; + + // ============ Events ============ + + event Claim(address indexed holder, uint256 origin, uint256 claimed, uint256 amount); + + // ============ Modifiers ============ + + modifier beforeStartRelease() { + require(block.timestamp < _START_RELEASE_TIME_, "RELEASE START"); + _; + } + + modifier afterStartRelease() { + require(block.timestamp >= _START_RELEASE_TIME_, "RELEASE NOT START"); + _; + } + + modifier distributeNotFinished() { + require(!_DISTRIBUTE_FINISHED_, "DISTRIBUTE FINISHED"); + _; + } + + // ============ Init Functions ============ + + constructor( + address _token, + uint256 _startReleaseTime, + uint256 _releaseDuration, + uint256 _cliffRate + ) public { + _TOKEN_ = _token; + _START_RELEASE_TIME_ = _startReleaseTime; + _RELEASE_DURATION_ = _releaseDuration; + _CLIFF_RATE_ = _cliffRate; + } + + function updateParams(uint256 startReleaseTime, uint256 releaseDuration, uint256 cliffRate) external onlyOwner { + _START_RELEASE_TIME_ = startReleaseTime; + _RELEASE_DURATION_ = releaseDuration; + _CLIFF_RATE_ = cliffRate; + } + + function deposit(uint256 amount) external onlyOwner { + _tokenTransferIn(_OWNER_, amount); + // _UNDISTRIBUTED_AMOUNT_ = _UNDISTRIBUTED_AMOUNT_.add(amount); + } + + function withdraw(uint256 amount) external onlyOwner { + // _UNDISTRIBUTED_AMOUNT_ = _UNDISTRIBUTED_AMOUNT_.sub(amount); + _tokenTransferOut(_OWNER_, amount); + } + + function finishDistribute() external onlyOwner { + _DISTRIBUTE_FINISHED_ = true; + } + + // ============ For DODOIncentiveBsc =========== + + function tradeIncentive(address trader, uint256 amount) external { + require(_DODO_INCENTIVE_ == msg.sender, "LockedTokenVault02:Access restricted"); + originBalances[trader] = originBalances[trader].add(amount); + // _UNDISTRIBUTED_AMOUNT_ = _UNDISTRIBUTED_AMOUNT_.sub(amount); + } + + // ================== For Owner ================ + + function grant(address[] calldata holderList, uint256[] calldata amountList) + external + onlyOwner + { + require(holderList.length == amountList.length, "batch grant length not match"); + uint256 amount = 0; + for (uint256 i = 0; i < holderList.length; ++i) { + // for saving gas, no event for grant + originBalances[holderList[i]] = originBalances[holderList[i]].add(amountList[i]); + amount = amount.add(amountList[i]); + } + // _UNDISTRIBUTED_AMOUNT_ = _UNDISTRIBUTED_AMOUNT_.sub(amount); + } + + function recall(address holder) external onlyOwner distributeNotFinished { + // _UNDISTRIBUTED_AMOUNT_ = _UNDISTRIBUTED_AMOUNT_.add(originBalances[holder]).sub( + // claimedBalances[holder] + // ); + originBalances[holder] = 0; + claimedBalances[holder] = 0; + } + + // ============ For Holder ============ + + function transferLockedToken(address to) external { + originBalances[to] = originBalances[to].add(originBalances[msg.sender]); + claimedBalances[to] = claimedBalances[to].add(claimedBalances[msg.sender]); + + originBalances[msg.sender] = 0; + claimedBalances[msg.sender] = 0; + } + + function claim() external { + uint256 claimableToken = getClaimableBalance(msg.sender); + _tokenTransferOut(msg.sender, claimableToken); + claimedBalances[msg.sender] = claimedBalances[msg.sender].add(claimableToken); + emit Claim( + msg.sender, + originBalances[msg.sender], + claimedBalances[msg.sender], + claimableToken + ); + } + + // ============ View ============ + + function isReleaseStart() external view returns (bool) { + return block.timestamp >= _START_RELEASE_TIME_; + } + + function getOriginBalance(address holder) external view returns (uint256) { + return originBalances[holder]; + } + + function getClaimedBalance(address holder) external view returns (uint256) { + return claimedBalances[holder]; + } + + function getClaimableBalance(address holder) public view returns (uint256) { + uint256 remainingToken = getRemainingBalance(holder); + return originBalances[holder].sub(remainingToken).sub(claimedBalances[holder]); + } + + function getRemainingBalance(address holder) public view returns (uint256) { + uint256 remainingRatio = getRemainingRatio(block.timestamp); + return DecimalMath.mulFloor(originBalances[holder], remainingRatio); + } + + function getRemainingRatio(uint256 timestamp) public view returns (uint256) { + if (timestamp < _START_RELEASE_TIME_) { + return DecimalMath.ONE; + } + uint256 timePast = timestamp.sub(_START_RELEASE_TIME_); + if (timePast < _RELEASE_DURATION_) { + uint256 remainingTime = _RELEASE_DURATION_.sub(timePast); + return DecimalMath.ONE.sub(_CLIFF_RATE_).mul(remainingTime).div(_RELEASE_DURATION_); + } else { + return 0; + } + } + + // ============ Internal Helper ============ + + function _tokenTransferIn(address from, uint256 amount) internal { + IERC20(_TOKEN_).safeTransferFrom(from, address(this), amount); + } + + function _tokenTransferOut(address to, uint256 amount) internal { + IERC20(_TOKEN_).safeTransfer(to, amount); + } +} diff --git a/contracts/DODOVendingMachine/impl/DVM.sol b/contracts/DODOVendingMachine/impl/DVM.sol index 197c4c1..0673063 100644 --- a/contracts/DODOVendingMachine/impl/DVM.sol +++ b/contracts/DODOVendingMachine/impl/DVM.sol @@ -91,6 +91,6 @@ contract DVM is DVMTrader, DVMFunding { // ============ Version Control ============ function version() external pure returns (string memory) { - return "DVM 1.0.0"; + return "DVM 1.0.1"; } } diff --git a/contracts/Factory/UpCrowdPoolingFactory.sol b/contracts/Factory/UpCrowdPoolingFactory.sol index 2e238f4..79e4f15 100644 --- a/contracts/Factory/UpCrowdPoolingFactory.sol +++ b/contracts/Factory/UpCrowdPoolingFactory.sol @@ -33,11 +33,9 @@ contract UpCrowdPoolingFactory is InitializableOwnable { address public _CP_TEMPLATE_; // ============ Settings ============= - uint256 public _FREEZE_DURATION_ = 30 days; uint256 public _CALM_DURATION_ = 0; uint256 public _VEST_DURATION_ = 0; - uint256 public _K_ = 0; uint256 public _CLIFF_RATE_ = 10**18; @@ -56,11 +54,10 @@ contract UpCrowdPoolingFactory is InitializableOwnable { uint256[] memory timeLine, uint256[] memory valueList) { - require(timeLine[2] == _CALM_DURATION_, "CP_FACTORY : PHASE_CALM_DURATION_INVALID"); + require(timeLine[2] <= _CALM_DURATION_, "CP_FACTORY : PHASE_CALM_DURATION_INVALID"); require(timeLine[4] == _VEST_DURATION_, "CP_FACTORY : VEST_DURATION_INVALID"); - require(valueList[1] == _K_, "CP_FACTORY : K_INVALID"); require(valueList[3] == _CLIFF_RATE_, "CP_FACTORY : CLIFF_RATE_INVALID"); - require(timeLine[3]>= _FREEZE_DURATION_, "CP_FACTORY : FREEZE_DURATION_INVALID"); + require(timeLine[3] >= _FREEZE_DURATION_, "CP_FACTORY : FREEZE_DURATION_INVALID"); _; } @@ -174,11 +171,6 @@ contract UpCrowdPoolingFactory is InitializableOwnable { _VEST_DURATION_ = _newVestDuration; } - function setK(uint256 _newK) public onlyOwner { - require(_newK <= 10**18, "CP_FACTORY : INVALID"); - _K_ = _newK; - } - function setCliffRate(uint256 _newCliffRate) public onlyOwner { require(_newCliffRate <= 10**18, "CP_FACTORY : INVALID"); _CLIFF_RATE_ = _newCliffRate; diff --git a/contracts/SmartRoute/DODOV2Proxy03Bsc.sol b/contracts/SmartRoute/DODOV2Proxy03Bsc.sol new file mode 100644 index 0000000..97ee66a --- /dev/null +++ b/contracts/SmartRoute/DODOV2Proxy03Bsc.sol @@ -0,0 +1,811 @@ +/* + + 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 {IDODOV1} from "./intf/IDODOV1.sol"; +import {IDODOApproveProxy} from "./DODOApproveProxy.sol"; +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 {IDODOIncentiveBsc} from "../intf/IDODOIncentiveBsc.sol"; +import {IDODOAdapter} from "./intf/IDODOAdapter.sol"; + +/** + * @title DODOV2Proxy03Bsc + * @author DODO Breeder + * + * @notice Entrance of trading in DODO platform + */ +contract DODOV2Proxy03Bsc is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable { + using SafeMath for uint256; + using UniversalERC20 for IERC20; + + // ============ Storage ============ + + address constant _ETH_ADDRESS_ = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address public immutable _WETH_; + address public immutable _DODO_APPROVE_PROXY_; + address public immutable _DODO_SELL_HELPER_; + address public immutable _DVM_FACTORY_; + address public immutable _DPP_FACTORY_; + address public immutable _CP_FACTORY_; + address public immutable _CHI_TOKEN_; + uint256 public _GAS_DODO_MAX_RETURN_ = 0; + uint256 public _GAS_EXTERNAL_RETURN_ = 0; + + address public _DODO_INCENTIVE_; + mapping (address => bool) public isWhiteListed; + + // ============ Events ============ + + event OrderHistory( + address fromToken, + address toToken, + address sender, + uint256 fromAmount, + uint256 returnAmount + ); + + // ============ Modifiers ============ + + modifier judgeExpired(uint256 deadLine) { + require(deadLine >= block.timestamp, "DODOV2Proxy03Bsc: EXPIRED"); + _; + } + + fallback() external payable {} + + receive() external payable {} + + constructor( + address dvmFactory, + address dppFactory, + address cpFactory, + address payable weth, + address dodoApproveProxy, + address dodoSellHelper, + address chiToken, + address dodoIncentive + ) public { + _DVM_FACTORY_ = dvmFactory; + _DPP_FACTORY_ = dppFactory; + _CP_FACTORY_ = cpFactory; + _WETH_ = weth; + _DODO_APPROVE_PROXY_ = dodoApproveProxy; + _DODO_SELL_HELPER_ = dodoSellHelper; + _CHI_TOKEN_ = chiToken; + _DODO_INCENTIVE_ = dodoIncentive; + } + + function addWhiteList (address contractAddr) public onlyOwner { + isWhiteListed[contractAddr] = true; + } + + function removeWhiteList (address contractAddr) public onlyOwner { + isWhiteListed[contractAddr] = false; + } + + function updateDodoIncentive (address newDodoIncentive) public onlyOwner { + _DODO_INCENTIVE_ = newDodoIncentive; + } + + function updateGasReturn(uint256 newDodoGasReturn, uint256 newExternalGasReturn) public onlyOwner { + _GAS_DODO_MAX_RETURN_ = newDodoGasReturn; + _GAS_EXTERNAL_RETURN_ = newExternalGasReturn; + } + + // ============ DVM Functions (create & add liquidity) ============ + + function createDODOVendingMachine( + address baseToken, + address quoteToken, + uint256 baseInAmount, + uint256 quoteInAmount, + uint256 lpFeeRate, + uint256 i, + uint256 k, + bool isOpenTWAP, + uint256 deadLine + ) + external + override + payable + preventReentrant + judgeExpired(deadLine) + returns (address newVendingMachine, uint256 shares) + { + { + address _baseToken = baseToken == _ETH_ADDRESS_ ? _WETH_ : baseToken; + address _quoteToken = quoteToken == _ETH_ADDRESS_ ? _WETH_ : quoteToken; + newVendingMachine = IDODOV2(_DVM_FACTORY_).createDODOVendingMachine( + _baseToken, + _quoteToken, + lpFeeRate, + i, + k, + isOpenTWAP + ); + } + + { + address _baseToken = baseToken; + address _quoteToken = quoteToken; + _deposit( + msg.sender, + newVendingMachine, + _baseToken, + baseInAmount, + _baseToken == _ETH_ADDRESS_ + ); + _deposit( + msg.sender, + newVendingMachine, + _quoteToken, + quoteInAmount, + _quoteToken == _ETH_ADDRESS_ + ); + } + + (shares, , ) = IDODOV2(newVendingMachine).buyShares(msg.sender); + } + + function addDVMLiquidity( + address dvmAddress, + uint256 baseInAmount, + uint256 quoteInAmount, + uint256 baseMinAmount, + uint256 quoteMinAmount, + uint8 flag, // 0 - ERC20, 1 - baseInETH, 2 - quoteInETH + uint256 deadLine + ) + external + override + payable + preventReentrant + judgeExpired(deadLine) + returns ( + uint256 shares, + uint256 baseAdjustedInAmount, + uint256 quoteAdjustedInAmount + ) + { + address _dvm = dvmAddress; + (baseAdjustedInAmount, quoteAdjustedInAmount) = _addDVMLiquidity( + _dvm, + baseInAmount, + quoteInAmount + ); + require( + baseAdjustedInAmount >= baseMinAmount && quoteAdjustedInAmount >= quoteMinAmount, + "DODOV2Proxy03Bsc: deposit amount is not enough" + ); + + _deposit(msg.sender, _dvm, IDODOV2(_dvm)._BASE_TOKEN_(), baseAdjustedInAmount, flag == 1); + _deposit(msg.sender, _dvm, IDODOV2(_dvm)._QUOTE_TOKEN_(), quoteAdjustedInAmount, flag == 2); + + (shares, , ) = IDODOV2(_dvm).buyShares(msg.sender); + // refund dust eth + if (flag == 1 && msg.value > baseAdjustedInAmount) msg.sender.transfer(msg.value - baseAdjustedInAmount); + if (flag == 2 && msg.value > quoteAdjustedInAmount) msg.sender.transfer(msg.value - quoteAdjustedInAmount); + } + + function _addDVMLiquidity( + address dvmAddress, + uint256 baseInAmount, + uint256 quoteInAmount + ) internal view returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount) { + (uint256 baseReserve, uint256 quoteReserve) = IDODOV2(dvmAddress).getVaultReserve(); + if (quoteReserve == 0 && baseReserve == 0) { + baseAdjustedInAmount = baseInAmount; + quoteAdjustedInAmount = quoteInAmount; + } + 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); + } + } + } + + // ============ DPP Functions (create & reset) ============ + + function createDODOPrivatePool( + address baseToken, + address quoteToken, + uint256 baseInAmount, + uint256 quoteInAmount, + uint256 lpFeeRate, + uint256 i, + uint256 k, + bool isOpenTwap, + uint256 deadLine + ) + external + override + payable + preventReentrant + judgeExpired(deadLine) + returns (address newPrivatePool) + { + newPrivatePool = IDODOV2(_DPP_FACTORY_).createDODOPrivatePool(); + + address _baseToken = baseToken; + address _quoteToken = quoteToken; + _deposit(msg.sender, newPrivatePool, _baseToken, baseInAmount, _baseToken == _ETH_ADDRESS_); + _deposit( + msg.sender, + newPrivatePool, + _quoteToken, + quoteInAmount, + _quoteToken == _ETH_ADDRESS_ + ); + + if (_baseToken == _ETH_ADDRESS_) _baseToken = _WETH_; + if (_quoteToken == _ETH_ADDRESS_) _quoteToken = _WETH_; + + IDODOV2(_DPP_FACTORY_).initDODOPrivatePool( + newPrivatePool, + msg.sender, + _baseToken, + _quoteToken, + lpFeeRate, + k, + i, + isOpenTwap + ); + } + + function resetDODOPrivatePool( + address dppAddress, + uint256[] memory paramList, //0 - newLpFeeRate, 1 - newI, 2 - newK + uint256[] memory amountList, //0 - baseInAmount, 1 - quoteInAmount, 2 - baseOutAmount, 3- quoteOutAmount + uint8 flag, // 0 - ERC20, 1 - baseInETH, 2 - quoteInETH, 3 - baseOutETH, 4 - quoteOutETH + uint256 minBaseReserve, + uint256 minQuoteReserve, + uint256 deadLine + ) external override payable preventReentrant judgeExpired(deadLine) { + _deposit( + msg.sender, + dppAddress, + IDODOV2(dppAddress)._BASE_TOKEN_(), + amountList[0], + flag == 1 + ); + _deposit( + msg.sender, + dppAddress, + IDODOV2(dppAddress)._QUOTE_TOKEN_(), + amountList[1], + flag == 2 + ); + + require(IDODOV2(IDODOV2(dppAddress)._OWNER_()).reset( + msg.sender, + paramList[0], + paramList[1], + paramList[2], + amountList[2], + amountList[3], + minBaseReserve, + minQuoteReserve + ), "Reset Failed"); + + _withdraw(msg.sender, IDODOV2(dppAddress)._BASE_TOKEN_(), amountList[2], flag == 3); + _withdraw(msg.sender, IDODOV2(dppAddress)._QUOTE_TOKEN_(), amountList[3], flag == 4); + } + + // ============ Swap ============ + + function dodoSwapV2ETHToToken( + address toToken, + uint256 minReturnAmount, + address[] memory dodoPairs, + uint256 directions, + bool isIncentive, + uint256 deadLine + ) + external + override + payable + judgeExpired(deadLine) + returns (uint256 returnAmount) + { + require(dodoPairs.length > 0, "DODOV2Proxy03Bsc: PAIRS_EMPTY"); + require(minReturnAmount > 0, "DODOV2Proxy03Bsc: RETURN_AMOUNT_ZERO"); + uint256 originGas = gasleft(); + + uint256 originToTokenBalance = IERC20(toToken).balanceOf(msg.sender); + IWETH(_WETH_).deposit{value: msg.value}(); + IWETH(_WETH_).transfer(dodoPairs[0], msg.value); + + for (uint256 i = 0; i < dodoPairs.length; i++) { + if (i == dodoPairs.length - 1) { + if (directions & 1 == 0) { + IDODOV2(dodoPairs[i]).sellBase(msg.sender); + } else { + IDODOV2(dodoPairs[i]).sellQuote(msg.sender); + } + } else { + if (directions & 1 == 0) { + IDODOV2(dodoPairs[i]).sellBase(dodoPairs[i + 1]); + } else { + IDODOV2(dodoPairs[i]).sellQuote(dodoPairs[i + 1]); + } + } + directions = directions >> 1; + } + + returnAmount = IERC20(toToken).balanceOf(msg.sender).sub(originToTokenBalance); + require(returnAmount >= minReturnAmount, "DODOV2Proxy03Bsc: Return amount is not enough"); + + _dodoGasReturn(originGas); + + _execIncentive(isIncentive, _ETH_ADDRESS_, toToken, msg.value, returnAmount); + + emit OrderHistory( + _ETH_ADDRESS_, + toToken, + msg.sender, + msg.value, + returnAmount + ); + } + + function dodoSwapV2TokenToETH( + address fromToken, + uint256 fromTokenAmount, + uint256 minReturnAmount, + address[] memory dodoPairs, + uint256 directions, + bool isIncentive, + uint256 deadLine + ) + external + override + judgeExpired(deadLine) + returns (uint256 returnAmount) + { + require(dodoPairs.length > 0, "DODOV2Proxy03Bsc: PAIRS_EMPTY"); + require(minReturnAmount > 0, "DODOV2Proxy03Bsc: RETURN_AMOUNT_ZERO"); + uint256 originGas = gasleft(); + + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(fromToken, msg.sender, dodoPairs[0], fromTokenAmount); + + for (uint256 i = 0; i < dodoPairs.length; i++) { + if (i == dodoPairs.length - 1) { + if (directions & 1 == 0) { + IDODOV2(dodoPairs[i]).sellBase(address(this)); + } else { + IDODOV2(dodoPairs[i]).sellQuote(address(this)); + } + } else { + if (directions & 1 == 0) { + IDODOV2(dodoPairs[i]).sellBase(dodoPairs[i + 1]); + } else { + IDODOV2(dodoPairs[i]).sellQuote(dodoPairs[i + 1]); + } + } + directions = directions >> 1; + } + returnAmount = IWETH(_WETH_).balanceOf(address(this)); + require(returnAmount >= minReturnAmount, "DODOV2Proxy03Bsc: Return amount is not enough"); + IWETH(_WETH_).withdraw(returnAmount); + msg.sender.transfer(returnAmount); + + _dodoGasReturn(originGas); + + _execIncentive(isIncentive, fromToken, _ETH_ADDRESS_, fromTokenAmount, returnAmount); + + emit OrderHistory( + fromToken, + _ETH_ADDRESS_, + msg.sender, + fromTokenAmount, + returnAmount + ); + } + + function dodoSwapV2TokenToToken( + address fromToken, + address toToken, + uint256 fromTokenAmount, + uint256 minReturnAmount, + address[] memory dodoPairs, + uint256 directions, + bool isIncentive, + uint256 deadLine + ) + external + override + judgeExpired(deadLine) + returns (uint256 returnAmount) + { + require(dodoPairs.length > 0, "DODOV2Proxy03Bsc: PAIRS_EMPTY"); + require(minReturnAmount > 0, "DODOV2Proxy03Bsc: RETURN_AMOUNT_ZERO"); + uint256 originGas = gasleft(); + + uint256 originToTokenBalance = IERC20(toToken).balanceOf(msg.sender); + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(fromToken, msg.sender, dodoPairs[0], fromTokenAmount); + + for (uint256 i = 0; i < dodoPairs.length; i++) { + if (i == dodoPairs.length - 1) { + if (directions & 1 == 0) { + IDODOV2(dodoPairs[i]).sellBase(msg.sender); + } else { + IDODOV2(dodoPairs[i]).sellQuote(msg.sender); + } + } else { + if (directions& 1 == 0) { + IDODOV2(dodoPairs[i]).sellBase(dodoPairs[i + 1]); + } else { + IDODOV2(dodoPairs[i]).sellQuote(dodoPairs[i + 1]); + } + } + directions = directions >> 1; + } + returnAmount = IERC20(toToken).balanceOf(msg.sender).sub(originToTokenBalance); + require(returnAmount >= minReturnAmount, "DODOV2Proxy03Bsc: Return amount is not enough"); + + _dodoGasReturn(originGas); + + _execIncentive(isIncentive, fromToken, toToken, fromTokenAmount, returnAmount); + + emit OrderHistory( + fromToken, + toToken, + msg.sender, + fromTokenAmount, + returnAmount + ); + } + + function externalSwap( + address fromToken, + address toToken, + address approveTarget, + address swapTarget, + uint256 fromTokenAmount, + uint256 minReturnAmount, + bytes memory callDataConcat, + bool isIncentive, + uint256 deadLine + ) + external + override + payable + judgeExpired(deadLine) + returns (uint256 returnAmount) + { + require(minReturnAmount > 0, "DODOV2Proxy03Bsc: RETURN_AMOUNT_ZERO"); + require(fromToken != _CHI_TOKEN_, "DODOV2Proxy03Bsc: NOT_SUPPORT_SELL_CHI"); + require(toToken != _CHI_TOKEN_, "DODOV2Proxy03Bsc: NOT_SUPPORT_BUY_CHI"); + + uint256 toTokenOriginBalance = IERC20(toToken).universalBalanceOf(msg.sender); + if (fromToken != _ETH_ADDRESS_) { + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens( + fromToken, + msg.sender, + address(this), + fromTokenAmount + ); + IERC20(fromToken).universalApproveMax(approveTarget, fromTokenAmount); + } + + require(isWhiteListed[swapTarget], "DODOV2Proxy03Bsc: Not Whitelist Contract"); + (bool success, ) = swapTarget.call{value: fromToken == _ETH_ADDRESS_ ? msg.value : 0}(callDataConcat); + + require(success, "DODOV2Proxy03Bsc: 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, "DODOV2Proxy03Bsc: Return amount is not enough"); + + _externalGasReturn(); + + _execIncentive(isIncentive, fromToken, toToken, fromTokenAmount, returnAmount); + + emit OrderHistory( + fromToken, + toToken, + msg.sender, + fromTokenAmount, + returnAmount + ); + } + + function dodoSwapV1( + address fromToken, + address toToken, + uint256 fromTokenAmount, + uint256 minReturnAmount, + address[] memory dodoPairs, + uint256 directions, + bool isIncentive, + uint256 deadLine + ) + external + override + payable + judgeExpired(deadLine) + returns (uint256 returnAmount) + { + require(dodoPairs.length > 0, "DODOV2Proxy03Bsc: PAIRS_EMPTY"); + require(minReturnAmount > 0, "DODOV2Proxy03Bsc: RETURN_AMOUNT_ZERO"); + require(fromToken != _CHI_TOKEN_, "DODOV2Proxy03Bsc: NOT_SUPPORT_SELL_CHI"); + require(toToken != _CHI_TOKEN_, "DODOV2Proxy03Bsc: NOT_SUPPORT_BUY_CHI"); + + 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 & 1 == 0) { + address curDodoBase = IDODOV1(curDodoPair)._BASE_TOKEN_(); + require(curDodoBase != _CHI_TOKEN_, "DODOV2Proxy03Bsc: 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_, "DODOV2Proxy03Bsc: 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, "DODOV2Proxy03Bsc: Return amount is not enough"); + IERC20(_toToken).universalTransfer(msg.sender, returnAmount); + + _dodoGasReturn(originGas); + + _execIncentive(isIncentive, _fromToken, _toToken, fromTokenAmount, returnAmount); + + emit OrderHistory(_fromToken, _toToken, msg.sender, fromTokenAmount, returnAmount); + } + + + function mixSwap( + address fromToken, + address toToken, + uint256 fromTokenAmount, + uint256 minReturnAmount, + address[] memory mixAdapters, + address[] memory mixPairs, + address[] memory assetTo, + uint256 directions, + bool isIncentive, + uint256 deadLine + ) external override payable judgeExpired(deadLine) returns (uint256 returnAmount) { + require(mixPairs.length > 0, "DODOV2Proxy03Bsc: PAIRS_EMPTY"); + require(mixPairs.length == mixAdapters.length, "DODOV2Proxy03Bsc: PAIR_ADAPTER_NOT_MATCH"); + require(mixPairs.length == assetTo.length - 1, "DODOV2Proxy03Bsc: PAIR_ASSETTO_NOT_MATCH"); + require(minReturnAmount > 0, "DODOV2Proxy03Bsc: RETURN_AMOUNT_ZERO"); + + address _fromToken = fromToken; + address _toToken = toToken; + uint256 _fromTokenAmount = fromTokenAmount; + + require(_fromToken != _CHI_TOKEN_, "DODOV2Proxy03Bsc: NOT_SUPPORT_SELL_CHI"); + require(_toToken != _CHI_TOKEN_, "DODOV2Proxy03Bsc: NOT_SUPPORT_BUY_CHI"); + + uint256 originGas = gasleft(); + uint256 toTokenOriginBalance = IERC20(_toToken).universalBalanceOf(msg.sender); + + _deposit(msg.sender, assetTo[0], _fromToken, _fromTokenAmount, _fromToken == _ETH_ADDRESS_); + + for (uint256 i = 0; i < mixPairs.length; i++) { + if (directions & 1 == 0) { + IDODOAdapter(mixAdapters[i]).sellBase(assetTo[i + 1],mixPairs[i]); + } else { + IDODOAdapter(mixAdapters[i]).sellQuote(assetTo[i + 1],mixPairs[i]); + } + directions = directions >> 1; + } + + 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, "DODOV2Proxy03Bsc: Return amount is not enough"); + + _dodoGasReturn(originGas); + + _execIncentive(isIncentive, _fromToken, _toToken, _fromTokenAmount, returnAmount); + + emit OrderHistory( + _fromToken, + _toToken, + msg.sender, + _fromTokenAmount, + returnAmount + ); + } + + //============ CrowdPooling Functions (create & bid) ============ + + function createCrowdPooling( + address baseToken, + address quoteToken, + uint256 baseInAmount, + uint256[] memory timeLine, + uint256[] memory valueList, + bool isOpenTWAP, + uint256 deadLine + ) external override payable preventReentrant judgeExpired(deadLine) returns (address payable newCrowdPooling) { + address _baseToken = baseToken; + address _quoteToken = quoteToken == _ETH_ADDRESS_ ? _WETH_ : quoteToken; + + newCrowdPooling = IDODOV2(_CP_FACTORY_).createCrowdPooling(); + + _deposit( + msg.sender, + newCrowdPooling, + _baseToken, + baseInAmount, + false + ); + + newCrowdPooling.transfer(msg.value); + + IDODOV2(_CP_FACTORY_).initCrowdPooling( + newCrowdPooling, + msg.sender, + _baseToken, + _quoteToken, + timeLine, + valueList, + isOpenTWAP + ); + } + + function bid( + address cpAddress, + uint256 quoteAmount, + uint8 flag, // 0 - ERC20, 1 - quoteInETH + uint256 deadLine + ) external override payable preventReentrant judgeExpired(deadLine) { + _deposit(msg.sender, cpAddress, IDODOV2(cpAddress)._QUOTE_TOKEN_(), quoteAmount, flag == 1); + IDODOV2(cpAddress).bid(msg.sender); + } + + + function addLiquidityToV1( + address pair, + uint256 baseAmount, + uint256 quoteAmount, + uint256 baseMinShares, + uint256 quoteMinShares, + uint8 flag, // 0 erc20 In 1 baseInETH 2 quoteIn ETH + uint256 deadLine + ) external override payable preventReentrant judgeExpired(deadLine) returns(uint256 baseShares, uint256 quoteShares) { + address _baseToken = IDODOV1(pair)._BASE_TOKEN_(); + address _quoteToken = IDODOV1(pair)._QUOTE_TOKEN_(); + + _deposit(msg.sender, address(this), _baseToken, baseAmount, flag == 1); + _deposit(msg.sender, address(this), _quoteToken, quoteAmount, flag == 2); + + + if(baseAmount > 0) { + IERC20(_baseToken).universalApproveMax(pair, baseAmount); + baseShares = IDODOV1(pair).depositBaseTo(msg.sender, baseAmount); + } + if(quoteAmount > 0) { + IERC20(_quoteToken).universalApproveMax(pair, quoteAmount); + quoteShares = IDODOV1(pair).depositQuoteTo(msg.sender, quoteAmount); + } + + require(baseShares >= baseMinShares && quoteShares >= quoteMinShares,"DODOV2Proxy03Bsc: Return DLP is not enough"); + } + + + 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); + } + } + + function _withdraw( + address payable to, + address token, + uint256 amount, + bool isETH + ) internal { + if (isETH) { + if (amount > 0) { + IWETH(_WETH_).withdraw(amount); + to.transfer(amount); + } + } else { + if (amount > 0) { + 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); + } + } + + function _execIncentive(bool isIncentive, address fromToken, address toToken, uint256 fromAmount, uint256 returnAmount) internal { + //TODO: gas 测算 + if(isIncentive) { + IDODOIncentiveBsc(_DODO_INCENTIVE_).triggerIncentive(fromToken, toToken, fromAmount, returnAmount, msg.sender); + } + } + +} diff --git a/contracts/intf/IDODOIncentiveBsc.sol b/contracts/intf/IDODOIncentiveBsc.sol new file mode 100644 index 0000000..4a41033 --- /dev/null +++ b/contracts/intf/IDODOIncentiveBsc.sol @@ -0,0 +1,19 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +interface IDODOIncentiveBsc { + function triggerIncentive( + address fromToken, + address toToken, + uint256 fromAmount, + uint256 returnAmount, + address assetTo + ) external; +} diff --git a/contracts/intf/ILockedTokenVault02.sol b/contracts/intf/ILockedTokenVault02.sol new file mode 100644 index 0000000..3979303 --- /dev/null +++ b/contracts/intf/ILockedTokenVault02.sol @@ -0,0 +1,13 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +interface ILockedTokenVault02 { + function tradeIncentive(address trader, uint256 amount) external; +} diff --git a/deploy-detail-periphery.txt b/deploy-detail-periphery.txt index ced498b..f1b0d8e 100644 --- a/deploy-detail-periphery.txt +++ b/deploy-detail-periphery.txt @@ -16,10 +16,6 @@ vDODOToken changeReward tx: 0x8ed4b35edff17b8ef9480b9c40b7673cf7a50088afb9fb7c7 vDODOToken setDODOCirculationHelper tx: 0x402a9d1abd299ac8df019cff5d6f79e49b82cae34b356a0f1e0afdb47d7bd811 ==================================================== network type: live -Deploy time: 2021/2/8 下午3:06:45 -Deploy type: DODOMigrationBSC -==================================================== -network type: live Deploy time: 2021/2/8 下午3:08:35 Deploy type: DODOMigrationBSC DODOMigrationBSCAddress: 0xb159260989012fA98af560A3Fa6D9cd11a64cf6E @@ -190,3 +186,31 @@ Deploy time: 2021/3/4 下午3:10:50 Deploy type: UpCrowdPoolingFactory UpCrowdPoolingFactory address: 0xD734a08359296e44b87F4d404135cd0832A7a363 Init UpCpFactory Tx: 0x58798e736d419ef9ce9753fc32a847d2a5a65957623a83e3d258ef2a1c9f8ca8 +==================================================== +network type: kovan +Deploy time: 2021/3/10 下午9:18:58 +Deploy type: UpCrowdPoolingFactory +UpCrowdPoolingFactory address: 0x84F391bE6EE31bC32f0C956C7997E0a2eCF36c6d +Init UpCpFactory Tx: 0xfab59285ac78ad435db86bf96e38f34a7757ab97151250863e6ea19c762483f3 +==================================================== +network type: kovan +Deploy time: 2021/3/10 下午11:27:00 +Deploy type: UpCrowdPoolingFactory +UpCrowdPoolingFactory address: 0xe1C4300B47ccE8B162D8d036Db356c563a904757 +Init UpCpFactory Tx: 0x97ad372abbb2321e24e3e388585d47f0b1120980566abc3268c8305b04dc61f7==================================================== +network type: bsclive +Deploy time: 2021/3/13 下午11:36:30 +Deploy type: UpCrowdPoolingFactory +UpCrowdPoolingFactory address: 0x69f52AC40185A2A005D49114F0B77b7bA856F0a0 +==================================================== +network type: bsclive +Deploy time: 2021/3/13 下午11:39:14 +Deploy type: UpCrowdPoolingFactory +UpCrowdPoolingFactory address: 0xeCEaDe494FD5F913Fd937C5CAc4577236395Dc32 +Init UpCpFactory Tx: 0x0f78a88904cc4bb32147014c85f4154be41959ed64f24eea75ac7c0d66277725 +==================================================== +network type: live +Deploy time: 2021/3/13 下午11:43:58 +Deploy type: UpCrowdPoolingFactory +UpCrowdPoolingFactory address: 0x0c4b4F1D5F5c989457cdD6f5102308b33c922281 +Init UpCpFactory Tx: 0xd03546fa116a1eff47d729bc86e5b56143522fc846f0413f6b0b7dda1dd37a04 diff --git a/deploy-detail-v2.0.txt b/deploy-detail-v2.0.txt index ac08503..86b8497 100644 --- a/deploy-detail-v2.0.txt +++ b/deploy-detail-v2.0.txt @@ -565,3 +565,36 @@ Init CpFactory Tx: 0x93e987f7a665ff36e22275af6f399d5cbb88509107f7b223b8b7db353ba DODOV2RouteHelper Address: 0x8e66867Ff57250AF985Da3a81eE480fb6889EA9B DODOV2Proxy02 Address: 0x750C200d7c7C426da169742f705CA5268e1736b4 Init DODOProxyV2 Tx: 0x47f86534b348ac846c8fe2014f7debe91a763ff708bb377774933985d5105a6c +==================================================== +network type: bsclive +Deploy time: 2021/3/11 下午10:25:45 +Deploy type: V2 +multiSigAddress: 0xcaa42F09AF66A8BAE3A7445a7f63DAD97c11638b +DvmTemplateAddress: 0xC3BeD579CaB3EC29B22D9AB99F4E586af42496b9 +==================================================== +network type: bsclive +Deploy time: 2021/3/11 下午10:28:36 +Deploy type: V2 +multiSigAddress: 0xcaa42F09AF66A8BAE3A7445a7f63DAD97c11638b +DvmTemplateAddress: 0x02607600407329389C2912F46DD357d7fa33d901 +DvmFactoryAddress: 0xa1254eE5c6d6616904A82c55C6e134557096B6D4 +Init DvmFactory Tx: 0x2c4cf896c289a44977d8587f2608b8b46b2edd7cacd68c3dd22fcbf06d9864e5 +DODOV2RouteHelper Address: 0xd72b354BD39f8F11D0cA07bD5724896Bb1a42707 +DODOV2Proxy02 Address: 0x3a343F2e4e142412c5dD130359edb765a6054965 +Init DODOProxyV2 Tx: 0xa30500fe62320a53e7e5c73ab529f809302c01c0e42dc2f4050be1c948c7c81e +==================================================== +network type: live +Deploy time: 2021/3/12 上午8:38:27 +Deploy type: V2 +multiSigAddress: 0x95C4F5b83aA70810D4f142d58e5F7242Bd891CB0 +DvmTemplateAddress: 0x8a538751A501A9785F93727d4cB7b7827FAb1ad0 +DvmFactoryAddress: 0xc9eD9B18e447e600238fe50e944B9062B664DEa4 +Init DvmFactory Tx: 0x2e9b1a5f11bd9fa802a17404fd46b01b23741c8ebe61d27db1f567f30e83a742 +DODOV2RouteHelper Address: 0xeAB910bea37DD837dDCED91C8E99dBcC4DBcCc01 +==================================================== +network type: live +Deploy time: 2021/3/12 上午9:42:10 +Deploy type: V2 +multiSigAddress: 0x95C4F5b83aA70810D4f142d58e5F7242Bd891CB0 +DODOV2Proxy02 Address: 0x1cf4Ae0Fae772B64d83D175d9E3eE06240f6Dc9a +Init DODOProxyV2 Tx: 0x46f0fe1d79f83d0c7fb887c8e9c43fbf846b9096d39438c68ab48ecec33185f0 diff --git a/test/V2Proxy/proxy.incentive.bsc.test.ts b/test/V2Proxy/proxy.incentive.bsc.test.ts new file mode 100644 index 0000000..7cf2e80 --- /dev/null +++ b/test/V2Proxy/proxy.incentive.bsc.test.ts @@ -0,0 +1,254 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +import { decimalStr, MAX_UINT256, 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 deployer: string; +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 { + deployer = ctx.Deployer; + lp = ctx.SpareAccounts[0]; + project = ctx.SpareAccounts[1]; + trader = ctx.SpareAccounts[2]; + + await ctx.mintTestToken(deployer, ctx.DODO, decimalStr("1000000")); + 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.DODO.methods.approve(ctx.LockedVault02.options.address, MAX_UINT256).send(ctx.sendParam(deployer)); + 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, + false, + 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, + false, + 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, delay: number): Promise { + await ctx.LockedVault02.methods.deposit(decimalStr("1000000")).send(ctx.sendParam(ctx.Deployer)); + await ctx.LockedVault02.methods.updateParams( + Math.floor(new Date().getTime() / 1000 + delay), + 60 * 60 * 24 * 30, + 300000000000000000 + ).send(ctx.sendParam(ctx.Deployer)); +} + +async function getUserInfo(ctx: ProxyContext, user: string, logInfo?: string) { + var DODO = await ctx.DODO.methods.balanceOf(trader).call() + var USDT = await ctx.USDT.methods.balanceOf(trader).call() + console.log("DODO balance:" + DODO + "; USDT balance:" + USDT + "==" + logInfo); + var originBalance = await ctx.LockedVault02.methods.getOriginBalance(user).call(); + var claimedBalance = await ctx.LockedVault02.methods.getClaimedBalance(user).call(); + console.log("originBalance:" + originBalance + "; ClaimedBalance:" + claimedBalance + "==" + logInfo); + var res = { + "dodoBalance": DODO, + "usdtBalance": USDT, + "originBalance": originBalance, + "claimedBalance": claimedBalance + } + return res +} + + +describe("DODOProxyV2-Incentive-BSC", () => { + 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("5000"), mweiStr("20000"), "0", mweiStr("4")); + console.log("dpp_DODO_USDT:", dpp_DODO_USDT); + }); + + beforeEach(async () => { + snapshotId = await ctx.EVM.snapshot(); + }); + + afterEach(async () => { + await ctx.EVM.reset(snapshotId); + }); + + describe("DODOIncentiveBsc", () => { + + it("tigger - incentive - notstart", async () => { + await initIncentive(ctx, 60 * 10); + + await ctx.mintTestToken(trader, ctx.DODO, decimalStr("1000")); + await getUserInfo(ctx, trader, "Before Trade"); + + var dodoPairs = [ + dpp_DODO_USDT + ] + var directions = 0 + + await logGas(await ctx.DODOProxyV2.methods.dodoSwapV2TokenToToken( + ctx.DODO.options.address, + ctx.USDT.options.address, + decimalStr("200"), + 1, + dodoPairs, + directions, + true, + Math.floor(new Date().getTime() / 1000 + 60 * 10) + ), ctx.sendParam(trader), "swap with incentive first"); + + await getUserInfo(ctx, trader, "Trade One"); + + await logGas(await ctx.DODOProxyV2.methods.dodoSwapV2TokenToToken( + ctx.DODO.options.address, + ctx.USDT.options.address, + decimalStr("200"), + 1, + dodoPairs, + directions, + true, + Math.floor(new Date().getTime() / 1000 + 60 * 10) + ), ctx.sendParam(trader), "swap with incentive second"); + + await getUserInfo(ctx, trader, "Trade Twice"); + + await logGas(await ctx.LockedVault02.methods.claim(), ctx.sendParam(trader), "exec claim"); + + await getUserInfo(ctx, trader, "After claim"); + + await logGas(await ctx.DODOProxyV2.methods.dodoSwapV2TokenToToken( + ctx.DODO.options.address, + ctx.USDT.options.address, + decimalStr("200"), + 1, + dodoPairs, + directions, + false, + Math.floor(new Date().getTime() / 1000 + 60 * 10) + ), ctx.sendParam(trader), "swap without incentive"); + + await getUserInfo(ctx, trader, "Trade Without Incentive"); + // assert(a_DODO, "1095000000000000000"); + }); + + + it("tigger - incentive - start", async () => { + await initIncentive(ctx, -1); + //Incentive前LockedVault的两个值状态 + //Incentive执行-两笔 + //Incentive后LockedVault的状态 + //执行claim + //LockedVault状态 + //Incentive关闭 + //LockedVault状态 + await ctx.mintTestToken(trader, ctx.DODO, decimalStr("1000")); + await getUserInfo(ctx, trader, "Before Trade"); + + var dodoPairs = [ + dpp_DODO_USDT + ] + var directions = 0 + + await logGas(await ctx.DODOProxyV2.methods.dodoSwapV2TokenToToken( + ctx.DODO.options.address, + ctx.USDT.options.address, + decimalStr("200"), + 1, + dodoPairs, + directions, + true, + Math.floor(new Date().getTime() / 1000 + 60 * 10) + ), ctx.sendParam(trader), "swap with incentive first"); + + await getUserInfo(ctx, trader, "Trade One"); + + await logGas(await ctx.DODOProxyV2.methods.dodoSwapV2TokenToToken( + ctx.DODO.options.address, + ctx.USDT.options.address, + decimalStr("200"), + 1, + dodoPairs, + directions, + true, + Math.floor(new Date().getTime() / 1000 + 60 * 10) + ), ctx.sendParam(trader), "swap with incentive second"); + + await getUserInfo(ctx, trader, "Trade Twice"); + + await logGas(await ctx.LockedVault02.methods.claim(), ctx.sendParam(trader), "exec claim"); + + await getUserInfo(ctx, trader, "After claim"); + + await logGas(await ctx.DODOProxyV2.methods.dodoSwapV2TokenToToken( + ctx.DODO.options.address, + ctx.USDT.options.address, + decimalStr("200"), + 1, + dodoPairs, + directions, + false, + Math.floor(new Date().getTime() / 1000 + 60 * 10) + ), ctx.sendParam(trader), "swap without incentive"); + + await getUserInfo(ctx, trader, "Trade Without Incentive"); + }); + }); +}); diff --git a/test/utils/Contracts.ts b/test/utils/Contracts.ts index 7bc7c90..eb6d42f 100644 --- a/test/utils/Contracts.ts +++ b/test/utils/Contracts.ts @@ -35,7 +35,6 @@ export const DVM_PROXY_NAME = "DVMProxy" export const CONST_FEE_RATE_MODEL_NAME = "ConstFeeRateModel" export const PERMISSION_MANAGER_NAME = "PermissionManager" export const EXTERNAL_VALUE_NAME = "ExternalValue" -export const DODO_PROXY_NAME = "DODOV2Proxy02" export const FEE_RATE_MODEL_NAME = "FeeRateModel" export const DPP_NAME = "DPP" export const DPP_FACTORY_NAME = "DPPFactory" @@ -47,9 +46,13 @@ export const DODO_CALLEE_HELPER_NAME = "DODOCalleeHelper" export const CROWD_POOLING_NAME = "CP" export const CROWD_POOLING_FACTORY = "CrowdPoolingFactory" export const DODO_INCENTIVE = "DODOIncentive" +export const DODO_INCENTIVE_BSC = "DODOIncentiveBsc" export const VDODO_NAME = "vDODOToken" export const DODO_CULATION_HELPER = "DODOCirculationHelper" export const DODO_GOVERNANCE = "Governance" +export const LOCKED_VAULT_02 = "LockedTokenVault02" +export const DODO_PROXY_NAME = "DODOV2Proxy02" +export const DODO_PROXY_NAME_BSC = "DODOV2Proxy03Bsc" interface ContractJson { abi: any; diff --git a/test/utils/ProxyContextV2.ts b/test/utils/ProxyContextV2.ts index ada49e0..a422a74 100644 --- a/test/utils/ProxyContextV2.ts +++ b/test/utils/ProxyContextV2.ts @@ -24,6 +24,7 @@ export class ProxyContext { EVM: EVM; Web3: Web3; DODOProxyV2: Contract; + DODOProxyV2Bsc: Contract; DVMFactory: Contract; DPPFactory: Contract; CPFactory: Contract; @@ -40,6 +41,8 @@ export class ProxyContext { //Functions DODOIncentive: Contract; + DODOIncentiveBsc: Contract; + LockedVault02: Contract; mtFeeRateModel: Contract; Deployer: string; @@ -48,7 +51,7 @@ export class ProxyContext { constructor() { } - async init(weth:string) { + async init(weth: string) { this.EVM = new EVM(); this.Web3 = getDefaultWeb3(); const allAccounts = await this.Web3.eth.getAccounts(); @@ -89,7 +92,7 @@ export class ProxyContext { dvmTemplate.options.address, this.Deployer, mtFeeRateModelTemplate.options.address - ] + ] ) this.DODOApprove = await contracts.newContract( @@ -101,12 +104,18 @@ export class ProxyContext { [this.DODOApprove.options.address] ) - //DODO Incentive + //DODO Incentive (ETH) this.DODOIncentive = await contracts.newContract( contracts.DODO_INCENTIVE, [this.DODO.options.address] ) + //DODO Incentive (BSC) + this.DODOIncentiveBsc = await contracts.newContract( + contracts.DODO_INCENTIVE_BSC, + [this.DODO.options.address] + ) + this.DPPFactory = await contracts.newContract(contracts.DPP_FACTORY_NAME, [ cloneFactory.options.address, @@ -126,14 +135,26 @@ export class ProxyContext { this.Deployer, mtFeeRateModelTemplate.options.address, permissionManagerTemplate.options.address - ] + ] ) this.DODOSellHelper = await contracts.newContract( contracts.DODO_SELL_HELPER ); + //BSC 空投锁仓合约 + this.LockedVault02 = await contracts.newContract(contracts.LOCKED_VAULT_02, + [ + this.DODO.options.address, + Math.floor(new Date().getTime() / 1000), + 60 * 60 * 24 * 30, + 300000000000000000 + ] + ) + await this.LockedVault02.methods.initOwner(this.Deployer).send(this.sendParam(this.Deployer)); + + //ETH proxy this.DODOProxyV2 = await contracts.newContract(contracts.DODO_PROXY_NAME, [ this.DVMFactory.options.address, @@ -148,12 +169,36 @@ export class ProxyContext { ); await this.DODOProxyV2.methods.initOwner(this.Deployer).send(this.sendParam(this.Deployer)); - await this.DODOApprove.methods.init(this.Deployer,this.DODOApproveProxy.options.address).send(this.sendParam(this.Deployer)); - await this.DODOApproveProxy.methods.init(this.Deployer, [this.DODOProxyV2.options.address]).send(this.sendParam(this.Deployer)); + //BSC proxy + this.DODOProxyV2Bsc = await contracts.newContract(contracts.DODO_PROXY_NAME_BSC, + [ + this.DVMFactory.options.address, + this.DPPFactory.options.address, + this.CPFactory.options.address, + this.WETH.options.address, + this.DODOApproveProxy.options.address, + this.DODOSellHelper.options.address, + "0x0000000000000000000000000000000000000000", + this.DODOIncentiveBsc.options.address + ] + ); + + await this.DODOProxyV2Bsc.methods.initOwner(this.Deployer).send(this.sendParam(this.Deployer)); + + await this.DODOApprove.methods.init(this.Deployer, this.DODOApproveProxy.options.address).send(this.sendParam(this.Deployer)); + await this.DODOApproveProxy.methods.init(this.Deployer, [this.DODOProxyV2.options.address, this.DODOProxyV2Bsc.options.address]).send(this.sendParam(this.Deployer)); + + //DODOIncentive ETH 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)); + //DODOIncentive BSC + await this.DODOIncentiveBsc.methods.initOwner(this.Deployer).send(this.sendParam(this.Deployer)); + await this.DODOIncentiveBsc.methods.setContract(this.DODOProxyV2Bsc.options.address, this.LockedVault02.options.address).send(this.sendParam(this.Deployer)); + await this.DODOIncentiveBsc.methods.setStableList(this.USDC.options.address, true).send(this.sendParam(this.Deployer)); + await this.DODOIncentiveBsc.methods.setTokenList(this.DODO.options.address, true).send(this.sendParam(this.Deployer)); + this.DODOCalleeHelper = await contracts.newContract( contracts.DODO_CALLEE_HELPER_NAME, [this.WETH.options.address] @@ -191,7 +236,7 @@ export class ProxyContext { } } -export async function getProxyContext(weth:string): Promise { +export async function getProxyContext(weth: string): Promise { var context = new ProxyContext(); await context.init(weth); return context; diff --git a/truffle-config.js b/truffle-config.js index 2d75d3a..590b04f 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -39,7 +39,7 @@ module.exports = { */ deploySwitch: { DEPLOY_V1: false, - DEPLOY_V2: true, + DEPLOY_V2: false, ADAPTER: false, MOCK_TOKEN: false, MOCK_V2_POOL: false, @@ -49,7 +49,7 @@ module.exports = { FEERATEIMPL: false, WETH: false, DODO: false, - UpCP: false + UpCP: true }, networks: { @@ -84,7 +84,7 @@ module.exports = { return new HDWalletProvider(privKey, "https://mainnet.infura.io/v3/" + infuraId); }, gas: 6000000, - gasPrice: 90000000000, + gasPrice: 180000000000, network_id: 1, skipDryRun: true },