Platform: Code4rena
Start Date: 09/02/2024
Pot Size: $60,500 USDC
Total HM: 17
Participants: 283
Period: 12 days
Judge:
Id: 328
League: ETH
Rank: 251/283
Findings: 1
Award: $0.23
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: ahmedaghadi
Also found by: 0x13, 0xAleko, 0xDetermination, 0xKowalski, 0xPluto, 0xRiO, 0xvj, AlexCzm, Avci, BARW, BigVeezus, Cryptor, DeFiHackLabs, Draiakoo, Fitro, Giorgio, GoSlang, Greed, Honour, Kalogerone, KmanOfficial, Krace, McToady, MidgarAudits, MrPotatoMagic, Nyxaris, ReadyPlayer2, Ryonen, SovaSlava, SpicyMeatball, VAD37, _eperezok, alexzoid, almurhasan, btk, cu5t0mpeo, deadrxsezzz, djxploit, dvrkzy, emrekocak, erosjohn, evmboi32, fnanni, grearlake, inzinko, jesjupyter, jesusrod15, josephdara, ke1caM, klau5, ktg, ladboy233, merlinboii, nuthan2x, peanuts, pipidu83, pontifex, radev_sw, sl1, sobieski, soliditywala, t0x1c, taner2344, vnavascues, y4y, yovchev_yoan, zaevlad
0.2347 USDC - $0.23
https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/MergingPool.sol#L118-L132 https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/MergingPool.sol#L139-L167 https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L484-L495
If a user gets Selected as winner in a round in the merging pool contract, they are added to a winnersAddress
mapping, and they can claim their reward in the form of a fighter which has to be minted in the FighterFarm
contract, This vunerability comes up in a specific situation, where a user that does not claim their fighter after every round and it accumulates to up to 11 rounds where they are entitled to receive a NFT, then when the user is about to call claimRewards
in the Merging Pool contract, and the minting is called, this action will fail due to the require(balanceOf(to) < MAX_FIGHTERS_ALLOWED);
check in the code.
This will lead to a situation where claimRewards
will be DOS and the affected user will be unable to claim rewards again, even if they are added as winners in the future.
Users are added as winners for a particular round from the admins through the pickWinner
function
Inside that function the currentWinnerAddresses
array is updated IN accordance with the winners per period requirement as shown below
...more code for (uint256 i = 0; i < winnersLength; i++) { currentWinnerAddresses[i] = _fighterFarmInstance.ownerOf(winners[i]); totalPoints -= fighterPoints[winners[i]]; fighterPoints[winners[i]] = 0; } winnerAddresses[roundId] = currentWinnerAddresses; isSelectionComplete[roundId] = true; roundId += 1;
When a user wants to claim their rewards they call the claimRewards
function that enables them as shown below
function claimRewards( string[] calldata modelURIs, string[] calldata modelTypes, uint256[2][] calldata customAttributes ) external { uint256 winnersLength; uint32 claimIndex = 0; uint32 lowerBound = numRoundsClaimed[msg.sender]; for (uint32 currentRound = lowerBound; currentRound < roundId; currentRound++) { numRoundsClaimed[msg.sender] += 1; winnersLength = winnerAddresses[currentRound].length; for (uint32 j = 0; j < winnersLength; j++) { if (msg.sender == winnerAddresses[currentRound][j]) { _fighterFarmInstance.mintFromMergingPool( msg.sender, modelURIs[claimIndex], modelTypes[claimIndex], customAttributes[claimIndex] ); claimIndex += 1; } } } if (claimIndex > 0) { emit Claimed(msg.sender, claimIndex); } }
The issue as described in the impact section refers to a peculiar situation where a user or group of users, are not consistent claimers, which means they let their rewards accumulate, if now, a particular user is added to the winners array 11 diffrent times and they refuse to claim their rewards, any call to claimRewards
function will lead to a revert as the check require(balanceOf(to) < MAX_FIGHTERS_ALLOWED);
in the _createNewFighter
function in the FighterFarm
contract will revert when the check gets to the 11th fighter to mint that check will fail and the whole operation will revert, leading to a DOS. This check is shown below
function _createNewFighter( address to, uint256 dna, string memory modelHash, string memory modelType, uint8 fighterType, uint8 iconsType, uint256[2] memory customAttributes ) private { require(balanceOf(to) < MAX_FIGHTERS_ALLOWED); ...more code
As Clearification for the Particular Number 11 that will result in DOS, This number is gotten when you think about it like this
Maual Reveiew
A maximum Number of Pending or Unclaimed Rewards Limit should be set, if a User has a Certain number of Unclaimed Rewards That User Should be Unable to be added as a winner that would be entitled to receive Fighters, This Would prevent situations described in the Report.
DoS
#0 - c4-pre-sort
2024-02-22T09:27:07Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-02-22T09:27:15Z
raymondfam marked the issue as duplicate of #216
#2 - c4-judge
2024-03-11T12:49:32Z
HickupHH3 changed the severity to 2 (Med Risk)
#3 - c4-judge
2024-03-11T12:53:25Z
HickupHH3 marked the issue as satisfactory