diff --git a/contracts/CrowdPooling/impl/CP.sol b/contracts/CrowdPooling/impl/CP.sol index 71c3181..d04809e 100644 --- a/contracts/CrowdPooling/impl/CP.sol +++ b/contracts/CrowdPooling/impl/CP.sol @@ -48,39 +48,38 @@ contract CP is CPVesting { 0. phase bid starttime 1. phase bid duration 2. phase calm duration - 3. vesting duration + 3. freeze duration + 4. vesting duration */ - require(timeLine.length == 4, "LIST_LENGTH_WRONG"); + require(timeLine.length == 5, "LIST_LENGTH_WRONG"); _PHASE_BID_STARTTIME_ = timeLine[0]; _PHASE_BID_ENDTIME_ = _PHASE_BID_STARTTIME_.add(timeLine[1]); _PHASE_CALM_ENDTIME_ = _PHASE_BID_ENDTIME_.add(timeLine[2]); - _VESTING_DURATION_ = timeLine[3]; + _FREEZE_DURATION_ = timeLine[3]; + _VESTING_DURATION_ = timeLine[4]; require(block.timestamp <= _PHASE_BID_STARTTIME_, "TIMELINE_WRONG"); /* Value List 0. pool quote cap - 1. owner quote ratio - 2. k - 3. i - 4. cliff rate + 1. k + 2. i + 3. cliff rate */ - require(valueList.length == 5, "LIST_LENGTH_WRONG"); + require(valueList.length == 4, "LIST_LENGTH_WRONG"); _POOL_QUOTE_CAP_ = valueList[0]; - _OWNER_QUOTE_RATIO_ = valueList[1]; - _K_ = valueList[2]; - _I_ = valueList[3]; - _CLIFF_RATE_ = valueList[4]; + _K_ = valueList[1]; + _I_ = valueList[2]; + _CLIFF_RATE_ = valueList[3]; 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 330a6b8..6ea5569 100644 --- a/contracts/CrowdPooling/impl/CPFunding.sol +++ b/contracts/CrowdPooling/impl/CPFunding.sol @@ -89,7 +89,7 @@ contract CPFunding is CPStorage { _poolBaseToken = address(_QUOTE_TOKEN_); _poolQuoteToken = address(_BASE_TOKEN_); uint256 ratio = DecimalMath.ONE.sub(DecimalMath.divCeil(baseDepth, poolQuote)); - _poolI = ratio.mul(ratio).div(avgPrice).div(DecimalMath.ONE2); + _poolI = ratio.mul(ratio).div(avgPrice); } _POOL_ = IUnownedDVMFactory(_POOL_FACTORY_).createDODOVendingMachine( address(this), diff --git a/contracts/CrowdPooling/impl/CPStorage.sol b/contracts/CrowdPooling/impl/CPStorage.sol index 89fff01..662b378 100644 --- a/contracts/CrowdPooling/impl/CPStorage.sol +++ b/contracts/CrowdPooling/impl/CPStorage.sol @@ -67,6 +67,7 @@ contract CPStorage is InitializableOwnable, ReentrancyGuard { // ============ LP Token Vesting ============ uint256 public _TOTAL_LP_AMOUNT_; + uint256 public _FREEZE_DURATION_; uint256 public _VESTING_DURATION_; uint256 public _CLIFF_RATE_; diff --git a/contracts/CrowdPooling/impl/CPVesting.sol b/contracts/CrowdPooling/impl/CPVesting.sol index 627b97c..4988a24 100644 --- a/contracts/CrowdPooling/impl/CPVesting.sol +++ b/contracts/CrowdPooling/impl/CPVesting.sol @@ -31,6 +31,11 @@ contract CPVesting is CPFunding { _; } + modifier afterFreeze() { + require(_SETTLED_ && block.timestamp >= _SETTLED_TIME_.add(_FREEZE_DURATION_), "FREEZED"); + _; + } + // ============ Bidder Functions ============ function bidderClaim() external afterSettlement { @@ -43,11 +48,11 @@ contract CPVesting is CPFunding { // ============ Owner Functions ============ - function claimLPToken() external onlyOwner afterSettlement { + function claimLPToken() external onlyOwner afterFreeze { IERC20(_POOL_).safeTransfer(_OWNER_, getClaimableLPToken()); } - function getClaimableLPToken() public view afterSettlement returns (uint256) { + function getClaimableLPToken() public view afterFreeze returns (uint256) { uint256 remainingLPToken = DecimalMath.mulFloor( getRemainingLPRatio(block.timestamp), _TOTAL_LP_AMOUNT_ @@ -55,8 +60,8 @@ contract CPVesting is CPFunding { return IERC20(_POOL_).balanceOf(address(this)).sub(remainingLPToken); } - function getRemainingLPRatio(uint256 timestamp) public view afterSettlement returns (uint256) { - uint256 timePast = timestamp.sub(_SETTLED_TIME_); + function getRemainingLPRatio(uint256 timestamp) public view afterFreeze returns (uint256) { + uint256 timePast = timestamp.sub(_SETTLED_TIME_.add(_FREEZE_DURATION_)); if (timePast < _VESTING_DURATION_) { uint256 remainingTime = _VESTING_DURATION_.sub(timePast); return DecimalMath.ONE.sub(_CLIFF_RATE_).mul(remainingTime).div(_VESTING_DURATION_); diff --git a/test/CrowdPooling/CPBid.test.ts b/test/CrowdPooling/CPBid.test.ts index 57ddb91..15133af 100644 --- a/test/CrowdPooling/CPBid.test.ts +++ b/test/CrowdPooling/CPBid.test.ts @@ -33,13 +33,14 @@ describe("Funding", () => { config = { totalBase: decimalStr("10000"), poolQuoteCap: decimalStr("50000"), - ownerQuoteRatio: decimalStr("0.1"), k: decimalStr("0.5"), i: decimalStr("10"), lpFeeRate: decimalStr("0.002"), bidDuration: new BigNumber(86400), calmDuration: new BigNumber(86400), freezeDuration: new BigNumber(86400), + vestingDuration: new BigNumber(86400), + cliffRate: decimalStr("0.1"), } ctx = new CPContext(); await ctx.init(config); diff --git a/test/CrowdPooling/CPSettle.test.ts b/test/CrowdPooling/CPSettle.test.ts index db0fb81..03e7e58 100644 --- a/test/CrowdPooling/CPSettle.test.ts +++ b/test/CrowdPooling/CPSettle.test.ts @@ -32,13 +32,14 @@ describe("Funding", () => { config = { totalBase: decimalStr("10000"), poolQuoteCap: decimalStr("50000"), - ownerQuoteRatio: decimalStr("0.1"), k: decimalStr("0.5"), i: decimalStr("10"), lpFeeRate: decimalStr("0.002"), bidDuration: new BigNumber(86400), calmDuration: new BigNumber(86400), freezeDuration: new BigNumber(86400), + vestingDuration: new BigNumber(86400), + cliffRate: decimalStr("0.1"), } ctx = new CPContext(); await ctx.init(config); @@ -68,18 +69,16 @@ describe("Funding", () => { var poolAddress = await ctx.CP.methods._POOL_().call() var pool = getContractWithAddress(DVM_NAME, poolAddress) - assert.equal(await pool.methods.getMidPrice().call(), "10050199494025273102") + assert.equal(await pool.methods.getMidPrice().call(), "10050199494025273134") assert.equal(await ctx.CP.methods._AVG_SETTLED_PRICE_().call(), "10050199494025273136") assert.equal(await ctx.CP.methods._UNUSED_QUOTE_().call(), "0") assert.equal(await ctx.CP.methods._UNUSED_BASE_().call(), "99401011949453729399") - assert.equal(await ctx.BASE.methods.balanceOf(ctx.Deployer).call(), "0") assert.equal(await ctx.BASE.methods.balanceOf(poolAddress).call(), "9900598988050546270601") assert.equal(await ctx.BASE.methods.balanceOf(ctx.CP.options.address).call(), "99401011949453729399") - assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.Deployer).call(), decimalStr("99.9")) - assert.equal(await ctx.QUOTE.methods.balanceOf(poolAddress).call(), decimalStr("899.1")) + assert.equal(await ctx.QUOTE.methods.balanceOf(poolAddress).call(), decimalStr("999")) assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.CP.options.address).call(), "0") }) @@ -95,7 +94,7 @@ describe("Funding", () => { var poolAddress = await ctx.CP.methods._POOL_().call() var pool = getContractWithAddress(DVM_NAME, poolAddress) - assert.equal(await pool.methods.getMidPrice().call(), "13090169943749474216") + assert.equal(await pool.methods.getMidPrice().call(), "13090169943749474228") assert.equal(await ctx.CP.methods._AVG_SETTLED_PRICE_().call(), "13090169943749474242") assert.equal(await ctx.CP.methods._UNUSED_QUOTE_().call(), decimalStr("49900")) @@ -105,8 +104,7 @@ describe("Funding", () => { assert.equal(await ctx.BASE.methods.balanceOf(poolAddress).call(), "6180339887498948482045") assert.equal(await ctx.BASE.methods.balanceOf(ctx.CP.options.address).call(), "3819660112501051517955") - assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.Deployer).call(), decimalStr("5000")) - assert.equal(await ctx.QUOTE.methods.balanceOf(poolAddress).call(), decimalStr("45000")) + assert.equal(await ctx.QUOTE.methods.balanceOf(poolAddress).call(), decimalStr("50000")) assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.CP.options.address).call(), decimalStr("49900")) }) diff --git a/test/CrowdPooling/CPSettleReversePool.test.ts b/test/CrowdPooling/CPSettleReversePool.test.ts index 63debf5..8aafec9 100644 --- a/test/CrowdPooling/CPSettleReversePool.test.ts +++ b/test/CrowdPooling/CPSettleReversePool.test.ts @@ -32,14 +32,15 @@ describe("Funding", () => { before(async () => { config = { totalBase: decimalStr("10000"), - poolQuoteCap: decimalStr("100000"), - ownerQuoteRatio: decimalStr("0.1"), - k: decimalStr("0.1"), + poolQuoteCap: decimalStr("80000"), + k: decimalStr("0.5"), i: decimalStr("10"), lpFeeRate: decimalStr("0.002"), bidDuration: new BigNumber(86400), calmDuration: new BigNumber(86400), freezeDuration: new BigNumber(86400), + vestingDuration: new BigNumber(86400), + cliffRate: decimalStr("0.1"), } ctx = new CPContext(); await ctx.init(config); @@ -68,19 +69,17 @@ describe("Funding", () => { var poolAddress = await ctx.CP.methods._POOL_().call() var pool = getContractWithAddress(DVM_NAME, poolAddress) - assert.equal(await pool.methods.getMidPrice().call(), "76012678448689469") - assert.equal(await ctx.CP.methods._AVG_SETTLED_PRICE_().call(), "13155700080678329720") + assert.equal(await pool.methods.getMidPrice().call(), "64921894064178782") + assert.equal(await ctx.CP.methods._AVG_SETTLED_PRICE_().call(), "15403124237432848687") - assert.equal(await ctx.CP.methods._UNUSED_QUOTE_().call(), "0") - assert.equal(await ctx.CP.methods._UNUSED_BASE_().call(), "7593666577024078089065") + assert.equal(await ctx.CP.methods._UNUSED_QUOTE_().call(), decimalStr("19900")) + assert.equal(await ctx.CP.methods._UNUSED_BASE_().call(), "5193751525134302627024") - assert.equal(await ctx.BASE.methods.balanceOf(ctx.Deployer).call(), "0") - assert.equal(await ctx.BASE.methods.balanceOf(poolAddress).call(), "2406333422975921910935") - assert.equal(await ctx.BASE.methods.balanceOf(ctx.CP.options.address).call(), "7593666577024078089065") + assert.equal(await ctx.BASE.methods.balanceOf(poolAddress).call(), "4806248474865697372976") + assert.equal(await ctx.BASE.methods.balanceOf(ctx.CP.options.address).call(), "5193751525134302627024") - assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.Deployer).call(), decimalStr("9990")) - assert.equal(await ctx.QUOTE.methods.balanceOf(poolAddress).call(), decimalStr("89910")) - assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.CP.options.address).call(), "0") + assert.equal(await ctx.QUOTE.methods.balanceOf(poolAddress).call(), decimalStr("80000")) + assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.CP.options.address).call(), decimalStr("19900")) }) }) diff --git a/test/CrowdPooling/CPVesting.test.ts b/test/CrowdPooling/CPVesting.test.ts index d6d2c5e..c737e3a 100644 --- a/test/CrowdPooling/CPVesting.test.ts +++ b/test/CrowdPooling/CPVesting.test.ts @@ -31,13 +31,14 @@ describe("Funding", () => { config = { totalBase: decimalStr("10000"), poolQuoteCap: decimalStr("50000"), - ownerQuoteRatio: decimalStr("0.1"), k: decimalStr("0.5"), i: decimalStr("10"), lpFeeRate: decimalStr("0.002"), bidDuration: new BigNumber(86400), calmDuration: new BigNumber(86400), freezeDuration: new BigNumber(86400), + vestingDuration: new BigNumber(86400), + cliffRate: decimalStr("0.1"), } ctx = new CPContext(); await ctx.init(config); @@ -68,13 +69,11 @@ describe("Funding", () => { assert.equal(await ctx.BASE.methods.balanceOf(ctx.CP.options.address).call(), "2557555139280633184959") assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.CP.options.address).call(), "0") - await ctx.CP.methods.claimBase().send(ctx.sendParam(bidder1)) - await ctx.CP.methods.claimQuote().send(ctx.sendParam(bidder1)) + await ctx.CP.methods.bidderClaim().send(ctx.sendParam(bidder1)) assert.equal(await ctx.BASE.methods.balanceOf(bidder1).call(), "852518379760211061653") assert.equal(await ctx.QUOTE.methods.balanceOf(bidder1).call(), "0") - await ctx.CP.methods.claimBase().send(ctx.sendParam(bidder2)) - await ctx.CP.methods.claimQuote().send(ctx.sendParam(bidder2)) + await ctx.CP.methods.bidderClaim().send(ctx.sendParam(bidder2)) assert.equal(await ctx.BASE.methods.balanceOf(bidder2).call(), "1705036759520422123306") assert.equal(await ctx.QUOTE.methods.balanceOf(bidder2).call(), "0") @@ -93,13 +92,11 @@ describe("Funding", () => { assert.equal(await ctx.BASE.methods.balanceOf(ctx.CP.options.address).call(), "3819660112501051517955") assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.CP.options.address).call(), decimalStr("39910")) - await ctx.CP.methods.claimBase().send(ctx.sendParam(bidder1)) - await ctx.CP.methods.claimQuote().send(ctx.sendParam(bidder1)) + await ctx.CP.methods.bidderClaim().send(ctx.sendParam(bidder1)) assert.equal(await ctx.BASE.methods.balanceOf(bidder1).call(), "1273220037500350505985") assert.equal(await ctx.QUOTE.methods.balanceOf(bidder1).call(), "13303333333333333333333") - await ctx.CP.methods.claimBase().send(ctx.sendParam(bidder2)) - await ctx.CP.methods.claimQuote().send(ctx.sendParam(bidder2)) + await ctx.CP.methods.bidderClaim().send(ctx.sendParam(bidder2)) assert.equal(await ctx.BASE.methods.balanceOf(bidder2).call(), "2546440075000701011970") assert.equal(await ctx.QUOTE.methods.balanceOf(bidder2).call(), "26606666666666666666666") }) @@ -114,15 +111,22 @@ describe("Funding", () => { await logGas(ctx.CP.methods.settle(), ctx.sendParam(ctx.Deployer), "settle") await truffleAssert.reverts(ctx.CP.methods.claimLPToken().send(ctx.sendParam(ctx.Deployer)), "FREEZED") + // Cliff await ctx.EVM.increaseTime(86400) - await ctx.CP.methods.claimLPToken().send(ctx.sendParam(ctx.Deployer)) - var poolAddress = await ctx.CP.methods._POOL_().call() var pool = getContractWithAddress(DVM_NAME, poolAddress) + await ctx.CP.methods.claimLPToken().send(ctx.sendParam(ctx.Deployer)) + assert.equal(await pool.methods.balanceOf(ctx.Deployer).call(), "618033988749894848205") + + // Vesting end + await ctx.EVM.increaseTime(86400) + await ctx.CP.methods.claimLPToken().send(ctx.sendParam(ctx.Deployer)) + assert.equal(await pool.methods.balanceOf(ctx.Deployer).call(), "6180339887498948482045") + await pool.methods.sellShares("6180339887498948482045", bidder1, 0, 0, "0x", MAX_UINT256).send(ctx.sendParam(ctx.Deployer)) assert.equal(await ctx.BASE.methods.balanceOf(bidder1).call(), "6180339887498948482045") - assert.equal(await ctx.QUOTE.methods.balanceOf(bidder1).call(), "45000000000000000000000") + assert.equal(await ctx.QUOTE.methods.balanceOf(bidder1).call(), "50000000000000000000000") assert.equal(await ctx.BASE.methods.balanceOf(poolAddress).call(), "0") assert.equal(await ctx.QUOTE.methods.balanceOf(poolAddress).call(), "0") diff --git a/test/utils/CrowdPoolingContext.ts b/test/utils/CrowdPoolingContext.ts index 2c2aed2..1b66edc 100644 --- a/test/utils/CrowdPoolingContext.ts +++ b/test/utils/CrowdPoolingContext.ts @@ -22,13 +22,14 @@ BigNumber.config({ export interface CPContextInitConfig { totalBase: string; poolQuoteCap: string; - ownerQuoteRatio: string; k: string; i: string; lpFeeRate: string; bidDuration: BigNumber; calmDuration: BigNumber; freezeDuration: BigNumber; + vestingDuration: BigNumber; + cliffRate: string; } @@ -100,13 +101,14 @@ export class CPContext { (await this.Web3.eth.getBlock(await this.Web3.eth.getBlockNumber())).timestamp, config.bidDuration, config.calmDuration, - config.freezeDuration + config.freezeDuration, + config.vestingDuration ], [ config.poolQuoteCap, - config.ownerQuoteRatio, config.k, - config.i + config.i, + config.cliffRate ] ).send(this.sendParam(this.Deployer))