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: 225/283
Findings: 2
Award: $1.35
π Selected for report: 0
π Solo Findings: 0
π Selected for report: klau5
Also found by: 0xAleko, 0xAlix2, 0xAsen, 0xCiphky, 0xKowalski, 0xlemon, 0xvj, 14si2o_Flint, Aamir, AlexCzm, Aymen0909, BARW, Blank_Space, DanielArmstrong, Davide, Draiakoo, Giorgio, McToady, MrPotatoMagic, PoeAudits, Ryonen, Silvermist, SpicyMeatball, Tychai0s, VAD37, Varun_05, alexxander, alexzoid, aslanbek, blutorque, btk, cats, d3e4, denzi_, evmboi32, fnanni, givn, grearlake, haxatron, jesjupyter, juancito, ke1caM, ktg, lanrebayode77, linmiaomiao, matejdb, merlinboii, n0kto, novamanbg, nuthan2x, petro_1912, pynschon, radin100, sashik_eth, shaka, sl1, soliditywala, solmaxis69, t0x1c, ubl4nk, vnavascues, xchen1130, yotov721, zhaojohnson
1.1225 USDC - $1.12
A user can bypass the maxRerollsAllowed requirement by using a different fighterType instead of his fighters, if he has enough NRC for pay the reroll cost.
add some changes in testReroll.
A gift to the user, sufficient for the cost of a neuron token reroll.
When using merging pool minting as we know, fighter types will be 0. https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/FighterFarm.sol#L322-L329
reRoll our nft fighter using fighterType 1, 3x times because maxReroll defaults is 3 and after change fighterType to 0. This way we can do it multiple times and bypass the maxRerollsAllowed check.
function testRerollAnotherFighterType() public { _mintFromMergingPool(_ownerAddress); // get 4k neuron from treasury vm.prank(_treasuryAddress); _neuronContract.transfer(_ownerAddress, 10_000 * 10 ** 18); assertEq(10_000 * 10 ** 18 == _neuronContract.balanceOf(_ownerAddress), true); // after successfully minting a fighter, update the model if (_fighterFarmContract.ownerOf(0) == _ownerAddress) { uint256 neuronBalanceBeforeReRoll = _neuronContract.balanceOf(_ownerAddress); uint8 tokenId = 0; uint8 fighterType = 1; _neuronContract.addSpender(address(_fighterFarmContract)); _fighterFarmContract.reRoll(tokenId, fighterType); _fighterFarmContract.reRoll(tokenId, fighterType); _fighterFarmContract.reRoll(tokenId, fighterType); fighterType = 0; _fighterFarmContract.reRoll(tokenId, fighterType); _fighterFarmContract.reRoll(tokenId, fighterType); _fighterFarmContract.reRoll(tokenId, fighterType); assertEq(_fighterFarmContract.numRerolls(0), 6); assertEq(neuronBalanceBeforeReRoll > _neuronContract.balanceOf(_ownerAddress), true); } }
Manual Review, Foundry.
require(numRerolls[tokenId] < maxRerollsAllowed[fighterType]);
- add check for NFTs fighter type.
Other
#0 - raymondfam
2024-02-21T23:34:35Z
Unlikely as numRerolls[tokenId] has been incremented given that maxRerollsAllowed = [3, 3]. The POC will have to elicit the dodging path on how [3, 3] could eventually change to a different set via incrementGeneration().
#1 - c4-pre-sort
2024-02-21T23:34:44Z
raymondfam marked the issue as insufficient quality report
#2 - c4-pre-sort
2024-02-21T23:34:47Z
raymondfam marked the issue as primary issue
#3 - c4-pre-sort
2024-02-22T02:50:09Z
raymondfam marked the issue as duplicate of #306
#4 - c4-pre-sort
2024-02-22T02:50:15Z
raymondfam marked the issue as sufficient quality report
#5 - c4-judge
2024-03-05T04:30:23Z
HickupHH3 changed the severity to 2 (Med Risk)
#6 - c4-judge
2024-03-05T04:37:52Z
HickupHH3 marked the issue as satisfactory
#7 - c4-judge
2024-03-19T09:01:12Z
HickupHH3 changed the severity to 3 (High Risk)
π 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/1d18d1298729e443e14fea08149c77182a65da32/src/MergingPool.sol#L139-L167 https://github.com/code-423n4/2024-02-ai-arena/blob/1d18d1298729e443e14fea08149c77182a65da32/src/FighterFarm.sol#L495
If a user already has, for example, 9 fighters and the MAX_FIGHTERS_ALLOWED=10
, their transaction to claim a 2 fighters reward will be reverted in claimRewards()
if they are selected as the winner in pickWinner()
, because the limit is 10. Consequently, they cannot mint even 1 additional fighter.
The user can still claim part of the reward. However, due to the function's all-or-nothing design, they must either claim the entire reward or none of it.
pickWinner()
function.getUnclaimedRewards()
is 2._ownerAddress
- owner of fighters, try to claimRewards.function testClaimRewardsMaxAllowedFighter() public { /// mint 9 fighter for owner - less than max allowed count of fighters _mintFromMergingPool(_ownerAddress); _mintFromMergingPool(_ownerAddress); _mintFromMergingPool(_ownerAddress); _mintFromMergingPool(_ownerAddress); _mintFromMergingPool(_ownerAddress); _mintFromMergingPool(_ownerAddress); _mintFromMergingPool(_ownerAddress); _mintFromMergingPool(_ownerAddress); _mintFromMergingPool(_ownerAddress); uint256[] memory _winners = new uint256[](2); _winners[0] = 0; _winners[1] = 1; _mergingPoolContract.pickWinner(_winners); uint256 numRewards = _mergingPoolContract.getUnclaimedRewards(_ownerAddress); assertEq(numRewards, 2); string[] memory _modelURIs = new string[](2); _modelURIs[0] = "ipfs://bafybeiaatcgqvzvz3wrjiqmz2ivcu2c5sqxgipv5w2hzy4pdlw7hfox42m"; _modelURIs[1] = "ipfs://bafybeiaatcgqvzvz3wrjiqmz2ivcu2c5sqxgipv5w2hzy4pdlw7hfox42m"; string[] memory _modelTypes = new string[](2); _modelTypes[0] = "original"; _modelTypes[1] = "original"; uint256[2][] memory _customAttributes = new uint256[2][](2); _customAttributes[0][0] = uint256(1); _customAttributes[0][1] = uint256(80); _customAttributes[1][0] = uint256(1); _customAttributes[1][1] = uint256(80); vm.prank(_ownerAddress); vm.expectRevert(); // expect revert from mintFromMergingPool() - `require(balanceOf(to) < MAX_FIGHTERS_ALLOWED);` _mergingPoolContract.claimRewards(_modelURIs, _modelTypes, _customAttributes); }
Manual review, foundry.
Refactor claimRewards()
function, so that winner can claim part of the winnings if he can't claim all.
Invalid Validation
#0 - c4-pre-sort
2024-02-22T08:44:06Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-02-22T08:44:21Z
raymondfam marked the issue as duplicate of #216
#2 - c4-judge
2024-03-11T12:49:25Z
HickupHH3 marked the issue as satisfactory
#3 - c4-judge
2024-03-11T12:49:34Z
HickupHH3 changed the severity to 2 (Med Risk)