MasterChefJoeV2 is a modified version of Sushi's MasterChefV2, which allows farms to offer two rewards instead of one.
For example, instead of just rewarding JOE, it has the ability to offer JOE and your project's token.
The only thing you need to get this to work with MasterChefJoeV2 is to implement a contract that conforms to the IRewarder interface.
This interface describes two functions:
interface IRewarder {
using SafeERC20 for IERC20;
function onJoeReward(address user, uint256 newLpAmount) external;
function pendingTokens(address user) external view returns (uint256 pending);
}
pendingTokens
is purely for displaying stats on the frontend.
The most important is onJoeReward
, which is called whenever a user harvests from our MasterChefJoeV2.
It is in this function where you would want to contain the logic to mint/transfer your project's tokens to the user.
The implementation is completely up to you and it is does not matter if your tokens are minted per block or per second - either will work.
But to make your life easier, we have implemented two types of rewarders: a simple version and a version if your project also uses a Sushi-style masterchef.
Both types come in per block or per second:
- contracts/rewarders/SimpleRewarderPerBlock.sol
- contracts/rewarders/SimpleRewarderPerSec.sol (recommended)
- contracts/rewarders/MasterChefRewarderPerBlock.sol
- contract/rewarders/MasterChefRewarderPerSec.sol
This is the version we recommend simply because it's the easiest and less prone to accidental failures.
The concept is simple: a fixed amount of reward tokens is transferred to the contract prior. Then our masterchef will distribute it according to the reward rate set on it. This requires no coordination with your own masterchef whatsoever.
Key points:
- Easy setup, no coordination with your masterchef.
- Needs to be funded with your reward tokens beforehand.
- Once the rewarder is funded with your reward tokens, there is no way to get them back.
Setup:
- The rewarder contract is deployed.
- A fixed amount of your token is transferred to the contract.
- The reward rate is set on the rewarder contract.
- The rewarder contract is added to the pool on our MasterChefJoeV2.
- Users will now be able to claim double rewards when they start staking.
To stop:
- Set reward rate on rewarder contract to 0.
This is only applicable if your project uses a Sushi-style MasterChef contract.
Even if it does, we still recommend the Simple Rewarder. But in some cases, your project may not be able to pre-fund the rewarder. In this case, the MasterChef Rewarder is suitable.
It works by creating a proxy pool in your own MasterChef using a dummy token, which the MasterChef Rewarder contract deposits in order to receive your reward tokens. Once it harvests your reward tokens, it is then able to distribute them to the users.
Key points:
- Requires coordination with your masterchef.
- Does not need pre-funding beforehand.
- Highly recommend not to change any pool weights and/or add new pools in your MasterChef for the duration of the rewarder is live. If you do need to change any pool's weights or add new pools, please inform us as it requires coordination to ensure users don't under/over harvest rewards.
Setup:
- Create a new dummy token,
DUMMY
, with supply of 1 (in Wei). - Transfer 1
DUMMY
to the deployer and then renounce ownership of the token. - Create a new pool in your MasterChef for
DUMMY
. - Deploy the rewarder contract.
- Approve the rewarder contract to spend 1
DUMMY
. - Call the
init()
method in IRewarder contract, passing in theDUMMY
token address - this will allow the rewarder to deposit the dummy token into your MasterChef and start receiving your rewards. - The rewarder contract is added to the pool on our MasterChefJoeV2.
- Users will now be able to claim double rewards when they start staking.
To stop:
- Set allocation point of dummy pool on your MasterChef to 0.
- Call
updatePool
on rewarder contract. - Set reward rate on rewarder contract to 0.
- Set allocation point on rewarder contract to 0.
None of these contracts are audited.
However, we have gone to extreme lengths to test every edge case possible. We implore you take a look at our test cases to be satisfied:
A quick note about testing with timestamp: it's less predictable than testing with blocks so instead of asserting the reward is an exact amount, we assert it falls within a certain range.
To run:
yarn test test/MasterChefJoeV2.test.ts
To run coverage:
yarn test:coverage --testfiles "test/MasterChefJoeV2.test.ts"
Coverage results:
File | Statements | Branches |
---|---|---|
MasterChefJoeV2.sol | 100% | 100% |
SimpleRewarderPerBlock.sol | 100% | 93.75% |
SimpleRewarerPerSec.sol | 100% | 93.75% |
MasterChefRewarderPerBlockMock.sol | 98.28% | 90.91% |
MasterChefRewarderPerSecMock.sol | 98.28% | 90.91% |
Notes:
- Rewarders have branches that are really hard to hit, hence why statements are not 100%.
- Same with the branches.