Skip to content

Latest commit

 

History

History
828 lines (684 loc) · 27.3 KB

StakingWithdrawModule.md

File metadata and controls

828 lines (684 loc) · 27.3 KB

Staking withdrawal functionality module* (StakingWithdrawModule.sol)

View Source: contracts/governance/Staking/modules/StakingWithdrawModule.sol

↗ Extends: IFunctionsList, StakingShared, CheckpointsShared

StakingWithdrawModule contract

Structs

VestingConfig

struct VestingConfig {
 address vestingAddress,
 uint256 startDate,
 uint256 endDate,
 uint256 cliff,
 uint256 duration,
 address tokenOwner
}

Events

event MaxVestingWithdrawIterationsUpdated(uint256  oldMaxIterations, uint256  newMaxIterations);
event StakingWithdrawn(address indexed staker, uint256  amount, uint256  until, address indexed receiver, bool  isGovernance);
event VestingTokensWithdrawn(address  vesting, address  receiver);
event TokensUnlocked(uint256  amount);

Functions


withdraw

Withdraw the given amount of tokens if they are unlocked.

function withdraw(uint96 amount, uint256 until, address receiver) external nonpayable whenNotFrozen 

Arguments

Name Type Description
amount uint96 The number of tokens to withdraw.
until uint256 The date until which the tokens were staked.
receiver address The receiver of the tokens. If not specified, send to the msg.sender
Source Code
function withdraw(
        uint96 amount,
        uint256 until,
        address receiver
    ) external whenNotFrozen {
        // adjust until here to avoid adjusting multiple times, and to make sure an adjusted date is passed to
        // _notSameBlockAsStakingCheckpoint
        until = _adjustDateForOrigin(until);

        _notSameBlockAsStakingCheckpoint(until, msg.sender);

        _withdraw(amount, until, receiver, false);
        // @dev withdraws tokens for lock date 2 weeks later than given lock date if sender is a contract
        //		we need to check block.timestamp here
        _withdrawNext(until, receiver, false);
    }

cancelTeamVesting

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. *

function cancelTeamVesting(address vesting, address receiver, uint256 startFrom) external nonpayable onlyAuthorized whenNotFrozen 

Arguments

Name Type Description
vesting address The vesting address.
receiver address The receiving address.
startFrom uint256 The start value for the iterations.
Source Code
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);
    }

_cancelTeamVesting

Withdraws tokens from the staking contract and forwards them to an address specified by the token owner. Low level function.

function _cancelTeamVesting(address _vesting, address _receiver, uint256 _startFrom) private nonpayable

Arguments

Name Type Description
_vesting address The vesting address.
_receiver address The receiving address.
_startFrom uint256 The start value for the iterations. or just unlocked tokens (false).
Source Code
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;

        /// @dev max iterations need to be decreased by 1, otherwise the iteration will always be surplus by 1
        uint256 totalIterationValue =
            (_startFrom + (TWO_WEEKS * (maxVestingWithdrawIterations - 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);
        }
    }

_withdraw

Send user' staked tokens to a receiver taking into account punishments. Sovryn encourages long-term commitment and thinking. When/if you unstake before the end of the staking period, a percentage of the original staking amount will be slashed. This amount is also added to the reward pool and is distributed between all other stakers. *

function _withdraw(uint96 amount, uint256 until, address receiver, bool isGovernance) internal nonpayable

Arguments

Name Type Description
amount uint96 The number of tokens to withdraw.
until uint256 The date until which the tokens were staked. Needs to be adjusted to the next valid lock date before calling this function.
receiver address The receiver of the tokens. If not specified, send to the msg.sender
isGovernance bool Whether all tokens (true) or just unlocked tokens (false).
Source Code
function _withdraw(
        uint96 amount,
        uint256 until,
        address receiver,
        bool isGovernance
    ) internal {
        // @dev it's very unlikely some one will have 1/10**18 SOV staked in Vesting contract
        //		this check is a part of workaround for Vesting.withdrawTokens issue
        if (amount == 1 && _isVestingContract(msg.sender)) {
            return;
        }
        _validateWithdrawParams(msg.sender, amount, until);

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

        /// @dev Update the checkpoints.
        _decreaseDailyStake(until, amount);
        _decreaseUserStake(msg.sender, until, amount);
        if (_isVestingContract(msg.sender)) _decreaseVestingStake(until, amount);
        _decreaseDelegateStake(delegates[msg.sender][until], until, amount);

        /// @dev Early unstaking should be punished.
        if (block.timestamp < until && !allUnlocked && !isGovernance) {
            uint96 punishedAmount = _getPunishedAmount(amount, until);
            amount -= punishedAmount;

            /// @dev punishedAmount can be 0 if block.timestamp are very close to 'until'
            if (punishedAmount > 0) {
                require(address(feeSharing) != address(0), "FeeSharing address wasn't set"); // S08
                /// @dev Move punished amount to fee sharing.
                /// @dev Approve transfer here and let feeSharing do transfer and write checkpoint.
                SOVToken.approve(address(feeSharing), punishedAmount);
                feeSharing.transferTokens(address(SOVToken), punishedAmount);
            }
        }

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

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

_withdrawFromTeamVesting

Send user' staked tokens to a receiver. This function is dedicated only for direct withdrawal from staking contract. Currently only being used by cancelTeamVesting() *

function _withdrawFromTeamVesting(uint96 amount, uint256 until, address receiver, struct StakingWithdrawModule.VestingConfig vestingConfig) internal nonpayable

Arguments

Name Type Description
amount uint96 The number of tokens to withdraw.
until uint256 The date until which the tokens were staked.
receiver address The receiver of the tokens. If not specified, send to the msg.sender.
vestingConfig struct StakingWithdrawModule.VestingConfig The vesting config.
Source Code
function _withdrawFromTeamVesting(
        uint96 amount,
        uint256 until,
        address receiver,
        VestingConfig memory vestingConfig
    ) internal {
        address vesting = vestingConfig.vestingAddress;

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

        /// @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);
    }

_withdrawNext

function _withdrawNext(uint256 until, address receiver, bool isGovernance) internal nonpayable

Arguments

Name Type Description
until uint256
receiver address
isGovernance bool
Source Code
function _withdrawNext(
        uint256 until,
        address receiver,
        bool isGovernance
    ) internal {
        if (_isVestingContract(msg.sender)) {
            // nextLock needs to be adjusted to the next valid lock date to make sure we don't accidentally
            // withdraw stakes that are in the future and would get slashed (if until is not
            // a valid lock date). but until is already handled in the withdraw function
            uint256 nextLock = until.add(TWO_WEEKS);
            if (isGovernance || block.timestamp >= nextLock) {
                uint96 stakes = _getPriorUserStakeByDate(msg.sender, nextLock, block.number - 1);
                if (stakes > 0) {
                    _withdraw(stakes, nextLock, receiver, isGovernance);
                }
            }
        }
    }

getWithdrawAmounts

Get available and punished amount for withdrawing.

function getWithdrawAmounts(uint96 amount, uint256 until) external view
returns(uint96, uint96)

Arguments

Name Type Description
amount uint96 The number of tokens to withdraw.
until uint256 The date until which the tokens were staked. Adjusted to the next valid lock date, if necessary.
Source Code
function getWithdrawAmounts(uint96 amount, uint256 until)
        external
        view
        returns (uint96, uint96)
    {
        until = _adjustDateForOrigin(until);
        _validateWithdrawParams(msg.sender, amount, until);
        uint96 punishedAmount = _getPunishedAmount(amount, until);
        return (amount - punishedAmount, punishedAmount);
    }

_getPunishedAmount

Get punished amount for withdrawing.

function _getPunishedAmount(uint96 amount, uint256 until) internal view
returns(uint96)

Arguments

Name Type Description
amount uint96 The number of tokens to withdraw.
until uint256 The date until which the tokens were staked.
Source Code
function _getPunishedAmount(uint96 amount, uint256 until) internal view returns (uint96) {
        uint256 date = _timestampToLockDate(block.timestamp);
        uint96 weight = _computeWeightByDate(until, date); /// @dev (10 - 1) * WEIGHT_FACTOR
        weight = weight * weightScaling;
        return (amount * weight) / WEIGHT_FACTOR / 100;
    }

_validateWithdrawParams

Validate withdraw parameters.

function _validateWithdrawParams(address account, uint96 amount, uint256 until) internal view

Arguments

Name Type Description
account address Address to be validated.
amount uint96 The number of tokens to withdraw.
until uint256 The date until which the tokens were staked.
Source Code
function _validateWithdrawParams(
        address account,
        uint96 amount,
        uint256 until
    ) internal view {
        require(amount > 0, "Amount of tokens to withdraw must be > 0"); // S10
        uint96 balance = _getPriorUserStakeByDate(account, until, block.number - 1);
        require(amount <= balance, "Staking::withdraw: not enough balance"); // S11
    }

unlockAllTokens

Allow the owner to unlock all tokens in case the staking contract is going to be replaced Note: Not reversible on purpose. once unlocked, everything is unlocked. The owner should not be able to just quickly unlock to withdraw his own tokens and lock again.

function unlockAllTokens() external nonpayable onlyOwner whenNotFrozen 
Source Code
function unlockAllTokens() external onlyOwner whenNotFrozen {
        allUnlocked = true;
        emit TokensUnlocked(SOVToken.balanceOf(address(this)));
    }

setMaxVestingWithdrawIterations

set max withdraw iterations. *

function setMaxVestingWithdrawIterations(uint256 newMaxIterations) external nonpayable onlyAuthorized whenNotFrozen 

Arguments

Name Type Description
newMaxIterations uint256 new max iterations value.
Source Code
function setMaxVestingWithdrawIterations(uint256 newMaxIterations)
        external
        onlyAuthorized
        whenNotFrozen
    {
        require(newMaxIterations > 0, "Invalid max iterations");
        emit MaxVestingWithdrawIterationsUpdated(maxVestingWithdrawIterations, newMaxIterations);
        maxVestingWithdrawIterations = newMaxIterations;
    }

governanceWithdrawVesting

Withdraw tokens for vesting contract.

function governanceWithdrawVesting(address vesting, address receiver) public nonpayable onlyAuthorized whenNotFrozen 

Arguments

Name Type Description
vesting address The address of Vesting contract.
receiver address The receiver of the tokens. If not specified, send to the msg.sender
Source Code
function governanceWithdrawVesting(address vesting, address receiver)
        public
        onlyAuthorized
        whenNotFrozen
    {
        vestingWhitelist[vesting] = true;
        ITeamVesting(vesting).governanceWithdrawTokens(receiver);
        vestingWhitelist[vesting] = false;

        emit VestingTokensWithdrawn(vesting, receiver);
    }

governanceWithdraw

Withdraw the given amount of tokens.

function governanceWithdraw(uint96 amount, uint256 until, address receiver) external nonpayable whenNotFrozen 

Arguments

Name Type Description
amount uint96 The number of tokens to withdraw.
until uint256 The date until which the tokens were staked.
receiver address The receiver of the tokens. If not specified, send to the msg.sender
Source Code
function governanceWithdraw(
        uint96 amount,
        uint256 until,
        address receiver
    ) external whenNotFrozen {
        require(vestingWhitelist[msg.sender], "unauthorized"); // S07

        _notSameBlockAsStakingCheckpoint(until, msg.sender);

        _withdraw(amount, until, receiver, true);
        // @dev withdraws tokens for lock date 2 weeks later than given lock date if sender is a contract
        //		we don't need to check block.timestamp here
        _withdrawNext(until, receiver, true);
    }

getFunctionsList

undefined

function getFunctionsList() external pure
returns(bytes4[])
Source Code
function getFunctionsList() external pure returns (bytes4[] memory) {
        bytes4[] memory functionsList = new bytes4[](7);
        functionsList[0] = this.withdraw.selector;
        functionsList[1] = this.cancelTeamVesting.selector;
        functionsList[2] = this.getWithdrawAmounts.selector;
        functionsList[3] = this.unlockAllTokens.selector;
        functionsList[4] = this.setMaxVestingWithdrawIterations.selector;
        functionsList[5] = this.governanceWithdraw.selector;
        functionsList[6] = this.governanceWithdrawVesting.selector;
        return functionsList;
    }

Contracts