-
Notifications
You must be signed in to change notification settings - Fork 152
SC-19/improve schedule checkpoint module #857
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Parent:
[WIP]:Merge dev-3.1.0 to master
satyamakgec
merged 7 commits into
dev-3.1.0
from
SC-19/improve-schedule-checkpoint-module
Dec 12, 2019
Merged
Changes from 6 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
efb5d2d
improve the checkpoint module
SatyamSB eee7f15
add endTime and test suite
SatyamSB 7e2a874
unskipping the test
SatyamSB af48a2c
minor fixes
SatyamSB c0d5fdd
ScheduledCheckpoints: limit schedules to 10
ross-rosario 14327c2
Merge branch 'dev-3.1.0' into SC-19/improve-schedule-checkpoint-module
satyamakgec 99e31ce
Merge branch 'dev-3.1.0' into SC-19/improve-schedule-checkpoint-module
satyamakgec File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
276 changes: 276 additions & 0 deletions
276
contracts/modules/Checkpoint/Automation/ScheduleCheckpoint.sol
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,276 @@ | ||
| pragma solidity 0.5.8; | ||
|
|
||
| import "../ICheckpoint.sol"; | ||
| import "../../TransferManager/TransferManager.sol"; | ||
| import "openzeppelin-solidity/contracts/math/SafeMath.sol"; | ||
| import "../../../libraries/BokkyPooBahsDateTimeLibrary.sol"; | ||
| import "./ScheduleCheckpointStorage.sol"; | ||
|
|
||
| /** | ||
| * @title Burn module for burning tokens and keeping track of burnt amounts | ||
| */ | ||
| contract ScheduleCheckpoint is ScheduleCheckpointStorage, TransferManager, ICheckpoint { | ||
| using SafeMath for uint256; | ||
|
|
||
| event AddSchedule(bytes32 _name, uint256 _startTime, uint256 _endTime, uint256 _frequency, FrequencyUnit _frequencyUnit); | ||
| event RemoveSchedule(bytes32 _name); | ||
| event ModifyScheduleEndTime(bytes32 _name, uint256 _oldEndTime, uint256 _newEndTime); | ||
|
|
||
| /** | ||
| * @notice Constructor | ||
| * @param _securityToken Address of the security token | ||
| */ | ||
| constructor(address _securityToken, address _polyToken) public Module(_securityToken, _polyToken) { | ||
|
|
||
| } | ||
|
|
||
| /** | ||
| * @notice This function returns the signature of configure function | ||
| */ | ||
| function getInitFunction() public pure returns(bytes4) { | ||
| 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 _endTime End time of the schedule | ||
| * @param _frequency How frequent checkpoint will being created | ||
| * @param _frequencyUnit Unit of frequency i.e If issuer puts _frequency = 10 | ||
| * & frequency unit is DAYS then it means every 10 day frequency new checkpoint will be created | ||
| */ | ||
| function addSchedule(bytes32 _name, uint256 _startTime, uint256 _endTime, uint256 _frequency, FrequencyUnit _frequencyUnit) withPerm(OPERATOR) external { | ||
| require(_name != bytes32(0), "Empty name"); | ||
| require(_startTime > now, "Start time must be in the future"); | ||
| require(schedules[_name].name == bytes32(0), "Name already in use"); | ||
| _validateMaximumLimitCount(); | ||
| uint256 endTime = _endTime; | ||
| if (_endTime <= _startTime) | ||
| endTime = uint256(0); | ||
| schedules[_name].name = _name; | ||
| schedules[_name].startTime = _startTime; | ||
| schedules[_name].endTime = endTime; | ||
| schedules[_name].createNextCheckpointAt = _startTime; | ||
| schedules[_name].frequency = _frequency; | ||
| schedules[_name].frequencyUnit = _frequencyUnit; | ||
| schedules[_name].index = names.length; | ||
| names.push(_name); | ||
satyamakgec marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| emit AddSchedule(_name, _startTime, endTime, _frequency, _frequencyUnit); | ||
| } | ||
|
|
||
| /** | ||
| * @notice removes a schedule for checkpoints | ||
| * @param _name name of the schedule to be removed | ||
| */ | ||
| function removeSchedule(bytes32 _name) withPerm(OPERATOR) external { | ||
| require(_name != bytes32(0), "Invalid schedule name"); | ||
| require(schedules[_name].name == _name, "Schedule does not exist"); | ||
| uint256 index = schedules[_name].index; | ||
| uint256 lengthOfNameArray = names.length; | ||
| if (index != lengthOfNameArray - 1) { | ||
| names[index] = names[lengthOfNameArray - 1]; | ||
| schedules[names[index]].index = index; | ||
| } | ||
| names.length--; | ||
| delete schedules[_name]; | ||
| emit RemoveSchedule(_name); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Used to modify the end time of the schedule | ||
| * @dev new endtime can be set as 0 or any value greater than now. | ||
| * @param _name Name of the schedule that need to modify | ||
| * @param _newEndTime New end time of the schedule | ||
| */ | ||
| function modifyScheduleEndTime(bytes32 _name, uint256 _newEndTime) withPerm(OPERATOR) external { | ||
| Schedule memory _schedule = schedules[_name]; | ||
| require(_schedule.name != bytes32(0), "Invalid name"); | ||
| if (_schedule.endTime > 0) | ||
| require(_schedule.endTime > now, "Schedule already ended"); | ||
| if (_newEndTime > 0) | ||
| require(_newEndTime > now && _newEndTime > _schedule.startTime, "Invalid end time"); | ||
| emit ModifyScheduleEndTime(_name, _schedule.endTime, _newEndTime); | ||
satyamakgec marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| schedules[_name].endTime = _newEndTime; | ||
| } | ||
|
|
||
| /** | ||
| * @notice Used to create checkpoints that correctly reflect balances | ||
| * @return always returns Result.NA | ||
| */ | ||
| function executeTransfer( | ||
| address, /* _from */ | ||
| address, /* _to */ | ||
| uint256, /* _amount */ | ||
| bytes calldata /* _data */ | ||
| ) | ||
| external | ||
| onlySecurityToken | ||
| returns(Result) | ||
| { | ||
| if (!paused) { | ||
| _updateAll(); | ||
| } | ||
| return (Result.NA); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Used to create checkpoints that correctly reflect balances | ||
| * @return always returns Result.NA | ||
| */ | ||
| function verifyTransfer( | ||
| address, /* _from */ | ||
| address, /* _to */ | ||
| uint256, /* _amount */ | ||
| bytes memory /* _data */ | ||
| ) | ||
| public | ||
| view | ||
| returns(Result, bytes32) | ||
| { | ||
| return (Result.NA, bytes32(0)); | ||
| } | ||
|
|
||
| /** | ||
| * @notice gets schedule details | ||
| * @param name name of the schedule. | ||
| * @return name Name of the schedule | ||
| * @return startTime Unix timestamps at which schedule of creating the checkpoint will start | ||
| * @return endTime Unix timestamps at which schedule of creation the checkpoint will stop | ||
| * @return createNextCheckpointAt Unix timestamp at which next checkpoint will be created | ||
| * @return frequency Frequency at which checkpoint has been created | ||
| * @return frequencyUnit Unit of frequency | ||
| * @return checkpointIds List of checkpoint Ids that been created in the schedule | ||
| * @return timestamps List of unix timestamp at which checkpoints have been created | ||
| * @return periods List of periods covered | ||
| * @return totalPeriods Total periods covered | ||
| */ | ||
| function getSchedule(bytes32 _name) external view returns( | ||
| bytes32 name, | ||
| uint256 startTime, | ||
| uint256 endTime, | ||
| uint256 createNextCheckpointAt, | ||
| uint256 frequency, | ||
| FrequencyUnit frequencyUnit, | ||
| uint256[] memory checkpointIds, | ||
| uint256[] memory timestamps, | ||
| uint256[] memory periods, | ||
| uint256 totalPeriods | ||
| ){ | ||
| Schedule storage schedule = schedules[_name]; | ||
| return ( | ||
| schedule.name, | ||
| schedule.startTime, | ||
| schedule.endTime, | ||
| schedule.createNextCheckpointAt, | ||
| schedule.frequency, | ||
| schedule.frequencyUnit, | ||
| schedule.checkpointIds, | ||
| schedule.timestamps, | ||
| schedule.periods, | ||
| schedule.totalPeriods | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * @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) withPerm(OPERATOR) external { | ||
| _update(_name); | ||
| } | ||
|
|
||
| function _update(bytes32 _name) internal { | ||
| Schedule storage schedule = schedules[_name]; | ||
| if (_isScheduleActive(schedule.createNextCheckpointAt, schedule.endTime)) { | ||
| uint256 newCheckpointId = securityToken.createCheckpoint(); | ||
| schedule.checkpointIds.push(newCheckpointId); | ||
| // Checkpoint is already been create in the above two lines now `createNextCheckpointAt` treated as `lastCheckpointCreatedAt` | ||
| uint256 lastCheckpointCreatedAt = schedule.createNextCheckpointAt; | ||
| schedule.timestamps.push(lastCheckpointCreatedAt); | ||
| uint256 periods; | ||
| if (schedule.frequencyUnit == FrequencyUnit.SECONDS ) { | ||
| periods = now | ||
| .sub(lastCheckpointCreatedAt) | ||
| .div(schedule.frequency) | ||
| .add(1); // 1 is added for the next period | ||
| schedule.createNextCheckpointAt = periods.mul(schedule.frequency).add(lastCheckpointCreatedAt); | ||
| } else if (schedule.frequencyUnit == FrequencyUnit.DAYS ) { | ||
| periods = BokkyPooBahsDateTimeLibrary | ||
| .diffDays(lastCheckpointCreatedAt, now) | ||
| .div(schedule.frequency) | ||
| .add(1); // 1 is added for the next period | ||
| schedule.createNextCheckpointAt = BokkyPooBahsDateTimeLibrary.addDays( | ||
| lastCheckpointCreatedAt, periods.mul(schedule.frequency) | ||
| ); | ||
| } else if (schedule.frequencyUnit == FrequencyUnit.WEEKS ) { | ||
| periods = BokkyPooBahsDateTimeLibrary | ||
| .diffDays(lastCheckpointCreatedAt, now) | ||
| .div(7) | ||
| .div(schedule.frequency) | ||
| .add(1); // 1 is added for the next period | ||
| schedule.createNextCheckpointAt = BokkyPooBahsDateTimeLibrary.addDays( | ||
| lastCheckpointCreatedAt, periods.mul(schedule.frequency).mul(7) | ||
| ); | ||
| } else if (schedule.frequencyUnit == FrequencyUnit.MONTHS ) { | ||
| periods = BokkyPooBahsDateTimeLibrary | ||
| .diffMonths(lastCheckpointCreatedAt, now) | ||
| .div(schedule.frequency) | ||
| .add(1); // 1 is added for the next period | ||
| schedule.createNextCheckpointAt = BokkyPooBahsDateTimeLibrary.addMonths( | ||
| lastCheckpointCreatedAt, periods.mul(schedule.frequency) | ||
| ); | ||
| } else if (schedule.frequencyUnit == FrequencyUnit.QUATER ) { | ||
| periods = BokkyPooBahsDateTimeLibrary | ||
| .diffMonths(lastCheckpointCreatedAt, now) | ||
| .div(3) | ||
| .div(schedule.frequency) | ||
| .add(1); // 1 is added for the next period | ||
| schedule.createNextCheckpointAt = BokkyPooBahsDateTimeLibrary.addMonths( | ||
| lastCheckpointCreatedAt, periods.mul(schedule.frequency).mul(3) | ||
| ); | ||
| } else if (schedule.frequencyUnit == FrequencyUnit.YEARS ) { | ||
| periods = BokkyPooBahsDateTimeLibrary | ||
| .diffYears(lastCheckpointCreatedAt, now) | ||
| .div(schedule.frequency) | ||
| .add(1); // 1 is added for the next period | ||
| schedule.createNextCheckpointAt = BokkyPooBahsDateTimeLibrary.addYears( | ||
| lastCheckpointCreatedAt, periods.mul(schedule.frequency) | ||
| ); | ||
| } | ||
| schedule.totalPeriods = schedule.totalPeriods.add(periods); | ||
| schedule.periods.push(periods); | ||
| } | ||
| } | ||
|
|
||
| function _isScheduleActive(uint256 _createNextCheckpointAt, uint256 _endTime) internal view returns(bool isActive) { | ||
| isActive = _endTime > 0 ? _createNextCheckpointAt <= now && _createNextCheckpointAt <= _endTime : _createNextCheckpointAt <= now; | ||
| } | ||
|
|
||
| function _validateMaximumLimitCount() internal view { | ||
| require(names.length < MAXLIMIT, "Max Limit Reached"); | ||
| } | ||
|
|
||
| /** | ||
| * @notice manually triggers update outside of transfer request for all schedules (can be used to reduce user gas costs) | ||
| */ | ||
| function updateAll() withPerm(OPERATOR) external { | ||
| _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 | ||
| */ | ||
| function getPermissions() external view returns(bytes32[] memory) { | ||
| bytes32[] memory allPermissions = new bytes32[](1); | ||
| allPermissions[0] = OPERATOR; | ||
| return allPermissions; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
contracts/modules/Checkpoint/Automation/ScheduleCheckpointProxy.sol
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| pragma solidity 0.5.8; | ||
|
|
||
| import "../../../proxy/OwnedUpgradeabilityProxy.sol"; | ||
| import "./ScheduleCheckpointStorage.sol"; | ||
| import "../../../Pausable.sol"; | ||
| import "../../../storage/modules/ModuleStorage.sol"; | ||
|
|
||
| /** | ||
| * @title Automate the checkpoint creation | ||
| */ | ||
| contract ScheduleCheckpointProxy is ScheduleCheckpointStorage, ModuleStorage, Pausable, OwnedUpgradeabilityProxy { | ||
| /** | ||
| * @notice Constructor | ||
| * @param _securityToken Address of the security token | ||
| * @param _polyAddress Address of the polytoken | ||
| * @param _implementation representing the address of the new implementation to be set | ||
| */ | ||
| constructor( | ||
| string memory _version, | ||
| address _securityToken, | ||
| address _polyAddress, | ||
| address _implementation | ||
| ) | ||
| public | ||
| ModuleStorage(_securityToken, _polyAddress) | ||
| { | ||
| require(_implementation != address(0), "Implementation address should not be 0x"); | ||
| _upgradeTo(_version, _implementation); | ||
| } | ||
|
|
||
| } |
28 changes: 28 additions & 0 deletions
28
contracts/modules/Checkpoint/Automation/ScheduleCheckpointStorage.sol
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| pragma solidity 0.5.8; | ||
|
|
||
| contract ScheduleCheckpointStorage { | ||
|
|
||
| enum FrequencyUnit { SECONDS, DAYS, WEEKS, MONTHS, QUATER, YEARS } | ||
|
|
||
| bytes32 constant OPERATOR = "OPERATOR"; | ||
| uint256 internal constant MAXLIMIT = uint256(10); | ||
|
|
||
| struct Schedule { | ||
| uint256 index; | ||
| bytes32 name; | ||
| uint256 startTime; | ||
| uint256 endTime; | ||
| uint256 createNextCheckpointAt; | ||
| uint256 frequency; | ||
| FrequencyUnit frequencyUnit; | ||
| uint256[] checkpointIds; | ||
| uint256[] timestamps; | ||
| uint256[] periods; | ||
| uint256 totalPeriods; | ||
| } | ||
|
|
||
| bytes32[] public names; | ||
|
|
||
| mapping(bytes32 => Schedule) public schedules; | ||
|
|
||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.