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: 20/283
Findings: 4
Award: $268.27
π Selected for report: 0
π Solo Findings: 0
π 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
Game Items have a transferable
property which is used to prevent item transfers between addresses. However this restriction is ineffective as a check is only implemented on the safeTransferFrom()
function.
Users can still transfer tokens using just the safeBatchTransferFrom()
function which has not been restricted. THis function can be used to transfer one or multiple NFTs.
/// @notice Safely transfers an NFT from one address to another. /// @dev Added a check to see if the game item is transferable. //@audit safeBatchTransferFrom can be used to transfer nfts function safeTransferFrom( address from, address to, uint256 tokenId, uint256 amount, bytes memory data ) public override(ERC1155) { require(allGameItemAttributes[tokenId].transferable); super.safeTransferFrom(from, to, tokenId, amount, data); }
Manual review
Override the safeBatchTransferFrom()
function as well to enable restriction on the batch transfer functions
Token-Transfer
#0 - c4-pre-sort
2024-02-22T04:31:14Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-02-22T04:31:23Z
raymondfam marked the issue as duplicate of #18
#2 - c4-pre-sort
2024-02-26T00:29:33Z
raymondfam marked the issue as duplicate of #575
#3 - c4-judge
2024-03-05T04:47:38Z
HickupHH3 changed the severity to 3 (High Risk)
#4 - c4-judge
2024-03-05T04:57:58Z
HickupHH3 marked the issue as satisfactory
π Selected for report: nuthan2x
Also found by: 0xE1, 0xblackskull, 0xgrbr, 0xvj, Greed, McToady, MidgarAudits, PetarTolev, Sabit, SovaSlava, SpicyMeatball, Timenov, Tychai0s, _eperezok, alexxander, btk, c0pp3rscr3w3r, favelanky, jesjupyter, josephdara, juancito, klau5, kutugu, lil_eth, merlinboii, pynschon, sandy, shaflow2, zaevlad
29.1474 USDC - $29.15
The ownerAddress in the Neuron.sol
is used to set the access control on the different roles within the contract by utilizing AccessControl.sol
from openzeppelin. However the address cannot manage any of the roles in the contract apart from adding them.
Whenever the owner address tries to remove a role from an address, the transaction reverts because the DEFAULT_ADMIN_ROLE
is never set and there is no way for the ownerAddress to claim ownership.
As seen in the contract, the default admin is not set in the constructor:
constructor(address ownerAddress, address treasuryAddress_, address contributorAddress) ERC20("Neuron", "NRN") { _ownerAddress = ownerAddress; treasuryAddress = treasuryAddress_; isAdmin[_ownerAddress] = true; _mint(treasuryAddress, INITIAL_TREASURY_MINT); _mint(contributorAddress, INITIAL_CONTRIBUTOR_MINT); }
Also in the specialized functions made for adding roles and addresses, it can't be used to remove roles. And revokeRole()
cannot be used:
/// @notice Adds a new address to the minter role. /// @dev Only the owner address is authorized to call this function. /// @param newMinterAddress The address to be added as a minter function addMinter(address newMinterAddress) external { require(msg.sender == _ownerAddress); _setupRole(MINTER_ROLE, newMinterAddress); } /// @notice Adds a new address to the staker role. /// @dev Only the owner address is authorized to call this function. /// @param newStakerAddress The address to be added as a staker function addStaker(address newStakerAddress) external { require(msg.sender == _ownerAddress); _setupRole(STAKER_ROLE, newStakerAddress); } /// @notice Adds a new address to the spender role. /// @dev Only the owner address is authorized to call this function. /// @param newSpenderAddress The address to be added as a spender function addSpender(address newSpenderAddress) external { require(msg.sender == _ownerAddress); _setupRole(SPENDER_ROLE, newSpenderAddress); }
Manual Review
Set the DEFAULT_ADMIN_ROLE
in the constructor.
Access Control
#0 - c4-pre-sort
2024-02-22T05:13:05Z
raymondfam marked the issue as insufficient quality report
#1 - c4-pre-sort
2024-02-22T05:13:27Z
raymondfam marked the issue as duplicate of #20
#2 - c4-judge
2024-03-05T09:57:31Z
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
The claimRewards()
is used to claim rewards by comparing winner addresses from the beginning of the contract to it's current round. This is done via a loop&compare method. However the game rounds will reach a max limit where loops will exceed the gas limit for a transaction. THis will prevent new winners who have to loop from numRoundsClaimed[msg.sender] = 0
up to current roundds of a 100 or 1000.
function claimRewards( string[] calldata modelURIs, string[] calldata modelTypes, uint256[2][] calldata customAttributes ) external { uint256 winnersLength; uint32 claimIndex = 0; uint32 lowerBound = numRoundsClaimed[msg.sender]; //@audit issue new winners will not be able to claim after multiple rounds 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; } } }
For all users, the numRoundsClaimed
will start from zero. At the point where the loop gets to large, the transactions will fail for all new claimers,causing lose of funds
Manual Review
Enable users claim for a specific round, Then maintain a mapping signifying that they have claimed
DoS
#0 - c4-pre-sort
2024-02-24T00:05:20Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-02-24T00:05:30Z
raymondfam marked the issue as duplicate of #1541
#2 - c4-judge
2024-03-11T13:01:34Z
HickupHH3 marked the issue as duplicate of #216
#3 - c4-judge
2024-03-12T02:36:29Z
HickupHH3 marked the issue as partial-50
#4 - c4-judge
2024-03-21T02:59:34Z
HickupHH3 marked the issue as satisfactory
π Selected for report: Timenov
Also found by: 0x11singh99, 0xblackskull, CodeWasp, MidgarAudits, MrPotatoMagic, Rolezn, Sabit, SovaSlava, andywer, btk, josephdara, lil_eth, merlinboii, sobieski, vnavascues
238.8948 USDC - $238.89
The game items contract allows different specialized roles including the allowedBurningAddresses
which is set using the setAllowedBurningAddresses
function. However unlike other roles in this contract as well as other contracts, the addresses allowed for burning are non removeable.
This is particularly an issue as it grants non revokeable access to users tokens.
function setAllowedBurningAddresses(address newBurningAddress) public { require(isAdmin[msg.sender]); allowedBurningAddresses[newBurningAddress] = true; }
As seen above, the boolean value to be set for every new address is hardcoded to zero, therefore admins can never remove access for burning addresses.
Manual Review
Add an extra parameter to the function to allow passing in a boolean variable. This will enable setting addresses to true or false.
function setAllowedBurningAddresses(address newBurningAddress, bool allowed) public { require(isAdmin[msg.sender]); allowedBurningAddresses[newBurningAddress] = allowed; }
Access Control
#0 - c4-pre-sort
2024-02-22T19:32:59Z
raymondfam marked the issue as insufficient quality report
#1 - c4-pre-sort
2024-02-22T19:33:07Z
raymondfam marked the issue as duplicate of #47
#2 - c4-judge
2024-03-08T03:30:47Z
HickupHH3 marked the issue as satisfactory