Platform: Code4rena
Start Date: 08/05/2023
Pot Size: $90,500 USDC
Total HM: 17
Participants: 102
Period: 7 days
Judge: 0xean
Total Solo HM: 4
Id: 236
League: ETH
Rank: 21/102
Findings: 2
Award: $798.59
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Team_Rocket
Also found by: 0xkazim, BPZ, Bauchibred, BoltzmannBrain, Brenzee, DeliChainSec, Franfran, Lilyjjo, MohammedRizwan, SaeedAlipoor01988, Yardi256, ast3ros, berlin-101, carlitox477, fs0c, peritoflores, sashik_eth, sces60107, thekmj, volodya, zzykxx
66.5871 USDC - $66.59
In WhitePaperInterestRateModel
, the code is using 2102400
as its value of blocksPerYear
. This is equivalent to a block every 15 seconds (a.k.a. 4 times a minute), since 4 * 60 * 24 * 365 == 2102400
. However, Venus protocol is deployed on BSC, which produces a block every 3 seconds, so blocksPerYear
should be 5 times larger at 10512000
(which matches the value in BaseJumpRateModelV2
). Because of this discrepancy, every market that uses the WhitePaperInterestRateModel
will have interest rates 5x larger than they were intended when baseRatePerYear
and multiplierPerYear
was passed in the constructor. This can lead to unexpected behavior and individuals might accidentally borrow at a much higher rate than they expected.
See blocksPerYear.
Manual review.
Change blocksPerYear
to 10512000
in WhitePaperInterestRateModel
. Also keep this in mind for the future - if BSC produces blocks at a different rate, then the interest rate models should be updated.
Timing
#0 - c4-judge
2023-05-16T09:23:39Z
0xean marked the issue as duplicate of #559
#1 - c4-judge
2023-06-05T14:03:07Z
0xean marked the issue as satisfactory
#2 - c4-judge
2023-06-05T14:38:22Z
0xean changed the severity to 2 (Med Risk)
#3 - c4-judge
2023-06-05T14:38:32Z
0xean changed the severity to 3 (High Risk)
🌟 Selected for report: xuwinnie
Also found by: BoltzmannBrain, Udsen, mussucal
731.996 USDC - $732.00
In the liquidateAccount
control flow, the user must specify the repayAmount
for each element of the orders
array. This value is passed directly to the VToken
function forceLiquidateBorrow
, which eventually directly passes it to preLiquidateHook
. Since skipLiquidityCheck
is hardcoded to true
in this control flow, the preLiquidateHook
will revert if repayAmount > borrowBalance
. On the other hand, if repayAmount < borrowBalance
, then the borrowBalance == 0
check at the end of liquidateAccount
will cause a revert.
So, the liquidateAccount
function must be called with the exact correct repayAmount
. Since this exact correct amount changes every 3s as the account accumulates interest each block, it will be very difficult for a user to liquidate an account properly. Moreover, a motivated attacker could frontrun and slightly modify their underwater positions so that any attempt to liquidate them will revert d/t an incorrect repayAmount
. The attacker benefits from not being liquidated, so there is an incentive to do this.
This check causes repayAmount > borrowBalance
to revert. This check causes repayAmount < borrowBalance
to revert.
Also, you can verify this by changing this test case to be 1 wei larger or 1 wei smaller to confirm that both will revert (with either TooMuchRepay
or Nonzero borrow balance after liquidation
).
Manual review.
In the liquidateAccount
control flow, the liquidator should be allowed to safely "overshoot" the repay amount, similar to the check that happens here. To achieve this logic, I would recommend adding something like this to the start of liquidateAccount
:
uint256 accountBorrowsPrev = _borrowBalanceStored(borrower); repayAmount = repayAmount > accountBorrowsPrev ? accountBorrowsPrev : repayAmount;
DoS
#0 - c4-judge
2023-05-17T18:34:44Z
0xean changed the severity to 2 (Med Risk)
#1 - c4-judge
2023-05-17T20:50:55Z
0xean marked the issue as duplicate of #365
#2 - c4-judge
2023-06-05T14:11:59Z
0xean marked the issue as satisfactory