diff --git a/contracts/Factory/UpCrowdPoolingFactory.sol b/archive/UpCrowdPoolingFactory.sol similarity index 100% rename from contracts/Factory/UpCrowdPoolingFactory.sol rename to archive/UpCrowdPoolingFactory.sol diff --git a/contracts/CrowdPooling/impl/CP.sol b/contracts/CrowdPooling/impl/CP.sol index 856aff7..c85120e 100644 --- a/contracts/CrowdPooling/impl/CP.sol +++ b/contracts/CrowdPooling/impl/CP.sol @@ -59,10 +59,11 @@ contract CP is CPVesting { 2. phase calm duration 3. freeze duration 4. vesting duration - 5. claim lock duration + 5. claim freeze duration + 6. claim vesting duration */ - require(timeLine.length == 6, "LIST_LENGTH_WRONG"); + require(timeLine.length == 7, "LIST_LENGTH_WRONG"); _PHASE_BID_STARTTIME_ = timeLine[0]; _PHASE_BID_ENDTIME_ = _PHASE_BID_STARTTIME_.add(timeLine[1]); @@ -70,7 +71,8 @@ contract CP is CPVesting { _FREEZE_DURATION_ = timeLine[3]; _VESTING_DURATION_ = timeLine[4]; - _CLAIM_LOCK_DURATION_ = timeLine[5]; + _TOKEN_CLAIM_DURATION_ = timeLine[5]; + _TOKEN_VESTING_DURATION_ = timeLine[6]; require(block.timestamp <= _PHASE_BID_STARTTIME_, "TIMELINE_WRONG"); /* @@ -78,19 +80,22 @@ contract CP is CPVesting { 0. pool quote cap 1. k 2. i - 3. cliff rate + 3. lp cliff rate + 4. base token cliff rate */ - require(valueList.length == 4, "LIST_LENGTH_WRONG"); + require(valueList.length == 5, "LIST_LENGTH_WRONG"); _POOL_QUOTE_CAP_ = valueList[0]; _K_ = valueList[1]; _I_ = valueList[2]; _CLIFF_RATE_ = valueList[3]; + _TOKEN_CLIFF_RATE_ = valueList[4]; require(_I_ > 0 && _I_ <= 1e36, "I_VALUE_WRONG"); require(_K_ <= 1e18, "K_VALUE_WRONG"); require(_CLIFF_RATE_ <= 1e18, "CLIFF_RATE_WRONG"); + require(_TOKEN_CLIFF_RATE_ <= 1e18, "TOKEN_CLIFF_RATE_WRONG"); _TOTAL_BASE_ = _BASE_TOKEN_.balanceOf(address(this)); diff --git a/contracts/CrowdPooling/impl/CPStorage.sol b/contracts/CrowdPooling/impl/CPStorage.sol index f497427..f1131fb 100644 --- a/contracts/CrowdPooling/impl/CPStorage.sol +++ b/contracts/CrowdPooling/impl/CPStorage.sol @@ -52,7 +52,7 @@ contract CPStorage is InitializableOwnable, ReentrancyGuard { uint256 public _TOTAL_SHARES_; mapping(address => uint256) internal _SHARES_; - mapping(address => bool) public _CLAIMED_; + mapping(address => bool) public _CLAIMED_QUOTE_; address public _POOL_FACTORY_; address public _POOL_; @@ -75,7 +75,11 @@ contract CPStorage is InitializableOwnable, ReentrancyGuard { uint256 public _FREEZE_DURATION_; uint256 public _VESTING_DURATION_; uint256 public _CLIFF_RATE_; - uint256 public _CLAIM_LOCK_DURATION_; + + uint256 public _TOKEN_CLAIM_DURATION_; + uint256 public _TOKEN_VESTING_DURATION_; + uint256 public _TOKEN_CLIFF_RATE_; + mapping(address => uint256) _CLAIMED_BASE_TOKEN_; // ============ Modifiers ============ diff --git a/contracts/CrowdPooling/impl/CPVesting.sol b/contracts/CrowdPooling/impl/CPVesting.sol index 86a7c67..1c6f770 100644 --- a/contracts/CrowdPooling/impl/CPVesting.sol +++ b/contracts/CrowdPooling/impl/CPVesting.sol @@ -29,7 +29,8 @@ contract CPVesting is CPFunding { // ============ Events ============ - event Claim(address user, uint256 baseAmount, uint256 quoteAmount); + event ClaimBaseToken(address user, uint256 baseAmount); + event ClaimQuoteToken(address user, uint256 quoteAmount); event ClaimLP(uint256 amount); @@ -40,33 +41,59 @@ contract CPVesting is CPFunding { _; } - modifier afterClaimLockDuration() { - require(_SETTLED_ && block.timestamp >= _SETTLED_TIME_.add(_CLAIM_LOCK_DURATION_), "CLAIM_LOCKED"); - _; - } - modifier afterFreeze() { require(_SETTLED_ && block.timestamp >= _SETTLED_TIME_.add(_FREEZE_DURATION_), "FREEZED"); _; } + modifier afterClaimFreeze() { + require(_SETTLED_ && block.timestamp >= _SETTLED_TIME_.add(_TOKEN_CLAIM_DURATION_), "CLAIM_FREEZED"); + _; + } + // ============ Bidder Functions ============ - function bidderClaim(address to,bytes calldata data) external afterClaimLockDuration { - require(!_CLAIMED_[msg.sender], "ALREADY_CLAIMED"); - _CLAIMED_[msg.sender] = true; + function claimQuoteToken(address to,bytes calldata data) external afterSettlement { + require(!_CLAIMED_QUOTE_[msg.sender], "ALREADY_CLAIMED_FUND"); + _CLAIMED_QUOTE_[msg.sender] = true; - uint256 baseAmount = _UNUSED_BASE_.mul(_SHARES_[msg.sender]).div(_TOTAL_SHARES_); uint256 quoteAmount = _UNUSED_QUOTE_.mul(_SHARES_[msg.sender]).div(_TOTAL_SHARES_); - _transferBaseOut(to, baseAmount); _transferQuoteOut(to, quoteAmount); if(data.length>0){ - IDODOCallee(to).CPClaimBidCall(msg.sender,baseAmount,quoteAmount,data); + IDODOCallee(to).CPClaimBidCall(msg.sender,0,quoteAmount,data); } - emit Claim(msg.sender, baseAmount, quoteAmount); + emit ClaimQuoteToken(msg.sender, quoteAmount); + } + + + function claimBaseToken() external afterClaimFreeze { + uint256 claimableBaseAmount = getClaimableBaseToken(msg.sender); + _transferBaseOut(msg.sender, claimableBaseAmount); + _CLAIMED_BASE_TOKEN_[msg.sender] = _CLAIMED_BASE_TOKEN_[msg.sender].add(claimableBaseAmount); + emit ClaimBaseToken(msg.sender, claimableBaseAmount); + } + + function getClaimableBaseToken(address user) public view afterClaimFreeze returns (uint256) { + uint256 baseTotalAmount = _UNUSED_BASE_.mul(_SHARES_[user]).div(_TOTAL_SHARES_); + + uint256 remainingBaseToken = DecimalMath.mulFloor( + getRemainingBaseTokenRatio(block.timestamp), + baseTotalAmount + ); + return baseTotalAmount.sub(remainingBaseToken).sub(_CLAIMED_BASE_TOKEN_[user]); + } + + function getRemainingBaseTokenRatio(uint256 timestamp) public view afterClaimFreeze returns (uint256) { + uint256 timePast = timestamp.sub(_SETTLED_TIME_.add(_TOKEN_CLAIM_DURATION_)); + if (timePast < _TOKEN_VESTING_DURATION_) { + uint256 remainingTime = _TOKEN_VESTING_DURATION_.sub(timePast); + return DecimalMath.ONE.sub(_TOKEN_CLIFF_RATE_).mul(remainingTime).div(_TOKEN_VESTING_DURATION_); + } else { + return 0; + } } // ============ Owner Functions ============ diff --git a/contracts/CrowdPooling/intf/ICP.sol b/contracts/CrowdPooling/intf/ICP.sol index 469ffd2..350b41a 100644 --- a/contracts/CrowdPooling/intf/ICP.sol +++ b/contracts/CrowdPooling/intf/ICP.sol @@ -24,9 +24,9 @@ interface ICP { function emergencySettle() external; - function claimBase() external; + function claimBaseToken() external; - function claimQuote() external; + function ClaimQuoteToken(address to,bytes calldata data) external; function claimLPToken() external; } diff --git a/contracts/DODOFee/FeeRateImpl.sol b/contracts/DODOFee/FeeRateImpl.sol deleted file mode 100644 index bc70d42..0000000 --- a/contracts/DODOFee/FeeRateImpl.sol +++ /dev/null @@ -1,190 +0,0 @@ -/* - - Copyright 2021 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; -import {ICloneFactory} from "../lib/CloneFactory.sol"; -import {IERC20} from "../intf/IERC20.sol"; -import {SafeMath} from "../lib/SafeMath.sol"; - -interface ICrowdPooling { - function _QUOTE_RESERVE_() external view returns (uint256); - function getShares(address user) external view returns (uint256); - function _OWNER_() external returns (address); - function _QUOTE_TOKEN_() external view returns(address); -} - -interface IFee { - function getUserFee(address user) external view returns (uint256); -} - -interface IQuota { - function getUserQuota(address user) external view returns (int); - function initOwner(address newOwner) external; -} - -interface IPool { - function version() external pure returns (string memory); - function _LP_FEE_RATE_() external view returns (uint256); -} - -contract FeeRateImpl is InitializableOwnable { - using SafeMath for uint256; - - // ============ Storage ============ - address public _CLONE_FACTORY_; - address public _QUOTA_TEMPLATE_; - uint256 public _LP_MT_RATIO_ = 25; - - struct CPPoolInfo { - address quoteToken; - int globalQuota; - address feeAddr; - address quotaAddr; - } - - mapping(address => CPPoolInfo) cpPools; - mapping(address => uint256) public specPoolList; - - - function init( - address owner, - address cloneFactory, - address quotaTemplate - ) external { - initOwner(owner); - _CLONE_FACTORY_ = cloneFactory; - _QUOTA_TEMPLATE_ = quotaTemplate; - } - - - // ============ Ownable Functions ============ - - function addCpPoolInfo(address cpPool, address quoteToken, int globalQuota, address feeAddr, address quotaAddr) external onlyOwner { - CPPoolInfo memory cpPoolInfo = CPPoolInfo({ - quoteToken: quoteToken, - feeAddr: feeAddr, - quotaAddr: quotaAddr, - globalQuota: globalQuota - }); - cpPools[cpPool] = cpPoolInfo; - } - - function setCpPoolInfo(address cpPool, address quoteToken, int globalQuota, address feeAddr, address quotaAddr) external onlyOwner { - cpPools[cpPool].quoteToken = quoteToken; - cpPools[cpPool].feeAddr = feeAddr; - cpPools[cpPool].quotaAddr = quotaAddr; - cpPools[cpPool].globalQuota = globalQuota; - } - - function setLpMtRatio(uint256 newLpMtRatio) external onlyOwner { - _LP_MT_RATIO_ = newLpMtRatio; - } - - - function setSpecPoolList (address poolAddr, uint256 mtFeeRate) public onlyOwner { - specPoolList[poolAddr] = mtFeeRate; - } - - function setQuotaTemplate(address newQuotaTemplate) public onlyOwner { - _QUOTA_TEMPLATE_ = newQuotaTemplate; - } - - function createWhitelist(address cpPool, address quotaOwner) external returns (address quotaAddr) { - require(msg.sender == ICrowdPooling(cpPool)._OWNER_(), "Access restricted"); - - quotaAddr = ICloneFactory(_CLONE_FACTORY_).clone(_QUOTA_TEMPLATE_); - IQuota(quotaAddr).initOwner(quotaOwner); - address quoteToken = ICrowdPooling(cpPool)._QUOTE_TOKEN_(); - - CPPoolInfo memory cpPoolInfo = CPPoolInfo({ - quoteToken: quoteToken, - feeAddr: address(0x0), - quotaAddr: quotaAddr, - globalQuota: 0 - }); - cpPools[cpPool] = cpPoolInfo; - } - - // ============ View Functions ============ - - function getFeeRate(address pool, address user) external view returns (uint256) { - if(specPoolList[pool] != 0) { - return specPoolList[pool]; - } - - try IPool(pool).version() returns (string memory poolVersion) { - bytes32 hashPoolVersion = keccak256(abi.encodePacked(poolVersion)); - if(hashPoolVersion == keccak256(abi.encodePacked("CP 1.0.0"))) { - CPPoolInfo memory cpPoolInfo = cpPools[pool]; - address quoteToken = cpPoolInfo.quoteToken; - if(quoteToken == address(0)) { - return 0; - }else { - uint256 userInput = IERC20(quoteToken).balanceOf(pool).sub(ICrowdPooling(pool)._QUOTE_RESERVE_()); - uint256 userStake = ICrowdPooling(pool).getShares(user); - address feeAddr = cpPoolInfo.feeAddr; - address quotaAddr = cpPoolInfo.quotaAddr; - int curQuota = cpPoolInfo.globalQuota; - if(quotaAddr != address(0)) - curQuota = IQuota(quotaAddr).getUserQuota(user); - - require(curQuota == -1 || (curQuota != -1 && int(userInput.add(userStake)) <= curQuota), "DODOFeeImpl: EXCEED_YOUR_QUOTA"); - - if(feeAddr == address(0)) { - return 0; - } else { - return IFee(feeAddr).getUserFee(user); - } - } - } else if(hashPoolVersion == keccak256(abi.encodePacked("DVM 1.0.2")) || hashPoolVersion == keccak256(abi.encodePacked("DSP 1.0.1"))) { - uint256 lpFeeRate = IPool(pool)._LP_FEE_RATE_(); - uint256 mtFeeRate = lpFeeRate.mul(_LP_MT_RATIO_).div(100); - if(lpFeeRate.add(mtFeeRate) >= 10**18) { - return 0; - } else { - return mtFeeRate; - } - } else { - return 0; - } - } catch (bytes memory) { - return 0; - } - } - - function getCPInfoByUser(address pool, address user) external view returns (bool isHaveCap, int curQuota, uint256 userFee) { - CPPoolInfo memory cpPoolInfo = cpPools[pool]; - if(cpPoolInfo.quoteToken == address(0)) { - isHaveCap = false; - curQuota = -1; - userFee = 0; - }else { - address quotaAddr = cpPoolInfo.quotaAddr; - curQuota = cpPoolInfo.globalQuota; - if(quotaAddr != address(0)) - curQuota = IQuota(quotaAddr).getUserQuota(user); - - if(curQuota == -1) { - isHaveCap = false; - }else { - isHaveCap = true; - uint256 userStake = ICrowdPooling(pool).getShares(user); - curQuota = int(uint256(curQuota).sub(userStake)); - } - - address feeAddr = cpPoolInfo.feeAddr; - if(feeAddr == address(0)) { - userFee = 0; - } else { - userFee = IFee(feeAddr).getUserFee(user); - } - } - } -} diff --git a/contracts/SmartRoute/proxies/DODOCpProxy.sol b/contracts/SmartRoute/proxies/DODOCpProxy.sol index 5615d69..d67fb28 100644 --- a/contracts/SmartRoute/proxies/DODOCpProxy.sol +++ b/contracts/SmartRoute/proxies/DODOCpProxy.sol @@ -29,7 +29,7 @@ contract DODOCpProxy is ReentrancyGuard { address constant _ETH_ADDRESS_ = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; address public immutable _WETH_; address public immutable _DODO_APPROVE_PROXY_; - address public immutable _UPCP_FACTORY_; + // address public immutable _UPCP_FACTORY_; address public immutable _CP_FACTORY_; // ============ Modifiers ============ @@ -46,55 +46,55 @@ contract DODOCpProxy is ReentrancyGuard { constructor( address payable weth, address cpFactory, - address upCpFactory, + // address upCpFactory, address dodoApproveProxy ) public { _WETH_ = weth; _CP_FACTORY_ = cpFactory; - _UPCP_FACTORY_ = upCpFactory; + // _UPCP_FACTORY_ = upCpFactory; _DODO_APPROVE_PROXY_ = dodoApproveProxy; } //============ UpCrowdPooling Functions (create) ============ - function createUpCrowdPooling( - address baseToken, - address quoteToken, - uint256 baseInAmount, - uint256[] memory timeLine, - uint256[] memory valueList, - bool[] memory switches, - uint256 deadLine - ) external payable preventReentrant judgeExpired(deadLine) returns (address payable newUpCrowdPooling) { - address _baseToken = baseToken; - address _quoteToken = quoteToken == _ETH_ADDRESS_ ? _WETH_ : quoteToken; + // function createUpCrowdPooling( + // address baseToken, + // address quoteToken, + // uint256 baseInAmount, + // uint256[] memory timeLine, + // uint256[] memory valueList, + // bool[] memory switches, + // uint256 deadLine + // ) external payable preventReentrant judgeExpired(deadLine) returns (address payable newUpCrowdPooling) { + // address _baseToken = baseToken; + // address _quoteToken = quoteToken == _ETH_ADDRESS_ ? _WETH_ : quoteToken; - newUpCrowdPooling = IDODOV2(_UPCP_FACTORY_).createCrowdPooling(); + // newUpCrowdPooling = IDODOV2(_UPCP_FACTORY_).createCrowdPooling(); - _deposit( - msg.sender, - newUpCrowdPooling, - _baseToken, - baseInAmount, - false - ); + // _deposit( + // msg.sender, + // newUpCrowdPooling, + // _baseToken, + // baseInAmount, + // false + // ); - (bool success, ) = newUpCrowdPooling.call{value: msg.value}(""); - require(success, "DODOCpProxy: Transfer failed"); + // (bool success, ) = newUpCrowdPooling.call{value: msg.value}(""); + // require(success, "DODOCpProxy: Transfer failed"); - address[] memory tokens = new address[](2); - tokens[0] = _baseToken; - tokens[1] = _quoteToken; + // address[] memory tokens = new address[](2); + // tokens[0] = _baseToken; + // tokens[1] = _quoteToken; - IDODOV2(_UPCP_FACTORY_).initCrowdPooling( - newUpCrowdPooling, - msg.sender, - tokens, - timeLine, - valueList, - switches - ); - } + // IDODOV2(_UPCP_FACTORY_).initCrowdPooling( + // newUpCrowdPooling, + // msg.sender, + // tokens, + // timeLine, + // valueList, + // switches + // ); + // } //============ CrowdPooling Functions (create) ============