- 
                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
      
      
            satyamakgec
  merged 7 commits into
  dev-3.1.0
from
SC-19/improve-schedule-checkpoint-module
  
      
      
   
  Dec 12, 2019 
      
    
  
     Merged
                    Changes from 3 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
        
          
          
            269 changes: 269 additions & 0 deletions
          
          269 
        
  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,269 @@ | ||
| 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"); | ||
| uint256 endTime = _endTime; | ||
| if (_endTime <= _startTime) | ||
|         
                  satyamakgec marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
| endTime = uint256(0); | ||
| schedules[_name].name = _name; | ||
| schedules[_name].startTime = _startTime; | ||
| schedules[_name].endTime = endTime; | ||
| schedules[_name].nextCheckPointCreatedAt = _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, "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 nextCheckPointCreatedAt 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 nextCheckPointCreatedAt, | ||
| 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.nextCheckPointCreatedAt, | ||
| 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.nextCheckPointCreatedAt, schedule.endTime)) { | ||
| uint256 newCheckpointId = securityToken.createCheckpoint(); | ||
| schedule.checkpointIds.push(newCheckpointId); | ||
| schedule.timestamps.push(schedule.nextCheckPointCreatedAt); | ||
| uint256 periods; | ||
| if (schedule.frequencyUnit == FrequencyUnit.SECONDS ) { | ||
| periods = now | ||
| .sub(schedule.nextCheckPointCreatedAt) | ||
| .div(schedule.frequency) | ||
| .add(1); // 1 is added for the next period | ||
| schedule.nextCheckPointCreatedAt = periods.mul(schedule.frequency).add(schedule.nextCheckPointCreatedAt); | ||
| } else if (schedule.frequencyUnit == FrequencyUnit.DAYS ) { | ||
| periods = BokkyPooBahsDateTimeLibrary | ||
| .diffDays(schedule.nextCheckPointCreatedAt, now) | ||
| .div(schedule.frequency) | ||
| .add(1); // 1 is added for the next period | ||
| schedule.nextCheckPointCreatedAt = BokkyPooBahsDateTimeLibrary.addDays( | ||
| schedule.nextCheckPointCreatedAt, periods.mul(schedule.frequency) | ||
| ); | ||
| } else if (schedule.frequencyUnit == FrequencyUnit.WEEKS ) { | ||
| periods = BokkyPooBahsDateTimeLibrary | ||
| .diffDays(schedule.nextCheckPointCreatedAt, now) | ||
| .div(7) | ||
| .div(schedule.frequency) | ||
| .add(1); // 1 is added for the next period | ||
| schedule.nextCheckPointCreatedAt = BokkyPooBahsDateTimeLibrary.addDays( | ||
| schedule.nextCheckPointCreatedAt, periods.mul(schedule.frequency).mul(7) | ||
| ); | ||
| } else if (schedule.frequencyUnit == FrequencyUnit.MONTHS ) { | ||
| periods = BokkyPooBahsDateTimeLibrary | ||
| .diffMonths(schedule.nextCheckPointCreatedAt, now) | ||
| .div(schedule.frequency) | ||
| .add(1); // 1 is added for the next period | ||
| schedule.nextCheckPointCreatedAt = BokkyPooBahsDateTimeLibrary.addMonths( | ||
| schedule.nextCheckPointCreatedAt, periods.mul(schedule.frequency) | ||
| ); | ||
| } else if (schedule.frequencyUnit == FrequencyUnit.QUATER ) { | ||
| periods = BokkyPooBahsDateTimeLibrary | ||
| .diffMonths(schedule.nextCheckPointCreatedAt, now) | ||
| .div(3) | ||
| .div(schedule.frequency) | ||
| .add(1); // 1 is added for the next period | ||
| schedule.nextCheckPointCreatedAt = BokkyPooBahsDateTimeLibrary.addMonths( | ||
| schedule.nextCheckPointCreatedAt, periods.mul(schedule.frequency).mul(3) | ||
| ); | ||
| } else if (schedule.frequencyUnit == FrequencyUnit.YEARS ) { | ||
| periods = BokkyPooBahsDateTimeLibrary | ||
| .diffYears(schedule.nextCheckPointCreatedAt, now) | ||
| .div(schedule.frequency) | ||
| .add(1); // 1 is added for the next period | ||
| schedule.nextCheckPointCreatedAt = BokkyPooBahsDateTimeLibrary.addYears( | ||
| schedule.nextCheckPointCreatedAt, periods.mul(schedule.frequency) | ||
| ); | ||
| } | ||
| schedule.totalPeriods = schedule.totalPeriods.add(periods); | ||
| schedule.periods.push(periods); | ||
| } | ||
| } | ||
|  | ||
| function _isScheduleActive(uint256 _nextCheckPointCreatedAt, uint256 _endTime) internal view returns(bool isActive) { | ||
| isActive = _endTime > 0 ? _nextCheckPointCreatedAt <= now && _nextCheckPointCreatedAt <= _endTime : _nextCheckPointCreatedAt <= now; | ||
| } | ||
|  | ||
| /** | ||
| * @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); | ||
| } | ||
|  | ||
| } | 
        
          
          
            27 changes: 27 additions & 0 deletions
          
          27 
        
  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,27 @@ | ||
| pragma solidity 0.5.8; | ||
|  | ||
| contract ScheduleCheckpointStorage { | ||
|  | ||
| enum FrequencyUnit { SECONDS, DAYS, WEEKS, MONTHS, QUATER, YEARS } | ||
|  | ||
| bytes32 constant OPERATOR = "OPERATOR"; | ||
|  | ||
| struct Schedule { | ||
| uint256 index; | ||
| bytes32 name; | ||
| uint256 startTime; | ||
| uint256 endTime; | ||
| uint256 nextCheckPointCreatedAt; | ||
|         
                  satyamakgec marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| 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.