Full Report
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.
Analysis Summary
# Research: Understanding SushiSwap's MasterChef Staking Rewards
## Metadata
- **Authors:** Mark Kop
- **Institution:** Halborn (Software Engineer)
- **Publication:** DEV Community
- **Date:** April 20, 2022
## Abstract
This technical analysis deconstructs the SushiSwap MasterChef smart contract, the industry standard for decentralized finance (DeFi) staking rewards. The research explains the transition from a linear, gas-intensive reward calculation (looping through users) to a highly efficient constant-time complexity ($O(1)$) algorithm. The article clarifies the mathematical roles of `accSushiPerShare` and `rewardDebt` in maintaining fair distribution without exhaustive iteration.
## Research Objective
The study addresses the scalability problem in blockchain staking: **How can a smart contract distribute rewards to an infinite number of users without hitting gas limits?** The objective is to explain the "MasterChef" algorithm that avoids $O(n)$ loops through stakers by using accumulated global state variables.
## Methodology
### Approach
The researcher employs a **first-principles mathematical simulation** followed by a code-level deconstruction. By comparing a "naive" manual reward calculation (tracking every user per block) against the MasterChef implementation, the author illustrates the logic of "reward debt" and accumulated shares.
### Dataset/Environment
- **Primary Artifact:** The SushiSwap `MasterChef.sol` smart contract (Open Source on GitHub).
- **Environment:** Ethereum Virtual Machine (EVM) context, specifically focusing on Solidity's lack of floating-point arithmetic and gas exhaustion vulnerabilities.
### Tools & Technologies
- **Solidity:** For smart contract analysis.
- **Fixed-Point Arithmetic:** Used to handle precision in reward distribution.
## Key Findings
### Primary Results
1. **Accumulated Distribution:** Instead of calculating rewards *per user* per block, the contract tracks a global `accSushiPerShare` (Accumulated Rewards Per Share).
2. **Reward Debt Logic:** The `rewardDebt` variable serves as a mathematical "offset," ensuring that users who join late do not receive rewards accumulated before their entry.
3. **Gas Optimization:** The calculation is performed only when a user interacts with the pool (deposit, withdraw, harvest), shifting the computational burden from the protocol to the individual transacting user.
### Supporting Evidence
- **Numerical Simulation:** Using a $1/block reward scenario, the author demonstrates that manually tracking $14 for Staker A and $16 for Staker B matches the output of the MasterChef mathematical formula exactly.
### Novel Contributions
- Explains the **Accumulation/Offset pattern**, which has since become the "gold standard" for yield farming and liquidity mining in DeFi.
## Technical Details
The core logic relies on the following formula for a user's pending rewards:
$$\text{Pending} = (\text{UserShare} \times \text{accSushiPerShare}) - \text{rewardDebt}$$
- **accSushiPerShare:** A monotonically increasing value representing the total rewards one share would have earned since the pool's inception.
- **rewardDebt:** Set when a user enters/modifies their position. It equals `user.amount * accSushiPerShare`. This effectively "zeros out" their rewards at the moment of entry.
- **Precision Handling:** Because Solidity does not support decimals, the contract multiplies values by `1e12` to maintain high precision during divisions.
## Practical Implications
### For Security Practitioners
- **Inflation Risks:** Understanding these rewards is critical for auditing "minting" logic. If `sushiPerBlock` is manipulated, the entire pool's rewards can be drained.
- **Rounding Errors:** Security professionals must check for "dust" accumulations or precision loss in the `1e12` scaling factor.
### For Defenders
- **Gas Limit Protection:** By using this $O(1)$ approach, defenders protect the contract from "Denial of Service" (DoS) attacks that would occur if the contract had to loop through thousands of stakers to distribute rewards.
### For Researchers
- **Template for DeFi:** Most forks (PancakeSwap, Goose Finance, etc.) use this exact logic. Understanding this one contract provides a schematic for auditing a vast percentage of the DeFi ecosystem.
## Limitations
- **Static Rewards:** The analysis notes that the reward per block is often static or changed manually by an owner, rather than being dynamically responsive to external market values.
- **Fixed-Point Constraints:** While `1e12` provides precision, it still carries a theoretical risk of overflow if the reward tokens or accumulation periods are extremely large.
## Comparison to Prior Work
The "Naive Initial Approach" (looping through users) is shown to be functionally correct but economically impossible on-chain. This work builds on the "Scalable Reward Distribution" concepts pioneered by Synthetix and refined by SushiSwap, providing a more accessible entry point for developers.
## Real-world Applications
- **Liquidity Mining:** Encouraging users to provide liquidity by offering "governance tokens" in return.
- **Staking Tiers:** Can be adapted to handle weighted staking where certain users have multipliers.
## Future Work
- **Dynamic Reward Scaling:** Investigating algorithms where the `rewardPerBlock` scales based on Total Value Locked (TVL) or external price oracles.
- **MasterChef V2/V3:** Exploring how multiple reward tokens (Dual Rewards) are managed using this same accumulation logic.
## References
- [SushiSwap MasterChef.sol GitHub](https://github.com/sushiswap/sushiswap/blob/master/contracts/MasterChef.sol)
- [Solidity Documentation - Fixed-Point Arithmetic]
- [Synthetix Staking Rewards (Prior Art)]