diff --git a/contracts/ConstantsHolder.sol b/contracts/ConstantsHolder.sol index 4804f3050..234f1e9a0 100644 --- a/contracts/ConstantsHolder.sol +++ b/contracts/ConstantsHolder.sol @@ -114,6 +114,8 @@ contract ConstantsHolder is IConstants, Permissions { uint public rotationDelay; + uint public proofOfUseLockUpPeriodDays; + /** * Set reward and delta periods to new one, run only by owner. This function * only for tests. @@ -168,6 +170,10 @@ contract ConstantsHolder is IConstants, Permissions { rotationDelay = newDelay; } + function setProofOfUseLockUpPeriod(uint periodDays) external onlyOwner { + proofOfUseLockUpPeriodDays = periodDays; + } + /** * @dev constructor in Permissions approach * @param contractsAddress needed in Permissions constructor @@ -184,5 +190,6 @@ contract ConstantsHolder is IConstants, Permissions { lastTimeOverloaded = 0; launchTimestamp = now; rotationDelay = 12 hours; + proofOfUseLockUpPeriodDays = 90; } } diff --git a/contracts/delegation/TimeHelpers.sol b/contracts/delegation/TimeHelpers.sol index 0977f1d8f..7d14245a1 100644 --- a/contracts/delegation/TimeHelpers.sol +++ b/contracts/delegation/TimeHelpers.sol @@ -69,6 +69,10 @@ contract TimeHelpers { } } + function calculateProofOfUseLockEndTime(uint month, uint lockUpPeriodDays) external pure returns (uint timestamp) { + timestamp = BokkyPooBahsDateTimeLibrary.addDays(monthToTimestamp(month), lockUpPeriodDays); + } + function addMonths(uint fromTimestamp, uint n) external pure returns (uint) { uint year; uint month; @@ -91,7 +95,11 @@ contract TimeHelpers { second); } - function monthToTimestamp(uint _month) external pure returns (uint timestamp) { + function getCurrentMonth() external view returns (uint) { + return timestampToMonth(now); + } + + function monthToTimestamp(uint _month) public pure returns (uint timestamp) { uint year = ZERO_YEAR; uint month = _month; year = year.add(month.div(12)); @@ -100,10 +108,6 @@ contract TimeHelpers { return BokkyPooBahsDateTimeLibrary.timestampFromDate(year, month, 1); } - function getCurrentMonth() external view returns (uint) { - return timestampToMonth(now); - } - function timestampToMonth(uint timestamp) public pure returns (uint) { uint year; uint month; diff --git a/contracts/delegation/TokenLaunchLocker.sol b/contracts/delegation/TokenLaunchLocker.sol index 3c3acf291..df812b387 100644 --- a/contracts/delegation/TokenLaunchLocker.sol +++ b/contracts/delegation/TokenLaunchLocker.sol @@ -23,6 +23,7 @@ import "@openzeppelin/contracts/math/SafeMath.sol"; import "../Permissions.sol"; import "../interfaces/delegation/ILocker.sol"; +import "../ConstantsHolder.sol"; import "./DelegationController.sol"; import "./TimeHelpers.sol"; @@ -96,10 +97,11 @@ contract TokenLaunchLocker is Permissions, ILocker { if (_locked[wallet] > 0) { DelegationController delegationController = DelegationController(contractManager.getContract("DelegationController")); TimeHelpers timeHelpers = TimeHelpers(contractManager.getContract("TimeHelpers")); + ConstantsHolder constantsHolder = ConstantsHolder(contractManager.getContract("ConstantsHolder")); uint currentMonth = timeHelpers.getCurrentMonth(); if (_totalDelegatedAmount[wallet].delegated.mul(2) >= _locked[wallet] && - _totalDelegatedAmount[wallet].month.add(3) <= currentMonth) { + timeHelpers.calculateProofOfUseLockEndTime(_totalDelegatedAmount[wallet].month, constantsHolder.proofOfUseLockUpPeriodDays()) <= now) { unlock(wallet); return 0; } else { diff --git a/contracts/test/ReentrancyTester.sol b/contracts/test/ReentrancyTester.sol index 6d8fac5ce..e828c43d3 100644 --- a/contracts/test/ReentrancyTester.sol +++ b/contracts/test/ReentrancyTester.sol @@ -18,12 +18,12 @@ contract ReentrancyTester is Permissions, IERC777Recipient { } function tokensReceived( - address operator, - address from, - address to, + address /* operator */, + address /* from */, + address /* to */, uint256 amount, - bytes calldata userData, - bytes calldata operatorData + bytes calldata /* userData */, + bytes calldata /* operatorData */ ) external { diff --git a/test/delegation/TokenLaunch.ts b/test/delegation/TokenLaunch.ts index f32190e60..cf4e73437 100644 --- a/test/delegation/TokenLaunch.ts +++ b/test/delegation/TokenLaunch.ts @@ -5,7 +5,7 @@ import { ContractManagerInstance, TokenLaunchManagerInstance, ValidatorServiceInstance} from "../../types/truffle-contracts"; -import { skipTime, skipTimeToDate } from "../utils/time"; +import { isLeapYear, skipTime, skipTimeToDate } from "../utils/time"; import * as chai from "chai"; import * as chaiAsPromised from "chai-as-promised"; @@ -142,7 +142,9 @@ contract("TokenLaunchManager", ([owner, holder, delegation, validator, seller, h // TODO: move undelegated tokens too }); - it("should unlock all tokens if 50% was delegated", async () => { + it("should unlock all tokens if 50% was delegated for 90 days", async () => { + await skipTimeToDate(web3, 1, 0); // January + const amount = Math.ceil(totalAmount / 2); const period = 3; await delegationController.delegate(validatorId, amount, period, "INFO", {from: holder}); @@ -158,12 +160,37 @@ contract("TokenLaunchManager", ([owner, holder, delegation, validator, seller, h await delegationController.requestUndelegation(delegationId, {from: holder}); - skipTime(web3, month * period); - - const state = await delegationController.getState(delegationId); - state.toNumber().should.be.equal(State.COMPLETED); - const locked = await skaleToken.getAndUpdateLockedAmount.call(holder); - locked.toNumber().should.be.equal(0); + // skip 89 days + const leapYear = await isLeapYear(web3); + if (leapYear) { + await skipTimeToDate(web3, 30, 3); + } else { + await skipTimeToDate(web3, 1, 4); + } + + if (leapYear) { + const state = await delegationController.getState(delegationId); + state.toNumber().should.be.equal(State.UNDELEGATION_REQUESTED); + const locked = await skaleToken.getAndUpdateLockedAmount.call(holder); + locked.toNumber().should.be.equal(totalAmount); + delegated = await skaleToken.getAndUpdateDelegatedAmount.call(holder); + delegated.toNumber().should.be.equal(amount); + } else { + const state = await delegationController.getState(delegationId); + state.toNumber().should.be.equal(State.COMPLETED); + const locked = await skaleToken.getAndUpdateLockedAmount.call(holder); + locked.toNumber().should.be.equal(totalAmount); + delegated = await skaleToken.getAndUpdateDelegatedAmount.call(holder); + delegated.toNumber().should.be.equal(0); + } + + // skip one more day + skipTime(web3, 60 * 60 * 24); + + const finalState = await delegationController.getState(delegationId); + finalState.toNumber().should.be.equal(State.COMPLETED); + const finalLocked = await skaleToken.getAndUpdateLockedAmount.call(holder); + finalLocked.toNumber().should.be.equal(0); delegated = await skaleToken.getAndUpdateDelegatedAmount.call(holder); delegated.toNumber().should.be.equal(0); }); diff --git a/test/utils/deploy/delegation/tokenLaunchLocker.ts b/test/utils/deploy/delegation/tokenLaunchLocker.ts index 3fcba5e8c..3cdb3961e 100644 --- a/test/utils/deploy/delegation/tokenLaunchLocker.ts +++ b/test/utils/deploy/delegation/tokenLaunchLocker.ts @@ -1,4 +1,5 @@ import { ContractManagerInstance, TokenLaunchLockerInstance } from "../../../../types/truffle-contracts"; +import { deployConstantsHolder } from "../constantsHolder"; import { deployFunctionFactory } from "../factory"; import { deployDelegationController } from "./delegationController"; import { deployTimeHelpers } from "./timeHelpers"; @@ -8,6 +9,7 @@ const deployTokenLaunchLocker: (contractManager: ContractManagerInstance) => Pro async (contractManager: ContractManagerInstance) => { await deployTimeHelpers(contractManager); await deployDelegationController(contractManager); + await deployConstantsHolder(contractManager); }); export { deployTokenLaunchLocker }; diff --git a/test/utils/time.ts b/test/utils/time.ts index a49327c8d..242f74494 100644 --- a/test/utils/time.ts +++ b/test/utils/time.ts @@ -50,3 +50,9 @@ export async function currentTime(web3: Web3) { } export const months = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"]; + +export async function isLeapYear(web3: Web3) { + const timestamp = await currentTime(web3); + const now = new Date(timestamp * 1000); + return now.getFullYear() % 4 === 0; +}