Skip to content

Commit

Permalink
updated VestingCreator
Browse files Browse the repository at this point in the history
  • Loading branch information
jameshowlett977 committed Apr 20, 2021
1 parent a31fb9b commit 03eaccd
Showing 1 changed file with 109 additions and 19 deletions.
128 changes: 109 additions & 19 deletions contracts/governance/Vesting/VestingCreator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,31 @@ import "./VestingLogic.sol";
contract VestingCreator is AdminRole {
using SafeMath for uint;

///@notice the SOV token contract
//@notice the SOV token contract
IERC20 public SOV;
//@notice the vesting registry contract
VestingRegistry public vestingRegistry;

//@notice list of vesting to be processed
VestingData[] public vestingDataList;

//@notice list of vesting with errors, can't be processed
VestingData[] public vestingDataErrorList;

//TODO check storage slots
//TODO check storage slots and max values of the fields
struct VestingData {
address tokenOwner;
uint96 amount;
uint120 cliff;
uint120 duration;
//@dev true - tokens can be withdrawn by governance
bool governanceControl;
}

//TODO add events

event SOVTransferred(address indexed receiver, uint256 amount);
event TokensStaked(address indexed vesting, uint256 amount);
event VestingDataRemoved(address indexed caller, address tokenOwner);
event DataCleared(address indexed caller);
event ErrorDataCleared(address indexed caller);

constructor(address _SOV, address _vestingRegistry) public {
require(_SOV != address(0), "SOV address invalid");
Expand All @@ -51,6 +56,11 @@ contract VestingCreator is AdminRole {
emit SOVTransferred(_receiver, _amount);
}

/**
* @notice adds vestings to be processed to the list
* @dev if account doesn't have another vesting of the same type (controlled or not controlled by governance), with another schedule
* @dev vesting data will be added to vestingDataList, otherwise it will be added to vestingDataErrorList
*/
function addVestings(
address[] memory _tokenOwners,
uint96[] memory _amounts,
Expand All @@ -64,6 +74,8 @@ contract VestingCreator is AdminRole {
&& _tokenOwners.length == _governanceControls.length,
"arrays mismatch");

//TODO we need to validate vestings data (cliff, duration, etc.)

for (uint i = 0; i < _tokenOwners.length; i++) {
address vestingAddress = _getVesting(_tokenOwners[i], _governanceControls[i]);
VestingData memory vestingData =
Expand All @@ -78,42 +90,112 @@ contract VestingCreator is AdminRole {
vestingDataList.push(vestingData);
} else {
//account already has vesting contract with different schedule
//TODO do we need to save vestings with errors ?
//we need to save it to have list with wrong vesting data
vestingDataErrorList.push(vestingData);
}
}
}

function getUnprocessedCount() public view returns (uint) {
return vestingDataList.length;
}

function getUnprocessedAmount() public view returns (uint) {
uint amount = 0;
for (uint i = 0; i < vestingDataList.length; i++) {
amount = amount.add(vestingDataList[i].amount);
}
return amount;
}

function process() onlyAuthorized public {
/**
* @notice creates vesting contract (if it hasn't been created yet) and stakes tokens
*/
function processNextVesting() onlyAuthorized public {
if (vestingDataList.length > 0) {
VestingData storage vestingData = vestingDataList[vestingDataList.length - 1];
uint amount = vestingData.amount;
require(SOV.balanceOf(address(this)) >= amount, "balance isn't enough");

VestingLogic vesting = _createAndGetVesting(vestingData);

//TODO check if tokens can be staked after vesting creation
SOV.approve(address(vesting), amount);
vesting.stakeTokens(amount);
delete vestingDataList[vestingDataList.length - 1];
emit TokensStaked(address(vesting), amount);
}
}

/**
* @notice creates vesting contract without staking any tokens
* @dev it can be the case when vesting creation and tokens staking can't be done in one transaction because of block gas limit
*/
function processVestingCreation() onlyAuthorized public {
if (vestingDataList.length > 0) {
VestingData storage vestingData = vestingDataList[vestingDataList.length - 1];
_createAndGetVesting(vestingData);
}
}

/**
* @notice removes next vesting data from the list
* @dev we process inverted list
* @dev we should be able to remove incorrect vesting data that can't be processed
*/
function removeNextVesting() onlyAuthorized public {
if (vestingDataList.length > 0) {
VestingData storage vestingData = vestingDataList[vestingDataList.length - 1];
delete vestingDataList[vestingDataList.length - 1];
emit VestingDataRemoved(msg.sender, vestingData.tokenOwner);
}
}

/**
* @notice removes all data about unprocessed vestings to be processed
*/
function clearVestingDataList() onlyAuthorized public {
delete vestingDataList;
emit DataCleared(msg.sender);
}

/**
* @notice removes all data about unprocessed vestings with errors
* @dev account already has vesting contract with different schedule
* @dev we can't stake tokens to this vesting, because list of unlocked dates will be incorrect
*/
function clearVestingDataErrorList() onlyAuthorized public {
delete vestingDataErrorList;
emit ErrorDataCleared(msg.sender);
}

/**
* @notice returns count of vestings to be processed
*/
function getUnprocessedCount() public view returns (uint) {
return vestingDataList.length;
}

/**
* @notice returns total amount of vestings to be processed
*/
function getUnprocessedAmount() public view returns (uint) {
uint amount = 0;
for (uint i = 0; i < vestingDataList.length; i++) {
amount = amount.add(vestingDataList[i].amount);
}
return amount;
}

/**
* @notice checks if contract balance is enough to process all vestings
*/
function isEnoughBalance() public view returns (bool) {
return SOV.balanceOf(address(this)) >= getUnprocessedAmount();
}

/**
* @notice returns missed balance to process all vestings
*/
function getMissingBalance() public view returns (uint) {
if (isEnoughBalance()) {
return 0;
}
return getUnprocessedAmount() - SOV.balanceOf(address(this));
}

/**
* @notice creates TeamVesting or Vesting contract
* @dev new contract won't be created if account already has contract of the same type
*/
function _createAndGetVesting(VestingData storage vestingData) internal returns (VestingLogic) {
if (vestingData.governanceControl) {
vestingRegistry.createTeamVesting(vestingData.tokenOwner, vestingData.amount, vestingData.cliff, vestingData.duration);
Expand All @@ -123,6 +205,9 @@ contract VestingCreator is AdminRole {
return VestingLogic(_getVesting(vestingData.tokenOwner, vestingData.governanceControl));
}

/**
* @notice returns an address of TeamVesting or Vesting contract (depends on a governance control)
*/
function _getVesting(address _tokenOwner, bool _governanceControl) internal view returns (address vestingAddress) {
if (_governanceControl) {
vestingAddress = vestingRegistry.getTeamVesting(_tokenOwner);
Expand All @@ -131,6 +216,11 @@ contract VestingCreator is AdminRole {
}
}

/**
* @notice validates vesting schedule
* @dev checks whether account has vesting contract with different schedule
* @dev if account doesn't have vesting or schedules are the same everything is ok
*/
function _validateVestingSchedule(address _vestingAddress, uint256 _cliff, uint256 _duration) internal view returns (bool) {
if (_vestingAddress == address(0)) {
return true;
Expand Down

0 comments on commit 03eaccd

Please sign in to comment.