diff --git a/contracts/CrowdPooling/impl/CP.sol b/contracts/CrowdPooling/impl/CP.sol index fd123f2..b649a9d 100644 --- a/contracts/CrowdPooling/impl/CP.sol +++ b/contracts/CrowdPooling/impl/CP.sol @@ -48,7 +48,7 @@ contract CP is CPVesting { 0. phase bid starttime 1. phase bid duration 2. phase calm duration - 3. freeze duration + 3. vesting duration */ require(timeLine.length == 4, "LIST_LENGTH_WRONG"); @@ -57,7 +57,7 @@ contract CP is CPVesting { _PHASE_BID_ENDTIME_ = _PHASE_BID_STARTTIME_.add(timeLine[1]); _PHASE_CALM_ENDTIME_ = _PHASE_BID_ENDTIME_.add(timeLine[2]); - _FREEZE_DURATION_ = timeLine[3]; + _VESTING_DURATION_ = timeLine[3]; require(block.timestamp <= _PHASE_BID_STARTTIME_, "TIMELINE_WRONG"); @@ -67,6 +67,7 @@ contract CP is CPVesting { 1. owner quote ratio 2. k 3. i + 4. cliff rate */ require(valueList.length == 4, "LIST_LENGTH_WRONG"); @@ -75,10 +76,12 @@ contract CP is CPVesting { _OWNER_QUOTE_RATIO_ = valueList[1]; _K_ = valueList[2]; _I_ = valueList[3]; + _CLIFF_RATE_ = valueList[4]; - require(_I_ > 0 && _I_ <= 10**36, "I_VALUE_WRONG"); - require(_K_ <= 10**18, "K_VALUE_WRONG"); - require(_OWNER_QUOTE_RATIO_ <= 10**18, "OWNER_RATIO_WRONG"); + require(_I_ > 0 && _I_ <= 1e36, "I_VALUE_WRONG"); + require(_K_ <= 1e18, "K_VALUE_WRONG"); + require(_OWNER_QUOTE_RATIO_ <= 1e18, "OWNER_RATIO_WRONG"); + require(_CLIFF_RATE_ <= 1e18, "CLIFF_RATE_WRONG"); _TOTAL_BASE_ = _BASE_TOKEN_.balanceOf(address(this)); } diff --git a/contracts/CrowdPooling/impl/CPFunding.sol b/contracts/CrowdPooling/impl/CPFunding.sol index 6d5e723..35271da 100644 --- a/contracts/CrowdPooling/impl/CPFunding.sol +++ b/contracts/CrowdPooling/impl/CPFunding.sol @@ -61,7 +61,7 @@ contract CPFunding is CPStorage { _UNUSED_QUOTE_ = _QUOTE_TOKEN_.balanceOf(address(this)).sub(poolQuote).sub(ownerQuote); _UNUSED_BASE_ = _BASE_TOKEN_.balanceOf(address(this)).sub(poolBase); - // 这里的目的是让开盘价尽量等于avgPrice + // 这里的目的是让midPrice尽量等于avgPrice // 我们统一设定k=1,如果quote和base不平衡,就必然要截断一边 // DVM截断了quote,所以如果进入池子的quote很多,就要把quote设置成DVM的base // m = avgPrice @@ -79,16 +79,16 @@ contract CPFunding is CPStorage { _poolQuoteToken = address(_QUOTE_TOKEN_); _poolI = 1; } else if (poolQuote < baseDepth) { - // poolI up round + // poolI round up _poolBaseToken = address(_BASE_TOKEN_); _poolQuoteToken = address(_QUOTE_TOKEN_); - uint256 ratio = DecimalMath.ONE.sub(DecimalMath.divCeil(poolQuote, baseDepth)); + uint256 ratio = DecimalMath.ONE.sub(DecimalMath.divFloor(poolQuote, baseDepth)); _poolI = avgPrice.mul(ratio).mul(ratio).divCeil(DecimalMath.ONE2); } else if (poolQuote > baseDepth) { - // poolI down round + // poolI round down _poolBaseToken = address(_QUOTE_TOKEN_); _poolQuoteToken = address(_BASE_TOKEN_); - uint256 ratio = DecimalMath.ONE.sub(DecimalMath.divFloor(baseDepth, poolQuote)); + uint256 ratio = DecimalMath.ONE.sub(DecimalMath.divCeil(baseDepth, poolQuote)); _poolI = DecimalMath.reciprocalFloor(avgPrice).mul(ratio).mul(ratio).div( DecimalMath.ONE2 ); @@ -108,7 +108,7 @@ contract CPFunding is CPStorage { _transferQuoteOut(_POOL_, poolQuote); _transferQuoteOut(_OWNER_, ownerQuote); - IDVM(_POOL_).buyShares(address(this)); + _TOTAL_LP_AMOUNT_ = IDVM(_POOL_).buyShares(address(this)); } // in case something wrong with base token contract diff --git a/contracts/CrowdPooling/impl/CPStorage.sol b/contracts/CrowdPooling/impl/CPStorage.sol index 2920c83..b11f1e0 100644 --- a/contracts/CrowdPooling/impl/CPStorage.sol +++ b/contracts/CrowdPooling/impl/CPStorage.sol @@ -26,7 +26,6 @@ contract CPStorage is InitializableOwnable, ReentrancyGuard { uint256 public _PHASE_BID_ENDTIME_; uint256 public _PHASE_CALM_ENDTIME_; uint256 public _SETTLED_TIME_; - uint256 public _FREEZE_DURATION_; bool public _SETTLED_; // ============ Core Address ============ @@ -50,8 +49,7 @@ contract CPStorage is InitializableOwnable, ReentrancyGuard { uint256 public _TOTAL_SHARES_; mapping(address => uint256) internal _SHARES_; - mapping(address => bool) internal _QUOTE_CLAIMED_; - mapping(address => bool) internal _BASE_CLAIMED_; + mapping(address => bool) internal _CLAIMED_; address public _POOL_FACTORY_; address public _POOL_; @@ -68,6 +66,12 @@ contract CPStorage is InitializableOwnable, ReentrancyGuard { uint256 public _K_; uint256 public _I_; + // ============ LP Token Vesting ============ + + uint256 public _TOTAL_LP_AMOUNT_; + uint256 public _VESTING_DURATION_; + uint256 public _CLIFF_RATE_; + // ============ Modifiers ============ modifier phaseBid() { diff --git a/contracts/CrowdPooling/impl/CPVesting.sol b/contracts/CrowdPooling/impl/CPVesting.sol index 907f06e..627b97c 100644 --- a/contracts/CrowdPooling/impl/CPVesting.sol +++ b/contracts/CrowdPooling/impl/CPVesting.sol @@ -31,26 +31,37 @@ contract CPVesting is CPFunding { _; } - modifier afterFreeze() { - require(block.timestamp >= _SETTLED_TIME_.add(_FREEZE_DURATION_), "FREEZED"); - _; - } + // ============ Bidder Functions ============ - // ============ Functions ============ + function bidderClaim() external afterSettlement { + require(!_CLAIMED_[msg.sender], "ALREADY_CLAIMED"); + _CLAIMED_[msg.sender] = true; - function claimBase() external afterSettlement { - require(!_BASE_CLAIMED_[msg.sender], "BASE_CLAIMED"); - _BASE_CLAIMED_[msg.sender] = true; _transferBaseOut(msg.sender, _UNUSED_BASE_.mul(_SHARES_[msg.sender]).div(_TOTAL_SHARES_)); - } - - function claimQuote() external afterSettlement { - require(!_QUOTE_CLAIMED_[msg.sender], "QUOTE_CLAIMED"); - _QUOTE_CLAIMED_[msg.sender] = true; _transferQuoteOut(msg.sender, _UNUSED_QUOTE_.mul(_SHARES_[msg.sender]).div(_TOTAL_SHARES_)); } - function claimLPToken() external onlyOwner afterFreeze { - IERC20(_POOL_).safeTransfer(_OWNER_, IERC20(_POOL_).balanceOf(address(this))); + // ============ Owner Functions ============ + + function claimLPToken() external onlyOwner afterSettlement { + IERC20(_POOL_).safeTransfer(_OWNER_, getClaimableLPToken()); + } + + function getClaimableLPToken() public view afterSettlement returns (uint256) { + uint256 remainingLPToken = DecimalMath.mulFloor( + getRemainingLPRatio(block.timestamp), + _TOTAL_LP_AMOUNT_ + ); + return IERC20(_POOL_).balanceOf(address(this)).sub(remainingLPToken); + } + + function getRemainingLPRatio(uint256 timestamp) public view afterSettlement returns (uint256) { + uint256 timePast = timestamp.sub(_SETTLED_TIME_); + if (timePast < _VESTING_DURATION_) { + uint256 remainingTime = _VESTING_DURATION_.sub(timePast); + return DecimalMath.ONE.sub(_CLIFF_RATE_).mul(remainingTime).div(_VESTING_DURATION_); + } else { + return 0; + } } }