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: 41/102
Findings: 2
Award: $258.70
🌟 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
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/WhitePaperInterestRateModel.sol#L17
This protocol is going to be deployed to BNB chain so its needed to calculate blocksPerYear relevant to BNB Smart Chain. But here it has taken blocksPerYear relevant to the ethereum chain.
17 uint256 public constant blocksPerYear = 2102400;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/WhitePaperInterestRateModel.sol#L17
Here it should be blocksPerYear = 10512000
Due to this incorrect value its caused a incorrect values for baseRatePerBlock & multiplierPerBlock
37 baseRatePerBlock = baseRatePerYear / blocksPerYear; 38 multiplierPerBlock = multiplierPerYear / blocksPerYear;
Its also affected to getBorrowRate & getSupplyRate functions as shown below.
56 return ((ur * multiplierPerBlock) / BASE) + baseRatePerBlock;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/WhitePaperInterestRateModel.sol#L56
74 uint256 borrowRate = getBorrowRate(cash, borrows, reserves);
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/WhitePaperInterestRateModel.sol#L74
Manual Auditing
Use correct value for blocksPerYear relevant to binance smart chain. So it should be
blocksPerYear = 10512000
Correct value is in BaseJumpRateModelV2.sol file https://github.com/code-423n4/2023-05-venus/blob/main/contracts/BaseJumpRateModelV2.sol#L23
Math
#0 - c4-judge
2023-05-16T09:22:14Z
0xean marked the issue as duplicate of #559
#1 - c4-judge
2023-06-05T14:02:56Z
0xean marked the issue as satisfactory
#2 - c4-judge
2023-06-05T14:38:32Z
0xean changed the severity to 3 (High Risk)
🌟 Selected for report: fs0c
Also found by: 0xnev, BPZ, Brenzee, J4de, Team_Rocket, peanuts, rvierdiiev, yongskiws
192.105 USDC - $192.11
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Shortfall/Shortfall.sol#L393
When calculating the poolBadDebt in terms of USD value , first take the marketBadDebt in terms of underlying token then it is multiplied by the corresponding price finally its divided by the corresponding decimal places. But here it has been divided by the 1e18.
389 for (uint256 i; i < marketsCount; ++i) { 390 uint256 marketBadDebt = vTokens[i].badDebt(); 391 392 priceOracle.updatePrice(address(vTokens[i])); 393 uint256 usdValue = (priceOracle.getUnderlyingPrice(address(vTokens[i])) * marketBadDebt) / 1e18; 394 395 poolBadDebt = poolBadDebt + usdValue; 396 auction.markets[i] = vTokens[i]; 397 auction.marketDebt[vTokens[i]] = marketBadDebt; 398 marketsDebt[i] = marketBadDebt; 399 }
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Shortfall/Shortfall.sol#L393
If we consider the WBTC here
Consider the current price of WBTC = 30000 USD (UnderlyingPrice)
badDebt = 2 WBTC token = 2 x 10^8 (Decimals of WBTC = 10 ^ 8)
So usdValue = 30000 x 2 x 10^ 8 / 1e18
This value round off to zero.
But the correct value should be = 30000 x 2 x 10^ 8 / 10^ 8 = 60000 USD
Due to this miscalculation poolBadDebt is less compared with actual poobBadDebts it should be. So its unable to start auction even though there is enough poolBadDebts exists to start the auction. (Since its showing less poolBaddebts).
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Shortfall/Shortfall.sol#L401
Manual Auditing
It should be divided by the corresponding decimals for each underlying tokens.
392 priceOracle.updatePrice(address(vTokens[i])); ++ IERC20Upgradeable erc20 = IERC20Upgradeable(address(vTokens[i].underlying())); 393 uint256 usdValue = (priceOracle.getUnderlyingPrice(address(vTokens[i])) * marketBadDebt) / erc20.decimals() ;
Decimal
#0 - c4-judge
2023-05-17T16:43:54Z
0xean marked the issue as duplicate of #468
#1 - c4-judge
2023-06-05T14:17:26Z
0xean marked the issue as satisfactory
#2 - c4-judge
2023-06-05T14:42:12Z
0xean changed the severity to 2 (Med Risk)
🌟 Selected for report: fs0c
Also found by: 0xnev, BPZ, Brenzee, J4de, Team_Rocket, peanuts, rvierdiiev, yongskiws
192.105 USDC - $192.11
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Shortfall/Shortfall.sol#L406
poolReserves in RiskFund.sol returns the token amount. But its compaired with the incentivizedRiskFundBalance which is in USD value.
403 uint256 riskFundBalance = riskFund.poolReserves(comptroller); uint256 remainingRiskFundBalance = riskFundBalance; uint256 incentivizedRiskFundBalance = poolBadDebt + ((poolBadDebt * incentiveBps) / MAX_BPS); if (incentivizedRiskFundBalance >= riskFundBalance) { auction.startBidBps = (MAX_BPS * MAX_BPS * remainingRiskFundBalance) / (poolBadDebt * (MAX_BPS + incentiveBps)); remainingRiskFundBalance = 0; auction.auctionType = AuctionType.LARGE_POOL_DEBT;
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Shortfall/Shortfall.sol#L406
Here incentivizedRiskFundBalance in USD & riskFundBalance is token amount. Due to this its always incentivizedRiskFundBalance < riskFundBalance. Hence else part executed.
412 } else { uint256 maxSeizeableRiskFundBalance = incentivizedRiskFundBalance; remainingRiskFundBalance = remainingRiskFundBalance - maxSeizeableRiskFundBalance; auction.auctionType = AuctionType.LARGE_RISK_FUND; auction.startBidBps = MAX_BPS; } auction.seizedRiskFund = riskFundBalance - remainingRiskFundBalance; auction.startBlock = block.number; auction.status = AuctionStatus.STARTED; auction.highestBidder = address(0);
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Shortfall/Shortfall.sol#L412
Here auction.seizedRiskFund = riskFundBalance - remainingRiskFundBalance;
= riskFundBalance - ( remainingRiskFundBalance - maxSeizeableRiskFundBalance) = remainingRiskFundBalance - remainingRiskFundBalance + maxSeizeableRiskFundBalance = maxSeizeableRiskFundBalance = incentivizedRiskFundBalance = poolBadDebt + ((poolBadDebt * incentiveBps) / MAX_BPS)
This final value is in USD . This is the amount that going to be send after auction finished.
241 if (auction.auctionType == AuctionType.LARGE_POOL_DEBT) { riskFundBidAmount = auction.seizedRiskFund; } else { riskFundBidAmount = (auction.seizedRiskFund * auction.highestBidBps) / MAX_BPS; } uint256 transferredAmount = riskFund.transferReserveForAuction(comptroller, riskFundBidAmount); IERC20Upgradeable(convertibleBaseAsset).safeTransfer(auction.highestBidder, riskFundBidAmount);
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Shortfall/Shortfall.sol#L248
But on the other hand bidder needs to pay in tokens
188 } else { if (auction.highestBidder != address(0)) { erc20.safeTransfer(auction.highestBidder, auction.marketDebt[auction.markets[i]]); } erc20.safeTransferFrom(msg.sender, address(this), auction.marketDebt[auction.markets[i]]); }
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Shortfall/Shortfall.sol#L190
So Finally bidder get small amount of tokens compared to his spend for the bidding.
Manual Auditing
keep the incentivizedRiskFundBalance in terms of token value not USD value.
Math
#0 - 0xean
2023-05-17T21:52:40Z
will leave open for sponsor comment, I believe these are both in dollar terms, just not clearly labeled, but would be good for them to confirm
#1 - c4-judge
2023-05-18T10:20:14Z
0xean marked the issue as primary issue
#2 - c4-sponsor
2023-05-23T21:44:14Z
chechu marked the issue as sponsor confirmed
#3 - chechu
2023-05-23T21:44:24Z
Duplicated with issue #468
#4 - 0xean
2023-05-31T00:42:01Z
@chechu - I am not sure this is a duplicate with #468 - can you clarify why you believe they are the same?
#5 - c4-judge
2023-05-31T16:01:52Z
0xean marked the issue as duplicate of #468
#6 - chechu
2023-06-01T20:47:56Z
@chechu - I am not sure this is a duplicate with #468 - can you clarify why you believe they are the same?
I would say the root cause is the same in both issues: we are assuming riskFundBalance
is defined in USD (with 18 decimals), but that's wrong and we should convert it using a price oracle, for example. It's true that #468 put the focus on the decimals, that is not the only problem, but at the same time #468 says The problem is that incentivizedRiskFundBalance variable is in usd, when riskFundBalance is in convertibleBaseAsset
, that is really the root cause, as I said
#7 - c4-judge
2023-06-05T14:17:25Z
0xean marked the issue as satisfactory
#8 - c4-judge
2023-06-05T14:42:12Z
0xean changed the severity to 2 (Med Risk)