Skip to content

Commit

Permalink
Merge pull request #257 from VenusProtocol/feat/stop-rewards
Browse files Browse the repository at this point in the history
[VEN-1585] Stop Rewards at Specified Block
  • Loading branch information
web3rover authored Jul 10, 2023
2 parents f546394 + 71a36e6 commit f308a8c
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 6 deletions.
18 changes: 16 additions & 2 deletions contracts/Lens/PoolLens.sol
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ contract PoolLens is ExponentialNoError {
uint224 index;
// The block number the index was last updated at
uint32 block;
// The block number at which to stop rewards
uint32 lastRewardingBlock;
}

/**
Expand Down Expand Up @@ -444,9 +446,11 @@ contract PoolLens is ExponentialNoError {
for (uint256 i; i < markets.length; ++i) {
// Market borrow and supply state we will modify update in-memory, in order to not modify storage
RewardTokenState memory borrowState;
(borrowState.index, borrowState.block) = rewardsDistributor.rewardTokenBorrowState(address(markets[i]));
(borrowState.index, borrowState.block, borrowState.lastRewardingBlock) = rewardsDistributor
.rewardTokenBorrowState(address(markets[i]));
RewardTokenState memory supplyState;
(supplyState.index, supplyState.block) = rewardsDistributor.rewardTokenSupplyState(address(markets[i]));
(supplyState.index, supplyState.block, supplyState.lastRewardingBlock) = rewardsDistributor
.rewardTokenSupplyState(address(markets[i]));
Exp memory marketBorrowIndex = Exp({ mantissa: markets[i].borrowIndex() });

// Update market supply and borrow index in-memory
Expand Down Expand Up @@ -484,6 +488,11 @@ contract PoolLens is ExponentialNoError {
) internal view {
uint256 borrowSpeed = rewardsDistributor.rewardTokenBorrowSpeeds(vToken);
uint256 blockNumber = block.number;

if (borrowState.lastRewardingBlock > 0 && blockNumber > borrowState.lastRewardingBlock) {
blockNumber = borrowState.lastRewardingBlock;
}

uint256 deltaBlocks = sub_(blockNumber, uint256(borrowState.block));
if (deltaBlocks > 0 && borrowSpeed > 0) {
// Remove the total earned interest rate since the opening of the market from total borrows
Expand All @@ -505,6 +514,11 @@ contract PoolLens is ExponentialNoError {
) internal view {
uint256 supplySpeed = rewardsDistributor.rewardTokenSupplySpeeds(vToken);
uint256 blockNumber = block.number;

if (supplyState.lastRewardingBlock > 0 && blockNumber > supplyState.lastRewardingBlock) {
blockNumber = supplyState.lastRewardingBlock;
}

uint256 deltaBlocks = sub_(blockNumber, uint256(supplyState.block));
if (deltaBlocks > 0 && supplySpeed > 0) {
uint256 supplyTokens = VToken(vToken).totalSupply();
Expand Down
91 changes: 87 additions & 4 deletions contracts/Rewards/RewardsDistributor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ contract RewardsDistributor is ExponentialNoError, Ownable2StepUpgradeable, Acce
uint224 index;
// The block number the index was last updated at
uint32 block;
// The block number at which to stop rewards
uint32 lastRewardingBlock;
}

/// @notice The initial REWARD TOKEN index for a market
Expand Down Expand Up @@ -112,6 +114,12 @@ contract RewardsDistributor is ExponentialNoError, Ownable2StepUpgradeable, Acce
/// @notice Emitted when a reward for contributor is updated
event ContributorRewardsUpdated(address indexed contributor, uint256 rewardAccrued);

/// @notice Emitted when a reward token last rewarding block for supply is updated
event SupplyLastRewardingBlockUpdated(address indexed vToken, uint32 newBlock);

/// @notice Emitted when a reward token last rewarding block for borrow is updated
event BorrowLastRewardingBlockUpdated(address indexed vToken, uint32 newBlock);

modifier onlyComptroller() {
require(address(comptroller) == msg.sender, "Only comptroller can call this function");
_;
Expand Down Expand Up @@ -224,16 +232,39 @@ contract RewardsDistributor is ExponentialNoError, Ownable2StepUpgradeable, Acce
) external {
_checkAccessAllowed("setRewardTokenSpeeds(address[],uint256[],uint256[])");
uint256 numTokens = vTokens.length;
require(
numTokens == supplySpeeds.length && numTokens == borrowSpeeds.length,
"RewardsDistributor::setRewardTokenSpeeds invalid input"
);
require(numTokens == supplySpeeds.length && numTokens == borrowSpeeds.length, "invalid setRewardTokenSpeeds");

for (uint256 i; i < numTokens; ++i) {
_setRewardTokenSpeed(vTokens[i], supplySpeeds[i], borrowSpeeds[i]);
}
}

/**
* @notice Set REWARD TOKEN last rewarding block for the specified markets
* @param vTokens The markets whose REWARD TOKEN last rewarding block to update
* @param supplyLastRewardingBlocks New supply-side REWARD TOKEN last rewarding block for the corresponding market
* @param borrowLastRewardingBlocks New borrow-side REWARD TOKEN last rewarding block for the corresponding market
*/
function setLastRewardingBlocks(
VToken[] calldata vTokens,
uint32[] calldata supplyLastRewardingBlocks,
uint32[] calldata borrowLastRewardingBlocks
) external {
_checkAccessAllowed("setLastRewardingBlock(address[],uint32[],uint32[])");
uint256 numTokens = vTokens.length;
require(
numTokens == supplyLastRewardingBlocks.length && numTokens == borrowLastRewardingBlocks.length,
"RewardsDistributor::setLastRewardingBlocks invalid input"
);

for (uint256 i; i < numTokens; ) {
_setLastRewardingBlock(vTokens[i], supplyLastRewardingBlocks[i], borrowLastRewardingBlocks[i]);
unchecked {
++i;
}
}
}

/**
* @notice Set REWARD TOKEN speed for a single contributor
* @param contributor The contributor whose REWARD TOKEN speed to update
Expand Down Expand Up @@ -318,6 +349,47 @@ contract RewardsDistributor is ExponentialNoError, Ownable2StepUpgradeable, Acce
return block.number;
}

/**
* @notice Set REWARD TOKEN last rewarding block for a single market.
* @param vToken market's whose reward token last rewarding block to be updated
* @param supplyLastRewardingBlock New supply-side REWARD TOKEN last rewarding block for market
* @param borrowLastRewardingBlock New borrow-side REWARD TOKEN last rewarding block for market
*/
function _setLastRewardingBlock(
VToken vToken,
uint32 supplyLastRewardingBlock,
uint32 borrowLastRewardingBlock
) internal {
require(comptroller.isMarketListed(vToken), "rewardToken market is not listed");

uint256 blockNumber = getBlockNumber();

require(supplyLastRewardingBlock > blockNumber, "setting last rewarding block in the past is not allowed");
require(borrowLastRewardingBlock > blockNumber, "setting last rewarding block in the past is not allowed");

uint32 currentSupplyLastRewardingBlock = rewardTokenSupplyState[address(vToken)].lastRewardingBlock;
uint32 currentBorrowLastRewardingBlock = rewardTokenBorrowState[address(vToken)].lastRewardingBlock;

require(
currentSupplyLastRewardingBlock == 0 || currentSupplyLastRewardingBlock > blockNumber,
"this RewardsDistributor is already locked"
);
require(
currentBorrowLastRewardingBlock == 0 || currentBorrowLastRewardingBlock > blockNumber,
"this RewardsDistributor is already locked"
);

if (currentSupplyLastRewardingBlock != supplyLastRewardingBlock) {
rewardTokenSupplyState[address(vToken)].lastRewardingBlock = supplyLastRewardingBlock;
emit SupplyLastRewardingBlockUpdated(address(vToken), supplyLastRewardingBlock);
}

if (currentBorrowLastRewardingBlock != borrowLastRewardingBlock) {
rewardTokenBorrowState[address(vToken)].lastRewardingBlock = borrowLastRewardingBlock;
emit BorrowLastRewardingBlockUpdated(address(vToken), borrowLastRewardingBlock);
}
}

/**
* @notice Set REWARD TOKEN speed for a single market.
* @param vToken market's whose reward token rate to be updated
Expand Down Expand Up @@ -455,7 +527,13 @@ contract RewardsDistributor is ExponentialNoError, Ownable2StepUpgradeable, Acce
RewardToken storage supplyState = rewardTokenSupplyState[vToken];
uint256 supplySpeed = rewardTokenSupplySpeeds[vToken];
uint32 blockNumber = safe32(getBlockNumber(), "block number exceeds 32 bits");

if (supplyState.lastRewardingBlock > 0 && blockNumber > supplyState.lastRewardingBlock) {
blockNumber = supplyState.lastRewardingBlock;
}

uint256 deltaBlocks = sub_(uint256(blockNumber), uint256(supplyState.block));

if (deltaBlocks > 0 && supplySpeed > 0) {
uint256 supplyTokens = VToken(vToken).totalSupply();
uint256 accruedSinceUpdate = mul_(deltaBlocks, supplySpeed);
Expand Down Expand Up @@ -484,6 +562,11 @@ contract RewardsDistributor is ExponentialNoError, Ownable2StepUpgradeable, Acce
RewardToken storage borrowState = rewardTokenBorrowState[vToken];
uint256 borrowSpeed = rewardTokenBorrowSpeeds[vToken];
uint32 blockNumber = safe32(getBlockNumber(), "block number exceeds 32 bits");

if (borrowState.lastRewardingBlock > 0 && blockNumber > borrowState.lastRewardingBlock) {
blockNumber = borrowState.lastRewardingBlock;
}

uint256 deltaBlocks = sub_(uint256(blockNumber), uint256(borrowState.block));
if (deltaBlocks > 0 && borrowSpeed > 0) {
uint256 borrowAmount = div_(VToken(vToken).totalBorrows(), marketBorrowIndex);
Expand Down
7 changes: 7 additions & 0 deletions tests/hardhat/Lens/RewardsSummary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,12 @@ const rewardsFixture = async (): Promise<RewardsFixtire> => {
rewardDistributor1.rewardTokenBorrowState.returns({
index: convertToUnit(1, 18),
block: startBlock,
lastRewardingBlock: 0,
});
rewardDistributor1.rewardTokenSupplyState.returns({
index: convertToUnit(1, 18),
block: startBlock,
lastRewardingBlock: 0,
});

rewardDistributor2.rewardToken.returns(rewardToken2.address);
Expand All @@ -91,10 +93,12 @@ const rewardsFixture = async (): Promise<RewardsFixtire> => {
rewardDistributor2.rewardTokenBorrowState.returns({
index: convertToUnit(1, 18),
block: startBlock,
lastRewardingBlock: 0,
});
rewardDistributor2.rewardTokenSupplyState.returns({
index: convertToUnit(1, 18),
block: startBlock,
lastRewardingBlock: 0,
});

rewardDistributor3.rewardToken.returns(rewardToken3.address);
Expand All @@ -108,10 +112,12 @@ const rewardsFixture = async (): Promise<RewardsFixtire> => {
rewardDistributor3.rewardTokenBorrowState.returns({
index: convertToUnit(1, 18),
block: startBlock,
lastRewardingBlock: 0,
});
rewardDistributor3.rewardTokenSupplyState.returns({
index: convertToUnit(1, 18),
block: startBlock,
lastRewardingBlock: 0,
});

vBUSD.borrowIndex.returns(convertToUnit(1, 18));
Expand Down Expand Up @@ -232,6 +238,7 @@ describe("PoolLens: Rewards Summary", () => {
rewardDistributor3.rewardTokenBorrowState.returns({
index: convertToUnit(1, 36), // Current index is 1.0, double scale
block: await ethers.provider.getBlockNumber(),
lastRewardingBlock: 0,
});
rewardDistributor3.INITIAL_INDEX.returns(convertToUnit(0.6, 36)); // Should start accruing rewards at 0.6 of the current index

Expand Down
45 changes: 45 additions & 0 deletions tests/hardhat/Rewards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,4 +320,49 @@ describe("Rewards: Tests", async function () {
*/
expect((await xvs.balanceOf(user1.address)).toString()).be.equal(convertToUnit(500.5, 18));
});

it("pause rewards", async () => {
const [, user1, user2] = await ethers.getSigners();

await mockWBTC.connect(user1).faucet(convertToUnit(100, 8));
await mockDAI.connect(user2).faucet(convertToUnit(10000, 18));

await mockWBTC.connect(user1).approve(vWBTC.address, convertToUnit(10, 8));
await vWBTC.connect(user1).mint(convertToUnit(10, 8));

let lastRewardingBlock = (await ethers.provider.getBlockNumber()) + 2;

await rewardsDistributor.setLastRewardingBlocks(
[vWBTC.address, vDAI.address],
[lastRewardingBlock, lastRewardingBlock],
[lastRewardingBlock, lastRewardingBlock],
);

await mine(100);

await rewardsDistributor.functions["claimRewardToken(address,address[])"](user1.address, [
vWBTC.address,
vDAI.address,
]);

expect((await xvs.balanceOf(user1.address)).toString()).to.be.equal(convertToUnit(0.5, 18));

await expect(
rewardsDistributor.setLastRewardingBlocks(
[vWBTC.address, vDAI.address],
[lastRewardingBlock - 10, lastRewardingBlock - 10],
[lastRewardingBlock - 10, lastRewardingBlock - 10],
),
).to.be.revertedWith("setting last rewarding block in the past is not allowed");

lastRewardingBlock = (await ethers.provider.getBlockNumber()) + 2;

await expect(
rewardsDistributor.setLastRewardingBlocks(
[vWBTC.address, vDAI.address],
[lastRewardingBlock, lastRewardingBlock],
[lastRewardingBlock, lastRewardingBlock],
),
).to.be.revertedWith("this RewardsDistributor is already locked");
});
});

0 comments on commit f308a8c

Please sign in to comment.