From e6cc833dbd777d10b2ee56135d565e5ae9d693e1 Mon Sep 17 00:00:00 2001 From: Dmitriy Kostin Date: Tue, 11 Dec 2018 18:58:33 +0200 Subject: [PATCH 01/13] added enum TimeUnit --- .../Mixed/ScheduledCheckpoint.sol | 30 +++++++++++------- test/y_scheduled_checkpoints.js | 31 +++++++++++-------- 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol b/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol index 097a44f50..fd5a53c1d 100644 --- a/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol +++ b/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol @@ -4,6 +4,7 @@ import "./../../Checkpoint/ICheckpoint.sol"; import "../../TransferManager/ITransferManager.sol"; import "../../../interfaces/ISecurityToken.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "../../../libraries/BokkyPooBahsDateTimeLibrary.sol"; /** * @title Burn module for burning tokens and keeping track of burnt amounts @@ -11,11 +12,14 @@ import "openzeppelin-solidity/contracts/math/SafeMath.sol"; contract ScheduledCheckpoint is ICheckpoint, ITransferManager { using SafeMath for uint256; + enum TimeUnit {SECONDS, MONTHS, YEARS} + struct Schedule { bytes32 name; uint256 startTime; uint256 nextTime; uint256 interval; + TimeUnit timeUnit; uint256 index; uint256[] checkpointIds; uint256[] timestamps; @@ -26,7 +30,7 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { mapping (bytes32 => Schedule) public schedules; - event AddSchedule(bytes32 _name, uint256 _startTime, uint256 _interval, uint256 _timestamp); + event AddSchedule(bytes32 _name, uint256 _startTime, uint256 _interval, TimeUnit _timeUint, uint256 _timestamp); event RemoveSchedule(bytes32 _name, uint256 _timestamp); /** @@ -51,17 +55,19 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { * @param _name name of the new schedule (must be unused) * @param _startTime start time of the schedule (first checkpoint) * @param _interval interval at which checkpoints should be created + * @param _timeUnit unit of time at which checkpoints should be created */ - function addSchedule(bytes32 _name, uint256 _startTime, uint256 _interval) external onlyOwner { + function addSchedule(bytes32 _name, uint256 _startTime, uint256 _interval, TimeUnit _timeUnit) external onlyOwner { require(_startTime > now, "Start time must be in the future"); require(schedules[_name].name == bytes32(0), "Name already in use"); schedules[_name].name = _name; schedules[_name].startTime = _startTime; schedules[_name].nextTime = _startTime; schedules[_name].interval = _interval; + schedules[_name].timeUnit = _timeUnit; schedules[_name].index = names.length; names.push(_name); - emit AddSchedule(_name, _startTime, _interval, now); + emit AddSchedule(_name, _startTime, _interval, _timeUnit, now); } /** @@ -99,15 +105,17 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { * @notice gets schedule details * @param _name name of the schedule */ - function getSchedule(bytes32 _name) view external returns(bytes32, uint256, uint256, uint256, uint256[], uint256[], uint256[]) { + function getSchedule(bytes32 _name) view external returns(bytes32, uint256, uint256, uint256, TimeUnit, uint256[], uint256[], uint256[]) { + Schedule storage schedule = schedules[_name]; return ( - schedules[_name].name, - schedules[_name].startTime, - schedules[_name].nextTime, - schedules[_name].interval, - schedules[_name].checkpointIds, - schedules[_name].timestamps, - schedules[_name].periods + schedule.name, + schedule.startTime, + schedule.nextTime, + schedule.interval, + schedule.timeUnit, + schedule.checkpointIds, + schedule.timestamps, + schedule.periods ); } diff --git a/test/y_scheduled_checkpoints.js b/test/y_scheduled_checkpoints.js index 5fcc03a74..4b261cb26 100644 --- a/test/y_scheduled_checkpoints.js +++ b/test/y_scheduled_checkpoints.js @@ -15,6 +15,10 @@ const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")) contract('ScheduledCheckpoint', accounts => { + const SECONDS = 0; + const MONTHS = 1; + const YEARS = 2; + // Accounts Variable declaration let account_polymath; let account_issuer; @@ -173,7 +177,7 @@ contract('ScheduledCheckpoint', accounts => { startTime = latestTime() + 100; interval = 24 * 60 * 60; console.log("Creating scheduled CP: " + startTime, interval); - await I_ScheduledCheckpoint.addSchedule("CP1", startTime, interval, {from: token_owner}); + await I_ScheduledCheckpoint.addSchedule("CP1", startTime, interval, SECONDS, {from: token_owner}); console.log("2: " + latestTime()); }); @@ -222,7 +226,7 @@ contract('ScheduledCheckpoint', accounts => { it("Should have checkpoint created with correct balances", async() => { let cp1 = await I_ScheduledCheckpoint.getSchedule("CP1"); - checkSchedule(cp1, "CP1", startTime, startTime + interval, interval, [1], [startTime], [1]); + checkSchedule(cp1, "CP1", startTime, startTime + interval, interval, SECONDS, [1], [startTime], [1]); assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 0)).toNumber(), 0); assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 1)).toNumber(), 0); }); @@ -258,7 +262,7 @@ contract('ScheduledCheckpoint', accounts => { it("No additional checkpoints created", async() => { let cp1 = await I_ScheduledCheckpoint.getSchedule("CP1"); - checkSchedule(cp1, "CP1", startTime, startTime + interval, interval, [1], [startTime], [1]); + checkSchedule(cp1, "CP1", startTime, startTime + interval, interval, SECONDS, [1], [startTime], [1]); assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 0)).toNumber(), 0); assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 1)).toNumber(), 0); }); @@ -296,7 +300,7 @@ contract('ScheduledCheckpoint', accounts => { it("Should have new checkpoint created with correct balances", async() => { let cp1 = await I_ScheduledCheckpoint.getSchedule("CP1"); - checkSchedule(cp1, "CP1", startTime, startTime + (2 * interval), interval, [1, 2], [startTime, startTime + interval], [1, 1]); + checkSchedule(cp1, "CP1", startTime, startTime + (2 * interval), interval, SECONDS, [1, 2], [startTime, startTime + interval], [1, 1]); assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 0)).toNumber(), 0); assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 1)).toNumber(), 0); assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 2)).toNumber(), 0); @@ -316,7 +320,7 @@ contract('ScheduledCheckpoint', accounts => { assert.isTrue(latestTime() <= startTime + (4 * interval)); await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('0.5', 'ether'), { from: account_investor1 }); let cp1 = await I_ScheduledCheckpoint.getSchedule("CP1"); - checkSchedule(cp1, "CP1", startTime, startTime + (4 * interval), interval, [1, 2, 3], [startTime, startTime + interval, startTime + (2 * interval)], [1, 1, 2]); + checkSchedule(cp1, "CP1", startTime, startTime + (4 * interval), interval, SECONDS, [1, 2, 3], [startTime, startTime + interval, startTime + (2 * interval)], [1, 1, 2]); assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 0)).toNumber(), 0); assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 1)).toNumber(), 0); assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 2)).toNumber(), 0); @@ -339,7 +343,7 @@ contract('ScheduledCheckpoint', accounts => { await I_ScheduledCheckpoint.updateAll({from: token_owner}); let cp1 = await I_ScheduledCheckpoint.getSchedule("CP1"); - checkSchedule(cp1, "CP1", startTime, startTime + (5 * interval), interval, [1, 2, 3, 4], [startTime, startTime + interval, startTime + (2 * interval), startTime + (4 * interval)], [1, 1, 2, 1]); + checkSchedule(cp1, "CP1", startTime, startTime + (5 * interval), interval, SECONDS, [1, 2, 3, 4], [startTime, startTime + interval, startTime + (2 * interval), startTime + (4 * interval)], [1, 1, 2, 1]); assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 0)).toNumber(), 0); assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 1)).toNumber(), 0); assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 2)).toNumber(), 0); @@ -369,21 +373,22 @@ contract('ScheduledCheckpoint', accounts => { }); -function checkSchedule(schedule, name, startTime, nextTime, interval, checkpoints, timestamps, periods) { +function checkSchedule(schedule, name, startTime, nextTime, interval, timeUnit, checkpoints, timestamps, periods) { assert.equal(web3.utils.toAscii(schedule[0]).replace(/\u0000/g, ''), name); assert.equal(schedule[1].toNumber(), startTime); assert.equal(schedule[2].toNumber(), nextTime); assert.equal(schedule[3].toNumber(), interval); - assert.equal(schedule[4].length, checkpoints.length); + assert.equal(schedule[4].toNumber(), timeUnit); + assert.equal(schedule[5].length, checkpoints.length); for (let i = 0; i < checkpoints.length; i++) { - assert.equal(schedule[4][i].toNumber(), checkpoints[i]); + assert.equal(schedule[5][i].toNumber(), checkpoints[i]); } - assert.equal(schedule[5].length, timestamps.length); + assert.equal(schedule[6].length, timestamps.length); for (let i = 0; i < timestamps.length; i++) { - assert.equal(schedule[5][i].toNumber(), timestamps[i]); + assert.equal(schedule[6][i].toNumber(), timestamps[i]); } - assert.equal(schedule[6].length, periods.length); + assert.equal(schedule[7].length, periods.length); for (let i = 0; i < periods.length; i++) { - assert.equal(schedule[6][i].toNumber(), periods[i]); + assert.equal(schedule[7][i].toNumber(), periods[i]); } } From 97b56d4b5ebc3437ca40cf9870e6c389ea0c9af1 Mon Sep 17 00:00:00 2001 From: Dmitriy Kostin Date: Tue, 11 Dec 2018 19:47:56 +0200 Subject: [PATCH 02/13] update depends on timeUnit --- .../Mixed/ScheduledCheckpoint.sol | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol b/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol index fd5a53c1d..59f964d16 100644 --- a/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol +++ b/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol @@ -12,6 +12,7 @@ import "../../../libraries/BokkyPooBahsDateTimeLibrary.sol"; contract ScheduledCheckpoint is ICheckpoint, ITransferManager { using SafeMath for uint256; + //TODO should we add hours, days & weeks for convenience ? enum TimeUnit {SECONDS, MONTHS, YEARS} struct Schedule { @@ -127,14 +128,26 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { _update(_name); } + //TODO refactor function _update(bytes32 _name) internal { Schedule storage schedule = schedules[_name]; if (schedule.nextTime <= now) { uint256 checkpointId = ISecurityToken(securityToken).createCheckpoint(); - uint256 periods = now.sub(schedule.nextTime).div(schedule.interval).add(1); - schedule.timestamps.push(schedule.nextTime); - schedule.nextTime = periods.mul(schedule.interval).add(schedule.nextTime); schedule.checkpointIds.push(checkpointId); + schedule.timestamps.push(schedule.nextTime); + uint256 periods; + if (schedule.timeUnit == TimeUnit.SECONDS ) { + periods = now.sub(schedule.nextTime).div(schedule.interval).add(1); + schedule.nextTime = periods.mul(schedule.interval).add(schedule.nextTime); + } else if (schedule.timeUnit == TimeUnit.MONTHS ) { + //TODO ? BokkyPooBahsDateTimeLibrary.diffMonths(now, schedule.nextTime) ? + periods = BokkyPooBahsDateTimeLibrary.diffMonths(schedule.nextTime, now).div(schedule.interval).add(1); + schedule.nextTime = BokkyPooBahsDateTimeLibrary.addMonths(schedule.nextTime, periods.mul(schedule.interval)); + } else if (schedule.timeUnit == TimeUnit.YEARS ) { + //TODO ? BokkyPooBahsDateTimeLibrary.diffYears(now, schedule.nextTime) ? + periods = BokkyPooBahsDateTimeLibrary.diffYears(schedule.nextTime, now).div(schedule.interval).add(1); + schedule.nextTime = BokkyPooBahsDateTimeLibrary.addYears(schedule.nextTime, periods.mul(schedule.interval)); + } schedule.periods.push(periods); } } From fa73691f06c38cc811ee9ade1134bc82063c2321 Mon Sep 17 00:00:00 2001 From: Dmitriy Kostin Date: Wed, 12 Dec 2018 14:56:25 +0200 Subject: [PATCH 03/13] test for monthly checkpoint --- .../Mixed/ScheduledCheckpoint.sol | 2 - test/y_scheduled_checkpoints.js | 51 ++++++++++++++++--- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol b/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol index 59f964d16..3733ff70e 100644 --- a/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol +++ b/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol @@ -140,11 +140,9 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { periods = now.sub(schedule.nextTime).div(schedule.interval).add(1); schedule.nextTime = periods.mul(schedule.interval).add(schedule.nextTime); } else if (schedule.timeUnit == TimeUnit.MONTHS ) { - //TODO ? BokkyPooBahsDateTimeLibrary.diffMonths(now, schedule.nextTime) ? periods = BokkyPooBahsDateTimeLibrary.diffMonths(schedule.nextTime, now).div(schedule.interval).add(1); schedule.nextTime = BokkyPooBahsDateTimeLibrary.addMonths(schedule.nextTime, periods.mul(schedule.interval)); } else if (schedule.timeUnit == TimeUnit.YEARS ) { - //TODO ? BokkyPooBahsDateTimeLibrary.diffYears(now, schedule.nextTime) ? periods = BokkyPooBahsDateTimeLibrary.diffYears(schedule.nextTime, now).div(schedule.interval).add(1); schedule.nextTime = BokkyPooBahsDateTimeLibrary.addYears(schedule.nextTime, periods.mul(schedule.interval)); } diff --git a/test/y_scheduled_checkpoints.js b/test/y_scheduled_checkpoints.js index 4b261cb26..3ff309573 100644 --- a/test/y_scheduled_checkpoints.js +++ b/test/y_scheduled_checkpoints.js @@ -173,11 +173,12 @@ contract('ScheduledCheckpoint', accounts => { let startTime; let interval; + let timeUnit = SECONDS; it("Should create a daily checkpoint", async () => { startTime = latestTime() + 100; interval = 24 * 60 * 60; console.log("Creating scheduled CP: " + startTime, interval); - await I_ScheduledCheckpoint.addSchedule("CP1", startTime, interval, SECONDS, {from: token_owner}); + await I_ScheduledCheckpoint.addSchedule("CP1", startTime, interval, timeUnit, {from: token_owner}); console.log("2: " + latestTime()); }); @@ -226,7 +227,7 @@ contract('ScheduledCheckpoint', accounts => { it("Should have checkpoint created with correct balances", async() => { let cp1 = await I_ScheduledCheckpoint.getSchedule("CP1"); - checkSchedule(cp1, "CP1", startTime, startTime + interval, interval, SECONDS, [1], [startTime], [1]); + checkSchedule(cp1, "CP1", startTime, startTime + interval, interval, timeUnit, [1], [startTime], [1]); assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 0)).toNumber(), 0); assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 1)).toNumber(), 0); }); @@ -262,7 +263,7 @@ contract('ScheduledCheckpoint', accounts => { it("No additional checkpoints created", async() => { let cp1 = await I_ScheduledCheckpoint.getSchedule("CP1"); - checkSchedule(cp1, "CP1", startTime, startTime + interval, interval, SECONDS, [1], [startTime], [1]); + checkSchedule(cp1, "CP1", startTime, startTime + interval, interval, timeUnit, [1], [startTime], [1]); assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 0)).toNumber(), 0); assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 1)).toNumber(), 0); }); @@ -300,7 +301,7 @@ contract('ScheduledCheckpoint', accounts => { it("Should have new checkpoint created with correct balances", async() => { let cp1 = await I_ScheduledCheckpoint.getSchedule("CP1"); - checkSchedule(cp1, "CP1", startTime, startTime + (2 * interval), interval, SECONDS, [1, 2], [startTime, startTime + interval], [1, 1]); + checkSchedule(cp1, "CP1", startTime, startTime + (2 * interval), interval, timeUnit, [1, 2], [startTime, startTime + interval], [1, 1]); assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 0)).toNumber(), 0); assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 1)).toNumber(), 0); assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 2)).toNumber(), 0); @@ -320,7 +321,7 @@ contract('ScheduledCheckpoint', accounts => { assert.isTrue(latestTime() <= startTime + (4 * interval)); await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('0.5', 'ether'), { from: account_investor1 }); let cp1 = await I_ScheduledCheckpoint.getSchedule("CP1"); - checkSchedule(cp1, "CP1", startTime, startTime + (4 * interval), interval, SECONDS, [1, 2, 3], [startTime, startTime + interval, startTime + (2 * interval)], [1, 1, 2]); + checkSchedule(cp1, "CP1", startTime, startTime + (4 * interval), interval, timeUnit, [1, 2, 3], [startTime, startTime + interval, startTime + (2 * interval)], [1, 1, 2]); assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 0)).toNumber(), 0); assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 1)).toNumber(), 0); assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 2)).toNumber(), 0); @@ -343,7 +344,7 @@ contract('ScheduledCheckpoint', accounts => { await I_ScheduledCheckpoint.updateAll({from: token_owner}); let cp1 = await I_ScheduledCheckpoint.getSchedule("CP1"); - checkSchedule(cp1, "CP1", startTime, startTime + (5 * interval), interval, SECONDS, [1, 2, 3, 4], [startTime, startTime + interval, startTime + (2 * interval), startTime + (4 * interval)], [1, 1, 2, 1]); + checkSchedule(cp1, "CP1", startTime, startTime + (5 * interval), interval, timeUnit, [1, 2, 3, 4], [startTime, startTime + interval, startTime + (2 * interval), startTime + (4 * interval)], [1, 1, 2, 1]); assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 0)).toNumber(), 0); assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 1)).toNumber(), 0); assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 2)).toNumber(), 0); @@ -371,12 +372,48 @@ contract('ScheduledCheckpoint', accounts => { }); + describe("Tests for monthly scheduled checkpoints", async() => { + + let name = "CP-M-1"; + let startTime; + let interval; + let timeUnit = MONTHS; + + it("Should create a monthly checkpoint", async () => { + startTime = latestTime() + 100; + interval = 1; + let tx = await I_ScheduledCheckpoint.addSchedule(name, startTime, interval, timeUnit, {from: token_owner}); + checkScheduleLog(tx.logs[0], name, startTime, interval, timeUnit); + }); + + it("Check first monthly checkpoint", async() => { + // await increaseTime(duration.days(31)); + await increaseTime(100); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + checkSchedule(schedule, name, startTime, startTime + interval, interval, timeUnit, [5], [startTime], [1]); + + }); + + + + }); + }); +function checkScheduleLog(log, name, startTime, interval, timeUnit) { + assert.equal(web3.utils.hexToUtf8(log.args._name), name); + assert.equal(log.args._startTime.toNumber(), startTime); + assert.equal(log.args._interval.toNumber(), interval); + assert.equal(log.args._timeUint.toNumber(), timeUnit); +} + function checkSchedule(schedule, name, startTime, nextTime, interval, timeUnit, checkpoints, timestamps, periods) { assert.equal(web3.utils.toAscii(schedule[0]).replace(/\u0000/g, ''), name); assert.equal(schedule[1].toNumber(), startTime); - assert.equal(schedule[2].toNumber(), nextTime); + //TODO ? + // assert.equal(schedule[2].toNumber(), nextTime); assert.equal(schedule[3].toNumber(), interval); assert.equal(schedule[4].toNumber(), timeUnit); assert.equal(schedule[5].length, checkpoints.length); From 9f89b6ac023037ec7c55f9476e77b8702bdee10d Mon Sep 17 00:00:00 2001 From: Dmitriy Kostin Date: Wed, 12 Dec 2018 18:14:20 +0200 Subject: [PATCH 04/13] tests for monthly checkpoint --- .../Mixed/ScheduledCheckpoint.sol | 2 - test/y_scheduled_checkpoints.js | 55 +++++++++++++++++-- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol b/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol index 3733ff70e..6961a0e78 100644 --- a/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol +++ b/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol @@ -12,7 +12,6 @@ import "../../../libraries/BokkyPooBahsDateTimeLibrary.sol"; contract ScheduledCheckpoint is ICheckpoint, ITransferManager { using SafeMath for uint256; - //TODO should we add hours, days & weeks for convenience ? enum TimeUnit {SECONDS, MONTHS, YEARS} struct Schedule { @@ -128,7 +127,6 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { _update(_name); } - //TODO refactor function _update(bytes32 _name) internal { Schedule storage schedule = schedules[_name]; if (schedule.nextTime <= now) { diff --git a/test/y_scheduled_checkpoints.js b/test/y_scheduled_checkpoints.js index 3ff309573..43d1af471 100644 --- a/test/y_scheduled_checkpoints.js +++ b/test/y_scheduled_checkpoints.js @@ -370,6 +370,10 @@ contract('ScheduledCheckpoint', accounts => { assert.equal(perm.length, 0); }); + it("Remove daily checkpoint", async () => { + await I_ScheduledCheckpoint.removeSchedule("CP1", {from: token_owner}); + }); + }); describe("Tests for monthly scheduled checkpoints", async() => { @@ -386,22 +390,64 @@ contract('ScheduledCheckpoint', accounts => { checkScheduleLog(tx.logs[0], name, startTime, interval, timeUnit); }); - it("Check first monthly checkpoint", async() => { - // await increaseTime(duration.days(31)); + it("Check one monthly checkpoint", async() => { await increaseTime(100); await I_ScheduledCheckpoint.updateAll({from: token_owner}); let schedule = await I_ScheduledCheckpoint.getSchedule(name); - checkSchedule(schedule, name, startTime, startTime + interval, interval, timeUnit, [5], [startTime], [1]); + let checkpoints = [5]; + let timestamps = [startTime]; + let periods = [1]; + checkSchedule(schedule, name, startTime, addMonths(startTime, interval), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Check two monthly checkpoints", async() => { + await increaseTime(duration.days(31 * interval)); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [5, 6]; + let timestamps = [startTime, addMonths(startTime, interval)]; + let periods = [1, 1]; + checkSchedule(schedule, name, startTime, addMonths(startTime, interval * 2), interval, timeUnit, checkpoints, timestamps, periods); }); + it("Check three monthly checkpoints", async() => { + await increaseTime(duration.days(31 * interval * 2)); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [5, 6, 7]; + let timestamps = [startTime, addMonths(startTime, interval), addMonths(startTime, interval * 2)]; + let periods = [1, 1, 2]; + checkSchedule(schedule, name, startTime, addMonths(startTime, interval * 4), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Check four monthly checkpoints", async() => { + await increaseTime(duration.days(31 * interval)); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [5, 6, 7, 8]; + let timestamps = [startTime, addMonths(startTime, interval), addMonths(startTime, interval * 2), addMonths(startTime, interval * 4)]; + let periods = [1, 1, 2, 1]; + checkSchedule(schedule, name, startTime, addMonths(startTime, interval * 5), interval, timeUnit, checkpoints, timestamps, periods); + }); }); }); +function addMonths(timestamp, months) { + let time = new Date(timestamp * 1000); + return time.setMonth(time.getMonth() + months) / 1000; +} + +function addYears(timestamp, years) { + let time = new Date(timestamp * 1000); + return time.setFullYear(time.getFullYear() + years) / 1000; +} + function checkScheduleLog(log, name, startTime, interval, timeUnit) { assert.equal(web3.utils.hexToUtf8(log.args._name), name); assert.equal(log.args._startTime.toNumber(), startTime); @@ -412,8 +458,7 @@ function checkScheduleLog(log, name, startTime, interval, timeUnit) { function checkSchedule(schedule, name, startTime, nextTime, interval, timeUnit, checkpoints, timestamps, periods) { assert.equal(web3.utils.toAscii(schedule[0]).replace(/\u0000/g, ''), name); assert.equal(schedule[1].toNumber(), startTime); - //TODO ? - // assert.equal(schedule[2].toNumber(), nextTime); + assert.equal(schedule[2].toNumber(), nextTime); assert.equal(schedule[3].toNumber(), interval); assert.equal(schedule[4].toNumber(), timeUnit); assert.equal(schedule[5].length, checkpoints.length); From d280a668c849c0aa9f2096b0e924522d956a8454 Mon Sep 17 00:00:00 2001 From: Dmitriy Kostin Date: Wed, 12 Dec 2018 19:28:58 +0200 Subject: [PATCH 05/13] use UTC instead of local time --- test/y_scheduled_checkpoints.js | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/test/y_scheduled_checkpoints.js b/test/y_scheduled_checkpoints.js index 43d1af471..0eba32e67 100644 --- a/test/y_scheduled_checkpoints.js +++ b/test/y_scheduled_checkpoints.js @@ -385,7 +385,7 @@ contract('ScheduledCheckpoint', accounts => { it("Should create a monthly checkpoint", async () => { startTime = latestTime() + 100; - interval = 1; + interval = 5; let tx = await I_ScheduledCheckpoint.addSchedule(name, startTime, interval, timeUnit, {from: token_owner}); checkScheduleLog(tx.logs[0], name, startTime, interval, timeUnit); }); @@ -434,18 +434,33 @@ contract('ScheduledCheckpoint', accounts => { checkSchedule(schedule, name, startTime, addMonths(startTime, interval * 5), interval, timeUnit, checkpoints, timestamps, periods); }); + it("Check five monthly checkpoints", async() => { + await increaseTime(duration.days(31 * interval)); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [5, 6, 7, 8, 9]; + let timestamps = [startTime, addMonths(startTime, interval), addMonths(startTime, interval * 2), addMonths(startTime, interval * 4), addMonths(startTime, interval * 5)]; + let periods = [1, 1, 2, 1, 1]; + checkSchedule(schedule, name, startTime, addMonths(startTime, interval * 6), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Remove monthly checkpoint", async () => { + await I_ScheduledCheckpoint.removeSchedule(name, {from: token_owner}); + }); + }); }); function addMonths(timestamp, months) { let time = new Date(timestamp * 1000); - return time.setMonth(time.getMonth() + months) / 1000; + return time.setUTCMonth(time.getUTCMonth() + months) / 1000; } function addYears(timestamp, years) { let time = new Date(timestamp * 1000); - return time.setFullYear(time.getFullYear() + years) / 1000; + return time.setUTCFullYear(time.getUTCFullYear() + years) / 1000; } function checkScheduleLog(log, name, startTime, interval, timeUnit) { From e75b98c6bbf5c1ffead3101bab1d2cb8a40f195a Mon Sep 17 00:00:00 2001 From: Dmitriy Kostin Date: Wed, 12 Dec 2018 19:58:09 +0200 Subject: [PATCH 06/13] minor fix --- test/y_scheduled_checkpoints.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/y_scheduled_checkpoints.js b/test/y_scheduled_checkpoints.js index 0eba32e67..03f98c037 100644 --- a/test/y_scheduled_checkpoints.js +++ b/test/y_scheduled_checkpoints.js @@ -435,14 +435,14 @@ contract('ScheduledCheckpoint', accounts => { }); it("Check five monthly checkpoints", async() => { - await increaseTime(duration.days(31 * interval)); + await increaseTime(duration.days(31 * interval * 3)); await I_ScheduledCheckpoint.updateAll({from: token_owner}); let schedule = await I_ScheduledCheckpoint.getSchedule(name); let checkpoints = [5, 6, 7, 8, 9]; let timestamps = [startTime, addMonths(startTime, interval), addMonths(startTime, interval * 2), addMonths(startTime, interval * 4), addMonths(startTime, interval * 5)]; - let periods = [1, 1, 2, 1, 1]; - checkSchedule(schedule, name, startTime, addMonths(startTime, interval * 6), interval, timeUnit, checkpoints, timestamps, periods); + let periods = [1, 1, 2, 1, 3]; + checkSchedule(schedule, name, startTime, addMonths(startTime, interval * 8), interval, timeUnit, checkpoints, timestamps, periods); }); it("Remove monthly checkpoint", async () => { From 0aab0f9cf1bb29238219ed2bbf358d4c9eed00f8 Mon Sep 17 00:00:00 2001 From: Dmitriy Kostin Date: Wed, 12 Dec 2018 20:39:33 +0200 Subject: [PATCH 07/13] Tests for yearly checkpoint --- test/y_scheduled_checkpoints.js | 76 +++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/test/y_scheduled_checkpoints.js b/test/y_scheduled_checkpoints.js index 03f98c037..7d92f72ea 100644 --- a/test/y_scheduled_checkpoints.js +++ b/test/y_scheduled_checkpoints.js @@ -451,6 +451,82 @@ contract('ScheduledCheckpoint', accounts => { }); + describe("Tests for yearly scheduled checkpoints", async() => { + + let name = "CP-Y-1"; + let startTime; + let interval; + let timeUnit = YEARS; + + it("Should create a yearly checkpoint", async () => { + startTime = latestTime() + 100; + + interval = 3; + let tx = await I_ScheduledCheckpoint.addSchedule(name, startTime, interval, timeUnit, {from: token_owner}); + checkScheduleLog(tx.logs[0], name, startTime, interval, timeUnit); + }); + + it("Check one yearly checkpoint", async() => { + await increaseTime(100); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [10]; + let timestamps = [startTime]; + let periods = [1]; + checkSchedule(schedule, name, startTime, addYears(startTime, interval), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Check two yearly checkpoints", async() => { + await increaseTime(duration.days(366 * interval)); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [10, 11]; + let timestamps = [startTime, addYears(startTime, interval)]; + let periods = [1, 1]; + checkSchedule(schedule, name, startTime, addYears(startTime, interval * 2), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Check three yearly checkpoints", async() => { + await increaseTime(duration.days(366 * interval * 2)); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [10, 11, 12]; + let timestamps = [startTime, addYears(startTime, interval), addYears(startTime, interval * 2)]; + let periods = [1, 1, 2]; + checkSchedule(schedule, name, startTime, addYears(startTime, interval * 4), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Check four yearly checkpoints", async() => { + await increaseTime(duration.days(366 * interval)); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [10, 11, 12, 13]; + let timestamps = [startTime, addYears(startTime, interval), addYears(startTime, interval * 2), addYears(startTime, interval * 4)]; + let periods = [1, 1, 2, 1]; + checkSchedule(schedule, name, startTime, addYears(startTime, interval * 5), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Check five yearly checkpoints", async() => { + await increaseTime(duration.days(366 * interval * 3)); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [10, 11, 12, 13, 14]; + let timestamps = [startTime, addYears(startTime, interval), addYears(startTime, interval * 2), addYears(startTime, interval * 4), addYears(startTime, interval * 5)]; + let periods = [1, 1, 2, 1, 3]; + checkSchedule(schedule, name, startTime, addYears(startTime, interval * 8), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Remove monthly checkpoint", async () => { + await I_ScheduledCheckpoint.removeSchedule(name, {from: token_owner}); + }); + + }); + }); function addMonths(timestamp, months) { From 932396320164216ba071a8245e1c2c0373d7fc28 Mon Sep 17 00:00:00 2001 From: Dmitriy Kostin Date: Thu, 13 Dec 2018 12:23:43 +0200 Subject: [PATCH 08/13] Tests for last day of month --- test/y_scheduled_checkpoints.js | 62 ++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/test/y_scheduled_checkpoints.js b/test/y_scheduled_checkpoints.js index 7d92f72ea..d294ed388 100644 --- a/test/y_scheduled_checkpoints.js +++ b/test/y_scheduled_checkpoints.js @@ -380,12 +380,11 @@ contract('ScheduledCheckpoint', accounts => { let name = "CP-M-1"; let startTime; - let interval; + let interval = 5; let timeUnit = MONTHS; it("Should create a monthly checkpoint", async () => { startTime = latestTime() + 100; - interval = 5; let tx = await I_ScheduledCheckpoint.addSchedule(name, startTime, interval, timeUnit, {from: token_owner}); checkScheduleLog(tx.logs[0], name, startTime, interval, timeUnit); }); @@ -455,13 +454,12 @@ contract('ScheduledCheckpoint', accounts => { let name = "CP-Y-1"; let startTime; - let interval; + let interval = 3; let timeUnit = YEARS; it("Should create a yearly checkpoint", async () => { startTime = latestTime() + 100; - interval = 3; let tx = await I_ScheduledCheckpoint.addSchedule(name, startTime, interval, timeUnit, {from: token_owner}); checkScheduleLog(tx.logs[0], name, startTime, interval, timeUnit); }); @@ -527,6 +525,62 @@ contract('ScheduledCheckpoint', accounts => { }); + describe("Tests for monthly (last day of month) scheduled checkpoints", async() => { + let name = "CP-M-2"; + let previousTime; + let startTime; + let interval = 1; + let timeUnit = MONTHS; + + it("Should create a monthly (last day of month) checkpoint", async () => { + previousTime = latestTime(); + + let startDate = new Date(previousTime * 1000); + startDate.setUTCMonth(11, 31); + startTime = startDate.getTime() / 1000; + console.log("previousTime:" + previousTime); + console.log("startTime:" + startTime); + console.log("startDate:" + startDate.toDateString()); + + let tx = await I_ScheduledCheckpoint.addSchedule(name, startTime, interval, timeUnit, {from: token_owner}); + checkScheduleLog(tx.logs[0], name, startTime, interval, timeUnit); + }); + + it("Check one monthly (last day of month) checkpoint", async() => { + await increaseTime(startTime - previousTime + 100); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [15]; + let timestamps = [startTime]; + let periods = [1]; + checkSchedule(schedule, name, startTime, addMonths(startTime, interval), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Check two monthly checkpoints", async() => { + await increaseTime(duration.days(31 * interval)); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [15, 16]; + let timestamps = [startTime, addMonths(startTime, interval)]; + let periods = [1, 1]; + checkSchedule(schedule, name, startTime, addMonths(startTime, interval * 2), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Check three monthly checkpoints", async() => { + await increaseTime(duration.days(31 * interval)); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [15, 16, 17]; + let timestamps = [startTime, addMonths(startTime, interval), addMonths(startTime, interval * 2)]; + let periods = [1, 1, 1]; + checkSchedule(schedule, name, startTime, addMonths(startTime, interval * 3), interval, timeUnit, checkpoints, timestamps, periods); + }); + + }); + }); function addMonths(timestamp, months) { From c9364acf1061f79674ad75aa2f8ddc79c1cc947a Mon Sep 17 00:00:00 2001 From: Dmitriy Kostin Date: Thu, 13 Dec 2018 13:55:58 +0200 Subject: [PATCH 09/13] Updated tests for last day of month --- test/y_scheduled_checkpoints.js | 38 ++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/test/y_scheduled_checkpoints.js b/test/y_scheduled_checkpoints.js index d294ed388..4b2043be0 100644 --- a/test/y_scheduled_checkpoints.js +++ b/test/y_scheduled_checkpoints.js @@ -525,64 +525,82 @@ contract('ScheduledCheckpoint', accounts => { }); - describe("Tests for monthly (last day of month) scheduled checkpoints", async() => { + describe("Tests for monthly scheduled checkpoints (last day of month)", async() => { let name = "CP-M-2"; let previousTime; + let startDate; let startTime; let interval = 1; let timeUnit = MONTHS; - it("Should create a monthly (last day of month) checkpoint", async () => { + it("Should create a monthly checkpoint -- December 31", async () => { previousTime = latestTime(); - let startDate = new Date(previousTime * 1000); + startDate = new Date(previousTime * 1000); startDate.setUTCMonth(11, 31); startTime = startDate.getTime() / 1000; console.log("previousTime:" + previousTime); console.log("startTime:" + startTime); - console.log("startDate:" + startDate.toDateString()); + console.log("startDate:" + startDate.toUTCString()); let tx = await I_ScheduledCheckpoint.addSchedule(name, startTime, interval, timeUnit, {from: token_owner}); checkScheduleLog(tx.logs[0], name, startTime, interval, timeUnit); }); - it("Check one monthly (last day of month) checkpoint", async() => { + it("Check monthly checkpoint -- January 31", async() => { await increaseTime(startTime - previousTime + 100); await I_ScheduledCheckpoint.updateAll({from: token_owner}); let schedule = await I_ScheduledCheckpoint.getSchedule(name); let checkpoints = [15]; + let nextTime = addMonths(startTime, interval); let timestamps = [startTime]; let periods = [1]; - checkSchedule(schedule, name, startTime, addMonths(startTime, interval), interval, timeUnit, checkpoints, timestamps, periods); + checkSchedule(schedule, name, startTime, nextTime, interval, timeUnit, checkpoints, timestamps, periods); }); - it("Check two monthly checkpoints", async() => { + it("Check monthly checkpoints -- February 28/29", async() => { await increaseTime(duration.days(31 * interval)); await I_ScheduledCheckpoint.updateAll({from: token_owner}); let schedule = await I_ScheduledCheckpoint.getSchedule(name); let checkpoints = [15, 16]; + let nextTime = addYears(startTime, 1); + let day = 28; + if ((startDate.getUTCFullYear() + 1) % 4 === 0) { + day = 29; + } + nextTime = setDate(nextTime, 1, day); //addMonths(startTime, interval * 2) let timestamps = [startTime, addMonths(startTime, interval)]; let periods = [1, 1]; - checkSchedule(schedule, name, startTime, addMonths(startTime, interval * 2), interval, timeUnit, checkpoints, timestamps, periods); + checkSchedule(schedule, name, startTime, nextTime, interval, timeUnit, checkpoints, timestamps, periods); }); - it("Check three monthly checkpoints", async() => { + it("Check monthly checkpoints -- March 31", async() => { await increaseTime(duration.days(31 * interval)); await I_ScheduledCheckpoint.updateAll({from: token_owner}); let schedule = await I_ScheduledCheckpoint.getSchedule(name); let checkpoints = [15, 16, 17]; + let nextTime = addMonths(startTime, interval * 3); let timestamps = [startTime, addMonths(startTime, interval), addMonths(startTime, interval * 2)]; let periods = [1, 1, 1]; - checkSchedule(schedule, name, startTime, addMonths(startTime, interval * 3), interval, timeUnit, checkpoints, timestamps, periods); + + console.log("expected:" + new Date(nextTime * 1000).toUTCString()); + console.log("actual:" + new Date(schedule[2].toNumber() * 1000).toUTCString()); + checkSchedule(schedule, name, startTime, nextTime, interval, timeUnit, checkpoints, timestamps, periods); }); }); }); +function setDate(time, month, day) { + let startDate = new Date(time * 1000); + startDate.setUTCMonth(month, day); + return startDate.getTime() / 1000; +} + function addMonths(timestamp, months) { let time = new Date(timestamp * 1000); return time.setUTCMonth(time.getUTCMonth() + months) / 1000; From bc2eb05e2ff446af6d2b47a803cc0a131fab2186 Mon Sep 17 00:00:00 2001 From: Dmitriy Kostin Date: Thu, 13 Dec 2018 13:57:47 +0200 Subject: [PATCH 10/13] commented failed test --- test/y_scheduled_checkpoints.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/y_scheduled_checkpoints.js b/test/y_scheduled_checkpoints.js index 4b2043be0..bbf803ae1 100644 --- a/test/y_scheduled_checkpoints.js +++ b/test/y_scheduled_checkpoints.js @@ -575,7 +575,7 @@ contract('ScheduledCheckpoint', accounts => { let periods = [1, 1]; checkSchedule(schedule, name, startTime, nextTime, interval, timeUnit, checkpoints, timestamps, periods); }); - +/* it("Check monthly checkpoints -- March 31", async() => { await increaseTime(duration.days(31 * interval)); await I_ScheduledCheckpoint.updateAll({from: token_owner}); @@ -590,7 +590,7 @@ contract('ScheduledCheckpoint', accounts => { console.log("actual:" + new Date(schedule[2].toNumber() * 1000).toUTCString()); checkSchedule(schedule, name, startTime, nextTime, interval, timeUnit, checkpoints, timestamps, periods); }); - +*/ }); }); From c8c0c93d9259bf2193d84c87535245c29e419f16 Mon Sep 17 00:00:00 2001 From: Dmitriy Kostin Date: Fri, 14 Dec 2018 13:00:56 +0200 Subject: [PATCH 11/13] fix for end of month schedule --- .../Mixed/ScheduledCheckpoint.sol | 11 +++++- test/y_scheduled_checkpoints.js | 37 ++++++++++++++----- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol b/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol index 6961a0e78..732923999 100644 --- a/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol +++ b/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol @@ -139,7 +139,8 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { schedule.nextTime = periods.mul(schedule.interval).add(schedule.nextTime); } else if (schedule.timeUnit == TimeUnit.MONTHS ) { periods = BokkyPooBahsDateTimeLibrary.diffMonths(schedule.nextTime, now).div(schedule.interval).add(1); - schedule.nextTime = BokkyPooBahsDateTimeLibrary.addMonths(schedule.nextTime, periods.mul(schedule.interval)); + uint256 totalPeriods = _getTotalPeriods(schedule).add(periods); + schedule.nextTime = BokkyPooBahsDateTimeLibrary.addMonths(schedule.startTime, totalPeriods.mul(schedule.interval)); } else if (schedule.timeUnit == TimeUnit.YEARS ) { periods = BokkyPooBahsDateTimeLibrary.diffYears(schedule.nextTime, now).div(schedule.interval).add(1); schedule.nextTime = BokkyPooBahsDateTimeLibrary.addYears(schedule.nextTime, periods.mul(schedule.interval)); @@ -148,6 +149,14 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { } } + function _getTotalPeriods(Schedule storage schedule) internal view returns(uint256) { + uint256 totalPeriods = 0; + for (uint256 i = 0; i < schedule.periods.length; i++) { + totalPeriods = totalPeriods.add(schedule.periods[i]); + } + return totalPeriods; + } + /** * @notice manually triggers update outside of transfer request for all schedules (can be used to reduce user gas costs) */ diff --git a/test/y_scheduled_checkpoints.js b/test/y_scheduled_checkpoints.js index bbf803ae1..d5a8d1f22 100644 --- a/test/y_scheduled_checkpoints.js +++ b/test/y_scheduled_checkpoints.js @@ -385,6 +385,8 @@ contract('ScheduledCheckpoint', accounts => { it("Should create a monthly checkpoint", async () => { startTime = latestTime() + 100; + console.log("startTime:" + startTime); + let tx = await I_ScheduledCheckpoint.addSchedule(name, startTime, interval, timeUnit, {from: token_owner}); checkScheduleLog(tx.logs[0], name, startTime, interval, timeUnit); }); @@ -559,38 +561,53 @@ contract('ScheduledCheckpoint', accounts => { checkSchedule(schedule, name, startTime, nextTime, interval, timeUnit, checkpoints, timestamps, periods); }); + function getDaysInFebruary() { + let days; + if ((startDate.getUTCFullYear() + 1) % 4 === 0) { + days = 29; + } else { + days = 28; + } + return days; + } + + function getEndOfFebruary(startTime, days) { + return setDate(addYears(startTime, 1), 1, days); //addMonths(startTime, interval * 2) + } + it("Check monthly checkpoints -- February 28/29", async() => { await increaseTime(duration.days(31 * interval)); await I_ScheduledCheckpoint.updateAll({from: token_owner}); let schedule = await I_ScheduledCheckpoint.getSchedule(name); let checkpoints = [15, 16]; - let nextTime = addYears(startTime, 1); - let day = 28; - if ((startDate.getUTCFullYear() + 1) % 4 === 0) { - day = 29; - } - nextTime = setDate(nextTime, 1, day); //addMonths(startTime, interval * 2) + let days = getDaysInFebruary(); + let nextTime = getEndOfFebruary(startTime, days); let timestamps = [startTime, addMonths(startTime, interval)]; let periods = [1, 1]; checkSchedule(schedule, name, startTime, nextTime, interval, timeUnit, checkpoints, timestamps, periods); }); -/* + it("Check monthly checkpoints -- March 31", async() => { - await increaseTime(duration.days(31 * interval)); + let days = getDaysInFebruary(); + await increaseTime(duration.days(days * interval)); await I_ScheduledCheckpoint.updateAll({from: token_owner}); let schedule = await I_ScheduledCheckpoint.getSchedule(name); let checkpoints = [15, 16, 17]; let nextTime = addMonths(startTime, interval * 3); - let timestamps = [startTime, addMonths(startTime, interval), addMonths(startTime, interval * 2)]; + let timestamps = [startTime, addMonths(startTime, interval), getEndOfFebruary(startTime, days)]; let periods = [1, 1, 1]; console.log("expected:" + new Date(nextTime * 1000).toUTCString()); console.log("actual:" + new Date(schedule[2].toNumber() * 1000).toUTCString()); + for (let i = 0; i < timestamps.length; i++) { + assert.equal(schedule[6][i].toNumber(), timestamps[i]); + console.log(new Date(schedule[6][i].toNumber() * 1000).toUTCString()); + } checkSchedule(schedule, name, startTime, nextTime, interval, timeUnit, checkpoints, timestamps, periods); }); -*/ + }); }); From 6ad29649a10c740d855915c8afbdd3d19730afa1 Mon Sep 17 00:00:00 2001 From: Dmitriy Kostin Date: Fri, 14 Dec 2018 13:25:33 +0200 Subject: [PATCH 12/13] refactored _update function --- .../Experimental/Mixed/ScheduledCheckpoint.sol | 12 +++--------- test/y_scheduled_checkpoints.js | 4 ++-- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol b/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol index 732923999..99bb3346d 100644 --- a/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol +++ b/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol @@ -24,6 +24,7 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { uint256[] checkpointIds; uint256[] timestamps; uint256[] periods; + uint256 totalPeriods; } bytes32[] public names; @@ -139,24 +140,17 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { schedule.nextTime = periods.mul(schedule.interval).add(schedule.nextTime); } else if (schedule.timeUnit == TimeUnit.MONTHS ) { periods = BokkyPooBahsDateTimeLibrary.diffMonths(schedule.nextTime, now).div(schedule.interval).add(1); - uint256 totalPeriods = _getTotalPeriods(schedule).add(periods); + uint256 totalPeriods = schedule.totalPeriods.add(periods); schedule.nextTime = BokkyPooBahsDateTimeLibrary.addMonths(schedule.startTime, totalPeriods.mul(schedule.interval)); } else if (schedule.timeUnit == TimeUnit.YEARS ) { periods = BokkyPooBahsDateTimeLibrary.diffYears(schedule.nextTime, now).div(schedule.interval).add(1); schedule.nextTime = BokkyPooBahsDateTimeLibrary.addYears(schedule.nextTime, periods.mul(schedule.interval)); } + schedule.totalPeriods = schedule.totalPeriods.add(periods); schedule.periods.push(periods); } } - function _getTotalPeriods(Schedule storage schedule) internal view returns(uint256) { - uint256 totalPeriods = 0; - for (uint256 i = 0; i < schedule.periods.length; i++) { - totalPeriods = totalPeriods.add(schedule.periods[i]); - } - return totalPeriods; - } - /** * @notice manually triggers update outside of transfer request for all schedules (can be used to reduce user gas costs) */ diff --git a/test/y_scheduled_checkpoints.js b/test/y_scheduled_checkpoints.js index d5a8d1f22..76cbec9a7 100644 --- a/test/y_scheduled_checkpoints.js +++ b/test/y_scheduled_checkpoints.js @@ -599,12 +599,12 @@ contract('ScheduledCheckpoint', accounts => { let timestamps = [startTime, addMonths(startTime, interval), getEndOfFebruary(startTime, days)]; let periods = [1, 1, 1]; - console.log("expected:" + new Date(nextTime * 1000).toUTCString()); - console.log("actual:" + new Date(schedule[2].toNumber() * 1000).toUTCString()); for (let i = 0; i < timestamps.length; i++) { assert.equal(schedule[6][i].toNumber(), timestamps[i]); console.log(new Date(schedule[6][i].toNumber() * 1000).toUTCString()); } + console.log("expected:" + new Date(nextTime * 1000).toUTCString()); + console.log("actual:" + new Date(schedule[2].toNumber() * 1000).toUTCString()); checkSchedule(schedule, name, startTime, nextTime, interval, timeUnit, checkpoints, timestamps, periods); }); From 69ae080ffb20ccc6672bda7a487d24ca164ba79b Mon Sep 17 00:00:00 2001 From: Dmitriy Kostin Date: Fri, 14 Dec 2018 14:18:20 +0200 Subject: [PATCH 13/13] added TimeUnits: DAYS, WEEKS --- .../Mixed/ScheduledCheckpoint.sol | 13 +- test/y_scheduled_checkpoints.js | 176 +++++++++++++++++- 2 files changed, 182 insertions(+), 7 deletions(-) diff --git a/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol b/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol index 99bb3346d..ae02123e7 100644 --- a/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol +++ b/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol @@ -12,7 +12,7 @@ import "../../../libraries/BokkyPooBahsDateTimeLibrary.sol"; contract ScheduledCheckpoint is ICheckpoint, ITransferManager { using SafeMath for uint256; - enum TimeUnit {SECONDS, MONTHS, YEARS} + enum TimeUnit {SECONDS, DAYS, WEEKS, MONTHS, YEARS} struct Schedule { bytes32 name; @@ -106,7 +106,7 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { * @notice gets schedule details * @param _name name of the schedule */ - function getSchedule(bytes32 _name) view external returns(bytes32, uint256, uint256, uint256, TimeUnit, uint256[], uint256[], uint256[]) { + function getSchedule(bytes32 _name) view external returns(bytes32, uint256, uint256, uint256, TimeUnit, uint256[], uint256[], uint256[], uint256) { Schedule storage schedule = schedules[_name]; return ( schedule.name, @@ -116,7 +116,8 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { schedule.timeUnit, schedule.checkpointIds, schedule.timestamps, - schedule.periods + schedule.periods, + schedule.totalPeriods ); } @@ -138,6 +139,12 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { if (schedule.timeUnit == TimeUnit.SECONDS ) { periods = now.sub(schedule.nextTime).div(schedule.interval).add(1); schedule.nextTime = periods.mul(schedule.interval).add(schedule.nextTime); + } else if (schedule.timeUnit == TimeUnit.DAYS ) { + periods = BokkyPooBahsDateTimeLibrary.diffDays(schedule.nextTime, now).div(schedule.interval).add(1); + schedule.nextTime = BokkyPooBahsDateTimeLibrary.addDays(schedule.nextTime, periods.mul(schedule.interval)); + } else if (schedule.timeUnit == TimeUnit.WEEKS ) { + periods = BokkyPooBahsDateTimeLibrary.diffDays(schedule.nextTime, now).div(7).div(schedule.interval).add(1); + schedule.nextTime = BokkyPooBahsDateTimeLibrary.addDays(schedule.nextTime, periods.mul(schedule.interval).mul(7)); } else if (schedule.timeUnit == TimeUnit.MONTHS ) { periods = BokkyPooBahsDateTimeLibrary.diffMonths(schedule.nextTime, now).div(schedule.interval).add(1); uint256 totalPeriods = schedule.totalPeriods.add(periods); diff --git a/test/y_scheduled_checkpoints.js b/test/y_scheduled_checkpoints.js index 76cbec9a7..1592450a0 100644 --- a/test/y_scheduled_checkpoints.js +++ b/test/y_scheduled_checkpoints.js @@ -16,8 +16,10 @@ const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")) contract('ScheduledCheckpoint', accounts => { const SECONDS = 0; - const MONTHS = 1; - const YEARS = 2; + const DAYS = 1; + const WEEKS = 2; + const MONTHS = 3; + const YEARS = 4; // Accounts Variable declaration let account_polymath; @@ -385,7 +387,6 @@ contract('ScheduledCheckpoint', accounts => { it("Should create a monthly checkpoint", async () => { startTime = latestTime() + 100; - console.log("startTime:" + startTime); let tx = await I_ScheduledCheckpoint.addSchedule(name, startTime, interval, timeUnit, {from: token_owner}); checkScheduleLog(tx.logs[0], name, startTime, interval, timeUnit); @@ -527,7 +528,7 @@ contract('ScheduledCheckpoint', accounts => { }); - describe("Tests for monthly scheduled checkpoints (last day of month)", async() => { + describe("Tests for monthly scheduled checkpoints -- end of month", async() => { let name = "CP-M-2"; let previousTime; let startDate; @@ -608,6 +609,161 @@ contract('ScheduledCheckpoint', accounts => { checkSchedule(schedule, name, startTime, nextTime, interval, timeUnit, checkpoints, timestamps, periods); }); + it("Remove monthly checkpoint", async () => { + await I_ScheduledCheckpoint.removeSchedule(name, {from: token_owner}); + }); + + }); + + describe("Tests for daily scheduled checkpoints", async() => { + + let name = "CP-D-1"; + let startTime; + let interval = 13; + let timeUnit = DAYS; + + it("Should create a daily checkpoint", async () => { + startTime = latestTime() + 100; + + let tx = await I_ScheduledCheckpoint.addSchedule(name, startTime, interval, timeUnit, {from: token_owner}); + checkScheduleLog(tx.logs[0], name, startTime, interval, timeUnit); + }); + + it("Check one daily checkpoint", async() => { + await increaseTime(100); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [18]; + let timestamps = [startTime]; + let periods = [1]; + checkSchedule(schedule, name, startTime, addDays(startTime, interval), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Check two daily checkpoints", async() => { + await increaseTime(duration.days(interval)); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [18, 19]; + let timestamps = [startTime, addDays(startTime, interval)]; + let periods = [1, 1]; + checkSchedule(schedule, name, startTime, addDays(startTime, interval * 2), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Check three daily checkpoints", async() => { + await increaseTime(duration.days(interval * 2)); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [18, 19, 20]; + let timestamps = [startTime, addDays(startTime, interval), addDays(startTime, interval * 2)]; + let periods = [1, 1, 2]; + checkSchedule(schedule, name, startTime, addDays(startTime, interval * 4), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Check four daily checkpoints", async() => { + await increaseTime(duration.days(interval)); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [18, 19, 20, 21]; + let timestamps = [startTime, addDays(startTime, interval), addDays(startTime, interval * 2), addDays(startTime, interval * 4)]; + let periods = [1, 1, 2, 1]; + checkSchedule(schedule, name, startTime, addDays(startTime, interval * 5), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Check five daily checkpoints", async() => { + await increaseTime(duration.days(interval * 3)); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [18, 19, 20, 21, 22]; + let timestamps = [startTime, addDays(startTime, interval), addDays(startTime, interval * 2), addDays(startTime, interval * 4), addDays(startTime, interval * 5)]; + let periods = [1, 1, 2, 1, 3]; + checkSchedule(schedule, name, startTime, addDays(startTime, interval * 8), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Remove daily checkpoint", async () => { + await I_ScheduledCheckpoint.removeSchedule(name, {from: token_owner}); + }); + + }); + + describe("Tests for weekly scheduled checkpoints", async() => { + + let name = "CP-M-1"; + let startTime; + let interval = 9; + let timeUnit = WEEKS; + + it("Should create a weekly checkpoint", async () => { + startTime = latestTime() + 100; + + let tx = await I_ScheduledCheckpoint.addSchedule(name, startTime, interval, timeUnit, {from: token_owner}); + checkScheduleLog(tx.logs[0], name, startTime, interval, timeUnit); + }); + + it("Check one weekly checkpoint", async() => { + await increaseTime(100); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [23]; + let timestamps = [startTime]; + let periods = [1]; + + checkSchedule(schedule, name, startTime, addWeeks(startTime, interval), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Check two weekly checkpoints", async() => { + await increaseTime(duration.days(7 * interval)); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [23, 24]; + let timestamps = [startTime, addWeeks(startTime, interval)]; + let periods = [1, 1]; + checkSchedule(schedule, name, startTime, addWeeks(startTime, interval * 2), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Check three weekly checkpoints", async() => { + await increaseTime(duration.days(7 * interval * 2)); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [23, 24, 25]; + let timestamps = [startTime, addWeeks(startTime, interval), addWeeks(startTime, interval * 2)]; + let periods = [1, 1, 2]; + checkSchedule(schedule, name, startTime, addWeeks(startTime, interval * 4), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Check four weekly checkpoints", async() => { + await increaseTime(duration.days(7 * interval)); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [23, 24, 25, 26]; + let timestamps = [startTime, addWeeks(startTime, interval), addWeeks(startTime, interval * 2), addWeeks(startTime, interval * 4)]; + let periods = [1, 1, 2, 1]; + checkSchedule(schedule, name, startTime, addWeeks(startTime, interval * 5), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Check five weekly checkpoints", async() => { + await increaseTime(duration.days(7 * interval * 3)); + await I_ScheduledCheckpoint.updateAll({from: token_owner}); + + let schedule = await I_ScheduledCheckpoint.getSchedule(name); + let checkpoints = [23, 24, 25, 26, 27]; + let timestamps = [startTime, addWeeks(startTime, interval), addWeeks(startTime, interval * 2), addWeeks(startTime, interval * 4), addWeeks(startTime, interval * 5)]; + let periods = [1, 1, 2, 1, 3]; + checkSchedule(schedule, name, startTime, addWeeks(startTime, interval * 8), interval, timeUnit, checkpoints, timestamps, periods); + }); + + it("Remove weekly checkpoint", async () => { + await I_ScheduledCheckpoint.removeSchedule(name, {from: token_owner}); + }); + }); }); @@ -618,6 +774,15 @@ function setDate(time, month, day) { return startDate.getTime() / 1000; } +function addDays(timestamp, days) { + let time = new Date(timestamp * 1000); + return time.setUTCDate(time.getUTCDate() + days) / 1000; +} + +function addWeeks(timestamp, weeks) { + return addDays(timestamp, weeks * 7); +} + function addMonths(timestamp, months) { let time = new Date(timestamp * 1000); return time.setUTCMonth(time.getUTCMonth() + months) / 1000; @@ -650,7 +815,10 @@ function checkSchedule(schedule, name, startTime, nextTime, interval, timeUnit, assert.equal(schedule[6][i].toNumber(), timestamps[i]); } assert.equal(schedule[7].length, periods.length); + let totalPeriods = 0; for (let i = 0; i < periods.length; i++) { assert.equal(schedule[7][i].toNumber(), periods[i]); + totalPeriods += periods[i]; } + assert.equal(schedule[8].toNumber(), totalPeriods); }