Staking. What happens behind the scenes. Brief code walkthrough and mathematical calculation
amoweolubusayo
Posted on June 11, 2022
Hello again. I'm here to talk DeFi. Have you ever had to stake? Wonder what happens behind the scenes? I have staked on platforms like Binance and I have always wondered how the calculations were done. Haven't staked before? No worries. I will give a brief description of what staking means.
Think of staking as mining but using lesser resources. So like you know people mine bitcoin for example and are rewarded for it, for humor purposes only, some people also mine the famous PI in hope for rewards. Staking works similarly but instead of mining, you lock your crypto in order to receive rewards. Your locked crypto helps to secure and assist in operations of a blockchain network.
After doing some research, I got to know about what I would call The MasterChef Algorithm.
In this article, I will be taking you through Pancakeswap's MasterChef contract briefly as well as the basic Mathematical calculation involved in staking.
As it turns out, most staking contracts implement the MasterChef contract. Now, let's proceed to understanding basic variable meanings.
contract MasterChef is Ownable {
using SafeMath for uint256;
using SafeBEP20 for IBEP20;
struct UserInfo {
uint256 amount;
uint256 rewardDebt;
}
struct PoolInfo {
IBEP20 lpToken;
uint256 allocPoint;
uint256 lastRewardBlock;
uint256 accCakePerShare;
}
From this code above, there is a struct called
UserInfo
. This represents a user who has come in to stake.
unit256 amount
unit256 rewardDebt
amount
here stands for how many Liquidity Pool(LP) tokens the user has provided.
Notice we also have rewardDebt
. Reward debt is basically the reward that a user isn't entitled to.
The next struct is the
PoolInfo
This stands for the information of each pool. This pool is where tokens are locked in a smart contract
IBEP20 lpToken
uint256 allocPoint
uint256 lastRewardBlock
uint256 accCakePerShare
lpToken
here stands for the contract address of LP token.
allocPoint
here means that: How many tokens (which in this case is CAKE) is to be distributed to this pool? How many allocation points is assigned to this pool?
lastRewardBlock
here means that: What block number was the token (CAKE) distributed to last?
accCakePerShare
here means the Accumulated token (CAKEs) per share
function enterStaking(uint256 _amount) public {
PoolInfo storage pool = poolInfo[0];
UserInfo storage user = userInfo[0][msg.sender];
updatePool(0);
if (user.amount > 0) {
uint256 pending = user.amount.mul(pool.accCakePerShare).div(1e12).sub(user.rewardDebt);
if(pending > 0) {
safeCakeTransfer(msg.sender, pending);
}
}
if(_amount > 0) {
pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
user.amount = user.amount.add(_amount);
}
user.rewardDebt = user.amount.mul(pool.accCakePerShare).div(1e12);
}
This function above is a call to stake CAKE tokens to MasterChef and this function below is a call to Withdraw CAKE tokens from STAKING.
function leaveStaking(uint256 _amount) public {
PoolInfo storage pool = poolInfo[0];
UserInfo storage user = userInfo[0][msg.sender];
require(user.amount >= _amount, "withdraw: not good");
updatePool(0);
uint256 pending = user.amount.mul(pool.accCakePerShare).div(1e12).sub(user.rewardDebt);
if(pending > 0) {
safeCakeTransfer(msg.sender, pending);
}
if(_amount > 0) {
user.amount = user.amount.sub(_amount);
pool.lpToken.safeTransfer(address(msg.sender), _amount);
}
user.rewardDebt = user.amount.mul(pool.accCakePerShare).div(1e12);
syrup.burn(msg.sender, _amount);
emit Withdraw(msg.sender, 0, _amount);
}
With this code, let's do a little simulation.
Assuming that on every block, the reward a user can get is $1.
RewardsPerBlock = $1
User A deposits $100 on block 0
User B deposits $400 on block 10
We need to note these variables
RewardsPerBlock = Reward you can get per block
NumberOfBlocks = Number of blocks
TotalTokens = Total Tokens present in the block
DepositUserA = How much User A deposited
ShareUserA = Share for User A
AccumulatedRewardUserA = Accumulated Reward for User A
ShareUserA = DepositUserA / TotalTokens
AccumulatedRewardUserA = (RewardsPerBlock * NumberOfBlocks * ShareUserA) + AccumulatedRewardUserA previously
From block 0 to block 10, User A gets 100% of their rewards, which is calculated like this
Block 0 - Block 10
RewardsPerBlock = $1
NumberOfBlocks = 10
TotalTokens = $100
DepositUserA = $100
ShareUserA = 100/100 = 1
AccumulatedRewardUserA = (1 * 10 * 1) + 0 = $10
Now on block 10, a new User B who is just joining the pool deposits $400. It might interest you to know that User B will also get 100% of rewards from block 0 to 10. However, it becomes a debt. Some other contract use timestamp instead of a debt. If User B decides to harvest at block 15, User B's reward will be calculated as
Reward(Block 10 to Block 15) - Reward(Block 0 to Block 10)
The Reward(Block 0 to Block 10) in this case is the rewardDebt
Let's move to block 15. At block 15, User A wants to harvest their reward.
From block 10 to block 15, can you guess how much reward User A will get?
Block 10 - Block 15
Remember there is now User B
RewardsPerBlock = $1
NumberOfBlocks = 5
TotalTokens = $100 + $400 = $500
DepositUserA = $100
ShareUserA = 100/500 = 1/5
AccumulatedRewardUserA = (1 * 5 * 1/5) + 10 = $1 + $10 = $11
DepositUserB = $400
ShareUserB = 400/500 = 4/5
AccumulatedRewardUserB = (1 * 5 * 4/5) + 0 = $4
UserA will now 'harvest' the AccumulatedRewardUserA
thus getting $11.
After harvest, the pool needs to balance so AccumulatedRewardUserA = $0
Let's move to block 20. At block 20, User B wants to harvest their reward.
Block 15 - Block 20
RewardsPerBlock = $1
NumberOfBlocks = 5
TotalTokens = $100 + $400 = $500
DepositUserB = $400
ShareUserB = 400/500 = 4/5
AccumulatedRewardUserB = (1 * 5 * 4/5) + 4 = $4 + $4 = $8
UserB will now 'harvest' the AccumulatedRewardUserB
thus getting $8.
After harvest, the pool needs to balance so AccumulatedRewardUserB = $0
Let's move to block 30. At block 30, Both User A and User B wants to harvest their reward.
Block 20 - Block 30
RewardsPerBlock = $1
NumberOfBlocks = 10
TotalTokens = $100 + $400 = $500
DepositUserA = $100
ShareUserA = 100/500 = 1/5
AccumulatedRewardUserA = (1 * 10 * 1/5) + 0 = $2
DepositUserB = $400
ShareUserB = 400/500 = 4/5
AccumulatedRewardUserB = (1 * 10 * 4/5) + 0 = $8
The Total Number of harvested reward
User A = AccumulatedRewardUserA (Block 15) + AccumulatedRewardUserA (Block 30)
User A = $11 + $2 = $13
User B = AccumulatedRewardUserB (Block 20) + AccumulatedRewardUserB (Block 30)
User B = $8 + $8 = $16
User A harvested $13 as reward in total
User B harvested $16 as reward in total
With this base knowledge, you can chose to add your own customizable feature to your DeFi app.
This article is just gives the basic overview of the Algorithm. You will get more insight by implementing a contract of your own. I encourage you to do that.
Final Words
I hope this makes sense and you now understand staking better. Feel free to drop a comment or question. I'm willing to interact.
References
https://dev.to/heymarkkop/understanding-sushiswaps-masterchef-staking-rewards-1m6f
https://github.com/pancakeswap/pancake-farm/blob/master/contracts/MasterChef.sol
Posted on June 11, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.