In the Synthetix Staking Contract or even the following simple staking contract from Smart Contract Programmer there are a few parameters that govern the rewards dispersed, namely rewardRate
and rewardPerToken
.
Full Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
contract StakingRewards {
IERC20 public rewardsToken;
IERC20 public stakingToken;
uint public rewardRate = 100;
uint public lastUpdateTime;
uint public rewardPerTokenStored;
mapping(address => uint) public userRewardPerTokenPaid;
mapping(address => uint) public rewards;
uint private _totalSupply;
mapping(address => uint) private _balances;
constructor(address _stakingToken, address _rewardsToken) {
stakingToken = IERC20(_stakingToken);
rewardsToken = IERC20(_rewardsToken);
}
function rewardPerToken() public view returns (uint) {
if (_totalSupply == 0) {
return rewardPerTokenStored;
}
return
rewardPerTokenStored +
(((block.timestamp - lastUpdateTime) * rewardRate * 1e18) / _totalSupply);
}
function earned(address account) public view returns (uint) {
return
((_balances[account] *
(rewardPerToken() - userRewardPerTokenPaid[account])) / 1e18) +
rewards[account];
}
modifier updateReward(address account) {
rewardPerTokenStored = rewardPerToken();
lastUpdateTime = block.timestamp;
rewards[account] = earned(account);
userRewardPerTokenPaid[account] = rewardPerTokenStored;
_;
}
function stake(uint _amount) external updateReward(msg.sender) {
_totalSupply += _amount;
_balances[msg.sender] += _amount;
stakingToken.transferFrom(msg.sender, address(this), _amount);
}
function withdraw(uint _amount) external updateReward(msg.sender) {
_totalSupply -= _amount;
_balances[msg.sender] -= _amount;
stakingToken.transfer(msg.sender, _amount);
}
function getReward() external updateReward(msg.sender) {
uint reward = rewards[msg.sender];
rewards[msg.sender] = 0;
rewardsToken.transfer(msg.sender, reward);
}
}
interface IERC20 {
function totalSupply() external view returns (uint);
function balanceOf(address account) external view returns (uint);
function transfer(address recipient, uint amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}
I'm having a hard time understanding all the functions.
The ones that make sense:
earned
: function clearly just says how much a user has earned over.
The ones I'm having a hard time with:
rewardRate
: Is this like reward per second per token?rewardPerToken
: This is clearly the reward to be given per token staked, but why is the total supply being divided in the function?
Any and all help appreciated.