How would I go about implementing a staking contract similar to pancakeswap farms or synthetix staking rewards but with a reward rate which can be changed in the midst of staking duration? Where users gain rewards based on previous rate until the change and new rate after the change.
1 Answer
You could have a struct (timestamp, reward rate) and an array of those structs. Then when users calculate their rewards they'll go find the reward rate at the time of their deposit, multiply that by time until either next timestamp or present time, and continue along those lines.
Some example code:
uint256 mostRecentIndex;
struct rewardRate {
uint256 timestamp;
uint256 rewardPerSecond; //Multiplied by 10**18
}
struct user {
uint256 rewardsEarned;
uint256 amountStaked;
uint256 timestampDeposited;
uint256 currentRewardsIndex;
}
mapping(uint256 => rewardRate) rewards;
function claimRewards {
uint256 currentIndex = user.currentRewardsIndex;
uint256 previousTimestamp = user.timestampDeposited;
uint256 totalRewards;
//Add rewards for past indices if necessary
for (currentIndex; currentIndex < mostRecentIndex; currentIndex++) {
uint256 nextTimestamp = rewardRate[currentIndex+1].timestamp;
totalRewards += (nextTimestamp - previousTimestamp) * rewardRate[currentIndex].rewardPerSecond;
previousTimestamp = nextTimestamp;
}
//Add rewards for current index
totalRewards += (block.timestamp - previousTimestamp) * rewardRate[currentIndex];
user.rewardsEarned += totalRewards;
user.timestampDeposited = block.timestamp;
user.currentRewardsIndex = currentIndex;
}