Skip to content

Latest commit

 

History

History
677 lines (555 loc) · 20.9 KB

StakingRewards.md

File metadata and controls

677 lines (555 loc) · 20.9 KB

Staking Rewards Contract. (StakingRewards.sol)

View Source: contracts/governance/StakingRewards/StakingRewards.sol

↗ Extends: StakingRewardsStorage

StakingRewards contract

This is a trial incentive program. In this, the SOV emitted and becoming liquid from the Adoption Fund could be utilized to offset the higher APY's offered for Liquidity Mining events. Vesting contract stakes are excluded from these rewards. Only wallets which have staked previously liquid SOV are eligible for these rewards. Tokenholders who stake their SOV receive staking rewards, a pro-rata share of the revenue that the platform generates from various transaction fees plus revenues from stakers who have a portion of their SOV slashed for early unstaking.

Events

event RewardWithdrawn(address indexed receiver, uint256  amount);

Functions


initialize

Replacement of constructor by initialize function for Upgradable Contracts This function will be called only once by the owner.

function initialize(address _SOV, IStaking _staking) external nonpayable onlyOwner 

Arguments

Name Type Description
_SOV address SOV token address
_staking IStaking StakingProxy address should be passed
Source Code
function initialize(address _SOV, IStaking _staking) external onlyOwner {
        require(_SOV != address(0), "Invalid SOV Address.");
        require(Address.isContract(_SOV), "_SOV not a contract");
        SOV = IERC20(_SOV);
        staking = _staking;
        startTime = staking.timestampToLockDate(block.timestamp);
        setMaxDuration(15 * TWO_WEEKS);
        deploymentBlock = _getCurrentBlockNumber();
    }

stop

Stops the current rewards program.

function stop() external nonpayable onlyOwner 
Source Code
function stop() external onlyOwner {
        require(stopBlock == 0, "Already stopped");
        stopBlock = _getCurrentBlockNumber();
    }

collectReward

Collect rewards

function collectReward(uint256 restartTime) external nonpayable

Arguments

Name Type Description
restartTime uint256 The time from which the staking rewards calculation shall restart. The issue is that we can only run for a max duration and if someone stakes for the first time after the max duration is over, the reward will always return 0. Thus, we need to restart from the duration that elapsed without generating rewards.
Source Code
function collectReward(uint256 restartTime) external {
        (uint256 withdrawalTime, uint256 amount) = getStakerCurrentReward(true, restartTime);
        require(withdrawalTime > 0 && amount > 0, "no valid reward");
        withdrawals[msg.sender] = withdrawalTime;
        _payReward(msg.sender, amount);
    }

withdrawTokensByOwner

Withdraws all token from the contract by Multisig.

function withdrawTokensByOwner(address _receiverAddress) external nonpayable onlyOwner 

Arguments

Name Type Description
_receiverAddress address The address where the tokens has to be transferred.
Source Code
function withdrawTokensByOwner(address _receiverAddress) external onlyOwner {
        uint256 value = SOV.balanceOf(address(this));
        _transferSOV(_receiverAddress, value);
    }

setAverageBlockTime

Changes average block time - based on blockchain

function setAverageBlockTime(uint256 _averageBlockTime) external nonpayable onlyOwner 

Arguments

Name Type Description
_averageBlockTime uint256
Source Code
function setAverageBlockTime(uint256 _averageBlockTime) external onlyOwner {
        averageBlockTime = _averageBlockTime;
    }

setBlock

This function computes the last staking checkpoint and calculates the corresponding block number using the average block time which is then added to the mapping checkpointBlockDetails.

function setBlock() external nonpayable
Source Code
function setBlock() external {
        uint256 lastCheckpointTime = staking.timestampToLockDate(block.timestamp);
        _setBlock(lastCheckpointTime);
    }

setHistoricalBlock

This function computes the block number using the average block time for a given historical checkpoint which is added to the mapping checkpointBlockDetails.

function setHistoricalBlock(uint256 _time) external nonpayable

Arguments

Name Type Description
_time uint256 Exact staking checkpoint time
Source Code
function setHistoricalBlock(uint256 _time) external {
        _setBlock(_time);
    }

setMaxDuration

Sets the max duration

function setMaxDuration(uint256 _duration) public nonpayable onlyOwner 

Arguments

Name Type Description
_duration uint256 Max duration for which rewards can be collected at a go (in seconds)
Source Code
function setMaxDuration(uint256 _duration) public onlyOwner {
        maxDuration = _duration;
    }

_computeRewardForDate

Internal function to calculate weighted stake

function _computeRewardForDate(address _staker, uint256 _block, uint256 _date) internal view
returns(weightedStake uint256)

Arguments

Name Type Description
_staker address Staker address
_block uint256 Last finalised block
_date uint256 The date to compute prior weighted stakes

Returns

The weighted stake

Source Code
function _computeRewardForDate(
        address _staker,
        uint256 _block,
        uint256 _date
    ) internal view returns (uint256 weightedStake) {
        weightedStake = staking.getPriorWeightedStake(_staker, _block, _date);
        if (stopBlock > 0 && stopBlock < _block) {
            uint256 previousWeightedStake =
                staking.getPriorWeightedStake(_staker, stopBlock, _date);
            if (previousWeightedStake < weightedStake) {
                weightedStake = previousWeightedStake;
            }
        }
    }

_payReward

Internal function to pay rewards

function _payReward(address _staker, uint256 amount) internal nonpayable

Arguments

Name Type Description
_staker address User address
amount uint256 the reward amount
Source Code
function _payReward(address _staker, uint256 amount) internal {
        require(SOV.balanceOf(address(this)) >= amount, "not enough funds to reward user");
        claimedBalances[_staker] = claimedBalances[_staker].add(amount);
        _transferSOV(_staker, amount);
    }

_transferSOV

transfers SOV tokens to given address

function _transferSOV(address _receiver, uint256 _amount) internal nonpayable

Arguments

Name Type Description
_receiver address the address of the SOV receiver
_amount uint256 the amount to be transferred
Source Code
function _transferSOV(address _receiver, uint256 _amount) internal {
        require(_amount != 0, "amount invalid");
        require(SOV.transfer(_receiver, _amount), "transfer failed");
        emit RewardWithdrawn(_receiver, _amount);
    }

_getCurrentBlockNumber

Determine the current Block Number

function _getCurrentBlockNumber() internal view
returns(uint256)
Source Code
function _getCurrentBlockNumber() internal view returns (uint256) {
        return block.number;
    }

_setBlock

Internal function to calculate and set block

function _setBlock(uint256 _checkpointTime) internal nonpayable

Arguments

Name Type Description
_checkpointTime uint256
Source Code
function _setBlock(uint256 _checkpointTime) internal {
        uint256 currentTS = block.timestamp;
        uint256 lastFinalisedBlock = _getCurrentBlockNumber() - 1;
        require(checkpointBlockDetails[_checkpointTime] == 0, "block number already set");
        uint256 checkpointBlock =
            lastFinalisedBlock.sub(((currentTS.sub(_checkpointTime)).div(averageBlockTime)));
        checkpointBlockDetails[_checkpointTime] = checkpointBlock;
    }

getStakerCurrentReward

Get staker's current accumulated reward

function getStakerCurrentReward(bool considerMaxDuration, uint256 restartTime) public view
returns(lastWithdrawalInterval uint256, amount uint256)

Arguments

Name Type Description
considerMaxDuration bool True: Runs for the maximum duration - used in tx not to run out of gas False - to query total rewards
restartTime uint256 The time from which the staking rewards calculation shall restart.

Returns

The timestamp of last withdrawal

Source Code
function getStakerCurrentReward(bool considerMaxDuration, uint256 restartTime)
        public
        view
        returns (uint256 lastWithdrawalInterval, uint256 amount)
    {
        uint256 weightedStake;
        uint256 lastFinalisedBlock = _getCurrentBlockNumber() - 1;
        uint256 currentTS = block.timestamp;
        uint256 duration;
        address staker = msg.sender;
        uint256 lastWithdrawal = withdrawals[staker];

        uint256 lastStakingInterval = staking.timestampToLockDate(currentTS);
        lastWithdrawalInterval = lastWithdrawal > 0 ? lastWithdrawal : startTime;
        if (lastStakingInterval <= lastWithdrawalInterval) return (0, 0);
        /* Normally the restart time is 0. If this function returns a valid lastWithdrawalInterval
		and zero amount - that means there were no valid rewards for that period. So the new period must start
		from the end of the last interval or till the time no rewards are accumulated i.e. restartTime */
        if (restartTime >= lastWithdrawalInterval) {
            uint256 latestRestartTime = staking.timestampToLockDate(restartTime);
            lastWithdrawalInterval = latestRestartTime;
        }

        if (considerMaxDuration) {
            uint256 addedMaxDuration = lastWithdrawalInterval.add(maxDuration);
            duration = addedMaxDuration < currentTS
                ? staking.timestampToLockDate(addedMaxDuration)
                : lastStakingInterval;
        } else {
            duration = lastStakingInterval;
        }
        for (uint256 i = lastWithdrawalInterval; i < duration; i += TWO_WEEKS) {
            uint256 referenceBlock = checkpointBlockDetails[i];
            if (referenceBlock == 0) {
                referenceBlock = lastFinalisedBlock.sub(
                    ((currentTS.sub(i)).div(averageBlockTime))
                );
            }
            if (referenceBlock < deploymentBlock) referenceBlock = deploymentBlock;
            weightedStake = weightedStake.add(_computeRewardForDate(staker, referenceBlock, i));
        }
        lastWithdrawalInterval = duration;
        amount = weightedStake.mul(BASE_RATE).div(DIVISOR);
    }

Contracts