Platform: Code4rena
Start Date: 07/08/2023
Pot Size: $36,500 USDC
Total HM: 11
Participants: 125
Period: 3 days
Judge: alcueca
Total Solo HM: 4
Id: 274
League: ETH
Rank: 84/125
Findings: 1
Award: $9.82
π Selected for report: 0
π Solo Findings: 0
π Selected for report: RED-LOTUS-REACH
Also found by: 0x3b, 0x4non, 0xCiphky, 0xDING99YA, 0xDetermination, 0xE1, 0xG0P1, 0xStalin, 0xWaitress, 0xbrett8571, 0xhacksmithh, 0xkazim, 0xmuxyz, 0xweb3boy, 14si2o_Flint, AlexCzm, Alhakista, Bube, Bughunter101, Deekshith99, Eeyore, Giorgio, HChang26, InAllHonesty, JP_Courses, KmanOfficial, MatricksDeCoder, Mike_Bello90, MrPotatoMagic, Naubit, QiuhaoLi, RHaO-sec, Raihan, Rolezn, SUPERMAN_I4G, Shubham, Silverskrrrt, Strausses, T1MOH, Topmark, Tripathi, Watermelon, _eperezok, aakansha, auditsea, audityourcontracts, ayden, carlos__alegre, castle_chain, cducrest, ch0bu, d23e, deadrxsezzz, deth, devival, erebus, fatherOfBlocks, halden, hassan-truscova, hpsb, hunter_w3b, imkapadia, immeas, jat, kaden, kaveyjoe, klau5, koxuan, kutugu, ladboy233, lanrebayode77, leasowillow, lsaudit, markus_ether, matrix_0wl, merlin, nemveer, ni8mare, nonseodion, oakcobalt, owadez, p_crypt0, pipidu83, piyushshukla, popular00, ppetrov, rjs, sandy, sl1, supervrijdag, tay054, thekmj, wahedtalash77, windhustler, zhaojie
9.8204 USDC - $9.82
https://github.com/code-423n4/2023-08-verwa/blob/main/src/LendingLedger.sol#L153-L182
Claim()
will revert when marketBalance
= 0. Caller will waste gas doing complex computations and the function call will revert anyways.
Let's take a look at claim()
.
The recommended input is as follow:
/ @param _claimFromTimestamp From which epoch (provided as timestmap) should the claim start. Usually, this parameter should be set to 0, in which case the epoch of the last claim will be used.
/ However, it can be useful to skip certain epochs, e.g. when the balance was very low or 0 (after everything was withdrawn) and the gas usage should be reduced.
/ Note that all rewards are forfeited forever if epochs are explicitly skipped by providing this parameter
/ @param _claimUpToTimestamp Until which epoch (provided as timestamp) should the claim be applied. If it is higher than the timestamp of the previous epoch, this will be used
/ Set to type(uint256).max to claim all possible epochs.
marketBalance
may be 0 for specific epoch in between _claimFromTimestamp
and _claimUpToTimestamp
period. Solidity will revert when a dividing by 0. The only way to get around this issue, is to make multiple claim()
calls avoiding all epochs where marketBalance
= 0 across different markets. However, this does seem unrealistic especially for a regular user. It will also likely cost more gas.
if (claimEnd >= claimStart) { // This ensures that we only set userClaimedEpoch when a claim actually happened for (uint256 i = claimStart; i <= claimEnd; i += WEEK) { uint256 userBalance = lendingMarketBalances[_market][lender][i]; uint256 marketBalance = lendingMarketTotalBalance[_market][i]; RewardInformation memory ri = rewardInformation[i]; require(ri.set, "Reward not set yet"); uint256 marketWeight = gaugeController.gauge_relative_weight_write(_market, i); cantoToSend += (marketWeight * userBalance * ri.amount) / (1e18 * marketBalance); } userClaimedEpoch[_market][lender] = claimEnd + WEEK; } if (cantoToSend > 0) { (bool success, ) = msg.sender.call{value: cantoToSend}(""); require(success, "Failed to send CANTO"); }
Manual Review
if (claimEnd >= claimStart) { // This ensures that we only set userClaimedEpoch when a claim actually happened for (uint256 i = claimStart; i <= claimEnd; i += WEEK) { uint256 userBalance = lendingMarketBalances[_market][lender][i]; uint256 marketBalance = lendingMarketTotalBalance[_market][i]; RewardInformation memory ri = rewardInformation[i]; require(ri.set, "Reward not set yet"); uint256 marketWeight = gaugeController.gauge_relative_weight_write(_market, i); + if(marketBalance != 0){ cantoToSend += (marketWeight * userBalance * ri.amount) / (1e18 * marketBalance); + } } userClaimedEpoch[_market][lender] = claimEnd + WEEK; } if (cantoToSend > 0) { (bool success, ) = msg.sender.call{value: cantoToSend}(""); require(success, "Failed to send CANTO"); }
Math
#0 - 141345
2023-08-12T01:53:27Z
loss is only on gas
QA might be more appropriate.
#1 - c4-pre-sort
2023-08-12T07:38:07Z
141345 marked the issue as primary issue
#2 - c4-sponsor
2023-08-16T13:46:57Z
OpenCoreCH marked the issue as sponsor acknowledged
#3 - c4-sponsor
2023-08-16T13:47:02Z
OpenCoreCH marked the issue as disagree with severity
#4 - OpenCoreCH
2023-08-16T13:49:32Z
Problem can be circumvented by the user, a UX improvement in my opinion. Note that per definition, the user balances in this market will also be 0 for this epoch (otherwise, the overall market balance cannot be 0), so the user would just need to skip the epochs where they did not have any funds in the market (which they may do anyway to reduce gas usage).
#5 - alcueca
2023-08-24T06:43:02Z
Agree with sponsor
#6 - c4-judge
2023-08-24T06:43:12Z
alcueca changed the severity to QA (Quality Assurance)
#7 - c4-judge
2023-08-24T06:43:19Z
alcueca marked the issue as grade-b
#8 - c4-judge
2023-08-24T21:22:49Z
alcueca marked the issue as grade-a
#9 - alcueca
2023-08-24T21:23:13Z
Function incorrect as to spec, and a hassle for users