From 78ce59c2730c442360ceb0d2f530fb4c1f4b3f2a Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Thu, 4 Oct 2018 23:08:04 +0100 Subject: [PATCH 01/18] WIP --- .../libraries/BokkyPooBahsDateTimeLibrary.sol | 339 ++++++++++++++++++ .../Checkpoint/ScheduledCheckpoint.sol | 59 +++ .../Checkpoint/ScheduledCheckpointFactory.sol | 102 ++++++ 3 files changed, 500 insertions(+) create mode 100644 contracts/libraries/BokkyPooBahsDateTimeLibrary.sol create mode 100644 contracts/modules/Checkpoint/ScheduledCheckpoint.sol create mode 100644 contracts/modules/Checkpoint/ScheduledCheckpointFactory.sol diff --git a/contracts/libraries/BokkyPooBahsDateTimeLibrary.sol b/contracts/libraries/BokkyPooBahsDateTimeLibrary.sol new file mode 100644 index 000000000..29de4d7cd --- /dev/null +++ b/contracts/libraries/BokkyPooBahsDateTimeLibrary.sol @@ -0,0 +1,339 @@ +pragma solidity ^0.4.24; + +// ---------------------------------------------------------------------------- +// BokkyPooBah's DateTime Library v1.00 +// +// A gas-efficient Solidity date and time library +// +// https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary +// +// Tested date range 1970/01/01 to 2345/12/31 +// +// Conventions: +// Unit | Range | Notes +// :-------- |:-------------:|:----- +// timestamp | >= 0 | Unix timestamp, number of seconds since 1970/01/01 00:00:00 UTC +// year | 1970 ... 2345 | +// month | 1 ... 12 | +// day | 1 ... 31 | +// hour | 0 ... 23 | +// minute | 0 ... 59 | +// second | 0 ... 59 | +// dayOfWeek | 1 ... 7 | 1 = Monday, ..., 7 = Sunday +// +// +// Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2018. +// +// GNU Lesser General Public License 3.0 +// https://www.gnu.org/licenses/lgpl-3.0.en.html +// ---------------------------------------------------------------------------- + +library BokkyPooBahsDateTimeLibrary { + + uint constant SECONDS_PER_DAY = 24 * 60 * 60; + uint constant SECONDS_PER_HOUR = 60 * 60; + uint constant SECONDS_PER_MINUTE = 60; + int constant OFFSET19700101 = 2440588; + + uint constant DOW_MON = 1; + uint constant DOW_TUE = 2; + uint constant DOW_WED = 3; + uint constant DOW_THU = 4; + uint constant DOW_FRI = 5; + uint constant DOW_SAT = 6; + uint constant DOW_SUN = 7; + + // ------------------------------------------------------------------------ + // Calculate the number of days from 1970/01/01 to year/month/day using + // the date conversion algorithm from + // http://aa.usno.navy.mil/faq/docs/JD_Formula.php + // and subtracting the offset 2440588 so that 1970/01/01 is day 0 + // + // days = day + // - 32075 + // + 1461 * (year + 4800 + (month - 14) / 12) / 4 + // + 367 * (month - 2 - (month - 14) / 12 * 12) / 12 + // - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4 + // - offset + // ------------------------------------------------------------------------ + function _daysFromDate(uint year, uint month, uint day) internal pure returns (uint _days) { + require(year >= 1970); + int _year = int(year); + int _month = int(month); + int _day = int(day); + + int __days = _day + - 32075 + + 1461 * (_year + 4800 + (_month - 14) / 12) / 4 + + 367 * (_month - 2 - (_month - 14) / 12 * 12) / 12 + - 3 * ((_year + 4900 + (_month - 14) / 12) / 100) / 4 + - OFFSET19700101; + + _days = uint(__days); + } + + // ------------------------------------------------------------------------ + // Calculate year/month/day from the number of days since 1970/01/01 using + // the date conversion algorithm from + // http://aa.usno.navy.mil/faq/docs/JD_Formula.php + // and adding the offset 2440588 so that 1970/01/01 is day 0 + // + // int L = days + 68569 + offset + // int N = 4 * L / 146097 + // L = L - (146097 * N + 3) / 4 + // year = 4000 * (L + 1) / 1461001 + // L = L - 1461 * year / 4 + 31 + // month = 80 * L / 2447 + // dd = L - 2447 * month / 80 + // L = month / 11 + // month = month + 2 - 12 * L + // year = 100 * (N - 49) + year + L + // ------------------------------------------------------------------------ + function _daysToDate(uint _days) internal pure returns (uint year, uint month, uint day) { + int __days = int(_days); + + int L = __days + 68569 + OFFSET19700101; + int N = 4 * L / 146097; + L = L - (146097 * N + 3) / 4; + int _year = 4000 * (L + 1) / 1461001; + L = L - 1461 * _year / 4 + 31; + int _month = 80 * L / 2447; + int _day = L - 2447 * _month / 80; + L = _month / 11; + _month = _month + 2 - 12 * L; + _year = 100 * (N - 49) + _year + L; + + year = uint(_year); + month = uint(_month); + day = uint(_day); + } + + function timestampFromDate(uint year, uint month, uint day) internal pure returns (uint timestamp) { + timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY; + } + function timestampFromDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (uint timestamp) { + timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + hour * SECONDS_PER_HOUR + minute * SECONDS_PER_MINUTE + second; + } + function timestampToDate(uint timestamp) internal pure returns (uint year, uint month, uint day) { + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + } + function timestampToDateTime(uint timestamp) internal pure returns (uint year, uint month, uint day, uint hour, uint minute, uint second) { + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + uint secs = timestamp % SECONDS_PER_DAY; + hour = secs / SECONDS_PER_HOUR; + secs = secs % SECONDS_PER_HOUR; + minute = secs / SECONDS_PER_MINUTE; + second = secs % SECONDS_PER_MINUTE; + } + + function isValidDate(uint year, uint month, uint day) internal pure returns (bool valid) { + if (year >= 1970 && month > 0 && month <= 12) { + uint daysInMonth = _getDaysInMonth(year, month); + if (day > 0 && day <= daysInMonth) { + valid = true; + } + } + } + function isValidDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (bool valid) { + if (isValidDate(year, month, day)) { + if (hour < 24 && minute < 60 && second < 60) { + valid = true; + } + } + } + function isLeapYear(uint timestamp) internal pure returns (bool leapYear) { + uint year; + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + leapYear = _isLeapYear(year); + } + function _isLeapYear(uint year) internal pure returns (bool leapYear) { + leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); + } + function isWeekDay(uint timestamp) internal pure returns (bool weekDay) { + weekDay = getDayOfWeek(timestamp) <= DOW_FRI; + } + function isWeekEnd(uint timestamp) internal pure returns (bool weekEnd) { + weekEnd = getDayOfWeek(timestamp) >= DOW_SAT; + } + function getDaysInMonth(uint timestamp) internal pure returns (uint daysInMonth) { + uint year; + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + daysInMonth = _getDaysInMonth(year, month); + } + function _getDaysInMonth(uint year, uint month) internal pure returns (uint daysInMonth) { + if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) { + daysInMonth = 31; + } else if (month != 2) { + daysInMonth = 30; + } else { + daysInMonth = _isLeapYear(year) ? 29 : 28; + } + } + // 1 = Monday, 7 = Sunday + function getDayOfWeek(uint timestamp) internal pure returns (uint dayOfWeek) { + uint _days = timestamp / SECONDS_PER_DAY; + dayOfWeek = (_days + 3) % 7 + 1; + } + + function getYear(uint timestamp) internal pure returns (uint year) { + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + } + function getMonth(uint timestamp) internal pure returns (uint month) { + uint year; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + } + function getDay(uint timestamp) internal pure returns (uint day) { + uint year; + uint month; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + } + function getHour(uint timestamp) internal pure returns (uint hour) { + uint secs = timestamp % SECONDS_PER_DAY; + hour = secs / SECONDS_PER_HOUR; + } + function getMinute(uint timestamp) internal pure returns (uint minute) { + uint secs = timestamp % SECONDS_PER_HOUR; + minute = secs / SECONDS_PER_MINUTE; + } + function getSecond(uint timestamp) internal pure returns (uint second) { + second = timestamp % SECONDS_PER_MINUTE; + } + + function addYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) { + uint year; + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + year += _years; + uint daysInMonth = _getDaysInMonth(year, month); + if (day > daysInMonth) { + day = daysInMonth; + } + newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; + require(newTimestamp >= timestamp); + } + function addMonths(uint timestamp, uint _months) internal pure returns (uint newTimestamp) { + uint year; + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + month += _months; + year += (month - 1) / 12; + month = (month - 1) % 12 + 1; + uint daysInMonth = _getDaysInMonth(year, month); + if (day > daysInMonth) { + day = daysInMonth; + } + newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; + require(newTimestamp >= timestamp); + } + function addDays(uint timestamp, uint _days) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp + _days * SECONDS_PER_DAY; + require(newTimestamp >= timestamp); + } + function addHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp + _hours * SECONDS_PER_HOUR; + require(newTimestamp >= timestamp); + } + function addMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp + _minutes * SECONDS_PER_MINUTE; + require(newTimestamp >= timestamp); + } + function addSeconds(uint timestamp, uint _seconds) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp + _seconds; + require(newTimestamp >= timestamp); + } + + function subYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) { + uint year; + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + year -= _years; + uint daysInMonth = _getDaysInMonth(year, month); + if (day > daysInMonth) { + day = daysInMonth; + } + newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; + require(newTimestamp <= timestamp); + } + function subMonths(uint timestamp, uint _months) internal pure returns (uint newTimestamp) { + uint year; + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + uint yearMonth = year * 12 + (month - 1) - _months; + year = yearMonth / 12; + month = yearMonth % 12 + 1; + uint daysInMonth = _getDaysInMonth(year, month); + if (day > daysInMonth) { + day = daysInMonth; + } + newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; + require(newTimestamp <= timestamp); + } + function subDays(uint timestamp, uint _days) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp - _days * SECONDS_PER_DAY; + require(newTimestamp <= timestamp); + } + function subHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp - _hours * SECONDS_PER_HOUR; + require(newTimestamp <= timestamp); + } + function subMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp - _minutes * SECONDS_PER_MINUTE; + require(newTimestamp <= timestamp); + } + function subSeconds(uint timestamp, uint _seconds) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp - _seconds; + require(newTimestamp <= timestamp); + } + + function diffYears(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _years) { + require(fromTimestamp <= toTimestamp); + uint fromYear; + uint fromMonth; + uint fromDay; + uint toYear; + uint toMonth; + uint toDay; + (fromYear, fromMonth, fromDay) = _daysToDate(fromTimestamp / SECONDS_PER_DAY); + (toYear, toMonth, toDay) = _daysToDate(toTimestamp / SECONDS_PER_DAY); + _years = toYear - fromYear; + } + function diffMonths(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _months) { + require(fromTimestamp <= toTimestamp); + uint fromYear; + uint fromMonth; + uint fromDay; + uint toYear; + uint toMonth; + uint toDay; + (fromYear, fromMonth, fromDay) = _daysToDate(fromTimestamp / SECONDS_PER_DAY); + (toYear, toMonth, toDay) = _daysToDate(toTimestamp / SECONDS_PER_DAY); + _months = toYear * 12 + toMonth - fromYear * 12 - fromMonth; + } + function diffDays(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _days) { + require(fromTimestamp <= toTimestamp); + _days = (toTimestamp - fromTimestamp) / SECONDS_PER_DAY; + } + function diffHours(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _hours) { + require(fromTimestamp <= toTimestamp); + _hours = (toTimestamp - fromTimestamp) / SECONDS_PER_HOUR; + } + function diffMinutes(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _minutes) { + require(fromTimestamp <= toTimestamp); + _minutes = (toTimestamp - fromTimestamp) / SECONDS_PER_MINUTE; + } + function diffSeconds(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _seconds) { + require(fromTimestamp <= toTimestamp); + _seconds = toTimestamp - fromTimestamp; + } +} diff --git a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol b/contracts/modules/Checkpoint/ScheduledCheckpoint.sol new file mode 100644 index 000000000..21d614183 --- /dev/null +++ b/contracts/modules/Checkpoint/ScheduledCheckpoint.sol @@ -0,0 +1,59 @@ +pragma solidity ^0.4.24; + +import "./ICheckpoint.sol"; +import "../TransferManager/ITransferManager.sol"; +import "../Module.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 + */ +contract ScheduledCheckpoint is ICheckpoint, ITransferManager { + using SafeMath for uint256; + + struct Schedule { + bytes32 name; + uint256 startTime; + uint256 yearInterval; + uint256 monthInterval; + uint256 dayInterval; + } + + Schedule[] schedules; + + mapping (bytes32 => Schedule) public + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + */ + constructor (address _securityToken, address _polyAddress) public + Module(_securityToken, _polyAddress) + { + } + + /** + * @notice This function returns the signature of configure function + */ + function getInitFunction() public pure returns (bytes4) { + return bytes4(0); + } + + function addSchedule(bytes32 _name, uint256 _startTime, ) + + /// @notice Used to verify the transfer transaction according to the rule implemented in the trnasfer managers + function verifyTransfer(address /* _from */, address _to, uint256 /* _amount */, bool /* _isTransfer */) public returns(Result) { + return Result.NA; + } + + /** + * @notice Return the permissions flag that are associated with CountTransferManager + */ + function getPermissions() public view returns(bytes32[]) { + bytes32[] memory allPermissions = new bytes32[](0); + return allPermissions; + } +} diff --git a/contracts/modules/Checkpoint/ScheduledCheckpointFactory.sol b/contracts/modules/Checkpoint/ScheduledCheckpointFactory.sol new file mode 100644 index 000000000..87dfa61eb --- /dev/null +++ b/contracts/modules/Checkpoint/ScheduledCheckpointFactory.sol @@ -0,0 +1,102 @@ +pragma solidity ^0.4.24; + +import "./ScheduledCheckpoint.sol"; +import "../ModuleFactory.sol"; + +/** + * @title Factory for deploying EtherDividendCheckpoint module + */ +contract ScheduledCheckpointFactory is ModuleFactory { + + /** + * @notice Constructor + * @param _polyAddress Address of the polytoken + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _subscriptionCost Subscription cost of the module + */ + constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public + ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + { + version = "1.0.0"; + name = "ScheduledCheckpoint"; + title = "Schedule Checkpoints"; + description = "Allows you to schedule checkpoints in the future"; + compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + } + + /** + * @notice used to launch the Module with the help of factory + * @return address Contract address of the Module + */ + function deploy(bytes /* _data */) external returns(address) { + if(setupCost > 0) + require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom because of sufficent Allowance is not provided"); + address scheduledCheckpoint = new ScheduledCheckpoint(msg.sender, address(polyToken)); + emit GenerateModuleFromFactory(scheduledCheckpoint, getName(), address(this), msg.sender, setupCost, now); + return scheduledCheckpoint; + } + + /** + * @notice Type of the Module factory + */ + function getTypes() external view returns(uint8[]) { + uint8[] memory res = new uint8[](2); + res[0] = 4; + res[1] = 2; + return res; + } + + /** + * @notice Get the name of the Module + */ + function getName() public view returns(bytes32) { + return name; + } + + /** + * @notice Get the description of the Module + */ + function getDescription() external view returns(string) { + return description; + } + + /** + * @notice Get the title of the Module + */ + function getTitle() external view returns(string) { + return title; + } + + /** + * @notice Get the version of the Module + */ + function getVersion() external view returns(string) { + return version; + } + + /** + * @notice Get the setup cost of the module + */ + function getSetupCost() external view returns (uint256) { + return setupCost; + } + + /** + * @notice Get the Instructions that helped to used the module + */ + function getInstructions() external view returns(string) { + return "Schedule a series of future checkpoints by specifying a start time and interval of each checkpoint"; + } + + /** + * @notice Get the tags related to the module factory + */ + function getTags() external view returns(bytes32[]) { + bytes32[] memory availableTags = new bytes32[](2); + availableTags[0] = "Scheduled"; + availableTags[1] = "Checkpoint"; + return availableTags; + } +} From 11787317b3d831c94859fd1c742075d29782d4e7 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Thu, 4 Oct 2018 23:55:49 +0100 Subject: [PATCH 02/18] More WIP --- .../Checkpoint/ScheduledCheckpoint.sol | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol b/contracts/modules/Checkpoint/ScheduledCheckpoint.sol index 21d614183..d1ad6d358 100644 --- a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol +++ b/contracts/modules/Checkpoint/ScheduledCheckpoint.sol @@ -2,10 +2,9 @@ pragma solidity ^0.4.24; import "./ICheckpoint.sol"; import "../TransferManager/ITransferManager.sol"; -import "../Module.sol"; import "../../interfaces/ISecurityToken.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; -import "../../libraries/BokkyPooBahsDateTimeLibrary.sol"; +/* import "../../libraries/BokkyPooBahsDateTimeLibrary.sol"; */ /** * @title Burn module for burning tokens and keeping track of burnt amounts @@ -16,14 +15,16 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { struct Schedule { bytes32 name; uint256 startTime; - uint256 yearInterval; - uint256 monthInterval; - uint256 dayInterval; + uint256 nextTime; + uint256 interval; + uint256[] checkpointIds; + uint256[] timestamps; + uint256[] periods; } - Schedule[] schedules; + bytes32[] public names; - mapping (bytes32 => Schedule) public + mapping (bytes32 => Schedule) schedules; /** * @notice Constructor @@ -42,17 +43,41 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { return bytes4(0); } - function addSchedule(bytes32 _name, uint256 _startTime, ) + function addSchedule(bytes32 _name, uint256 _startTime, uint256 _interval) onlyOwner external { + // TODO: Ensure unique names + require(_startTime > now); + require(schedules[_name].name == bytes32(0)); + schedules[_name].name = _name; + schedules[_name].startTime = _startTime; + schedules[_name].nextTime = _startTime; + schedules[_name].interval = _interval; + names.push(_name); + } /// @notice Used to verify the transfer transaction according to the rule implemented in the trnasfer managers - function verifyTransfer(address /* _from */, address _to, uint256 /* _amount */, bool /* _isTransfer */) public returns(Result) { + function verifyTransfer(address /* _from */, address _to, uint256 /* _amount */, bytes /* _data */, bool _isTransfer) external returns(Result) { + if (!_isTransfer) { + return Result.NA; + } + uint256 i; + for (i = 0; i < names.length; i++) { + Schedule storage schedule = schedules[names[i]]; + if (schedule.nextTime <= now) { + uint256 checkpointId = ISecurityToken(securityToken).createCheckpoint(); + uint256 periods = now.sub(schedule.nextTime).div(schedule.interval).add(1); + schedule.nextTime = periods.mul(schedule.interval).add(schedule.nextTime); + schedule.checkpointIds.push(checkpointId); + schedule.timestamps.push(now); + schedule.periods.push(periods); + } + } return Result.NA; } /** * @notice Return the permissions flag that are associated with CountTransferManager */ - function getPermissions() public view returns(bytes32[]) { + function getPermissions() external view returns(bytes32[]) { bytes32[] memory allPermissions = new bytes32[](0); return allPermissions; } From dfe78fcc73f73ac8084a24756e074d5bd9666c3f Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Fri, 5 Oct 2018 00:03:17 +0100 Subject: [PATCH 03/18] Use a public function --- contracts/modules/Checkpoint/ScheduledCheckpoint.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol b/contracts/modules/Checkpoint/ScheduledCheckpoint.sol index d1ad6d358..f59b3ea50 100644 --- a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol +++ b/contracts/modules/Checkpoint/ScheduledCheckpoint.sol @@ -55,7 +55,7 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { } /// @notice Used to verify the transfer transaction according to the rule implemented in the trnasfer managers - function verifyTransfer(address /* _from */, address _to, uint256 /* _amount */, bytes /* _data */, bool _isTransfer) external returns(Result) { + function verifyTransfer(address /* _from */, address _to, uint256 /* _amount */, bytes /* _data */, bool _isTransfer) public returns(Result) { if (!_isTransfer) { return Result.NA; } From 0cf16c509d1d86abf95d2ff2521629f96f787a40 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Fri, 5 Oct 2018 00:03:42 +0100 Subject: [PATCH 04/18] Comment out to --- contracts/modules/Checkpoint/ScheduledCheckpoint.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol b/contracts/modules/Checkpoint/ScheduledCheckpoint.sol index f59b3ea50..ca87c1c97 100644 --- a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol +++ b/contracts/modules/Checkpoint/ScheduledCheckpoint.sol @@ -55,7 +55,7 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { } /// @notice Used to verify the transfer transaction according to the rule implemented in the trnasfer managers - function verifyTransfer(address /* _from */, address _to, uint256 /* _amount */, bytes /* _data */, bool _isTransfer) public returns(Result) { + function verifyTransfer(address /* _from */, address /* _to */, uint256 /* _amount */, bytes /* _data */, bool _isTransfer) public returns(Result) { if (!_isTransfer) { return Result.NA; } From 2834840ac4e92a42a4b1d6efb7ee3f67bd1af70b Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Tue, 9 Oct 2018 11:44:35 +0100 Subject: [PATCH 05/18] Wip --- .../Checkpoint/ScheduledCheckpoint.sol | 17 +- test/w_scheduled_checkpoints.js | 363 ++++++++++++++++++ 2 files changed, 378 insertions(+), 2 deletions(-) create mode 100644 test/w_scheduled_checkpoints.js diff --git a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol b/contracts/modules/Checkpoint/ScheduledCheckpoint.sol index ca87c1c97..ea2b10c7a 100644 --- a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol +++ b/contracts/modules/Checkpoint/ScheduledCheckpoint.sol @@ -17,6 +17,7 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { uint256 startTime; uint256 nextTime; uint256 interval; + uint256 index; uint256[] checkpointIds; uint256[] timestamps; uint256[] periods; @@ -44,19 +45,31 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { } function addSchedule(bytes32 _name, uint256 _startTime, uint256 _interval) onlyOwner external { - // TODO: Ensure unique names require(_startTime > now); require(schedules[_name].name == bytes32(0)); schedules[_name].name = _name; schedules[_name].startTime = _startTime; schedules[_name].nextTime = _startTime; schedules[_name].interval = _interval; + schedules[_name].index = names.length; names.push(_name); } + function removeSchedule(bytes32 _name) onlyOwner external { + require(schedules[_name].name == _name); + uint256 index = schedules[_name].index; + names[index] = names[names.length - 1]; + names.length--; + if (index != names.length) { + schedules[names[index]].index = index; + } + delete schedules[_name]; + } + + /// @notice Used to verify the transfer transaction according to the rule implemented in the trnasfer managers function verifyTransfer(address /* _from */, address /* _to */, uint256 /* _amount */, bytes /* _data */, bool _isTransfer) public returns(Result) { - if (!_isTransfer) { + if (!paused || !_isTransfer) { return Result.NA; } uint256 i; diff --git a/test/w_scheduled_checkpoints.js b/test/w_scheduled_checkpoints.js new file mode 100644 index 000000000..e68b382dd --- /dev/null +++ b/test/w_scheduled_checkpoints.js @@ -0,0 +1,363 @@ +import latestTime from './helpers/latestTime'; +import { duration, ensureException, promisifyLogWatch, latestBlock } from './helpers/utils'; +import takeSnapshot, { increaseTime, revertToSnapshot } from './helpers/time'; +import { encodeProxyCall, encodeModuleCall } from './helpers/encodeCall'; + +const PolymathRegistry = artifacts.require('./PolymathRegistry.sol') +const ModuleRegistry = artifacts.require('./ModuleRegistry.sol'); +const ModuleRegistryProxy = artifacts.require('./ModuleRegistryProxy.sol'); +const SecurityToken = artifacts.require('./SecurityToken.sol'); +const SecurityTokenRegistry = artifacts.require('./SecurityTokenRegistry.sol'); +const SecurityTokenRegistryProxy = artifacts.require('./SecurityTokenRegistryProxy.sol'); +const FeatureRegistry = artifacts.require('./FeatureRegistry.sol'); +const STFactory = artifacts.require('./STFactory.sol'); +const GeneralPermissionManagerFactory = artifacts.require('./GeneralPermissionManagerFactory.sol'); +const GeneralTransferManagerFactory = artifacts.require('./GeneralTransferManagerFactory.sol'); +const GeneralTransferManager = artifacts.require('./GeneralTransferManager'); +const ScheduledCheckpointFactory = artifacts.require('./ScheduledCheckpointFactory.sol'); +const ScheduledCheckpoint = artifacts.require('./ScheduledCheckpoint.sol'); +const GeneralPermissionManager = artifacts.require('./GeneralPermissionManager'); +const PolyTokenFaucet = artifacts.require('./PolyTokenFaucet.sol'); + +const Web3 = require('web3'); +const BigNumber = require('bignumber.js'); +const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")) // Hardcoded development port + +contract('ScheduledCheckpoint', accounts => { + + // Accounts Variable declaration + let account_polymath; + let account_issuer; + let token_owner; + let account_investor1; + let account_investor2; + let account_investor3; + let account_investor4; + + // investor Details + let fromTime = latestTime(); + let toTime = latestTime(); + let expiryTime = toTime + duration.days(15); + + let message = "Transaction Should Fail!"; + + // Contract Instance Declaration + let I_GeneralPermissionManagerFactory; + let I_SecurityTokenRegistryProxy; + let I_GeneralTransferManagerFactory; + let I_ScheduledCheckpointFactory; + let I_GeneralPermissionManager; + let I_ScheduledCheckpoint; + let I_GeneralTransferManager; + let I_ModuleRegistryProxy; + let I_ModuleRegistry; + let I_FeatureRegistry; + let I_SecurityTokenRegistry; + let I_STRProxied; + let I_MRProxied; + let I_STFactory; + let I_SecurityToken; + let I_PolyToken; + let I_PolymathRegistry; + + // SecurityToken Details + const name = "Team"; + const symbol = "sap"; + const tokenDetails = "This is equity type of issuance"; + const decimals = 18; + const contact = "team@polymath.network"; + + // Module key + const delegateManagerKey = 1; + const transferManagerKey = 2; + const stoKey = 3; + + // Initial fee for ticker registry and security token registry + const initRegFee = web3.utils.toWei("250"); + + const STRProxyParameters = ['address', 'address', 'uint256', 'uint256', 'address', 'address']; + const MRProxyParameters = ['address', 'address']; + + before(async() => { + // Accounts setup + account_polymath = accounts[0]; + account_issuer = accounts[1]; + + token_owner = account_issuer; + + account_investor1 = accounts[7]; + account_investor2 = accounts[8]; + account_investor3 = accounts[9]; + + // ----------- POLYMATH NETWORK Configuration ------------ + + // Step 0: Deploy the PolymathRegistry + I_PolymathRegistry = await PolymathRegistry.new({from: account_polymath}); + + // Step 1: Deploy the token Faucet and Mint tokens for token_owner + I_PolyToken = await PolyTokenFaucet.new(); + await I_PolyToken.getTokens((10000 * Math.pow(10, 18)), token_owner); + + // Step 2: Deploy the FeatureRegistry + + I_FeatureRegistry = await FeatureRegistry.new( + I_PolymathRegistry.address, + { + from: account_polymath + }); + + // STEP 3: Deploy the ModuleRegistry + + I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); + // Step 3 (b): Deploy the proxy and attach the implementation contract to it + I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); + let bytesMRProxy = encodeProxyCall(MRProxyParameters, [I_PolymathRegistry.address, account_polymath]); + await I_ModuleRegistryProxy.upgradeToAndCall("1.0.0", I_ModuleRegistry.address, bytesMRProxy, {from: account_polymath}); + I_MRProxied = await ModuleRegistry.at(I_ModuleRegistryProxy.address); + + // STEP 4(a): Deploy the GeneralTransferManagerFactory + + I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); + + assert.notEqual( + I_GeneralTransferManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "GeneralTransferManagerFactory contract was not deployed" + ); + + // STEP 4(b): Deploy the GeneralDelegateManagerFactory + + I_GeneralPermissionManagerFactory = await GeneralPermissionManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); + + assert.notEqual( + I_GeneralPermissionManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "GeneralDelegateManagerFactory contract was not deployed" + ); + + // STEP 4(c): Deploy the PercentageTransferManager + I_ScheduledCheckpointFactory = await ScheduledCheckpointFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); + assert.notEqual( + I_PercentageTransferManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "ScheduledCheckpointFactory contract was not deployed" + ); + + // Step 6: Deploy the STFactory contract + + I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, {from : account_polymath }); + + assert.notEqual( + I_STFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "STFactory contract was not deployed", + ); + + // Step 7: Deploy the SecurityTokenRegistry contract + + I_SecurityTokenRegistry = await SecurityTokenRegistry.new({from: account_polymath }); + + assert.notEqual( + I_SecurityTokenRegistry.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "SecurityTokenRegistry contract was not deployed", + ); + + // Step 8: Deploy the proxy and attach the implementation contract to it. + I_SecurityTokenRegistryProxy = await SecurityTokenRegistryProxy.new({from: account_polymath}); + let bytesProxy = encodeProxyCall(STRProxyParameters, [I_PolymathRegistry.address, I_STFactory.address, initRegFee, initRegFee, I_PolyToken.address, account_polymath]); + await I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, {from: account_polymath}); + I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); + + // Step 9: update the registries addresses from the PolymathRegistry contract + await I_PolymathRegistry.changeAddress("PolyToken", I_PolyToken.address, {from: account_polymath}) + await I_PolymathRegistry.changeAddress("ModuleRegistry", I_ModuleRegistryProxy.address, {from: account_polymath}); + await I_PolymathRegistry.changeAddress("FeatureRegistry", I_FeatureRegistry.address, {from: account_polymath}); + await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: account_polymath}); + await I_MRProxied.updateFromRegistry({from: account_polymath}); + + // STEP 5: Register the Modules with the ModuleRegistry contract + + // (A) : Register the GeneralTransferManagerFactory + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + + // (C) : Register the PercentageTransferManagerFactory + await I_MRProxied.registerModule(I_ScheduledCheckpointFactory.address, { from: account_polymath }); + await I_MRProxied.verifyModule(I_ScheduledCheckpointFactory.address, true, { from: account_polymath }); + + // Printing all the contract addresses + console.log(` + --------------------- Polymath Network Smart Contracts: --------------------- + PolymathRegistry: ${PolymathRegistry.address} + SecurityTokenRegistryProxy: ${SecurityTokenRegistryProxy.address} + SecurityTokenRegistry: ${SecurityTokenRegistry.address} + ModuleRegistry: ${ModuleRegistry.address} + ModuleRegistryProxy: ${ModuleRegistryProxy.address} + FeatureRegistry: ${FeatureRegistry.address} + + STFactory: ${STFactory.address} + GeneralTransferManagerFactory: ${GeneralTransferManagerFactory.address} + GeneralPermissionManagerFactory: ${GeneralPermissionManagerFactory.address} + + ScheduledCheckpointFactory: ${I_ScheduledCheckpointFactory.address} + ----------------------------------------------------------------------------- + `); + }); + + describe("Generate the SecurityToken", async() => { + + it("Should register the ticker before the generation of the security token", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { from : token_owner }); + assert.equal(tx.logs[0].args._owner, token_owner); + assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); + }); + + it("Should generate the new security token with the same symbol as registered above", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); + + // Verify the successful generation of the security token + assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + + I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + + const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({from: _blockNo}), 1); + + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._types[0].toNumber(), 2); + assert.equal( + web3.utils.toAscii(log.args._name) + .replace(/\u0000/g, ''), + "GeneralTransferManager" + ); + }); + + it("Should intialize the auto attached modules", async () => { + let moduleData = (await I_SecurityToken.getModulesByType(2))[0]; + I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + + }); + + }); + + describe("Buy tokens using on-chain whitelist", async() => { + + it("Should Buy the tokens", async() => { + // Add the Investor in to the whitelist + + let tx = await I_GeneralTransferManager.modifyWhitelist( + account_investor1, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, + { + from: account_issuer, + gas: 6000000 + }); + + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor1.toLowerCase(), "Failed in adding the investor in whitelist"); + + // Jump time + await increaseTime(5000); + + // Mint some tokens + await I_SecurityToken.mint(account_investor1, web3.utils.toWei('1', 'ether'), { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor1)).toNumber(), + web3.utils.toWei('1', 'ether') + ); + }); + + it("Should Buy some more tokens", async() => { + // Add the Investor in to the whitelist + + let tx = await I_GeneralTransferManager.modifyWhitelist( + account_investor2, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, + { + from: account_issuer, + gas: 6000000 + }); + + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor2.toLowerCase(), "Failed in adding the investor in whitelist"); + + // Mint some tokens + await I_SecurityToken.mint(account_investor2, web3.utils.toWei('1', 'ether'), { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor2)).toNumber(), + web3.utils.toWei('1', 'ether') + ); + }); + + it("Should successfully attach the ScheduledCheckpoint with the security token", async () => { + const tx = await I_SecurityToken.addModule(I_ScheduledCheckpointFactory.address, bytesSTO, 0, 0, { from: token_owner }); + assert.equal(tx.logs[2].args._types[0].toNumber(), 4, "ScheduledCheckpoint doesn't get deployed"); + assert.equal(tx.logs[2].args._types[0].toNumber(), 2, "ScheduledCheckpoint doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name) + .replace(/\u0000/g, ''), + "ScheduledCheckpoint", + "ScheduledCheckpoint module was not added" + ); + I_ScheduledCheckpoint = ScheduledCheckpoint.at(tx.logs[2].args._module); + }); + + it("Add a new token holder", async() => { + + let tx = await I_GeneralTransferManager.modifyWhitelist( + account_investor3, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, + { + from: account_issuer, + gas: 6000000 + }); + + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor3.toLowerCase(), "Failed in adding the investor in whitelist"); + + // Add the Investor in to the whitelist + // Mint some tokens + await I_SecurityToken.mint(account_investor3, web3.utils.toWei('1', 'ether'), { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor3)).toNumber(), + web3.utils.toWei('1', 'ether') + ); + }); + + it("Should get the permission", async() => { + let perm = await I_ScheduledCheckpoint.getPermissions.call(); + assert.equal(perm.length, 0); + }); + + }); + + describe("ScheduledCheckpoint Factory test cases", async() => { + + it("Should get the exact details of the factory", async() => { + assert.equal(await I_ScheduledCheckpointFactory.setupCost.call(),0); + assert.equal((await I_ScheduledCheckpointFactory.getTypes.call())[0],2); + assert.equal(web3.utils.toAscii(await I_ScheduledCheckpointFactory.getName.call()) + .replace(/\u0000/g, ''), + "ScheduledCheckpoint", + "Wrong Module added"); + }); + + }); + +}); From e2dc1e55c8274a36f127699c76d2387d08ec2259 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Tue, 9 Oct 2018 12:30:24 +0100 Subject: [PATCH 06/18] Add some test cases and fix --- .../Checkpoint/ScheduledCheckpoint.sol | 16 +++- contracts/tokens/SecurityToken.sol | 3 +- test/w_scheduled_checkpoints.js | 88 +++++++++++++------ 3 files changed, 76 insertions(+), 31 deletions(-) diff --git a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol b/contracts/modules/Checkpoint/ScheduledCheckpoint.sol index ea2b10c7a..f35e382e5 100644 --- a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol +++ b/contracts/modules/Checkpoint/ScheduledCheckpoint.sol @@ -69,7 +69,7 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { /// @notice Used to verify the transfer transaction according to the rule implemented in the trnasfer managers function verifyTransfer(address /* _from */, address /* _to */, uint256 /* _amount */, bytes /* _data */, bool _isTransfer) public returns(Result) { - if (!paused || !_isTransfer) { + if (paused || !_isTransfer) { return Result.NA; } uint256 i; @@ -78,15 +78,27 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { 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(now); schedule.periods.push(periods); } } return Result.NA; } + function getSchedule(bytes32 _name) view public returns(bytes32, uint256, uint256, uint256, uint256[], uint256[], uint256[]){ + return ( + schedules[_name].name, + schedules[_name].startTime, + schedules[_name].nextTime, + schedules[_name].interval, + schedules[_name].checkpointIds, + schedules[_name].timestamps, + schedules[_name].periods + ); + } + /** * @notice Return the permissions flag that are associated with CountTransferManager */ diff --git a/contracts/tokens/SecurityToken.sol b/contracts/tokens/SecurityToken.sol index 2b021c372..4a01a4187 100644 --- a/contracts/tokens/SecurityToken.sol +++ b/contracts/tokens/SecurityToken.sol @@ -506,10 +506,11 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr } function _updateTransfer(address _from, address _to, uint256 _value, bytes _data) internal returns(bool) { + bool verified = _verifyTransfer(_from, _to, _value, _data, true); _adjustInvestorCount(_from, _to, _value); _adjustBalanceCheckpoints(_from); _adjustBalanceCheckpoints(_to); - return _verifyTransfer(_from, _to, _value, _data, true); + return verified; } /** diff --git a/test/w_scheduled_checkpoints.js b/test/w_scheduled_checkpoints.js index e68b382dd..8cf39e3dc 100644 --- a/test/w_scheduled_checkpoints.js +++ b/test/w_scheduled_checkpoints.js @@ -138,7 +138,7 @@ contract('ScheduledCheckpoint', accounts => { // STEP 4(c): Deploy the PercentageTransferManager I_ScheduledCheckpointFactory = await ScheduledCheckpointFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); assert.notEqual( - I_PercentageTransferManagerFactory.address.valueOf(), + I_ScheduledCheckpointFactory.address.valueOf(), "0x0000000000000000000000000000000000000000", "ScheduledCheckpointFactory contract was not deployed" ); @@ -249,7 +249,25 @@ contract('ScheduledCheckpoint', accounts => { describe("Buy tokens using on-chain whitelist", async() => { - it("Should Buy the tokens", async() => { + it("Should successfully attach the ScheduledCheckpoint with the security token", async () => { + const tx = await I_SecurityToken.addModule(I_ScheduledCheckpointFactory.address, "", 0, 0, { from: token_owner }); + assert.equal(tx.logs[2].args._types[0].toNumber(), 4, "ScheduledCheckpoint doesn't get deployed"); + assert.equal(tx.logs[2].args._types[1].toNumber(), 2, "ScheduledCheckpoint doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name) + .replace(/\u0000/g, ''), + "ScheduledCheckpoint", + "ScheduledCheckpoint module was not added" + ); + I_ScheduledCheckpoint = ScheduledCheckpoint.at(tx.logs[2].args._module); + }); + let startTime = latestTime() + 100; + let interval = 24 * 60 * 60; + it("Should create a daily checkpoint", async () => { + await I_ScheduledCheckpoint.addSchedule("CP1", startTime, interval, {from: token_owner}); + }); + + it("Should Buy the tokens for account_investor1", async() => { // Add the Investor in to the whitelist let tx = await I_GeneralTransferManager.modifyWhitelist( @@ -267,6 +285,9 @@ contract('ScheduledCheckpoint', accounts => { // Jump time await increaseTime(5000); + // We should be after the first scheduled checkpoint, and before the second + assert.isTrue(latestTime() > startTime); + assert.isTrue(latestTime() <= startTime + interval); // Mint some tokens await I_SecurityToken.mint(account_investor1, web3.utils.toWei('1', 'ether'), { from: token_owner }); @@ -277,7 +298,14 @@ contract('ScheduledCheckpoint', accounts => { ); }); - it("Should Buy some more tokens", async() => { + 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]); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 0)).toNumber(), 0); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 1)).toNumber(), 0); + }); + + it("Should Buy some more tokens for account_investor2", async() => { // Add the Investor in to the whitelist let tx = await I_GeneralTransferManager.modifyWhitelist( @@ -293,6 +321,10 @@ contract('ScheduledCheckpoint', accounts => { assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor2.toLowerCase(), "Failed in adding the investor in whitelist"); + // We should be after the first scheduled checkpoint, and before the second + assert.isTrue(latestTime() > startTime); + assert.isTrue(latestTime() <= startTime + interval); + // Mint some tokens await I_SecurityToken.mint(account_investor2, web3.utils.toWei('1', 'ether'), { from: token_owner }); @@ -302,20 +334,14 @@ contract('ScheduledCheckpoint', accounts => { ); }); - it("Should successfully attach the ScheduledCheckpoint with the security token", async () => { - const tx = await I_SecurityToken.addModule(I_ScheduledCheckpointFactory.address, bytesSTO, 0, 0, { from: token_owner }); - assert.equal(tx.logs[2].args._types[0].toNumber(), 4, "ScheduledCheckpoint doesn't get deployed"); - assert.equal(tx.logs[2].args._types[0].toNumber(), 2, "ScheduledCheckpoint doesn't get deployed"); - assert.equal( - web3.utils.toAscii(tx.logs[2].args._name) - .replace(/\u0000/g, ''), - "ScheduledCheckpoint", - "ScheduledCheckpoint module was not added" - ); - I_ScheduledCheckpoint = ScheduledCheckpoint.at(tx.logs[2].args._module); + it("No additional checkpoints created", async() => { + let cp1 = await I_ScheduledCheckpoint.getSchedule("CP1"); + checkSchedule(cp1, "CP1", startTime, startTime + interval, interval, [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); }); - it("Add a new token holder", async() => { + it("Add a new token holder - account_investor3", async() => { let tx = await I_GeneralTransferManager.modifyWhitelist( account_investor3, @@ -347,17 +373,23 @@ contract('ScheduledCheckpoint', accounts => { }); - describe("ScheduledCheckpoint Factory test cases", async() => { - - it("Should get the exact details of the factory", async() => { - assert.equal(await I_ScheduledCheckpointFactory.setupCost.call(),0); - assert.equal((await I_ScheduledCheckpointFactory.getTypes.call())[0],2); - assert.equal(web3.utils.toAscii(await I_ScheduledCheckpointFactory.getName.call()) - .replace(/\u0000/g, ''), - "ScheduledCheckpoint", - "Wrong Module added"); - }); - - }); - }); + +function checkSchedule(schedule, name, startTime, nextTime, interval, checkpoints, timestamps, periods) { + assert.equal(web3.utils.toAscii(schedule[0]).replace(/\u0000/g, ''), name); + assert.equal(schedule[1], startTime); + assert.equal(schedule[2], nextTime); + assert.equal(schedule[3], interval); + assert.equal(schedule[4].length, checkpoints.length); + for (let i = 0; i < checkpoints.length; i++) { + assert.equal(schedule[4][i].toNumber(), checkpoints[i]); + } + assert.equal(schedule[5].length, timestamps.length); + for (let i = 0; i < timestamps.length; i++) { + assert.equal(schedule[5][i].toNumber(), timestamps[i]); + } + assert.equal(schedule[6].length, periods.length); + for (let i = 0; i < periods.length; i++) { + assert.equal(schedule[6][i].toNumber(), periods[i]); + } +} From 54b992919ebac6b96f773e52267adaa4038ae279 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Tue, 9 Oct 2018 16:19:10 +0100 Subject: [PATCH 07/18] Fix ordering. --- contracts/tokens/SecurityToken.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/tokens/SecurityToken.sol b/contracts/tokens/SecurityToken.sol index 4a01a4187..75ceecd37 100644 --- a/contracts/tokens/SecurityToken.sol +++ b/contracts/tokens/SecurityToken.sol @@ -505,9 +505,9 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr return true; } - function _updateTransfer(address _from, address _to, uint256 _value, bytes _data) internal returns(bool) { - bool verified = _verifyTransfer(_from, _to, _value, _data, true); + function _updateTransfer(address _from, address _to, uint256 _value, bytes _data) internal nonReentrant returns(bool) { _adjustInvestorCount(_from, _to, _value); + bool verified = _verifyTransfer(_from, _to, _value, _data, true); _adjustBalanceCheckpoints(_from); _adjustBalanceCheckpoints(_to); return verified; From 3c97e8abae046d2cf9840c74fbf53321530e6c31 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Tue, 9 Oct 2018 18:31:22 +0100 Subject: [PATCH 08/18] More test cases --- .../Checkpoint/ScheduledCheckpoint.sol | 41 ++++++--- contracts/tokens/SecurityToken.sol | 7 ++ test/w_scheduled_checkpoints.js | 85 +++++++++++++++++-- 3 files changed, 116 insertions(+), 17 deletions(-) diff --git a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol b/contracts/modules/Checkpoint/ScheduledCheckpoint.sol index f35e382e5..3d6002cfe 100644 --- a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol +++ b/contracts/modules/Checkpoint/ScheduledCheckpoint.sol @@ -69,21 +69,11 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { /// @notice Used to verify the transfer transaction according to the rule implemented in the trnasfer managers function verifyTransfer(address /* _from */, address /* _to */, uint256 /* _amount */, bytes /* _data */, bool _isTransfer) public returns(Result) { + require(_isTransfer == false || msg.sender == securityToken, "Sender is not owner"); if (paused || !_isTransfer) { return Result.NA; } - uint256 i; - for (i = 0; i < names.length; i++) { - Schedule storage schedule = schedules[names[i]]; - 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.periods.push(periods); - } - } + _updateAll(); return Result.NA; } @@ -99,6 +89,33 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { ); } + function update(bytes32 _name) public onlyOwner { + _update(_name); + } + + 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.periods.push(periods); + } + } + + function updateAll() public onlyOwner { + _updateAll(); + } + + function _updateAll() internal { + uint256 i; + for (i = 0; i < names.length; i++) { + _update(names[i]); + } + } + /** * @notice Return the permissions flag that are associated with CountTransferManager */ diff --git a/contracts/tokens/SecurityToken.sol b/contracts/tokens/SecurityToken.sol index 75ceecd37..a58cd173e 100644 --- a/contracts/tokens/SecurityToken.sol +++ b/contracts/tokens/SecurityToken.sol @@ -506,6 +506,13 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr } function _updateTransfer(address _from, address _to, uint256 _value, bytes _data) internal nonReentrant returns(bool) { + // NB - the ordering in this function implies the following: + // - investor counts are updated before transfer managers are called - i.e. transfer managers will eee + //investor counts including the current transfer. + // - checkpoints are updated after the transfer managers are called. This allows TMs to create + //checkpoints as though they have been created before the current transactions, + // - to avoid the situation where a transfer manager transfers tokens, and this function is called recursively, + //the function is marked as nonReentrant. This means that no TM can transfer (or mint / burn) tokens. _adjustInvestorCount(_from, _to, _value); bool verified = _verifyTransfer(_from, _to, _value, _data, true); _adjustBalanceCheckpoints(_from); diff --git a/test/w_scheduled_checkpoints.js b/test/w_scheduled_checkpoints.js index 8cf39e3dc..c9fcbfeb1 100644 --- a/test/w_scheduled_checkpoints.js +++ b/test/w_scheduled_checkpoints.js @@ -250,6 +250,7 @@ contract('ScheduledCheckpoint', accounts => { describe("Buy tokens using on-chain whitelist", async() => { it("Should successfully attach the ScheduledCheckpoint with the security token", async () => { + await I_SecurityToken.changeGranularity(1, {from: token_owner}); const tx = await I_SecurityToken.addModule(I_ScheduledCheckpointFactory.address, "", 0, 0, { from: token_owner }); assert.equal(tx.logs[2].args._types[0].toNumber(), 4, "ScheduledCheckpoint doesn't get deployed"); assert.equal(tx.logs[2].args._types[1].toNumber(), 2, "ScheduledCheckpoint doesn't get deployed"); @@ -261,9 +262,11 @@ contract('ScheduledCheckpoint', accounts => { ); I_ScheduledCheckpoint = ScheduledCheckpoint.at(tx.logs[2].args._module); }); + let startTime = latestTime() + 100; let interval = 24 * 60 * 60; it("Should create a daily checkpoint", async () => { + console.log("Creating scheduled CP: " + startTime, interval); await I_ScheduledCheckpoint.addSchedule("CP1", startTime, interval, {from: token_owner}); }); @@ -337,8 +340,8 @@ 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]); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 0)).toNumber(), 0); - assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 1)).toNumber(), 0); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 0)).toNumber(), 0); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 1)).toNumber(), 0); }); it("Add a new token holder - account_investor3", async() => { @@ -356,6 +359,12 @@ contract('ScheduledCheckpoint', accounts => { assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor3.toLowerCase(), "Failed in adding the investor in whitelist"); + // Jump time + await increaseTime(interval); + // We should be after the first scheduled checkpoint, and before the second + assert.isTrue(latestTime() > startTime + interval); + assert.isTrue(latestTime() <= startTime + (2 * interval)); + // Add the Investor in to the whitelist // Mint some tokens await I_SecurityToken.mint(account_investor3, web3.utils.toWei('1', 'ether'), { from: token_owner }); @@ -366,6 +375,72 @@ 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]); + 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); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 0)).toNumber(), 0); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 1)).toNumber(), 0); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 2)).toNumber(), web3.utils.toWei('1', 'ether')); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 0)).toNumber(), 0); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 1)).toNumber(), 0); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 2)).toNumber(), web3.utils.toWei('1', 'ether')); + }); + + it("Should have correct balances for investor 3 after new checkpoint", async() => { + // Jump time + await increaseTime(2 * interval); + // We should be after the first scheduled checkpoint, and before the second + assert.isTrue(latestTime() > startTime + (3 * interval)); + 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]); + 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); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 3)).toNumber(), web3.utils.toWei('1', 'ether')); + + assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 0)).toNumber(), 0); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 1)).toNumber(), 0); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 2)).toNumber(), web3.utils.toWei('1', 'ether')); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 3)).toNumber(), web3.utils.toWei('1', 'ether')); + + assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 0)).toNumber(), 0); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 1)).toNumber(), 0); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 2)).toNumber(), web3.utils.toWei('1', 'ether')); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 3)).toNumber(), web3.utils.toWei('1', 'ether')); + + }); + + it("Manually update checkpoints", async() => { + await increaseTime(interval); + 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]); + 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); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 3)).toNumber(), web3.utils.toWei('1', 'ether')); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor3, 4)).toNumber(), web3.utils.toWei('1.5', 'ether')); + + assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 0)).toNumber(), 0); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 1)).toNumber(), 0); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 2)).toNumber(), web3.utils.toWei('1', 'ether')); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 3)).toNumber(), web3.utils.toWei('1', 'ether')); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor2, 4)).toNumber(), web3.utils.toWei('1', 'ether')); + + assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 0)).toNumber(), 0); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 1)).toNumber(), 0); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 2)).toNumber(), web3.utils.toWei('1', 'ether')); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 3)).toNumber(), web3.utils.toWei('1', 'ether')); + assert.equal((await I_SecurityToken.balanceOfAt(account_investor1, 4)).toNumber(), web3.utils.toWei('0.5', 'ether')); + + }); + it("Should get the permission", async() => { let perm = await I_ScheduledCheckpoint.getPermissions.call(); assert.equal(perm.length, 0); @@ -377,9 +452,9 @@ contract('ScheduledCheckpoint', accounts => { function checkSchedule(schedule, name, startTime, nextTime, interval, checkpoints, timestamps, periods) { assert.equal(web3.utils.toAscii(schedule[0]).replace(/\u0000/g, ''), name); - assert.equal(schedule[1], startTime); - assert.equal(schedule[2], nextTime); - assert.equal(schedule[3], interval); + 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); for (let i = 0; i < checkpoints.length; i++) { assert.equal(schedule[4][i].toNumber(), checkpoints[i]); From a03f27d07c7c552c4cb2966b207112fe9fd58294 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Tue, 9 Oct 2018 19:03:57 +0100 Subject: [PATCH 09/18] Add natspec commitments --- .../Checkpoint/ScheduledCheckpoint.sol | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol b/contracts/modules/Checkpoint/ScheduledCheckpoint.sol index 3d6002cfe..bcca1f07c 100644 --- a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol +++ b/contracts/modules/Checkpoint/ScheduledCheckpoint.sol @@ -44,6 +44,12 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { return bytes4(0); } + /** + * @notice adds a new schedule for checkpoints + * @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 + */ function addSchedule(bytes32 _name, uint256 _startTime, uint256 _interval) onlyOwner external { require(_startTime > now); require(schedules[_name].name == bytes32(0)); @@ -55,6 +61,10 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { names.push(_name); } + /** + * @notice removes a schedule for checkpoints + * @param _name name of the schedule to be removed + */ function removeSchedule(bytes32 _name) onlyOwner external { require(schedules[_name].name == _name); uint256 index = schedules[_name].index; @@ -67,7 +77,11 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { } - /// @notice Used to verify the transfer transaction according to the rule implemented in the trnasfer managers + /** + * @notice Used to create checkpoints that correctly reflect balances + * @param _isTransfer whether or not an actual transfer is occuring + * @return always returns Result.NA + */ function verifyTransfer(address /* _from */, address /* _to */, uint256 /* _amount */, bytes /* _data */, bool _isTransfer) public returns(Result) { require(_isTransfer == false || msg.sender == securityToken, "Sender is not owner"); if (paused || !_isTransfer) { @@ -77,7 +91,11 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { return Result.NA; } - function getSchedule(bytes32 _name) view public returns(bytes32, uint256, uint256, uint256, uint256[], uint256[], uint256[]){ + /** + * @notice gets schedule details + * @param _name name of the schedule + */ + function getSchedule(bytes32 _name) view external returns(bytes32, uint256, uint256, uint256, uint256[], uint256[], uint256[]) { return ( schedules[_name].name, schedules[_name].startTime, @@ -89,7 +107,11 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { ); } - function update(bytes32 _name) public onlyOwner { + /** + * @notice manually triggers update outside of transfer request for named schedule (can be used to reduce user gas costs) + * @param _name name of the schedule + */ + function update(bytes32 _name) onlyOwner external { _update(_name); } @@ -105,7 +127,10 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { } } - function updateAll() public onlyOwner { + /** + * @notice manually triggers update outside of transfer request for all schedules (can be used to reduce user gas costs) + */ + function updateAll() onlyOwner external { _updateAll(); } @@ -119,7 +144,7 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { /** * @notice Return the permissions flag that are associated with CountTransferManager */ - function getPermissions() external view returns(bytes32[]) { + function getPermissions() view external returns(bytes32[]) { bytes32[] memory allPermissions = new bytes32[](0); return allPermissions; } From 1bdfb392ab2c602f9d6d4ae1ef5012d9126123db Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Tue, 9 Oct 2018 19:16:12 +0100 Subject: [PATCH 10/18] Remove some revert messages --- contracts/tokens/SecurityToken.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/tokens/SecurityToken.sol b/contracts/tokens/SecurityToken.sol index ad134c450..a79a6c072 100644 --- a/contracts/tokens/SecurityToken.sol +++ b/contracts/tokens/SecurityToken.sol @@ -730,7 +730,7 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr * @param _controller address of the controller */ function setController(address _controller) public onlyOwner { - require(!controllerDisabled,"Controller functions are disabled"); + require(!controllerDisabled); emit SetController(controller, _controller); controller = _controller; } @@ -740,7 +740,7 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr * @dev enabled via feature switch "disableControllerAllowed" */ function disableController() external isEnabled("disableControllerAllowed") onlyOwner { - require(!controllerDisabled,"Controller functions are disabled"); + require(!controllerDisabled); controllerDisabled = true; delete controller; emit DisableController(now); From c3a6e1fe0c0a9d5ac984180716860b0b50c59ac3 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Tue, 9 Oct 2018 19:17:58 +0100 Subject: [PATCH 11/18] Follow best practice for function modifiers --- .../modules/Checkpoint/ScheduledCheckpoint.sol | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol b/contracts/modules/Checkpoint/ScheduledCheckpoint.sol index bcca1f07c..fac853cb6 100644 --- a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol +++ b/contracts/modules/Checkpoint/ScheduledCheckpoint.sol @@ -4,7 +4,6 @@ import "./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 @@ -50,9 +49,9 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { * @param _startTime start time of the schedule (first checkpoint) * @param _interval interval at which checkpoints should be created */ - function addSchedule(bytes32 _name, uint256 _startTime, uint256 _interval) onlyOwner external { - require(_startTime > now); - require(schedules[_name].name == bytes32(0)); + function addSchedule(bytes32 _name, uint256 _startTime, uint256 _interval) 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; @@ -65,8 +64,8 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { * @notice removes a schedule for checkpoints * @param _name name of the schedule to be removed */ - function removeSchedule(bytes32 _name) onlyOwner external { - require(schedules[_name].name == _name); + function removeSchedule(bytes32 _name) external onlyOwner { + require(schedules[_name].name == _name, "Name does not exist"); uint256 index = schedules[_name].index; names[index] = names[names.length - 1]; names.length--; @@ -111,7 +110,7 @@ contract ScheduledCheckpoint is ICheckpoint, ITransferManager { * @notice manually triggers update outside of transfer request for named schedule (can be used to reduce user gas costs) * @param _name name of the schedule */ - function update(bytes32 _name) onlyOwner external { + function update(bytes32 _name) external onlyOwner { _update(_name); } From d8eb6992c6e3d936f2aaaa5b5a89138859f48443 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Wed, 10 Oct 2018 10:46:40 +0100 Subject: [PATCH 12/18] Change timing in test cases --- test/w_scheduled_checkpoints.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/test/w_scheduled_checkpoints.js b/test/w_scheduled_checkpoints.js index c9fcbfeb1..8e98fc1b5 100644 --- a/test/w_scheduled_checkpoints.js +++ b/test/w_scheduled_checkpoints.js @@ -263,15 +263,20 @@ contract('ScheduledCheckpoint', accounts => { I_ScheduledCheckpoint = ScheduledCheckpoint.at(tx.logs[2].args._module); }); - let startTime = latestTime() + 100; - let interval = 24 * 60 * 60; + let startTime; + let interval; it("Should create a daily checkpoint", async () => { + console.log("1: " + latestTime()); + startTime = latestTime() + 100; + interval = 24 * 60 * 60; console.log("Creating scheduled CP: " + startTime, interval); await I_ScheduledCheckpoint.addSchedule("CP1", startTime, interval, {from: token_owner}); + console.log("2: " + latestTime()); }); it("Should Buy the tokens for account_investor1", async() => { // Add the Investor in to the whitelist + console.log("3: " + latestTime()); let tx = await I_GeneralTransferManager.modifyWhitelist( account_investor1, @@ -287,10 +292,15 @@ contract('ScheduledCheckpoint', accounts => { assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor1.toLowerCase(), "Failed in adding the investor in whitelist"); // Jump time + console.log("4: " + latestTime()); + await increaseTime(5000); // We should be after the first scheduled checkpoint, and before the second + console.log("5: " + latestTime()); + assert.isTrue(latestTime() > startTime); assert.isTrue(latestTime() <= startTime + interval); + console.log("6: " + latestTime()); // Mint some tokens await I_SecurityToken.mint(account_investor1, web3.utils.toWei('1', 'ether'), { from: token_owner }); From 7e7b9bbdcdf771790e6b98c2315a702e0ff78e8d Mon Sep 17 00:00:00 2001 From: satyam Date: Wed, 10 Oct 2018 18:54:36 +0530 Subject: [PATCH 13/18] use the createInstance helper --- test/helpers/createInstances.js | 14 ++ ...ckpoints.js => y_scheduled_checkpoints.js} | 162 ++++-------------- 2 files changed, 47 insertions(+), 129 deletions(-) rename test/{w_scheduled_checkpoints.js => y_scheduled_checkpoints.js} (70%) diff --git a/test/helpers/createInstances.js b/test/helpers/createInstances.js index ff2a66f4b..bb5a15566 100644 --- a/test/helpers/createInstances.js +++ b/test/helpers/createInstances.js @@ -14,6 +14,7 @@ const ManualApprovalTransferManagerFactory = artifacts.require("./ManualApproval const SingleTradeVolumeRestrictionManagerFactory = artifacts.require('./SingleTradeVolumeRestrictionManagerFactory.sol'); const TrackedRedemptionFactory = artifacts.require("./TrackedRedemptionFactory.sol"); const PercentageTransferManagerFactory = artifacts.require("./PercentageTransferManagerFactory.sol"); +const ScheduledCheckpointFactory = artifacts.require('./ScheduledCheckpointFactory.sol'); const USDTieredSTOFactory = artifacts.require("./USDTieredSTOFactory.sol"); const USDTieredSTOProxyFactory = artifacts.require("./USDTieredSTOProxyFactory"); const ManualApprovalTransferManager = artifacts.require("./ManualApprovalTransferManager"); @@ -35,6 +36,7 @@ const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); let I_USDTieredSTOProxyFactory; let I_USDTieredSTOFactory; let I_TrackedRedemptionFactory; +let I_ScheduledCheckpointFactory; let I_SingleTradeVolumeRestrictionManagerFactory; let I_ManualApprovalTransferManagerFactory; let I_VolumeRestrictionTransferManagerFactory; @@ -261,6 +263,18 @@ export async function deploySingleTradeVolumeRMAndVerified(accountPolymath, MRPr return new Array(I_SingleTradeVolumeRestrictionManagerFactory); } +export async function deployScheduleCheckpointAndVerified(accountPolymath, MRProxyInstance, polyToken, setupCost) { + I_ScheduledCheckpointFactory = await ScheduledCheckpointFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); + assert.notEqual( + I_ScheduledCheckpointFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "ScheduledCheckpointFactory contract was not deployed" + ); + + await registerAndVerifyByMR(I_ScheduledCheckpointFactory.address, accountPolymath, MRProxyInstance); + return new Array(I_ScheduledCheckpointFactory); +} + /// Deploy the Permission Manager export async function deployGPMAndVerifyed(accountPolymath, MRProxyInstance, polyToken, setupCost) { diff --git a/test/w_scheduled_checkpoints.js b/test/y_scheduled_checkpoints.js similarity index 70% rename from test/w_scheduled_checkpoints.js rename to test/y_scheduled_checkpoints.js index 8e98fc1b5..d81d8c3f6 100644 --- a/test/w_scheduled_checkpoints.js +++ b/test/y_scheduled_checkpoints.js @@ -1,23 +1,13 @@ import latestTime from './helpers/latestTime'; -import { duration, ensureException, promisifyLogWatch, latestBlock } from './helpers/utils'; +import { duration, promisifyLogWatch, latestBlock } from './helpers/utils'; import takeSnapshot, { increaseTime, revertToSnapshot } from './helpers/time'; import { encodeProxyCall, encodeModuleCall } from './helpers/encodeCall'; +import { setUpPolymathNetwork, deployScheduleCheckpointAndVerified } from "./helpers/createInstances"; -const PolymathRegistry = artifacts.require('./PolymathRegistry.sol') -const ModuleRegistry = artifacts.require('./ModuleRegistry.sol'); -const ModuleRegistryProxy = artifacts.require('./ModuleRegistryProxy.sol'); const SecurityToken = artifacts.require('./SecurityToken.sol'); -const SecurityTokenRegistry = artifacts.require('./SecurityTokenRegistry.sol'); -const SecurityTokenRegistryProxy = artifacts.require('./SecurityTokenRegistryProxy.sol'); -const FeatureRegistry = artifacts.require('./FeatureRegistry.sol'); -const STFactory = artifacts.require('./STFactory.sol'); -const GeneralPermissionManagerFactory = artifacts.require('./GeneralPermissionManagerFactory.sol'); -const GeneralTransferManagerFactory = artifacts.require('./GeneralTransferManagerFactory.sol'); const GeneralTransferManager = artifacts.require('./GeneralTransferManager'); -const ScheduledCheckpointFactory = artifacts.require('./ScheduledCheckpointFactory.sol'); const ScheduledCheckpoint = artifacts.require('./ScheduledCheckpoint.sol'); -const GeneralPermissionManager = artifacts.require('./GeneralPermissionManager'); -const PolyTokenFaucet = artifacts.require('./PolyTokenFaucet.sol'); + const Web3 = require('web3'); const BigNumber = require('bignumber.js'); @@ -42,7 +32,6 @@ contract('ScheduledCheckpoint', accounts => { let message = "Transaction Should Fail!"; // Contract Instance Declaration - let I_GeneralPermissionManagerFactory; let I_SecurityTokenRegistryProxy; let I_GeneralTransferManagerFactory; let I_ScheduledCheckpointFactory; @@ -75,9 +64,6 @@ contract('ScheduledCheckpoint', accounts => { // Initial fee for ticker registry and security token registry const initRegFee = web3.utils.toWei("250"); - const STRProxyParameters = ['address', 'address', 'uint256', 'uint256', 'address', 'address']; - const MRProxyParameters = ['address', 'address']; - before(async() => { // Accounts setup account_polymath = accounts[0]; @@ -89,122 +75,40 @@ contract('ScheduledCheckpoint', accounts => { account_investor2 = accounts[8]; account_investor3 = accounts[9]; - // ----------- POLYMATH NETWORK Configuration ------------ - - // Step 0: Deploy the PolymathRegistry - I_PolymathRegistry = await PolymathRegistry.new({from: account_polymath}); - - // Step 1: Deploy the token Faucet and Mint tokens for token_owner - I_PolyToken = await PolyTokenFaucet.new(); - await I_PolyToken.getTokens((10000 * Math.pow(10, 18)), token_owner); - - // Step 2: Deploy the FeatureRegistry - - I_FeatureRegistry = await FeatureRegistry.new( - I_PolymathRegistry.address, - { - from: account_polymath - }); - - // STEP 3: Deploy the ModuleRegistry - - I_ModuleRegistry = await ModuleRegistry.new({from:account_polymath}); - // Step 3 (b): Deploy the proxy and attach the implementation contract to it - I_ModuleRegistryProxy = await ModuleRegistryProxy.new({from:account_polymath}); - let bytesMRProxy = encodeProxyCall(MRProxyParameters, [I_PolymathRegistry.address, account_polymath]); - await I_ModuleRegistryProxy.upgradeToAndCall("1.0.0", I_ModuleRegistry.address, bytesMRProxy, {from: account_polymath}); - I_MRProxied = await ModuleRegistry.at(I_ModuleRegistryProxy.address); - - // STEP 4(a): Deploy the GeneralTransferManagerFactory - - I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); - - assert.notEqual( - I_GeneralTransferManagerFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "GeneralTransferManagerFactory contract was not deployed" - ); - - // STEP 4(b): Deploy the GeneralDelegateManagerFactory - - I_GeneralPermissionManagerFactory = await GeneralPermissionManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); - - assert.notEqual( - I_GeneralPermissionManagerFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "GeneralDelegateManagerFactory contract was not deployed" - ); - - // STEP 4(c): Deploy the PercentageTransferManager - I_ScheduledCheckpointFactory = await ScheduledCheckpointFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); - assert.notEqual( - I_ScheduledCheckpointFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "ScheduledCheckpointFactory contract was not deployed" - ); - - // Step 6: Deploy the STFactory contract - - I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, {from : account_polymath }); - - assert.notEqual( - I_STFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "STFactory contract was not deployed", - ); - - // Step 7: Deploy the SecurityTokenRegistry contract - - I_SecurityTokenRegistry = await SecurityTokenRegistry.new({from: account_polymath }); - - assert.notEqual( - I_SecurityTokenRegistry.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "SecurityTokenRegistry contract was not deployed", - ); - - // Step 8: Deploy the proxy and attach the implementation contract to it. - I_SecurityTokenRegistryProxy = await SecurityTokenRegistryProxy.new({from: account_polymath}); - let bytesProxy = encodeProxyCall(STRProxyParameters, [I_PolymathRegistry.address, I_STFactory.address, initRegFee, initRegFee, I_PolyToken.address, account_polymath]); - await I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, {from: account_polymath}); - I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); - - // Step 9: update the registries addresses from the PolymathRegistry contract - await I_PolymathRegistry.changeAddress("PolyToken", I_PolyToken.address, {from: account_polymath}) - await I_PolymathRegistry.changeAddress("ModuleRegistry", I_ModuleRegistryProxy.address, {from: account_polymath}); - await I_PolymathRegistry.changeAddress("FeatureRegistry", I_FeatureRegistry.address, {from: account_polymath}); - await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, {from: account_polymath}); - await I_MRProxied.updateFromRegistry({from: account_polymath}); - - // STEP 5: Register the Modules with the ModuleRegistry contract - - // (A) : Register the GeneralTransferManagerFactory - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); - - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - - // (C) : Register the PercentageTransferManagerFactory - await I_MRProxied.registerModule(I_ScheduledCheckpointFactory.address, { from: account_polymath }); - await I_MRProxied.verifyModule(I_ScheduledCheckpointFactory.address, true, { from: account_polymath }); + // Step 1: Deploy the genral PM ecosystem + let instances = await setUpPolymathNetwork(account_polymath, token_owner); + + [ + I_PolymathRegistry, + I_PolyToken, + I_FeatureRegistry, + I_ModuleRegistry, + I_ModuleRegistryProxy, + I_MRProxied, + I_GeneralTransferManagerFactory, + I_STFactory, + I_SecurityTokenRegistry, + I_SecurityTokenRegistryProxy, + I_STRProxied + ] = instances; + + // STEP 2: Deploy the ScheduleCheckpointModule + [I_ScheduledCheckpointFactory] = await deployScheduleCheckpointAndVerified(account_polymath, I_MRProxied, I_PolyToken.address, 0); // Printing all the contract addresses console.log(` --------------------- Polymath Network Smart Contracts: --------------------- - PolymathRegistry: ${PolymathRegistry.address} - SecurityTokenRegistryProxy: ${SecurityTokenRegistryProxy.address} - SecurityTokenRegistry: ${SecurityTokenRegistry.address} - ModuleRegistry: ${ModuleRegistry.address} - ModuleRegistryProxy: ${ModuleRegistryProxy.address} - FeatureRegistry: ${FeatureRegistry.address} - - STFactory: ${STFactory.address} - GeneralTransferManagerFactory: ${GeneralTransferManagerFactory.address} - GeneralPermissionManagerFactory: ${GeneralPermissionManagerFactory.address} - - ScheduledCheckpointFactory: ${I_ScheduledCheckpointFactory.address} + PolymathRegistry: ${I_PolymathRegistry.address} + SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} + SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} + ModuleRegistry: ${I_ModuleRegistry.address} + ModuleRegistryProxy: ${I_ModuleRegistryProxy.address} + FeatureRegistry: ${I_FeatureRegistry.address} + + STFactory: ${I_STFactory.address} + GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} + + ----------------------------------------------------------------------------- `); }); From 709c5640fca350288f98f4e471df5f498679ff74 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Thu, 25 Oct 2018 19:49:42 +0100 Subject: [PATCH 14/18] Move modules --- contracts/mocks/MockBurnFactory.sol | 2 +- contracts/mocks/MockRedemptionManager.sol | 4 ++-- .../modules/{ => Experimental}/Burn/TrackedRedemption.sol | 8 ++++---- .../{ => Experimental}/Burn/TrackedRedemptionFactory.sol | 2 +- .../Mixed}/ScheduledCheckpoint.sol | 6 +++--- .../Mixed}/ScheduledCheckpointFactory.sol | 2 +- .../TransferManager/SingleTradeVolumeRestrictionTM.sol | 5 +++-- .../SingleTradeVolumeRestrictionTMFactory.sol | 5 +++-- 8 files changed, 18 insertions(+), 16 deletions(-) rename contracts/modules/{ => Experimental}/Burn/TrackedRedemption.sol (93%) rename contracts/modules/{ => Experimental}/Burn/TrackedRedemptionFactory.sol (98%) rename contracts/modules/{Checkpoint => Experimental/Mixed}/ScheduledCheckpoint.sol (97%) rename contracts/modules/{Checkpoint => Experimental/Mixed}/ScheduledCheckpointFactory.sol (98%) rename contracts/modules/{ => Experimental}/TransferManager/SingleTradeVolumeRestrictionTM.sol (99%) rename contracts/modules/{ => Experimental}/TransferManager/SingleTradeVolumeRestrictionTMFactory.sol (97%) diff --git a/contracts/mocks/MockBurnFactory.sol b/contracts/mocks/MockBurnFactory.sol index 5c9e7e793..efaacea21 100644 --- a/contracts/mocks/MockBurnFactory.sol +++ b/contracts/mocks/MockBurnFactory.sol @@ -1,7 +1,7 @@ pragma solidity ^0.4.24; import "./MockRedemptionManager.sol"; -import "../modules/Burn/TrackedRedemptionFactory.sol"; +import "../modules/Experimental/Burn/TrackedRedemptionFactory.sol"; /** * @title Mock Contract Not fit for production environment diff --git a/contracts/mocks/MockRedemptionManager.sol b/contracts/mocks/MockRedemptionManager.sol index 58177e280..70bc9bc7e 100644 --- a/contracts/mocks/MockRedemptionManager.sol +++ b/contracts/mocks/MockRedemptionManager.sol @@ -1,12 +1,12 @@ pragma solidity ^0.4.24; -import "../modules/Burn/TrackedRedemption.sol"; +import "../modules/Experimental/Burn/TrackedRedemption.sol"; /** * @title Burn module for burning tokens and keeping track of burnt amounts */ contract MockRedemptionManager is TrackedRedemption { - + mapping (address => uint256) tokenToRedeem; event RedeemedTokenByOwner(address _investor, address _byWhoom, uint256 _value, uint256 _timestamp); diff --git a/contracts/modules/Burn/TrackedRedemption.sol b/contracts/modules/Experimental/Burn/TrackedRedemption.sol similarity index 93% rename from contracts/modules/Burn/TrackedRedemption.sol rename to contracts/modules/Experimental/Burn/TrackedRedemption.sol index 493a74c02..0a907b467 100644 --- a/contracts/modules/Burn/TrackedRedemption.sol +++ b/contracts/modules/Experimental/Burn/TrackedRedemption.sol @@ -1,8 +1,8 @@ pragma solidity ^0.4.24; -import "./IBurn.sol"; -import "../Module.sol"; -import "../../interfaces/ISecurityToken.sol"; +import "../../Burn/IBurn.sol"; +import "../../Module.sol"; +import "../../../interfaces/ISecurityToken.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; /** @@ -41,7 +41,7 @@ contract TrackedRedemption is IBurn, Module { redeemedTokens[msg.sender] = redeemedTokens[msg.sender].add(_value); emit Redeemed(msg.sender, _value, now); } - + /** * @notice Returns the permissions flag that are associated with CountTransferManager */ diff --git a/contracts/modules/Burn/TrackedRedemptionFactory.sol b/contracts/modules/Experimental/Burn/TrackedRedemptionFactory.sol similarity index 98% rename from contracts/modules/Burn/TrackedRedemptionFactory.sol rename to contracts/modules/Experimental/Burn/TrackedRedemptionFactory.sol index abcdbadc3..41cfe7e4c 100644 --- a/contracts/modules/Burn/TrackedRedemptionFactory.sol +++ b/contracts/modules/Experimental/Burn/TrackedRedemptionFactory.sol @@ -1,7 +1,7 @@ pragma solidity ^0.4.24; import "./TrackedRedemption.sol"; -import "../ModuleFactory.sol"; +import "../../ModuleFactory.sol"; /** * @title Factory for deploying GeneralTransferManager module diff --git a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol b/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol similarity index 97% rename from contracts/modules/Checkpoint/ScheduledCheckpoint.sol rename to contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol index 2422b05bb..097a44f50 100644 --- a/contracts/modules/Checkpoint/ScheduledCheckpoint.sol +++ b/contracts/modules/Experimental/Mixed/ScheduledCheckpoint.sol @@ -1,8 +1,8 @@ pragma solidity ^0.4.24; -import "./ICheckpoint.sol"; -import "../TransferManager/ITransferManager.sol"; -import "../../interfaces/ISecurityToken.sol"; +import "./../../Checkpoint/ICheckpoint.sol"; +import "../../TransferManager/ITransferManager.sol"; +import "../../../interfaces/ISecurityToken.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; /** diff --git a/contracts/modules/Checkpoint/ScheduledCheckpointFactory.sol b/contracts/modules/Experimental/Mixed/ScheduledCheckpointFactory.sol similarity index 98% rename from contracts/modules/Checkpoint/ScheduledCheckpointFactory.sol rename to contracts/modules/Experimental/Mixed/ScheduledCheckpointFactory.sol index 87dfa61eb..1b5daac29 100644 --- a/contracts/modules/Checkpoint/ScheduledCheckpointFactory.sol +++ b/contracts/modules/Experimental/Mixed/ScheduledCheckpointFactory.sol @@ -1,7 +1,7 @@ pragma solidity ^0.4.24; import "./ScheduledCheckpoint.sol"; -import "../ModuleFactory.sol"; +import "../../ModuleFactory.sol"; /** * @title Factory for deploying EtherDividendCheckpoint module diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionTM.sol b/contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTM.sol similarity index 99% rename from contracts/modules/TransferManager/SingleTradeVolumeRestrictionTM.sol rename to contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTM.sol index d86fe5f64..d8e412620 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionTM.sol +++ b/contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTM.sol @@ -1,5 +1,6 @@ pragma solidity ^0.4.24; -import "./ITransferManager.sol"; + +import "./../../TransferManager/ITransferManager.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; /** @@ -317,4 +318,4 @@ contract SingleTradeVolumeRestrictionTM is ITransferManager { allPermissions[0] = ADMIN; return allPermissions; } -} \ No newline at end of file +} diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionTMFactory.sol b/contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTMFactory.sol similarity index 97% rename from contracts/modules/TransferManager/SingleTradeVolumeRestrictionTMFactory.sol rename to contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTMFactory.sol index d92a55813..78a4698fe 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionTMFactory.sol +++ b/contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTMFactory.sol @@ -1,8 +1,9 @@ pragma solidity ^0.4.24; -import "./../ModuleFactory.sol"; +import "./../../ModuleFactory.sol"; import "./SingleTradeVolumeRestrictionTM.sol"; -import "../../libraries/Util.sol"; +import "../../../libraries/Util.sol"; + /** * @title Factory for deploying SingleTradeVolumeRestrictionManager */ From 61c8bd2865bbb4e9d5e08731f468920a94a7b573 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Thu, 25 Oct 2018 20:08:30 +0100 Subject: [PATCH 15/18] Remove unused library --- .../libraries/BokkyPooBahsDateTimeLibrary.sol | 339 ------------------ 1 file changed, 339 deletions(-) delete mode 100644 contracts/libraries/BokkyPooBahsDateTimeLibrary.sol diff --git a/contracts/libraries/BokkyPooBahsDateTimeLibrary.sol b/contracts/libraries/BokkyPooBahsDateTimeLibrary.sol deleted file mode 100644 index 29de4d7cd..000000000 --- a/contracts/libraries/BokkyPooBahsDateTimeLibrary.sol +++ /dev/null @@ -1,339 +0,0 @@ -pragma solidity ^0.4.24; - -// ---------------------------------------------------------------------------- -// BokkyPooBah's DateTime Library v1.00 -// -// A gas-efficient Solidity date and time library -// -// https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary -// -// Tested date range 1970/01/01 to 2345/12/31 -// -// Conventions: -// Unit | Range | Notes -// :-------- |:-------------:|:----- -// timestamp | >= 0 | Unix timestamp, number of seconds since 1970/01/01 00:00:00 UTC -// year | 1970 ... 2345 | -// month | 1 ... 12 | -// day | 1 ... 31 | -// hour | 0 ... 23 | -// minute | 0 ... 59 | -// second | 0 ... 59 | -// dayOfWeek | 1 ... 7 | 1 = Monday, ..., 7 = Sunday -// -// -// Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2018. -// -// GNU Lesser General Public License 3.0 -// https://www.gnu.org/licenses/lgpl-3.0.en.html -// ---------------------------------------------------------------------------- - -library BokkyPooBahsDateTimeLibrary { - - uint constant SECONDS_PER_DAY = 24 * 60 * 60; - uint constant SECONDS_PER_HOUR = 60 * 60; - uint constant SECONDS_PER_MINUTE = 60; - int constant OFFSET19700101 = 2440588; - - uint constant DOW_MON = 1; - uint constant DOW_TUE = 2; - uint constant DOW_WED = 3; - uint constant DOW_THU = 4; - uint constant DOW_FRI = 5; - uint constant DOW_SAT = 6; - uint constant DOW_SUN = 7; - - // ------------------------------------------------------------------------ - // Calculate the number of days from 1970/01/01 to year/month/day using - // the date conversion algorithm from - // http://aa.usno.navy.mil/faq/docs/JD_Formula.php - // and subtracting the offset 2440588 so that 1970/01/01 is day 0 - // - // days = day - // - 32075 - // + 1461 * (year + 4800 + (month - 14) / 12) / 4 - // + 367 * (month - 2 - (month - 14) / 12 * 12) / 12 - // - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4 - // - offset - // ------------------------------------------------------------------------ - function _daysFromDate(uint year, uint month, uint day) internal pure returns (uint _days) { - require(year >= 1970); - int _year = int(year); - int _month = int(month); - int _day = int(day); - - int __days = _day - - 32075 - + 1461 * (_year + 4800 + (_month - 14) / 12) / 4 - + 367 * (_month - 2 - (_month - 14) / 12 * 12) / 12 - - 3 * ((_year + 4900 + (_month - 14) / 12) / 100) / 4 - - OFFSET19700101; - - _days = uint(__days); - } - - // ------------------------------------------------------------------------ - // Calculate year/month/day from the number of days since 1970/01/01 using - // the date conversion algorithm from - // http://aa.usno.navy.mil/faq/docs/JD_Formula.php - // and adding the offset 2440588 so that 1970/01/01 is day 0 - // - // int L = days + 68569 + offset - // int N = 4 * L / 146097 - // L = L - (146097 * N + 3) / 4 - // year = 4000 * (L + 1) / 1461001 - // L = L - 1461 * year / 4 + 31 - // month = 80 * L / 2447 - // dd = L - 2447 * month / 80 - // L = month / 11 - // month = month + 2 - 12 * L - // year = 100 * (N - 49) + year + L - // ------------------------------------------------------------------------ - function _daysToDate(uint _days) internal pure returns (uint year, uint month, uint day) { - int __days = int(_days); - - int L = __days + 68569 + OFFSET19700101; - int N = 4 * L / 146097; - L = L - (146097 * N + 3) / 4; - int _year = 4000 * (L + 1) / 1461001; - L = L - 1461 * _year / 4 + 31; - int _month = 80 * L / 2447; - int _day = L - 2447 * _month / 80; - L = _month / 11; - _month = _month + 2 - 12 * L; - _year = 100 * (N - 49) + _year + L; - - year = uint(_year); - month = uint(_month); - day = uint(_day); - } - - function timestampFromDate(uint year, uint month, uint day) internal pure returns (uint timestamp) { - timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY; - } - function timestampFromDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (uint timestamp) { - timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + hour * SECONDS_PER_HOUR + minute * SECONDS_PER_MINUTE + second; - } - function timestampToDate(uint timestamp) internal pure returns (uint year, uint month, uint day) { - (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); - } - function timestampToDateTime(uint timestamp) internal pure returns (uint year, uint month, uint day, uint hour, uint minute, uint second) { - (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); - uint secs = timestamp % SECONDS_PER_DAY; - hour = secs / SECONDS_PER_HOUR; - secs = secs % SECONDS_PER_HOUR; - minute = secs / SECONDS_PER_MINUTE; - second = secs % SECONDS_PER_MINUTE; - } - - function isValidDate(uint year, uint month, uint day) internal pure returns (bool valid) { - if (year >= 1970 && month > 0 && month <= 12) { - uint daysInMonth = _getDaysInMonth(year, month); - if (day > 0 && day <= daysInMonth) { - valid = true; - } - } - } - function isValidDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (bool valid) { - if (isValidDate(year, month, day)) { - if (hour < 24 && minute < 60 && second < 60) { - valid = true; - } - } - } - function isLeapYear(uint timestamp) internal pure returns (bool leapYear) { - uint year; - uint month; - uint day; - (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); - leapYear = _isLeapYear(year); - } - function _isLeapYear(uint year) internal pure returns (bool leapYear) { - leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); - } - function isWeekDay(uint timestamp) internal pure returns (bool weekDay) { - weekDay = getDayOfWeek(timestamp) <= DOW_FRI; - } - function isWeekEnd(uint timestamp) internal pure returns (bool weekEnd) { - weekEnd = getDayOfWeek(timestamp) >= DOW_SAT; - } - function getDaysInMonth(uint timestamp) internal pure returns (uint daysInMonth) { - uint year; - uint month; - uint day; - (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); - daysInMonth = _getDaysInMonth(year, month); - } - function _getDaysInMonth(uint year, uint month) internal pure returns (uint daysInMonth) { - if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) { - daysInMonth = 31; - } else if (month != 2) { - daysInMonth = 30; - } else { - daysInMonth = _isLeapYear(year) ? 29 : 28; - } - } - // 1 = Monday, 7 = Sunday - function getDayOfWeek(uint timestamp) internal pure returns (uint dayOfWeek) { - uint _days = timestamp / SECONDS_PER_DAY; - dayOfWeek = (_days + 3) % 7 + 1; - } - - function getYear(uint timestamp) internal pure returns (uint year) { - uint month; - uint day; - (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); - } - function getMonth(uint timestamp) internal pure returns (uint month) { - uint year; - uint day; - (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); - } - function getDay(uint timestamp) internal pure returns (uint day) { - uint year; - uint month; - (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); - } - function getHour(uint timestamp) internal pure returns (uint hour) { - uint secs = timestamp % SECONDS_PER_DAY; - hour = secs / SECONDS_PER_HOUR; - } - function getMinute(uint timestamp) internal pure returns (uint minute) { - uint secs = timestamp % SECONDS_PER_HOUR; - minute = secs / SECONDS_PER_MINUTE; - } - function getSecond(uint timestamp) internal pure returns (uint second) { - second = timestamp % SECONDS_PER_MINUTE; - } - - function addYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) { - uint year; - uint month; - uint day; - (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); - year += _years; - uint daysInMonth = _getDaysInMonth(year, month); - if (day > daysInMonth) { - day = daysInMonth; - } - newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; - require(newTimestamp >= timestamp); - } - function addMonths(uint timestamp, uint _months) internal pure returns (uint newTimestamp) { - uint year; - uint month; - uint day; - (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); - month += _months; - year += (month - 1) / 12; - month = (month - 1) % 12 + 1; - uint daysInMonth = _getDaysInMonth(year, month); - if (day > daysInMonth) { - day = daysInMonth; - } - newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; - require(newTimestamp >= timestamp); - } - function addDays(uint timestamp, uint _days) internal pure returns (uint newTimestamp) { - newTimestamp = timestamp + _days * SECONDS_PER_DAY; - require(newTimestamp >= timestamp); - } - function addHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) { - newTimestamp = timestamp + _hours * SECONDS_PER_HOUR; - require(newTimestamp >= timestamp); - } - function addMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) { - newTimestamp = timestamp + _minutes * SECONDS_PER_MINUTE; - require(newTimestamp >= timestamp); - } - function addSeconds(uint timestamp, uint _seconds) internal pure returns (uint newTimestamp) { - newTimestamp = timestamp + _seconds; - require(newTimestamp >= timestamp); - } - - function subYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) { - uint year; - uint month; - uint day; - (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); - year -= _years; - uint daysInMonth = _getDaysInMonth(year, month); - if (day > daysInMonth) { - day = daysInMonth; - } - newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; - require(newTimestamp <= timestamp); - } - function subMonths(uint timestamp, uint _months) internal pure returns (uint newTimestamp) { - uint year; - uint month; - uint day; - (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); - uint yearMonth = year * 12 + (month - 1) - _months; - year = yearMonth / 12; - month = yearMonth % 12 + 1; - uint daysInMonth = _getDaysInMonth(year, month); - if (day > daysInMonth) { - day = daysInMonth; - } - newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; - require(newTimestamp <= timestamp); - } - function subDays(uint timestamp, uint _days) internal pure returns (uint newTimestamp) { - newTimestamp = timestamp - _days * SECONDS_PER_DAY; - require(newTimestamp <= timestamp); - } - function subHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) { - newTimestamp = timestamp - _hours * SECONDS_PER_HOUR; - require(newTimestamp <= timestamp); - } - function subMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) { - newTimestamp = timestamp - _minutes * SECONDS_PER_MINUTE; - require(newTimestamp <= timestamp); - } - function subSeconds(uint timestamp, uint _seconds) internal pure returns (uint newTimestamp) { - newTimestamp = timestamp - _seconds; - require(newTimestamp <= timestamp); - } - - function diffYears(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _years) { - require(fromTimestamp <= toTimestamp); - uint fromYear; - uint fromMonth; - uint fromDay; - uint toYear; - uint toMonth; - uint toDay; - (fromYear, fromMonth, fromDay) = _daysToDate(fromTimestamp / SECONDS_PER_DAY); - (toYear, toMonth, toDay) = _daysToDate(toTimestamp / SECONDS_PER_DAY); - _years = toYear - fromYear; - } - function diffMonths(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _months) { - require(fromTimestamp <= toTimestamp); - uint fromYear; - uint fromMonth; - uint fromDay; - uint toYear; - uint toMonth; - uint toDay; - (fromYear, fromMonth, fromDay) = _daysToDate(fromTimestamp / SECONDS_PER_DAY); - (toYear, toMonth, toDay) = _daysToDate(toTimestamp / SECONDS_PER_DAY); - _months = toYear * 12 + toMonth - fromYear * 12 - fromMonth; - } - function diffDays(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _days) { - require(fromTimestamp <= toTimestamp); - _days = (toTimestamp - fromTimestamp) / SECONDS_PER_DAY; - } - function diffHours(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _hours) { - require(fromTimestamp <= toTimestamp); - _hours = (toTimestamp - fromTimestamp) / SECONDS_PER_HOUR; - } - function diffMinutes(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _minutes) { - require(fromTimestamp <= toTimestamp); - _minutes = (toTimestamp - fromTimestamp) / SECONDS_PER_MINUTE; - } - function diffSeconds(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _seconds) { - require(fromTimestamp <= toTimestamp); - _seconds = toTimestamp - fromTimestamp; - } -} From 98b273ddc9595e00f3761e3e409bc17e62ea56c4 Mon Sep 17 00:00:00 2001 From: Pablo Ruiz Date: Thu, 25 Oct 2018 16:26:20 -0300 Subject: [PATCH 16/18] updated solCover to skip experimental contracts --- .solcover.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.solcover.js b/.solcover.js index ffd077e28..d5b7b07d5 100644 --- a/.solcover.js +++ b/.solcover.js @@ -4,6 +4,6 @@ module.exports = { copyPackages: ['openzeppelin-solidity'], testCommand: 'node ../node_modules/.bin/truffle test `find test/*.js ! -name a_poly_oracle.js -and ! -name s_v130_to_v140_upgrade.js` --network coverage', deepSkip: true, - skipFiles: ['external', 'flat', 'helpers', 'mocks', 'oracles', 'libraries/KindMath.sol', 'storage'], + skipFiles: ['external', 'flat', 'helpers', 'mocks', 'oracles', 'libraries/KindMath.sol', 'storage', 'modules/Experimental'], forceParse: ['mocks', 'oracles'] }; From e8dbe3181bbdd605efb1b5c1637a936b4a3d0ad7 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Thu, 25 Oct 2018 20:43:39 +0100 Subject: [PATCH 17/18] Add test for removeSchedule --- test/y_scheduled_checkpoints.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/y_scheduled_checkpoints.js b/test/y_scheduled_checkpoints.js index d81d8c3f6..5fcc03a74 100644 --- a/test/y_scheduled_checkpoints.js +++ b/test/y_scheduled_checkpoints.js @@ -108,7 +108,7 @@ contract('ScheduledCheckpoint', accounts => { STFactory: ${I_STFactory.address} GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} - + ----------------------------------------------------------------------------- `); }); @@ -170,7 +170,6 @@ contract('ScheduledCheckpoint', accounts => { let startTime; let interval; it("Should create a daily checkpoint", async () => { - console.log("1: " + latestTime()); startTime = latestTime() + 100; interval = 24 * 60 * 60; console.log("Creating scheduled CP: " + startTime, interval); @@ -178,6 +177,12 @@ contract('ScheduledCheckpoint', accounts => { console.log("2: " + latestTime()); }); + it("Remove (temp) daily checkpoint", async () => { + let snap_Id = await takeSnapshot(); + await I_ScheduledCheckpoint.removeSchedule("CP1", {from: token_owner}); + await revertToSnapshot(snap_Id); + }); + it("Should Buy the tokens for account_investor1", async() => { // Add the Investor in to the whitelist console.log("3: " + latestTime()); From 147d6c56ed7a5df93d7843a56ae53711db35f6ae Mon Sep 17 00:00:00 2001 From: Pablo Ruiz Date: Thu, 25 Oct 2018 17:07:41 -0300 Subject: [PATCH 18/18] reverting solCover skip on experimental folder --- .solcover.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.solcover.js b/.solcover.js index d5b7b07d5..ffd077e28 100644 --- a/.solcover.js +++ b/.solcover.js @@ -4,6 +4,6 @@ module.exports = { copyPackages: ['openzeppelin-solidity'], testCommand: 'node ../node_modules/.bin/truffle test `find test/*.js ! -name a_poly_oracle.js -and ! -name s_v130_to_v140_upgrade.js` --network coverage', deepSkip: true, - skipFiles: ['external', 'flat', 'helpers', 'mocks', 'oracles', 'libraries/KindMath.sol', 'storage', 'modules/Experimental'], + skipFiles: ['external', 'flat', 'helpers', 'mocks', 'oracles', 'libraries/KindMath.sol', 'storage'], forceParse: ['mocks', 'oracles'] };