Lucene search

K
code423n4Code4renaCODE423N4:2023-09-VENUS-FINDINGS-ISSUES-628
HistoryOct 04, 2023 - 12:00 a.m.

A malicious user can reduce a staker's rewards

2023-10-0400:00:00
Code4rena
github.com
2
malicious user
staker's rewards
accrued interest
prime contract
vulnerability
loss
mitigation
manual review

AI Score

7.1

Confidence

Low

Lines of code

Vulnerability details

Impact

A user’s interest is accrued through the _executeBoost function, which calls _interestAccrued which performs calculations on how much the user has accrued. Said calculations are made by subtracting the user’s rewardIndex from the current market rewardIndex, multiplying the result by the user’s score and then dividing by EXP_SCALE.
The issue occurs when the rewardIndex difference multiplied by the user’s score is not larger than EXP_SCALE leading to an accrued interest of 0 even though the user’s rewardIndex was increased. Consequently, no rewards were accrued for that period but the user’s rewardIndex was increased.
This leads to a loss of the user’s accrued interest. Even though the impact may be minor, in some cases it could have a larger effect depending on the interest token decimals and liquidity distribution speed.

Proof of Concept

This coded POC presents a scenario where a malicious user calls the Prime contract’s function accrueInterestAndUpdateScore multiple times on a staker’s address, while the staker is accruing rewards.
Due to the MATIC token being the underlying token the fund losses are minor, however, if a token with fewer decimals like USDT is used the impact would be greater.
Insert these lines of code in the Prime.ts test file inside of the “PLP integration” describe block:

 it("reduces rewards", async () => {
      await prime["claimInterest(address,address)"](vmatic.address, user1.getAddress());
      for (let i = 0; i < 50; i++) {
        await prime.accrueInterestAndUpdateScore(user1.getAddress(), vmatic.address);
      }
      await prime["claimInterest(address,address)"](vmatic.address, user1.getAddress());
      const afterBalance = await matic.balanceOf(user1.getAddress());
      console.log("AFTER BALANCE: ", afterBalance.toString());
    })

As you can see from the log the after balance is: 52999999999999969572.
If the for loop is replaced with: await mine(50);, which has the same effect on the block.timestamp and is the intended way of implementation, the result is: 52999999999999998988 ( > 52999999999999969572).
Therefore, the staker suffered a loss of accrued interest.

Test scenario without the malicious behaviour:

 it("reduces rewards", async () => {
      await prime["claimInterest(address,address)"](vmatic.address, user1.getAddress());
      await mine(50);
      await prime["claimInterest(address,address)"](vmatic.address, user1.getAddress());
      const afterBalance = await matic.balanceOf(user1.getAddress());
      console.log("AFTER BALANCE: ", afterBalance.toString());
    })

Tools Used

Manual review

Recommended Mitigation Steps

Do not increase a user’s rewardIndex if (index * score) < EXP_SCALE in the _interestAccrued function.

Assessed type

Other


The text was updated successfully, but these errors were encountered:

All reactions

AI Score

7.1

Confidence

Low