Resources

People often ask me "How did you learn how to hack?" The answer: by reading. This page is a collection of the blog posts and other articles that I have accumulated over the years of my journey. Enjoy!

Understanding SushiSwap's MasterChef staking rewards - 1175

Mark KopPosted 2 Years Ago
  • Most staking contracts are simply a copy of the SushiSwap MasterChef contract. By understanding this, we will be able to understand most in-moment math handling and most staking contracts.
  • The original implementation that is shown uses a for loop for each of the stakers. Anytime an operation is performed, the rewards information is updated for each staking user. The following is used for the math:
    • blocksSinceLastReward: The amount of blocks since the reward information has been updated.
    • rewardTokensPerBlock: The amount of tokens that should be given per block.
    • stakerShare: The percentage of tokens of the whole staked. Since this is Solidity without floats, there is using fixed point arithmetic.
    • The algorithm for the rewards: blocksSinceLastReward * rewardTokensPerBlock * stakerShare
  • The problem with this implementation is the loop for every user. Altogether, the algorithm for this is defined below:
    (BlockARewards / TotalTokensBlockA) + 
    (BlockBRewards / TotalTokensBlockB) + ...
    (BlockNRewards / TotalTokensBlockN)
    
  • Instead of this, we can think about it differently: StakerAShareOnNtoM = StakerATokensOnNtoM / TotalTokensOnNtoM. This means that we're calculating the tokens we had over the timeframe of blocks and dividing this by the total tokens in this timeframe. This gives us the total shares of a user over a specific timeframe.
  • The trick to the Sushiswap algorithm is calculating RewardsPerShare instead of calculating the rewards per block for every user. This is because the RewardsPerShare is the same for every user. To reward a single user, we calculate the amount of shares for a given user.
  • At this point, we're keeping track of the rewards per share for each block but we ALSO need a way to subtract the previous rewards from a user. This is done by having a global AccumulatedRewardsPerShare that is stored for the last time a user performed some operation. Using accumulatedRewardsPerShare - previousAccumulatedRewardsPerShare allows us to calculate the proper amount for this.
  • Overall, a super interesting in-moment math handling to make the contract much more gas efficient. The article comes with numerical examples that make this easier to understand as well. I was surprised that the amount of tokens being added per block was static; I was expecting it to be dynamic to the some other value.