Skip to content
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

[Consolidating] PR#451: Incorporate PR#447 Governance vesting cancelling & PR#448 Staking refactoring #451

Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
f9c8a66
add max iterations in withdraw vesting
cwsnt Jul 11, 2022
500412a
feature: enable vesting withdrawal starting from specific date
cwsnt Jul 15, 2022
02893f1
remove counter usage for checking the max iterations
cwsnt Jul 19, 2022
be2f270
enable governance withdraw vesting directly from staking contract
cwsnt Jul 22, 2022
0a232d7
set max iterations to storage
cwsnt Jul 29, 2022
34d9d9e
remove irrelevant interface
cwsnt Jul 29, 2022
3d45f2d
prettier
cwsnt Jul 29, 2022
f938b80
remove max iterations wihtdrawal from vesting storage
cwsnt Aug 4, 2022
7d4a5a3
adjust getPriorUserStakeByDate for governance direct withdrawal
cwsnt Aug 4, 2022
294f94a
hh config add initialBaseFeePerGas: 0
tjcloa Aug 4, 2022
8be63db
update: remove ganache dependency from tests
tjcloa Aug 5, 2022
663d64c
fix: governance withdraw vesting script
cwsnt Aug 6, 2022
892d1be
remove superfluous event
cwsnt Aug 17, 2022
c4c43c2
move vestingCreationType to storage
cwsnt Aug 19, 2022
2de6d4f
revert the conditional for consistency
cwsnt Aug 19, 2022
19ef0de
add checker function of teamvesting in registry logic
cwsnt Aug 24, 2022
eebcc3b
adjust the incremental value for governance cancel vesting to two weeks
cwsnt Aug 25, 2022
d448c62
fix: better revert message
cwsnt Aug 25, 2022
3ad39d8
fix: consider the governance vesting withdrawal for 1 iteration
cwsnt Aug 25, 2022
79f72c8
improve naming storage var
cwsnt Aug 26, 2022
21dd284
revert vesting logic contract
cwsnt Aug 29, 2022
7f1c269
clean code based on pr review
cwsnt Aug 31, 2022
4f318d7
improve docs
cwsnt Sep 1, 2022
f2bc7c1
updateLMConfig script: iXUSD allocation 2500 -> 0
tjcloa Sep 2, 2022
c36cc0f
fix conflict with development branch
cwsnt Sep 4, 2022
2ccf825
merge: PR#447 with PR#448
cwsnt Sep 13, 2022
517922a
prettier
cwsnt Sep 13, 2022
68427ec
merge with latest staking refactoring branch
cwsnt Sep 14, 2022
fb64937
Revert back pause staking test
cwsnt Sep 14, 2022
eedc9b7
fix unauthorized error message test cases
cwsnt Sep 14, 2022
5182cf9
fix mockup vesting typo
cwsnt Sep 14, 2022
cd81c03
fix vesting unit test
cwsnt Sep 14, 2022
c5b06d6
prettier
cwsnt Sep 14, 2022
02425da
fix pause staking unit test
cwsnt Sep 14, 2022
62f0e75
fix: bring back set team vesting mockup function
cwsnt Sep 14, 2022
048c3bb
Add SIP tx code for setting max withdraw iterations in staking contract
cwsnt Sep 15, 2022
d5acfcd
vesting registry logic: refactor redundant code
cwsnt Sep 19, 2022
7c40c8a
merge with staking refactoring branch & fix conflict
cwsnt Sep 19, 2022
fb0984a
improve comment
cwsnt Sep 23, 2022
c7abe1b
remove the parameter for SIP-050 script
cwsnt Oct 26, 2022
e59834e
Merge branch 'refactoring/staking-contract-eip170' into feature/gover…
cwsnt Oct 31, 2022
b493b74
update vestingType datatype to uint32
cwsnt Nov 4, 2022
4418f6e
add task script to register the existing vesting address to creation …
cwsnt Nov 9, 2022
fc3b2ac
use vesting logic abi to avoid confusion
cwsnt Nov 10, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions contracts/governance/Staking/Checkpoints.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ contract Checkpoints is StakingStorage, SafeMath96 {
bool isGovernance
);

/// @notice An event emitted when vesting tokens get withdrawn.
event VestingTokensWithdrawn(address vesting, address receiver);

/// @notice An event emitted when the owner unlocks all tokens.
event TokensUnlocked(uint256 amount);

Expand Down Expand Up @@ -81,6 +78,16 @@ contract Checkpoints is StakingStorage, SafeMath96 {

event VestingStakeSet(uint256 lockedTS, uint96 value);

event TeamVestingCancelled(address indexed caller, address receiver);

event TeamVestingPartiallyCancelled(
address indexed caller,
address receiver,
uint256 lastProcessedDate
);

event MaxVestingWithdrawIterationsUpdated(uint256 oldMaxIterations, uint256 newMaxIterations);

/**
* @notice Increases the user's vesting stake for a giving lock date and writes a checkpoint.
* @param lockedTS The lock date.
Expand Down
167 changes: 149 additions & 18 deletions contracts/governance/Staking/Staking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -336,21 +336,82 @@ contract Staking is
}

/**
* @notice Withdraw tokens for vesting contract.
* @param vesting The address of Vesting contract.
* @param receiver The receiver of the tokens. If not specified, send to the msg.sender
* @dev Can be invoked only by whitelisted contract passed to governanceWithdrawVesting.
* @notice Governance withdraw vesting directly through staking contract.
* This direct withdraw vesting solves the out of gas issue when there are too many iterations when withdrawing.
* This function only allows cancelling vesting contract of the TeamVesting type.
*
* @param vesting The vesting address.
* @param receiver The receiving address.
* @param startFrom The start value for the iterations.
*/
function cancelTeamVesting(
address vesting,
address receiver,
uint256 startFrom
) external onlyAuthorized whenNotFrozen {
/// require the caller only for team vesting contract.
require(vestingRegistryLogic.isTeamVesting(vesting), "Only team vesting allowed");

_cancelTeamVesting(vesting, receiver, startFrom);
}

/**
* @notice Withdraws tokens from the staking contract and forwards them
* to an address specified by the token owner. Low level function.
* @dev Once here the caller permission is taken for granted.
* @param _vesting The vesting address.
* @param _receiver The receiving address.
* @param _startFrom The start value for the iterations.
* or just unlocked tokens (false).
* */
function governanceWithdrawVesting(address vesting, address receiver)
public
onlyAuthorized
whenNotFrozen
{
vestingWhitelist[vesting] = true;
ITeamVesting(vesting).governanceWithdrawTokens(receiver);
vestingWhitelist[vesting] = false;
function _cancelTeamVesting(
address _vesting,
address _receiver,
uint256 _startFrom
) private {
require(_receiver != address(0), "receiver address invalid");

ITeamVesting teamVesting = ITeamVesting(_vesting);

VestingConfig memory vestingConfig =
VestingConfig(
_vesting,
teamVesting.startDate(),
teamVesting.endDate(),
teamVesting.cliff(),
teamVesting.duration(),
teamVesting.tokenOwner()
);

/// @dev In the unlikely case that all tokens have been unlocked early,
/// allow to withdraw all of them, as long as the itrations less than maxVestingWithdrawIterations.
uint256 end = vestingConfig.endDate;

uint256 defaultStart = vestingConfig.startDate + vestingConfig.cliff;

_startFrom = _startFrom >= defaultStart ? _startFrom : defaultStart;

emit VestingTokensWithdrawn(vesting, receiver);
/// @dev max iterations need to be decreased by 1, otherwise the iteration will always be surplus by 1
uint256 totalIterationValue =
(_startFrom + (TWO_WEEKS * (getMaxVestingWithdrawIterations() - 1)));
uint256 adjustedEnd = end < totalIterationValue ? end : totalIterationValue;

/// @dev Withdraw for each unlocked position.
for (uint256 i = _startFrom; i <= adjustedEnd; i += TWO_WEEKS) {
/// @dev Read amount to withdraw.
uint96 tempStake = _getPriorUserStakeByDate(_vesting, i, block.number - 1);

if (tempStake > 0) {
/// @dev do governance direct withdraw for team vesting
_withdrawFromTeamVesting(tempStake, i, _receiver, vestingConfig);
}
}

if (adjustedEnd < end) {
emit TeamVestingPartiallyCancelled(msg.sender, _receiver, adjustedEnd);
} else {
emit TeamVestingCancelled(msg.sender, _receiver);
}
}

/**
Expand Down Expand Up @@ -378,7 +439,7 @@ contract Staking is
return;
}
until = _adjustDateForOrigin(until);
_validateWithdrawParams(amount, until);
_validateWithdrawParams(msg.sender, amount, until);

/// @dev Determine the receiver.
if (receiver == address(0)) receiver = msg.sender;
Expand Down Expand Up @@ -406,11 +467,56 @@ contract Staking is

/// @dev transferFrom
bool success = SOVToken.transfer(receiver, amount);
require(success, "S09"); // Token transfer failed
require(success, "Token transfer failed"); // S09

emit StakingWithdrawn(msg.sender, amount, until, receiver, isGovernance);
}

/**
* @notice Send user' staked tokens to a receiver.
* This function is dedicated only for direct withdrawal from staking contract.
* Currently only being used by cancelTeamVesting()
*
* @param amount The number of tokens to withdraw.
* @param until The date until which the tokens were staked.
* @param receiver The receiver of the tokens. If not specified, send to the msg.sender.
* @param vestingConfig The vesting config.
* @dev VestingConfig struct intended to avoid stack too deep issue, and it contains this properties:
address vestingAddress; // vesting contract address
uint256 startDate; //start date of vesting
uint256 endDate; // end date of vesting
uint256 cliff; // after this time period the tokens begin to unlock
uint256 duration; // after this period all the tokens will be unlocked
address tokenOwner; // owner of the vested tokens
* */
function _withdrawFromTeamVesting(
uint96 amount,
uint256 until,
address receiver,
VestingConfig memory vestingConfig
) internal {
address vesting = vestingConfig.vestingAddress;

until = _adjustDateForOrigin(until);
_validateWithdrawParams(vesting, amount, until);

/// @dev Determine the receiver.
if (receiver == address(0)) receiver = msg.sender;

/// @dev Update the checkpoints.
_decreaseDailyStake(until, amount);
_decreaseUserStake(vesting, until, amount);

_decreaseVestingStake(until, amount);
_decreaseDelegateStake(delegates[vesting][until], until, amount);

/// @dev transferFrom
bool success = SOVToken.transfer(receiver, amount);
require(success, "Token transfer failed"); // S09

emit StakingWithdrawn(vesting, amount, until, receiver, true);
}

// @dev withdraws tokens for lock date 2 weeks later than given lock date
function _withdrawNext(
uint256 until,
Expand Down Expand Up @@ -438,7 +544,7 @@ contract Staking is
view
returns (uint96, uint96)
{
_validateWithdrawParams(amount, until);
_validateWithdrawParams(msg.sender, amount, until);
uint96 punishedAmount = _getPunishedAmount(amount, until);
return (amount - punishedAmount, punishedAmount);
}
Expand All @@ -457,12 +563,17 @@ contract Staking is

/**
* @notice Validate withdraw parameters.
* @param account Address to be validated.
* @param amount The number of tokens to withdraw.
* @param until The date until which the tokens were staked.
* */
function _validateWithdrawParams(uint96 amount, uint256 until) internal view {
function _validateWithdrawParams(
address account,
uint96 amount,
uint256 until
) internal view {
require(amount > 0, "S10"); // Amount of tokens to withdraw must be > 0
uint96 balance = _getPriorUserStakeByDate(msg.sender, until, block.number - 1);
uint96 balance = _getPriorUserStakeByDate(account, until, block.number - 1);
require(amount <= balance, "S11"); // Staking::withdraw: not enough balance
}

Expand Down Expand Up @@ -812,4 +923,24 @@ contract Staking is
block.number;
require(notSameBlock, "S20"); //S20 : "cannot be mined in the same block as last stake"
}

/**
* @notice Max iteration for direct withdrawal from staking to prevent out of gas issue.
*
* @return max iteration value.
*/
function getMaxVestingWithdrawIterations() public view returns (uint256) {
return maxVestingWithdrawIterations;
}

/**
* @dev set max withdraw iterations.
*
* @param newMaxIterations new max iterations value.
*/
function setMaxVestingWithdrawIterations(uint256 newMaxIterations) external onlyAuthorized {
require(newMaxIterations > 0, "Invalid max iterations");
emit MaxVestingWithdrawIterationsUpdated(maxVestingWithdrawIterations, newMaxIterations);
maxVestingWithdrawIterations = newMaxIterations;
}
}
16 changes: 14 additions & 2 deletions contracts/governance/Staking/StakingStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,7 @@ contract StakingStorage is Ownable {
mapping(address => bool) public vestingWhitelist;

/// @dev user => flag whether user has admin role.
/// @dev multisig should be an admin, admin can invoke only governanceWithdrawVesting function,
/// this function works only with Team Vesting contracts
/// @dev used for administrative tasks not requiring governance approval and SIP; meant to be dedicated multisig(s)
mapping(address => bool) public admins;

/// @dev vesting contract code hash => flag whether it's registered code hash
Expand All @@ -152,4 +151,17 @@ contract StakingStorage is Ownable {

/// @dev Staking contract is frozen
bool public frozen;

/// @dev max iterations that can be supported in 1 tx for the withdrawal
uint256 internal maxVestingWithdrawIterations;

/// @dev Struct for direct withdraw function -- to avoid stack too deep issue
struct VestingConfig {
address vestingAddress;
uint256 startDate;
uint256 endDate;
uint256 cliff;
uint256 duration;
address tokenOwner;
}
}
37 changes: 29 additions & 8 deletions contracts/governance/Staking/interfaces/IStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -579,14 +579,6 @@ interface IStaking {
address receiver
) external;

/**
* @notice Withdraw tokens for vesting contract.
* @param vesting The address of Vesting contract.
* @param receiver The receiver of the tokens. If not specified, send to the msg.sender
* @dev Can be invoked only by whitelisted contract passed to governanceWithdrawVesting.
* */
function governanceWithdrawVesting(address vesting, address receiver) external;

/**
* @notice Get available and punished amount for withdrawing.
* @param amount The number of tokens to withdraw.
Expand Down Expand Up @@ -679,4 +671,33 @@ interface IStaking {
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) external;

/**
* @notice Governance withdraw vesting directly through staking contract.
* This direct withdraw vesting solves the out of gas issue when there are too many iterations when withdrawing.
* This function only allows cancelling vesting contract of the TeamVesting type.
*
* @param vesting The vesting address.
* @param receiver The receiving address.
* @param startFrom The start value for the iterations.
*/
function cancelTeamVesting(
address vesting,
address receiver,
uint256 startFrom
) external;

/**
* @notice Max iteration for direct withdrawal from staking to prevent out of gas issue.
*
* @return max iteration value.
*/
function getMaxVestingWithdrawIterations() external view returns (uint256);

/**
* @dev set max withdraw iterations.
*
* @param maxIterations new max iterations value.
*/
function setMaxVestingWithdrawIterations(uint256 maxIterations) external;
}
12 changes: 11 additions & 1 deletion contracts/governance/Staking/modules/StakingStorageModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,17 @@ contract StakingStorageModule is IFunctionsList, StakingStorageShared {
return name;
}

/**
* @notice Max iteration for direct withdrawal from staking to prevent out of gas issue.
*
* @return max iteration value.
*/
function getMaxVestingWithdrawIterations() public view returns (uint256) {
return maxVestingWithdrawIterations;
}

function getFunctionsList() external pure returns (bytes4[] memory) {
bytes4[] memory functionsList = new bytes4[](31);
bytes4[] memory functionsList = new bytes4[](32);
functionsList[0] = this.getStorageMaxDurationToStakeTokens.selector;
functionsList[1] = this.getStorageMaxVotingWeight.selector;
functionsList[2] = this.getStorageWeightFactor.selector;
Expand Down Expand Up @@ -90,6 +99,7 @@ contract StakingStorageModule is IFunctionsList, StakingStorageShared {
functionsList[28] = this.pausers.selector;
functionsList[29] = this.paused.selector;
functionsList[30] = this.frozen.selector;
functionsList[31] = this.getMaxVestingWithdrawIterations.selector;

return functionsList;
}
Expand Down
Loading