Platform: Code4rena
Start Date: 30/10/2023
Pot Size: $49,250 USDC
Total HM: 14
Participants: 243
Period: 14 days
Judge: 0xsomeone
Id: 302
League: ETH
Rank: 65/243
Findings: 3
Award: $96.20
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: smiling_heretic
Also found by: 00decree, 00xSEV, 0x180db, 0x3b, 0x656c68616a, 0xAadi, 0xAleko, 0xAsen, 0xDetermination, 0xJuda, 0xMAKEOUTHILL, 0xMango, 0xMosh, 0xSwahili, 0x_6a70, 0xarno, 0xgrbr, 0xpiken, 0xsagetony, 3th, 8olidity, ABA, AerialRaider, Al-Qa-qa, Arabadzhiev, AvantGard, CaeraDenoir, ChrisTina, DanielArmstrong, DarkTower, DeFiHackLabs, Deft_TT, Delvir0, Draiakoo, Eigenvectors, Fulum, Greed, HChang26, Haipls, Hama, Inference, Jiamin, JohnnyTime, Jorgect, Juntao, Kaysoft, Kose, Kow, Krace, MaNcHaSsS, Madalad, MrPotatoMagic, Neon2835, NoamYakov, Norah, Oxsadeeq, PENGUN, REKCAH, Ruhum, Shubham, Silvermist, Soul22, SovaSlava, SpicyMeatball, Talfao, TermoHash, The_Kakers, Toshii, TuringConsulting, Udsen, VAD37, Vagner, Zac, Zach_166, ZdravkoHr, _eperezok, ak1, aldarion, alexfilippov314, alexxander, amaechieth, aslanbek, ast3ros, audityourcontracts, ayden, bdmcbri, bird-flu, blutorque, bronze_pickaxe, btk, c0pp3rscr3w3r, c3phas, cartlex_, cccz, ciphermarco, circlelooper, crunch, cryptothemex, cu5t0mpeo, darksnow, degensec, dethera, devival, dimulski, droptpackets, epistkr, evmboi32, fibonacci, gumgumzum, immeas, innertia, inzinko, jasonxiale, joesan, ke1caM, kimchi, lanrebayode77, lsaudit, mahyar, max10afternoon, merlin, mrudenko, nuthan2x, oakcobalt, openwide, orion, phoenixV110, pontifex, r0ck3tz, rotcivegaf, rvierdiiev, seeques, shenwilly, sl1, slvDev, t0x1c, tallo, tnquanghuy0512, tpiliposian, trachev, twcctop, vangrim, volodya, xAriextz, xeros, xuwinnie, y4y, yobiz, zhaojie
0 USDC - $0.00
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/MinterContract.sol#L276 https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L124
Highest bidder can cancel the bid right after claiming the token.
A token minted for auction, it can be claimed by the highest bidder right after the auction ends.
function claimAuction(uint256 _tokenid) public WinnerOrAdminRequired(_tokenid,this.claimAuction.selector){ require(block.timestamp >= minter.getAuctionEndTime(_tokenid) && auctionClaim[_tokenid] == false && minter.getAuctionStatus(_tokenid) == true); ... }
The bid can also be cancelled at the end of the auction.
function cancelBid(uint256 _tokenid, uint256 index) public { require(block.timestamp <= minter.getAuctionEndTime(_tokenid), "Auction ended"); ... }
It can be seen that if block.timestamp
is minter.getAuctionEndTime(_tokenid), the token can be claimed and cancelled in the same transaction.
function getAuctionEndTime(uint256 _tokenId) external view returns (uint) { return mintToAuctionData[_tokenId]; }
This results in the highest bidder receives the token but not pays a price.
Manual Review
Please consider to change the requirement and not allow the token to be claimed at auction end time.
Access Control
#0 - c4-pre-sort
2023-11-15T07:46:33Z
141345 marked the issue as duplicate of #962
#1 - c4-judge
2023-12-01T15:25:56Z
alex-ppg marked the issue as not a duplicate
#2 - c4-judge
2023-12-01T15:26:05Z
alex-ppg marked the issue as duplicate of #1788
#3 - c4-judge
2023-12-08T18:05:47Z
alex-ppg marked the issue as partial-50
🌟 Selected for report: ast3ros
Also found by: 00xSEV, Al-Qa-qa, CaeraDenoir, Jiamin, Juntao, Ruhum, bart1e, circlelooper, crunch, gumgumzum, rishabh, smiling_heretic, ustas
95.7343 USDC - $95.73
Owner of burnToMint token can frontrun secondary sales.
The collection token can be traded on secondary market, since they are standard ERC721 tokens.
In a Burn-to-Mint sale model, a collection token can be burned to minted a different collection token, this is done through burnToMint method.
The minted token will be transferred to the receiver through ERC721's _safeMint
method, which will call _checkOnERC721Received
method if the receiver is a contract.
Then the collection token used for minting will be burned.
if (collectionAdditionalData[_mintCollectionID].collectionTotalSupply >= collectionAdditionalData[_mintCollectionID].collectionCirculationSupply) { _mintProcessing(mintIndex, ownerOf(_tokenId), tokenData[_tokenId], _mintCollectionID, _saltfun_o); // burn token _burn(_tokenId); burnAmount[_burnCollectionID] = burnAmount[_burnCollectionID] + 1; }
The problem is the _checkOnERC721Received
method in the receiver contract, a bad actor may sell the to-be-burned
collection token after receiving the new collection token, the buyer of the to-be-burned
collection token will get the token and lose it immediately, because the token will be burned then.
Manual Review
Please consider to burn the to-be-burned
collection token, then mint the new collection token.
Access Control
#0 - c4-pre-sort
2023-11-20T02:25:39Z
141345 marked the issue as duplicate of #1198
#1 - c4-pre-sort
2023-11-20T09:25:03Z
141345 marked the issue as duplicate of #1597
#2 - c4-pre-sort
2023-11-26T14:00:13Z
141345 marked the issue as duplicate of #1742
#3 - c4-judge
2023-11-29T19:54:35Z
alex-ppg marked the issue as not a duplicate
#4 - c4-judge
2023-11-29T19:54:59Z
alex-ppg marked the issue as duplicate of #1597
#5 - c4-judge
2023-12-05T12:24:54Z
alex-ppg changed the severity to 2 (Med Risk)
#6 - c4-judge
2023-12-08T21:24:54Z
alex-ppg marked the issue as partial-50
🌟 Selected for report: The_Kakers
Also found by: 00decree, 00xSEV, 0x180db, 0x3b, 0xJuda, 0x_6a70, 0xarno, 0xpiken, Arabadzhiev, Bauchibred, BugsFinder0x, BugzyVonBuggernaut, ChrisTina, DeFiHackLabs, Delvir0, HChang26, Haipls, Jiamin, Juntao, KupiaSec, Madalad, Neon2835, Nyx, Ocean_Sky, SpicyMeatball, Talfao, Taylor_Webb, Timenov, Tricko, ZdravkoHr, _eperezok, alexxander, amaechieth, bdmcbri, bronze_pickaxe, circlelooper, crunch, cu5t0mpeo, dimulski, fibonacci, funkornaut, immeas, ke1caM, lsaudit, nuthan2x, r0ck3tz, rotcivegaf, spark, tnquanghuy0512, twcctop, xeros
0.4703 USDC - $0.47
Auction refunding can be DOSed by malicious winner.
Auction winner is supposed to claim token after an auction, or the admins can claim for him, the other bidders will be refunded at the claiming.
function claimAuction(uint256 _tokenid) public WinnerOrAdminRequired(_tokenid,this.claimAuction.selector){ ... for (uint256 i=0; i< auctionInfoData[_tokenid].length; i ++) { if (auctionInfoData[_tokenid][i].bidder == highestBidder && auctionInfoData[_tokenid][i].bid == highestBid && auctionInfoData[_tokenid][i].status == true) { IERC721(gencore).safeTransferFrom(ownerOfToken, highestBidder, _tokenid); (bool success, ) = payable(owner()).call{value: highestBid}(""); emit ClaimAuction(owner(), _tokenid, success, highestBid); } else if (auctionInfoData[_tokenid][i].status == true) { (bool success, ) = payable(auctionInfoData[_tokenid][i].bidder).call{value: auctionInfoData[_tokenid][i].bid}(""); emit Refund(auctionInfoData[_tokenid][i].bidder, _tokenid, success, highestBid); } else {} } }
This method will loop through all the bidders, if the bidder is the winner, the token will be transferred to the winner through safeTransferFrom
method, if the winner is a contract, onERC721Received
method will be called in the receiver contract.
function _checkOnERC721Received( address from, address to, uint256 tokenId, bytes memory data ) private returns (bool) { if (to.isContract()) { try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) { return retval == IERC721Receiver.onERC721Received.selector; } catch (bytes memory reason) { if (reason.length == 0) { revert("ERC721: transfer to non ERC721Receiver implementer"); } else { /// @solidity memory-safe-assembly assembly { revert(add(32, reason), mload(reason)) } } } } else { return true; } }
If the bidder is not the winner, the ether will be refunded to the bidder.
} else if (auctionInfoData[_tokenid][i].status == true) { (bool success, ) = payable(auctionInfoData[_tokenid][i].bidder).call{value: auctionInfoData[_tokenid][i].bid}(""); emit Refund(auctionInfoData[_tokenid][i].bidder, _tokenid, success, highestBid); } else {}
If the winner is being malicious and revert on purpose in the onERC721Received
method, then the whole claiming will fail and the other bidders won't be refunded and their ether will be locked.
Please note it's not necessarily mean that the winner will lose the token as he can make the receiver contract to accept the token at any time he wants to.
Manual Review
Please consider to separate refunding from claiming, so the bidders can be refunded even if the claiming is failed.
Access Control
#0 - c4-pre-sort
2023-11-15T07:46:16Z
141345 marked the issue as duplicate of #1653
#1 - c4-pre-sort
2023-11-15T08:06:48Z
141345 marked the issue as duplicate of #843
#2 - c4-pre-sort
2023-11-16T13:36:20Z
141345 marked the issue as duplicate of #486
#3 - c4-judge
2023-12-01T22:34:26Z
alex-ppg marked the issue as not a duplicate
#4 - c4-judge
2023-12-01T22:34:51Z
alex-ppg marked the issue as duplicate of #1759
#5 - c4-judge
2023-12-08T22:13:38Z
alex-ppg marked the issue as partial-50
#6 - c4-judge
2023-12-09T00:23:13Z
alex-ppg changed the severity to 2 (Med Risk)