Platform: Code4rena
Start Date: 28/09/2023
Pot Size: $36,500 USDC
Total HM: 5
Participants: 115
Period: 6 days
Judge: 0xDjango
Total Solo HM: 1
Id: 290
League: ETH
Rank: 65/115
Findings: 2
Award: $20.06
π Selected for report: 0
π Solo Findings: 0
π Selected for report: Bauchibred
Also found by: 0x3b, 0xDetermination, 0xMosh, 0xScourgedev, 0xTheC0der, 0xTiwa, 0xWaitress, 0xdice91, 0xfusion, 0xpiken, 0xprinc, 0xweb3boy, ArmedGoose, Aymen0909, Breeje, Brenzee, Daniel526, DavidGiladi, DeFiHackLabs, Flora, Fulum, HChang26, Hama, IceBear, J4X, Krace, KrisApostolov, Maroutis, Mirror, MohammedRizwan, Norah, PwnStars, SPYBOY, TangYuanShen, Testerbot, ThreeSigma, Tricko, al88nsk, alexweb3, ast3ros, berlin-101, bin2chen, blutorque, btk, d3e4, deth, e0d1n, ether_sky, ge6a, gkrastenov, glcanvas, hals, imare, inzinko, jkoppel, jnforja, joaovwfreire, josephdara, kutugu, lotux, lsaudit, mahdirostami, merlin, n1punp, nadin, neumo, nisedo, nobody2018, oakcobalt, orion, peanuts, pep7siup, pina, ptsanev, rokinot, rvierdiiev, said, santipu_, sashik_eth, seerether, squeaky_cactus, terrancrypt, tonisives, twicek, vagrant, xAriextz, y4y
4.3669 USDC - $4.37
balanceOf of reward Tokens is meant to be strictly larger than accrued to in PrimeLiquidityProvider, however the owner has the ability to sweep reward tokens which could revert the accrue function
If the owner sweep the reward Tokens using sweepToken()
function sweepToken(IERC20Upgradeable token_, address to_, uint256 amount_) external onlyOwner { uint256 balance = token_.balanceOf(address(this)); if (amount_ > balance) { revert InsufficientBalance(amount_, balance); } emit SweepToken(address(token_), to_, amount_); token_.safeTransfer(to_, amount_); }
Then the balanceOf rewardTokens would not be larger than the accruedAmount tokenAmountAccrued
during accrueTokens
, balanceDiff would have underflow.
function accrueTokens(address token_) public { _ensureZeroAddress(token_); _ensureTokenInitialized(token_); uint256 blockNumber = getBlockNumber(); uint256 deltaBlocks = blockNumber - lastAccruedBlock[token_]; if (deltaBlocks > 0) { uint256 distributionSpeed = tokenDistributionSpeeds[token_]; uint256 balance = IERC20Upgradeable(token_).balanceOf(address(this)); uint256 balanceDiff = balance - tokenAmountAccrued[token_]; if (distributionSpeed > 0 && balanceDiff > 0) { uint256 accruedSinceUpdate = deltaBlocks * distributionSpeed; uint256 tokenAccrued = (balanceDiff <= accruedSinceUpdate ? balanceDiff : accruedSinceUpdate); tokenAmountAccrued[token_] += tokenAccrued; emit TokensAccrued(token_, tokenAccrued); } lastAccruedBlock[token_] = blockNumber; } }
Impact: accrueTokens, which is an essential flow for releaseFund
and _setTokenDistributionSpeed
would be reverted if owner sweep the reward tokens.
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
if a token has a non-zero distribution speed, validate and revert the sweepToken.
Context
#0 - c4-pre-sort
2023-10-05T01:02:54Z
0xRobocop marked the issue as primary issue
#1 - 0xRobocop
2023-10-05T01:03:05Z
Consider QA
#2 - c4-pre-sort
2023-10-05T01:03:11Z
0xRobocop marked the issue as low quality report
#3 - c4-pre-sort
2023-10-06T00:30:08Z
0xRobocop marked the issue as high quality report
#4 - c4-sponsor
2023-10-24T16:28:51Z
chechu marked the issue as disagree with severity
#5 - chechu
2023-10-24T16:29:24Z
Consider QA
sweepToken
is a privilege function, that would be executed in a VIP, with the votes of the Venus community, only in edge cases. There would be several options: for example, if part of the balance has already been allocated, we could sweep less than 100% of the balance. In the worst case (in an emergency), we would prefer to break the accounting of the contracts, recover as many tokens as possible, and later recover the accounting
#6 - c4-sponsor
2023-10-24T18:38:51Z
chechu (sponsor) acknowledged
#7 - c4-judge
2023-10-31T17:09:56Z
fatherGoose1 changed the severity to QA (Quality Assurance)
#8 - fatherGoose1
2023-10-31T17:11:04Z
Agree with sponsor. Besides being a privileged action, it could be fixed by directly transferring back the token if the original sweep were made in error.
#9 - c4-judge
2023-11-03T01:41:37Z
fatherGoose1 marked the issue as grade-b
π Selected for report: DavidGiladi
Also found by: 0x3b, 0xWaitress, 0xhacksmithh, 0xprinc, hihen, jkoppel, lsaudit, oakcobalt, pavankv, pontifex
15.6862 USDC - $15.69
[G-1] pendingScoreUpdates can be updated at the end of updateScores instead of using pendingScoreUpdates--
at every loop, with the use of an intermediate variable in memory.
function updateScores(address[] memory users) external { if (pendingScoreUpdates == 0) revert NoScoreUpdatesRequired(); if (nextScoreUpdateRoundId == 0) revert NoScoreUpdatesRequired(); for (uint256 i = 0; i < users.length; ) { address user = users[i]; if (!tokens[user].exists) revert UserHasNoPrimeToken(); if (isScoreUpdated[nextScoreUpdateRoundId][user]) continue; address[] storage _allMarkets = allMarkets; for (uint256 j = 0; j < _allMarkets.length; ) { address market = _allMarkets[j]; _executeBoost(user, market); _updateScore(user, market); unchecked { j++; } } pendingScoreUpdates--; isScoreUpdated[nextScoreUpdateRoundId][user] = true; unchecked { i++; } emit UserScoreUpdated(user); } }
move pendingScoreUpdates--
outside of the loop and replace it with the use of a counter.
uint256 counter = 0; ... --- pendingScoreUpdates--; +++ counter++; ... pendingScoreUpdates -= counter;
#0 - c4-pre-sort
2023-10-06T00:29:41Z
0xRobocop marked the issue as sufficient quality report
#1 - c4-judge
2023-11-02T02:30:11Z
fatherGoose1 marked the issue as grade-b