View Source: contracts/governance/Staking/modules/StakingStakeModule.sol
↗ Extends: IFunctionsList, StakingShared, CheckpointsShared, ApprovalReceiver
Implements staking functionality*
Events
event TokensStaked(address indexed staker, uint256 amount, uint256 lockedUntil, uint256 totalStaked);
event ExtendedStakingDuration(address indexed staker, uint256 previousDate, uint256 newDate, uint256 amountStaked);
- stake(uint96 amount, uint256 until, address stakeFor, address delegatee)
- stakeWithApproval(address sender, uint96 amount, uint256 until, address stakeFor, address delegatee)
- _stake(address sender, uint96 amount, uint256 until, address stakeFor, address delegatee, bool timeAdjusted)
- _stakeOptionalTokenTransfer(address sender, uint96 amount, uint256 until, address stakeFor, address delegatee, bool timeAdjusted, bool transferToken)
- extendStakingDuration(uint256 previousLock, uint256 until)
- _increaseStake(address sender, uint96 amount, address stakeFor, uint256 until, bool transferToken)
- stakesBySchedule(uint256 amount, uint256 cliff, uint256 duration, uint256 intervalLength, address stakeFor, address delegatee)
- stakeBySchedule(uint256 amount, uint256 cliff, uint256 duration, uint256 intervalLength, address stakeFor, address delegatee)
- _stakeBySchedule(uint256 amount, uint256 cliff, uint256 duration, uint256 intervalLength, address stakeFor, address delegatee)
- balanceOf(address account)
- getCurrentStakedUntil(uint256 lockedTS)
- getStakes(address account)
- _getToken()
- _getSelectors()
- timestampToLockDate(uint256 timestamp)
- getFunctionsList()
Stake the given amount for the given duration of time.
function stake(uint96 amount, uint256 until, address stakeFor, address delegatee) external nonpayable whenNotPaused whenNotFrozen
Arguments
Name | Type | Description |
---|---|---|
amount | uint96 | The number of tokens to stake. |
until | uint256 | Timestamp indicating the date until which to stake. |
stakeFor | address | The address to stake the tokens for or 0x0 if staking for oneself. |
delegatee | address | The address of the delegatee or 0x0 if there is none. |
Source Code
function stake(
uint96 amount,
uint256 until,
address stakeFor,
address delegatee
) external whenNotPaused whenNotFrozen {
_stake(msg.sender, amount, until, stakeFor, delegatee, false);
}
Stake the given amount for the given duration of time.
function stakeWithApproval(address sender, uint96 amount, uint256 until, address stakeFor, address delegatee) external nonpayable onlyThisContract whenNotPaused whenNotFrozen
Arguments
Name | Type | Description |
---|---|---|
sender | address | The sender of SOV.approveAndCall |
amount | uint96 | The number of tokens to stake. |
until | uint256 | Timestamp indicating the date until which to stake. |
stakeFor | address | The address to stake the tokens for or 0x0 if staking for oneself. |
delegatee | address | The address of the delegatee or 0x0 if there is none. |
Source Code
function stakeWithApproval(
address sender,
uint96 amount,
uint256 until,
address stakeFor,
address delegatee
) external onlyThisContract whenNotPaused whenNotFrozen {
_stake(sender, amount, until, stakeFor, delegatee, false);
}
Send sender's tokens to this contract and update its staked balance.
function _stake(address sender, uint96 amount, uint256 until, address stakeFor, address delegatee, bool timeAdjusted) internal nonpayable
Arguments
Name | Type | Description |
---|---|---|
sender | address | The sender of the tokens. |
amount | uint96 | The number of tokens to send. |
until | uint256 | The date until which the tokens will be staked. |
stakeFor | address | The beneficiary whose stake will be increased. |
delegatee | address | The address of the delegatee or stakeFor if default 0x0. |
timeAdjusted | bool | Whether fixing date to stacking periods or not. |
Source Code
function _stake(
address sender,
uint96 amount,
uint256 until,
address stakeFor,
address delegatee,
bool timeAdjusted
) internal {
_stakeOptionalTokenTransfer(
sender,
amount,
until,
stakeFor,
delegatee,
timeAdjusted,
true // transfer SOV
);
}
Send sender's tokens to this contract and update its staked balance.
function _stakeOptionalTokenTransfer(address sender, uint96 amount, uint256 until, address stakeFor, address delegatee, bool timeAdjusted, bool transferToken) internal nonpayable
Arguments
Name | Type | Description |
---|---|---|
sender | address | The sender of the tokens. |
amount | uint96 | The number of tokens to send. |
until | uint256 | The date until which the tokens will be staked. |
stakeFor | address | The beneficiary whose stake will be increased. |
delegatee | address | The address of the delegatee or stakeFor if default 0x0. |
timeAdjusted | bool | Whether fixing date to stacking periods or not. |
transferToken | bool | Should transfer SOV - false for multiple iterations like in stakeBySchedule |
Source Code
function _stakeOptionalTokenTransfer(
address sender,
uint96 amount,
uint256 until,
address stakeFor,
address delegatee,
bool timeAdjusted,
bool transferToken
) internal {
require(amount > 0, "amount needs to be bigger than 0"); // S01
if (!timeAdjusted) {
until = _timestampToLockDate(until);
}
require(
until > block.timestamp,
"Staking::_timestampToLockDate: staking period too short"
); // S02
/// @dev Stake for the sender if not specified otherwise.
if (stakeFor == address(0)) {
stakeFor = sender;
}
// must wait a block before staking again for that same deadline
_notSameBlockAsStakingCheckpoint(until, stakeFor);
/// @dev Delegate for stakeFor if not specified otherwise.
if (delegatee == address(0)) {
delegatee = stakeFor;
}
/// @dev Do not stake longer than the max duration.
if (!timeAdjusted) {
uint256 latest = _timestampToLockDate(block.timestamp + MAX_DURATION);
if (until > latest) until = latest;
}
uint96 previousBalance = _currentBalance(stakeFor, until);
/// @dev Increase stake.
_increaseStake(sender, amount, stakeFor, until, transferToken);
// @dev Previous version wasn't working properly for the following case:
// delegate checkpoint wasn't updating for the second and next stakes for the same date
// if first stake was withdrawn completely and stake was delegated to the staker
// (no delegation to another address).
address previousDelegatee = delegates[stakeFor][until];
if (previousDelegatee != delegatee) {
// @dev only the user that stakes for himself is allowed to delegate VP to another address
// which works with vesting stakes and prevents vulnerability of delegating VP to an arbitrary address from
// any address
if (delegatee != stakeFor) {
require(
stakeFor == sender,
"Only stakeFor account is allowed to change delegatee"
);
} else if (sender != stakeFor && previousDelegatee != address(0)) {
require(stakeFor == sender, "Only sender is allowed to change delegatee");
}
/// @dev Update delegatee.
delegates[stakeFor][until] = delegatee;
/// @dev Decrease stake on previous balance for previous delegatee.
_decreaseDelegateStake(previousDelegatee, until, previousBalance);
/// @dev Add previousBalance to amount.
amount = add96(previousBalance, amount, "add amounts failed");
}
/// @dev Increase stake.
_increaseDelegateStake(delegatee, until, amount);
emit DelegateChanged(stakeFor, until, previousDelegatee, delegatee);
}
Extend the staking duration until the specified date.
function extendStakingDuration(uint256 previousLock, uint256 until) external nonpayable whenNotPaused whenNotFrozen
Arguments
Name | Type | Description |
---|---|---|
previousLock | uint256 | The old unlocking timestamp. |
until | uint256 | The new unlocking timestamp in seconds. |
Source Code
function extendStakingDuration(uint256 previousLock, uint256 until)
external
whenNotPaused
whenNotFrozen
{
previousLock = _timestampToLockDate(previousLock);
until = _timestampToLockDate(until);
_notSameBlockAsStakingCheckpoint(previousLock, msg.sender);
/// @dev Do not exceed the max duration, no overflow possible.
uint256 latest = _timestampToLockDate(block.timestamp + MAX_DURATION);
if (until > latest) until = latest;
require(previousLock < until, "must increase staking duration"); // S04
/// @dev Update checkpoints.
/// @dev TODO James: Can reading stake at block.number -1 cause trouble with multiple tx in a block?
uint96 amount = _getPriorUserStakeByDate(msg.sender, previousLock, block.number - 1);
require(amount > 0, "no stakes till the prev lock date"); // S05
_decreaseUserStake(msg.sender, previousLock, amount);
_increaseUserStake(msg.sender, until, amount);
if (_isVestingContract(msg.sender)) {
_decreaseVestingStake(previousLock, amount);
_increaseVestingStake(until, amount);
}
_decreaseDailyStake(previousLock, amount);
_increaseDailyStake(until, amount);
/// @dev Delegate might change: if there is already a delegate set for the until date, it will remain the delegate for this position
address delegateFrom = delegates[msg.sender][previousLock];
delegates[msg.sender][previousLock] = address(0); //the previousLock delegates nullifying before reading that form `until` guards in case delegateTo == until
address delegateTo = delegates[msg.sender][until];
if (delegateTo == address(0)) {
delegateTo = delegateFrom;
delegates[msg.sender][until] = delegateFrom;
}
_decreaseDelegateStake(delegateFrom, previousLock, amount);
_increaseDelegateStake(delegateTo, until, amount);
emit ExtendedStakingDuration(msg.sender, previousLock, until, amount);
}
Send sender's tokens to this contract and update its staked balance.
function _increaseStake(address sender, uint96 amount, address stakeFor, uint256 until, bool transferToken) internal nonpayable
Arguments
Name | Type | Description |
---|---|---|
sender | address | The sender of the tokens. |
amount | uint96 | The number of tokens to send. |
stakeFor | address | The beneficiary whose stake will be increased. |
until | uint256 | The date until which the tokens will be staked. |
transferToken | bool | if false - token transfer should be handled separately |
Source Code
function _increaseStake(
address sender,
uint96 amount,
address stakeFor,
uint256 until,
bool transferToken
) internal {
/// @dev Retrieve the SOV tokens.
if (transferToken)
require(
SOVToken.transferFrom(sender, address(this), amount),
"Should transfer tokens successfully"
); // IS10
/// @dev Increase staked balance.
uint96 balance = _currentBalance(stakeFor, until);
balance = add96(balance, amount, "increaseStake: overflow"); // IS20
/// @dev Update checkpoints.
_increaseDailyStake(until, amount);
_increaseUserStake(stakeFor, until, amount);
if (_isVestingContract(stakeFor)) _increaseVestingStake(until, amount);
emit TokensStaked(stakeFor, amount, until, balance);
}
DO NOT USE this misspelled function. Use stakeBySchedule function instead. This function cannot be deprecated while we have non-upgradeable vesting contracts.
function stakesBySchedule(uint256 amount, uint256 cliff, uint256 duration, uint256 intervalLength, address stakeFor, address delegatee) external nonpayable whenNotPaused whenNotFrozen
Arguments
Name | Type | Description |
---|---|---|
amount | uint256 | |
cliff | uint256 | |
duration | uint256 | |
intervalLength | uint256 | |
stakeFor | address | |
delegatee | address |
Source Code
function stakesBySchedule(
uint256 amount,
uint256 cliff,
uint256 duration,
uint256 intervalLength,
address stakeFor,
address delegatee
) external whenNotPaused whenNotFrozen {
_stakeBySchedule(amount, cliff, duration, intervalLength, stakeFor, delegatee);
}
Stake tokens according to the vesting schedule.
function stakeBySchedule(uint256 amount, uint256 cliff, uint256 duration, uint256 intervalLength, address stakeFor, address delegatee) external nonpayable whenNotPaused whenNotFrozen
Arguments
Name | Type | Description |
---|---|---|
amount | uint256 | The amount of tokens to stake. |
cliff | uint256 | The time interval to the first withdraw. |
duration | uint256 | The staking duration. |
intervalLength | uint256 | The length of each staking interval when cliff passed. |
stakeFor | address | The address to stake the tokens for or 0x0 if staking for oneself. |
delegatee | address | The address of the delegatee or 0x0 if there is none. |
Source Code
function stakeBySchedule(
uint256 amount,
uint256 cliff,
uint256 duration,
uint256 intervalLength,
address stakeFor,
address delegatee
) external whenNotPaused whenNotFrozen {
_stakeBySchedule(amount, cliff, duration, intervalLength, stakeFor, delegatee);
}
Stake tokens according to the vesting schedule.
function _stakeBySchedule(uint256 amount, uint256 cliff, uint256 duration, uint256 intervalLength, address stakeFor, address delegatee) internal nonpayable
Arguments
Name | Type | Description |
---|---|---|
amount | uint256 | The amount of tokens to stake. |
cliff | uint256 | The time interval to the first withdraw. |
duration | uint256 | The staking duration. |
intervalLength | uint256 | The length of each staking interval when cliff passed. |
stakeFor | address | The address to stake the tokens for or 0x0 if staking for oneself. |
delegatee | address | The address of the delegatee or 0x0 if there is none. |
Source Code
function _stakeBySchedule(
uint256 amount,
uint256 cliff,
uint256 duration,
uint256 intervalLength,
address stakeFor,
address delegatee
) internal {
require(amount > 0, "Invalid amount");
require(duration <= MAX_DURATION, "Invalid duration");
require(intervalLength > 0, "Invalid interval length");
require(intervalLength % TWO_WEEKS == 0, "Invalid interval length");
if (delegatee != stakeFor && delegatee != address(0)) {
require(
stakeFor == msg.sender,
"Only stakeFor account is allowed to change delegatee"
);
}
/**
* @dev Stake them until lock dates according to the vesting schedule.
* Note: because staking is only possible in periods of 2 weeks,
* the total duration might end up a bit shorter than specified
* depending on the date of staking.
* */
uint256 start = _timestampToLockDate(block.timestamp + cliff);
uint256 end = _timestampToLockDate(block.timestamp + duration);
require(start <= end, "Invalid schedule");
uint256 numIntervals;
if (start < end) {
numIntervals = (end - start) / intervalLength + 1;
} else {
numIntervals = 1;
}
uint256 stakedPerInterval = amount / numIntervals;
/// @dev transferring total SOV amount before staking
require(
SOVToken.transferFrom(msg.sender, address(this), amount),
"Should transfer tokens successfully"
); // SS10
/// @dev stakedPerInterval might lose some dust on rounding. Add it to the first staking date.
if (numIntervals >= 1) {
_stakeOptionalTokenTransfer(
msg.sender,
uint96(amount - stakedPerInterval * (numIntervals - 1)),
start,
stakeFor,
delegatee,
true,
false
);
}
/// @dev Stake the rest in 4 week intervals.
for (uint256 i = start + intervalLength; i <= end; i += intervalLength) {
/// @dev Stakes for itself, delegates to the owner.
_notSameBlockAsStakingCheckpoint(i, stakeFor); // must wait a block before staking again for that same deadline
_stakeOptionalTokenTransfer(
msg.sender,
uint96(stakedPerInterval),
i,
stakeFor,
delegatee,
true,
false
);
}
}
Get the number of staked tokens held by the user account.
function balanceOf(address account) external view
returns(balance uint96)
Arguments
Name | Type | Description |
---|---|---|
account | address | The address of the account to get the balance of. |
Returns
The number of tokens held.
Source Code
function balanceOf(address account) external view returns (uint96 balance) {
for (uint256 i = kickoffTS; i <= block.timestamp + MAX_DURATION; i += TWO_WEEKS) {
balance = add96(balance, _currentBalance(account, i), "Staking::balanceOf: overflow"); // S12
}
}
Get the current number of tokens staked for a day.
function getCurrentStakedUntil(uint256 lockedTS) external view
returns(uint96)
Arguments
Name | Type | Description |
---|---|---|
lockedTS | uint256 | The timestamp to get the staked tokens for. |
Source Code
function getCurrentStakedUntil(uint256 lockedTS) external view returns (uint96) {
uint32 nCheckpoints = numTotalStakingCheckpoints[lockedTS];
return nCheckpoints > 0 ? totalStakingCheckpoints[lockedTS][nCheckpoints - 1].stake : 0;
}
Get list of stakes for a user account.
function getStakes(address account) external view
returns(dates uint256[], stakes uint96[])
Arguments
Name | Type | Description |
---|---|---|
account | address | The address to get stakes. |
Returns
The arrays of dates and stakes.
Source Code
function getStakes(address account)
external
view
returns (uint256[] memory dates, uint96[] memory stakes)
{
uint256 latest = _timestampToLockDate(block.timestamp + MAX_DURATION);
/// @dev Calculate stakes.
uint256 count = 0;
/// @dev We need to iterate from first possible stake date after deployment to the latest from current time.
for (uint256 i = kickoffTS + TWO_WEEKS; i <= latest; i += TWO_WEEKS) {
if (_currentBalance(account, i) > 0) {
count++;
}
}
dates = new uint256[](count);
stakes = new uint96[](count);
/// @dev We need to iterate from first possible stake date after deployment to the latest from current time.
uint256 j = 0;
for (uint256 i = kickoffTS + TWO_WEEKS; i <= latest; i += TWO_WEEKS) {
uint96 balance = _currentBalance(account, i);
if (balance > 0) {
dates[j] = i;
stakes[j] = balance;
j++;
}
}
}
undefined
Overrides default ApprovalReceiver._getToken function to register SOV token on this contract.
function _getToken() internal view
returns(address)
Source Code
function _getToken() internal view returns (address) {
return address(SOVToken);
}
undefined
Overrides default ApprovalReceiver._getSelectors function to register stakeWithApproval selector on this contract.
function _getSelectors() internal pure
returns(bytes4[])
Source Code
function _getSelectors() internal pure returns (bytes4[] memory) {
bytes4[] memory selectors = new bytes4[](1);
selectors[0] = this.stakeWithApproval.selector;
return selectors;
}
Unstaking is possible every 2 weeks only. This means, to calculate the key value for the staking checkpoints, we need to map the intended timestamp to the closest available date.
function timestampToLockDate(uint256 timestamp) external view
returns(uint256)
Arguments
Name | Type | Description |
---|---|---|
timestamp | uint256 | The unlocking timestamp. |
Returns
The actual unlocking date (might be up to 2 weeks shorter than intended).
Source Code
function timestampToLockDate(uint256 timestamp) external view returns (uint256) {
return _timestampToLockDate(timestamp);
}
⤾ overrides IFunctionsList.getFunctionsList
function getFunctionsList() external pure
returns(bytes4[])
Source Code
function getFunctionsList() external pure returns (bytes4[] memory) {
bytes4[] memory functionsList = new bytes4[](10);
functionsList[0] = this.stake.selector;
functionsList[1] = this.stakeWithApproval.selector;
functionsList[2] = this.extendStakingDuration.selector;
functionsList[3] = this.stakesBySchedule.selector;
functionsList[4] = this.stakeBySchedule.selector;
functionsList[5] = this.balanceOf.selector;
functionsList[6] = this.getCurrentStakedUntil.selector;
functionsList[7] = this.getStakes.selector;
functionsList[8] = this.timestampToLockDate.selector;
functionsList[9] = this.receiveApproval.selector;
return functionsList;
}
- Address
- Administered
- AdminRole
- AdvancedToken
- AdvancedTokenStorage
- Affiliates
- AffiliatesEvents
- ApprovalReceiver
- BProPriceFeed
- CheckpointsShared
- Constants
- Context
- DevelopmentFund
- DummyContract
- EnumerableAddressSet
- EnumerableBytes32Set
- EnumerableBytes4Set
- ERC20
- ERC20Detailed
- ErrorDecoder
- Escrow
- EscrowReward
- FeedsLike
- FeesEvents
- FeeSharingCollector
- FeeSharingCollectorProxy
- FeeSharingCollectorStorage
- FeesHelper
- FourYearVesting
- FourYearVestingFactory
- FourYearVestingLogic
- FourYearVestingStorage
- GenericTokenSender
- GovernorAlpha
- GovernorVault
- IApproveAndCall
- IChai
- IContractRegistry
- IConverterAMM
- IERC1820Registry
- IERC20_
- IERC20
- IERC777
- IERC777Recipient
- IERC777Sender
- IFeeSharingCollector
- IFourYearVesting
- IFourYearVestingFactory
- IFunctionsList
- ILiquidityMining
- ILiquidityPoolV1Converter
- ILoanPool
- ILoanToken
- ILoanTokenLogicBeacon
- ILoanTokenLogicModules
- ILoanTokenLogicProxy
- ILoanTokenModules
- ILoanTokenWRBTC
- ILockedSOV
- IMoCState
- IModulesProxyRegistry
- Initializable
- InterestUser
- IPot
- IPriceFeeds
- IPriceFeedsExt
- IProtocol
- IRSKOracle
- ISovryn
- ISovrynSwapNetwork
- IStaking
- ISwapsImpl
- ITeamVesting
- ITimelock
- IV1PoolOracle
- IVesting
- IVestingFactory
- IVestingRegistry
- IWrbtc
- IWrbtcERC20
- LenderInterestStruct
- LiquidationHelper
- LiquidityMining
- LiquidityMiningConfigToken
- LiquidityMiningProxy
- LiquidityMiningStorage
- LoanClosingsEvents
- LoanClosingsLiquidation
- LoanClosingsRollover
- LoanClosingsShared
- LoanClosingsWith
- LoanClosingsWithoutInvariantCheck
- LoanInterestStruct
- LoanMaintenance
- LoanMaintenanceEvents
- LoanOpenings
- LoanOpeningsEvents
- LoanParamsStruct
- LoanSettings
- LoanSettingsEvents
- LoanStruct
- LoanToken
- LoanTokenBase
- LoanTokenLogicBeacon
- LoanTokenLogicLM
- LoanTokenLogicProxy
- LoanTokenLogicStandard
- LoanTokenLogicStorage
- LoanTokenLogicWrbtc
- LoanTokenSettingsLowerAdmin
- LockedSOV
- MarginTradeStructHelpers
- Medianizer
- ModuleCommonFunctionalities
- ModulesCommonEvents
- ModulesProxy
- ModulesProxyRegistry
- MultiSigKeyHolders
- MultiSigWallet
- Mutex
- Objects
- OrderStruct
- OrigingVestingCreator
- OriginInvestorsClaim
- Ownable
- Pausable
- PausableOz
- PreviousLoanToken
- PreviousLoanTokenSettingsLowerAdmin
- PriceFeedRSKOracle
- PriceFeeds
- PriceFeedsLocal
- PriceFeedsMoC
- PriceFeedV1PoolOracle
- ProtocolAffiliatesInterface
- ProtocolLike
- ProtocolSettings
- ProtocolSettingsEvents
- ProtocolSettingsLike
- ProtocolSwapExternalInterface
- ProtocolTokenUser
- Proxy
- ProxyOwnable
- ReentrancyGuard
- RewardHelper
- RSKAddrValidator
- SafeERC20
- SafeMath
- SafeMath96
- setGet
- SharedReentrancyGuard
- SignedSafeMath
- SOV
- sovrynProtocol
- StakingAdminModule
- StakingGovernanceModule
- StakingInterface
- StakingProxy
- StakingRewards
- StakingRewardsProxy
- StakingRewardsStorage
- StakingShared
- StakingStakeModule
- StakingStorageModule
- StakingStorageShared
- StakingVestingModule
- StakingWithdrawModule
- State
- SwapsEvents
- SwapsExternal
- SwapsImplLocal
- SwapsImplSovrynSwap
- SwapsUser
- TeamVesting
- Timelock
- TimelockHarness
- TimelockInterface
- TokenSender
- UpgradableProxy
- USDTPriceFeed
- Utils
- VaultController
- Vesting
- VestingCreator
- VestingFactory
- VestingLogic
- VestingRegistry
- VestingRegistry2
- VestingRegistry3
- VestingRegistryLogic
- VestingRegistryProxy
- VestingRegistryStorage
- VestingStorage
- WeightedStakingModule
- WRBTC