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: 267/283
Findings: 2
Award: $0.10
π 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
Before Fighter NFTs can be transferred, two conditions must be met.
All these are enforced by overriding safeTransferFrom(address,address,uint256)
. However, safeTransferFrom(address,address,uint256,bytes)
is ignored, which makes it possible for NFTs to be transferred.
function test_TransferStakedNFT() public { vm.label(_DELEGATED_ADDRESS, "Receiver"); _mintFromMergingPool(_ownerAddress); _fighterFarmContract.addStaker(_ownerAddress); // Update NFT staking info to true _fighterFarmContract.updateFighterStaking(0, true); // Attempt to transfer Locked Fighter vm.expectRevert(); _fighterFarmContract.safeTransferFrom(_ownerAddress, _DELEGATED_ADDRESS, 0); // Transfer with safeTransferFrom(address,address,uint256,bytes) _fighterFarmContract.safeTransferFrom(_ownerAddress, _DELEGATED_ADDRESS, 0, ""); assertEq(_fighterFarmContract.ownerOf(0), _DELEGATED_ADDRESS); }
function test_TransferNFTsToMaxedReceiver() public { vm.label(_DELEGATED_ADDRESS, "Receiver"); _mintFromMergingPool(_ownerAddress); for (uint8 i = 0; i < 10; i++) { _mintFromMergingPool(_DELEGATED_ADDRESS); } console.log(_fighterFarmContract.balanceOf(_DELEGATED_ADDRESS)); // Attempt to transfer Locked Fighter vm.expectRevert(); _fighterFarmContract.safeTransferFrom(_ownerAddress, _DELEGATED_ADDRESS, 0); // Transfer with safeTransferFrom(address,address,uint256,bytes) _fighterFarmContract.safeTransferFrom(_ownerAddress, _DELEGATED_ADDRESS, 0, ""); assertEq(_fighterFarmContract.ownerOf(0), _DELEGATED_ADDRESS); console.log(_fighterFarmContract.balanceOf(_DELEGATED_ADDRESS)); }
β ββ emit FighterCreated(id: 10, weight: 80, element: 1, generation: 0) β β ββ β () β ββ β () ββ [695] FighterFarm::balanceOf(Receiver: [0x22F4441ad6DbD602dFdE5Cd8A38F6CAdE68860b0]) [staticcall] β ββ β 10 ββ [0] console::log(10) [staticcall] β ββ β () ββ [0] VM::expectRevert(custom error f4844814:) β ββ β () ββ [1283] FighterFarm::safeTransferFrom(FighterFarmTest: [0x90193C961A926261B756D1E5bb255e67ff9498A1], Receiver: [0x22F4441ad6DbD602dFdE5Cd8A38F6CAdE68860b0], 0) β ββ β EvmError: Revert ββ [33506] FighterFarm::safeTransferFrom(FighterFarmTest: [0x90193C961A926261B756D1E5bb255e67ff9498A1], Receiver: [0x22F4441ad6DbD602dFdE5Cd8A38F6CAdE68860b0], 0, 0x) β ββ emit Approval(owner: FighterFarmTest: [0x90193C961A926261B756D1E5bb255e67ff9498A1], approved: 0x0000000000000000000000000000000000000000, tokenId: 0) β ββ emit Transfer(from: FighterFarmTest: [0x90193C961A926261B756D1E5bb255e67ff9498A1], to: Receiver: [0x22F4441ad6DbD602dFdE5Cd8A38F6CAdE68860b0], tokenId: 0) β ββ β () ββ [581] FighterFarm::ownerOf(0) [staticcall] β ββ β Receiver: [0x22F4441ad6DbD602dFdE5Cd8A38F6CAdE68860b0] ββ [695] FighterFarm::balanceOf(Receiver: [0x22F4441ad6DbD602dFdE5Cd8A38F6CAdE68860b0]) [staticcall] β ββ β 11 ββ [0] console::log(11) [staticcall] β ββ β () ββ β ()
forge t -vvvvv --mt test_Transfer
Foundry
Override safeTransferFrom(address,address,uint256,bytes)
instead of safeTransferFrom(address,address,uint256)
FighterFarm.sol . This is because the former gets called by the latter with an empty bytes (openzeppelin@4.7.3:ERC721.sol#L169).
function safeTransferFrom( address from, address to, uint256 tokenId bytes memory data ) public override(ERC721, IERC721) { require(_ableToTransfer(tokenId, to)); _safeTransfer(from, to, tokenId, data); }
Token-Transfer
#0 - c4-pre-sort
2024-02-23T05:38:03Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-02-23T05:38:11Z
raymondfam marked the issue as duplicate of #739
#2 - c4-judge
2024-03-11T02:50:00Z
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
The lack of an override forΒ safeBatchTransferFrom
Β could lead to security vulnerabilities or unexpected behaviour in contracts that interact with the token. For example, a malicious actor could potentially transfer non-transferable tokens in a batch transfer, which could bypass checks for non-transferable tokens.
The providedΒ safeTransferFrom
Β function overrides the standard ERC1155 function to check if the NFT is transferable. However, theΒ safeBatchTransferFrom
Β function, which is also a part of the ERC1155 standard, is not overridden. This means that the additional check for transferability is not applied when transferring multiple tokens at once. This could lead to unexpected behaviour.
function testTransferNonTransferrableNFTs() public { vm.label(_DELEGATED_ADDRESS, "Receiver"); _fundUserWith4kNeuronByTreasury(_ownerAddress); // Mint NFT _gameItemsContract.mint(0, 1); // Set NFT as non-transferrable _gameItemsContract.adjustTransferability(0, false); // Attempt to transfer NFT with safeTransferFrom vm.expectRevert(); _gameItemsContract.safeTransferFrom(_ownerAddress, _DELEGATED_ADDRESS, 0, 1, ""); uint256[] memory id = new uint256[](1); id[0] = 0; uint256[] memory amount = new uint256[](1); amount[0] = 1; // Transfer NFT with safeBatchTransferFrom _gameItemsContract.safeBatchTransferFrom(_ownerAddress, _DELEGATED_ADDRESS, id, amount, ""); assertEq(_gameItemsContract.balanceOf(_DELEGATED_ADDRESS, 0), 1); assertEq(_gameItemsContract.balanceOf(_ownerAddress, 0), 0); }
ββ [6908] GameItems::adjustTransferability(0, false) β ββ emit Locked(tokenId: 0) β ββ β () ββ [0] VM::expectRevert(custom error f4844814:) β ββ β () ββ [1314] GameItems::safeTransferFrom(GameItemsTest: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496], Receiver: [0x22F4441ad6DbD602dFdE5Cd8A38F6CAdE68860b0], 0, 1, 0x) β ββ β EvmError: Revert ββ [31942] GameItems::safeBatchTransferFrom(GameItemsTest: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496], Receiver: [0x22F4441ad6DbD602dFdE5Cd8A38F6CAdE68860b0], [0], [1], 0x) β ββ emit TransferBatch(operator: GameItemsTest: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496], from: GameItemsTest: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496], to: Receiver: [0x22F4441ad6DbD602dFdE5Cd8A38F6CAdE68860b0], ids: [0], values: [1]) β ββ β () ββ [718] GameItems::balanceOf(Receiver: [0x22F4441ad6DbD602dFdE5Cd8A38F6CAdE68860b0], 0) [staticcall] β ββ β 1 ββ [718] GameItems::balanceOf(GameItemsTest: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496], 0) [staticcall] β ββ β 0 ββ β ()
forge t -vvvvv --mt testTransferNonTransferrableNFTs
Foundry
Override safeBatchTransferFrom
with the same check require(allGameItemAttributes[tokenId].transferable);
to GameItems.sol
function safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) public virtual override(ERC1155) { for (uint256 i = 0; i < ids.length; i++) { require(allGameItemAttributes[ids[i]].transferable); } super.safeBatchTransferFrom(from, to, ids, amounts, data); }
Token-Transfer
#0 - c4-pre-sort
2024-02-22T04:19:14Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-02-22T04:19:21Z
raymondfam marked the issue as duplicate of #18
#2 - c4-pre-sort
2024-02-26T00:29:09Z
raymondfam marked the issue as duplicate of #575
#3 - c4-judge
2024-03-05T04:56:40Z
HickupHH3 marked the issue as satisfactory