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: 27/115
Findings: 2
Award: $202.85
š Selected for report: 0
š Solo Findings: 0
š Selected for report: 0xDetermination
Also found by: DeFiHackLabs, Norah, Pessimistic, PwnStars, SpicyMeatball, Testerbot, ThreeSigma, bin2chen, blutorque, deadrxsezzz, dirk_y, ether_sky, hals, neumo, rokinot, said, seerether, turvy_fuzz
198.4834 USDC - $198.48
https://github.com/code-423n4/2023-09-venus/blob/b11d9ef9db8237678567e66759003138f2368d23/contracts/Tokens/Prime/Prime.sol#L820-L821 https://github.com/code-423n4/2023-09-venus/blob/b11d9ef9db8237678567e66759003138f2368d23/contracts/Tokens/Prime/Prime.sol#L221 https://github.com/code-423n4/2023-09-venus/blob/b11d9ef9db8237678567e66759003138f2368d23/contracts/Tokens/Prime/Prime.sol#L828
Burning during a score update round can leave pendingScoreUpdates incorrect. Can invalidate assumptions. Should prevent burning during round.
The Prime contract keeps track of pendingScoreUpdates, which is the number of user scores left to update in the current round. This is initialized to the total number of token holders: https://github.com/code-423n4/2023-09-venus/blob/b11d9ef9db8237678567e66759003138f2368d23/contracts/Tokens/Prime/Prime.sol#L820-L821
When a user's score is updated in the round, pendingScoreUpdates is decremented: https://github.com/code-423n4/2023-09-venus/blob/b11d9ef9db8237678567e66759003138f2368d23/contracts/Tokens/Prime/Prime.sol#L221
However, if a token is burned during the round, it decrements totalScoreUpdatesRequired but does not adjust pendingScoreUpdates: https://github.com/code-423n4/2023-09-venus/blob/b11d9ef9db8237678567e66759003138f2368d23/contracts/Tokens/Prime/Prime.sol#L828
This means pendingScoreUpdates could end up higher than totalScoreUpdatesRequired, invalidating the assumption that it tracks tokens left to update. For example, say there are 100 tokens initially, so pendingScoreUpdates is initialized to 100. 50 tokens are updated, so pendingScoreUpdates = 50. Now a token is burned, so totalScoreUpdatesRequired decreases to 99, but pendingScoreUpdates is still 50.
Manual
Burning tokens should be disabled during a score update round. pendingScoreUpdates should be recalculated if a token is burned. Something like:
// Disable burning if round active require(nextScoreUpdateRoundId == 0, "Burning not allowed during round");
// Adjust pendingScoreUpdates if burned _updateRoundAfterTokenBurned(user);
This will help ensure pendingScoreUpdates stays valid even if tokens are burned.
Other
#0 - c4-pre-sort
2023-10-05T20:27:56Z
0xRobocop marked the issue as low quality report
#1 - c4-pre-sort
2023-10-05T20:28:07Z
0xRobocop marked the issue as duplicate of #555
#2 - fatherGoose1
2023-10-31T20:57:38Z
This is a difficult decision. The large majority of the write-up does not reflect #555 but the top impact ("This can allow pendingScoreUpdates to reach 0 and finalize the round, even though not all tokens were updated.") does. I judge to keep this a duplicate.
#3 - c4-judge
2023-10-31T20:57:43Z
fatherGoose1 marked the issue as satisfactory
š 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
This can allow the owner to sweep more tokens than are available
The sweepToken() function allows the owner to transfer any amount of a token to a recipient address. It only checks that the amount is less than the token's balanceOf the contract: https://github.com/code-423n4/2023-09-venus/blob/b11d9ef9db8237678567e66759003138f2368d23/contracts/Tokens/Prime/PrimeLiquidityProvider.sol#L216-L220 However, it does not take into account the tokenAmountAccrued mapping, which tracks accrued but not yet transferred amounts for each token. For example: ⢠Contract balance of TokenA: 100 ⢠tokenAmountAccrued of TokenA: 50 ⢠Actual available balance: 100 - 50 = 50 The owner could call sweepToken(TokenA, owner, 80), which would succeed since 80 < 100 balanceOf contract. But in reality only 50 tokens are available.
In more details, Here is how an owner could exploit this to sweep more tokens than available:
To summarize, the vulnerability is that sweepToken relies only on the token contract's balanceOf() instead of checking the accrued but not transferred balance. An owner could exploit this to sweep more tokens than are available in the contract.
Manual
The sweepToken function should also subtract the accrued but not yet transferred amount for that token:
uint256 availableBalance = token_.balanceOf(address(this)) - tokenAmountAccrued[token_];
if (amount_ > availableBalance) {
revert InsufficientBalance(amount_, availableBalance);
}
This would properly account for both the actual balance and the accrued amount, preventing the owner from sweeping more than is available.
Other
#0 - c4-pre-sort
2023-10-05T01:05:52Z
0xRobocop marked the issue as duplicate of #42
#1 - c4-judge
2023-10-31T17:09:53Z
fatherGoose1 changed the severity to QA (Quality Assurance)
#2 - c4-judge
2023-11-03T01:41:50Z
fatherGoose1 marked the issue as grade-b