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: 105/283
Findings: 4
Award: $59.56
π Selected for report: 0
π Solo Findings: 0
π Selected for report: CodeWasp
Also found by: 0x13, 0xAlix2, 0xAsen, 0xCiphky, 0xE1, 0xLogos, 0xaghas, 0xlemon, 0xlyov, 0xvj, ADM, Aamir, BARW, Bauchibred, DMoore, DanielArmstrong, Draiakoo, Fulum, GhK3Ndf, Josh4324, Kalogerone, KmanOfficial, Krace, KupiaSec, Limbooo, MidgarAudits, MrPotatoMagic, PedroZurdo, Pelz, Tendency, _eperezok, adam-idarrha, al88nsk, alexxander, alexzoid, aslanbek, blutorque, btk, c0pp3rscr3w3r, cartlex_, cats, d3e4, deadrxsezzz, denzi_, devblixt, dimulski, erosjohn, evmboi32, fnanni, grearlake, hulkvision, immeas, israeladelaja, jaydhales, jesjupyter, jnforja, juancito, klau5, korok, ktg, ladboy233, matejdb, merlinboii, novamanbg, nuthan2x, oualidpro, peanuts, petro_1912, pkqs90, pynschon, radev_sw, rouhsamad, sashik_eth, shaka, sobieski, soliditywala, stackachu, tallo, thank_you, ubl4nk, vnavascues, web3pwn, xchen1130, zhaojohnson
0.1044 USDC - $0.10
https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L539 https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L346
function _ableToTransfer(uint256 tokenId, address to) private view returns(bool) { return ( _isApprovedOrOwner(msg.sender, tokenId) && balanceOf(to) < MAX_FIGHTERS_ALLOWED && !fighterStaked[tokenId] ); }
when the figher's nft is staked and fighterStaked[tokenId] flag is false,
the nft should not be transferrable
function transferFrom( address from, address to, uint256 tokenId ) public override(ERC721, IERC721) { require(_ableToTransfer(tokenId, to)); _transfer(from, to, tokenId); } /// @notice Safely transfers an NFT from one address to another. /// @dev Add a custom check for an ability to transfer the fighter. /// @param from Address of the current owner. /// @param to Address of the new owner. /// @param tokenId ID of the fighter being transferred. function safeTransferFrom( address from, address to, uint256 tokenId ) public override(ERC721, IERC721) { require(_ableToTransfer(tokenId, to)); _safeTransfer(from, to, tokenId, ""); }
the contract actually override
in two function to make sure that this checks correctly
However, the ERC721 openzepplin version used is 4.7.3
contract FighterFarm is ERC721, ERC721Enumerable {
ERC721 contract also defines another version of the safeTransferFrom
function
that has another overide
then the code does not override the method in FighterFarm.sol
/** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes memory data ) public virtual override { require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved"); _safeTransfer(from, to, tokenId, data); }
Thus, the user can always call the method above to bypass the staked fighter nft restriction.
should also override the safeTransferFrom
function in FighterFarm
contract.
This ensures that all transfers, regardless of which safeTransferFrom method is called,
will be subject to your custom restrictions.
Token-Transfer
#0 - c4-pre-sort
2024-02-23T05:21:41Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-02-23T05:21:56Z
raymondfam marked the issue as duplicate of #739
#2 - c4-judge
2024-03-11T02:47:15Z
HickupHH3 marked the issue as satisfactory
π Selected for report: CodeWasp
Also found by: 0x13, 0xAlix2, 0xAsen, 0xCiphky, 0xE1, 0xLogos, 0xaghas, 0xlemon, 0xlyov, 0xvj, ADM, Aamir, BARW, Bauchibred, DMoore, DanielArmstrong, Draiakoo, Fulum, GhK3Ndf, Josh4324, Kalogerone, KmanOfficial, Krace, KupiaSec, Limbooo, MidgarAudits, MrPotatoMagic, PedroZurdo, Pelz, Tendency, _eperezok, adam-idarrha, al88nsk, alexxander, alexzoid, aslanbek, blutorque, btk, c0pp3rscr3w3r, cartlex_, cats, d3e4, deadrxsezzz, denzi_, devblixt, dimulski, erosjohn, evmboi32, fnanni, grearlake, hulkvision, immeas, israeladelaja, jaydhales, jesjupyter, jnforja, juancito, klau5, korok, ktg, ladboy233, matejdb, merlinboii, novamanbg, nuthan2x, oualidpro, peanuts, petro_1912, pkqs90, pynschon, radev_sw, rouhsamad, sashik_eth, shaka, sobieski, soliditywala, stackachu, tallo, thank_you, ubl4nk, vnavascues, web3pwn, xchen1130, zhaojohnson
0.1044 USDC - $0.10
function _ableToTransfer(uint256 tokenId, address to) private view returns(bool) { return ( _isApprovedOrOwner(msg.sender, tokenId) && balanceOf(to) < MAX_FIGHTERS_ALLOWED && !fighterStaked[tokenId] ); }
when the a single address's nft balance exceed MAX_FIGHTERS_ALLOWED
the nft should not be transferrable
function transferFrom( address from, address to, uint256 tokenId ) public override(ERC721, IERC721) { require(_ableToTransfer(tokenId, to)); _transfer(from, to, tokenId); } /// @notice Safely transfers an NFT from one address to another. /// @dev Add a custom check for an ability to transfer the fighter. /// @param from Address of the current owner. /// @param to Address of the new owner. /// @param tokenId ID of the fighter being transferred. function safeTransferFrom( address from, address to, uint256 tokenId ) public override(ERC721, IERC721) { require(_ableToTransfer(tokenId, to)); _safeTransfer(from, to, tokenId, ""); }
the contract actually override
in two function to make sure that this checks correctly
However, the ERC721 openzepplin version used is 4.7.3
contract FighterFarm is ERC721, ERC721Enumerable {
ERC721 contract also defines another version of the safeTransferFrom
function
that has another overide
then the code does not override the method in FighterFarm.sol
/** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes memory data ) public virtual override { require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved"); _s
Thus, the user can always call the method above to bypass the MAX_FIGHTERS_ALLOWED per address restriction.
should also override the safeTransferFrom
function in FighterFarm
contract.
This ensures that all transfers, regardless of which safeTransferFrom method is called,
will be subject to your custom restrictions.
Invalid Validation
#0 - c4-pre-sort
2024-02-23T05:22:35Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-02-23T05:22:44Z
raymondfam marked the issue as duplicate of #739
#2 - c4-judge
2024-03-11T02:09:27Z
HickupHH3 changed the severity to 3 (High Risk)
#3 - c4-judge
2024-03-11T02:47:22Z
HickupHH3 marked the issue as satisfactory
π Selected for report: Aamir
Also found by: 0rpse, 0x11singh99, 0x13, 0xAlix2, 0xAsen, 0xBinChook, 0xCiphky, 0xE1, 0xKowalski, 0xLogos, 0xWallSecurity, 0xaghas, 0xbranded, 0xlemon, 0xlyov, 0xpoor4ever, 0xprinc, 0xvj, ADM, Aymen0909, BARW, Bauchibred, Breeje, CodeWasp, DMoore, DeFiHackLabs, Draiakoo, Fulum, GhK3Ndf, Greed, Jorgect, Josh4324, Kalogerone, KmanOfficial, Krace, Limbooo, McToady, MidgarAudits, MrPotatoMagic, PedroZurdo, Pelz, Ryonen, SovaSlava, SpicyMeatball, Tendency, Timenov, ZanyBonzy, _eperezok, al88nsk, alexxander, alexzoid, aslanbek, blutorque, btk, cartlex_, cats, csanuragjain, deadrxsezzz, denzi_, devblixt, dimulski, djxploit, erosjohn, evmboi32, fnanni, grearlake, haxatron, hulkvision, immeas, israeladelaja, jaydhales, jesjupyter, jnforja, josephdara, juancito, kiqo, klau5, korok, krikolkk, ktg, kutugu, ladboy233, lil_eth, m4ttm, matejdb, merlinboii, n0kto, ni8mare, novamanbg, nuthan2x, oualidpro, pa6kuda, peter, petro_1912, pkqs90, pynschon, sandy, sashik_eth, shaflow2, shaka, sobieski, soliditywala, solmaxis69, stackachu, tallo, thank_you, tpiliposian, ubl4nk, visualbits, vnavascues, web3pwn, xchen1130, zhaojohnson
0.0037 USDC - $0.00
https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/GameItems.sol#L212 https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/GameItems.sol#L301
function createGameItem( string memory name_, string memory tokenURI, bool finiteSupply, bool transferable, uint256 itemsRemaining, uint256 itemPrice, uint16 dailyAllowance )
the admin of game item can create game item for user to mint
if the admin sets the transferable flag to false,
that means the item should not be transferrable after the nft is minted.
{ require(allGameItemAttributes[tokenId].transferable); super.safeTransferFrom(from, to, tokenId, amount, data); }
However, this restriction can be bypassed, user can just call
function safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory values, bytes memory data ) public virtual { address sender = _msgSender(); if (from != sender && !isApprovedForAll(from, sender)) { revert ERC1155MissingApprovalForAll(sender, from); } _safeBatchTransferFrom(from, to, ids, values, data); }
to transfer not-transferrable item
because the game item is ERC1155 NFT
If any item in the batch is found to be non-transferable,
the function should revert the transaction,
preventing the transfer of non-transferable items.
Token-Transfer
#0 - c4-pre-sort
2024-02-22T04:03:09Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-02-22T04:03:17Z
raymondfam marked the issue as duplicate of #18
#2 - c4-pre-sort
2024-02-26T00:28:21Z
raymondfam marked the issue as duplicate of #575
#3 - c4-judge
2024-03-05T04:54:30Z
HickupHH3 marked the issue as satisfactory
π 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/RankedBattle.sol#L299 https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/RankedBattle.sol#L233
function claimNRN() external { require(numRoundsClaimed[msg.sender] < roundId, "Already claimed NRNs for this period"); uint256 claimableNRN = 0; uint256 nrnDistribution; uint32 lowerBound = numRoundsClaimed[msg.sender]; for (uint32 currentRound = lowerBound; currentRound < roundId; currentRound++) { nrnDistribution = getNrnDistribution(currentRound); claimableNRN += ( accumulatedPointsPerAddress[msg.sender][currentRound] * nrnDistribution ) / totalAccumulatedPoints[currentRound]; numRoundsClaimed[msg.sender] += 1; }
In the first time user call it numRoundsClaimed starts from 0
but roundId will keep incrementing.
function setNewRound() external { require(isAdmin[msg.sender]); require(totalAccumulatedPoints[roundId] > 0); roundId += 1; _stakeAtRiskInstance.setNewRound(roundId); rankedNrnDistribution[roundId] = rankedNrnDistribution[roundId - 1]; }
this leads to problem
suppose user participate in round 100, he does not claim the reward,
now the round id is 20000
even he does not participate in most of round between round id 100 to round 20000
the for loop has to interate 20000 times, and he has to pay for the gas fee for round that he does not participate.
this can eventually leads to out of gas and block user claim if the round id number is too large.
records the last round a user participated in,
instead of iterating from the user's last claim round to the current round.
The contract only iterates through the rounds in which the user actually has claimable rewards,
reducing the number of iterations and, consequently, the gas cost.
DoS
#0 - c4-pre-sort
2024-02-23T23:59:50Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-02-24T00:00:09Z
raymondfam marked the issue as duplicate of #1541
#2 - c4-judge
2024-03-11T13:01:19Z
HickupHH3 marked the issue as duplicate of #216
#3 - c4-judge
2024-03-12T02:36:04Z
HickupHH3 marked the issue as partial-50
#4 - c4-judge
2024-03-21T02:57:41Z
HickupHH3 marked the issue as satisfactory
π Selected for report: t0x1c
Also found by: 0xCiphky, 0xDetermination, Draiakoo, Greed, Kalogerone, MatricksDeCoder, MidgarAudits, MrPotatoMagic, PedroZurdo, Shubham, SpicyMeatball, VAD37, Velislav4o, ZanyBonzy, btk, cats, djxploit, forkforkdog, givn, ladboy233, lanrebayode77, lil_eth, visualbits, zaevlad
59.2337 USDC - $59.23
bool success = _neuronInstance.transferFrom(msg.sender, treasuryAddress, price); if (success) { if (dailyAllowanceReplenishTime[msg.sender][tokenId] <= block.timestamp) { _replenishDailyAllowance(tokenId); } allowanceRemaining[msg.sender][tokenId] -= quantity; if (allGameItemAttributes[tokenId].finiteSupply) { allGameItemAttributes[tokenId].itemsRemaining -= quantity; } _mint(msg.sender, tokenId, quantity, bytes("random")); emit BoughtItem(msg.sender, tokenId, quantity); }
this limit is track per address using msg.sender address
However, this restriction can be bypassed
A player uses their account (Account A) to mint items until they reach their daily limit.
The player then creates a new account (Account B), buys some NRN token, and uses this new account to mint more items.
If the items are allowed to be transferred between players,
the player can simply move the items from Account B back to their main Account A,
effectively bypassing the daily mint limit.
N/A
Token-Transfer
#0 - c4-pre-sort
2024-02-22T18:03:00Z
raymondfam marked the issue as insufficient quality report
#1 - c4-pre-sort
2024-02-22T18:03:16Z
raymondfam marked the issue as duplicate of #43
#2 - c4-judge
2024-03-07T04:16:50Z
HickupHH3 marked the issue as satisfactory