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: 66/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
User can win a token without paying the bid.
When a collection token is minted for auction, user place a bid through function participateToAuction(uint256 _tokenid) to participate. If a user's bid is the highest bid, the user can call function claimAuction(uint256 _tokenid) to claim the token.
function claimAuction(uint256 _tokenid) public WinnerOrAdminRequired(_tokenid,this.claimAuction.selector){ require(block.timestamp >= minter.getAuctionEndTime(_tokenid) && auctionClaim[_tokenid] == false && minter.getAuctionStatus(_tokenid) == true); auctionClaim[_tokenid] = true; uint256 highestBid = returnHighestBid(_tokenid); address ownerOfToken = IERC721(gencore).ownerOf(_tokenid); address highestBidder = returnHighestBidder(_tokenid); 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 {} } }
Function claimAuction
allows user to claim at minter.getAuctionEndTime(_tokenid)
.
require(block.timestamp >= minter.getAuctionEndTime(_tokenid) && auctionClaim[_tokenid] == false && minter.getAuctionStatus(_tokenid) == true);
However, user can also call function cancelBid(uint256 _tokenid, uint256 index) to cancel a bid at minter.getAuctionEndTime(_tokenid)
.
require(block.timestamp <= minter.getAuctionEndTime(_tokenid), "Auction ended");
It's possible that an auction winner claim the token at minter.getAuctionEndTime(_tokenid)
and then cancel the bid at the same time, the winner gets the token without paying the bid.
Manual Review
It's recommended that should only allow user to claim token after minter.getAuctionEndTime(_tokenid)
.
Access Control
#0 - c4-pre-sort
2023-11-15T08:38:15Z
141345 marked the issue as duplicate of #962
#1 - c4-judge
2023-12-01T15:39:04Z
alex-ppg marked the issue as not a duplicate
#2 - c4-judge
2023-12-01T15:39:13Z
alex-ppg marked the issue as duplicate of #1788
#3 - c4-judge
2023-12-08T18:14:43Z
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
User can mint a burnToMint token and then sell the burned
token.
If collection A token is able to be burned to mint a collection B token, user can call function burnToMint(uint256 mintIndex, uint256 _burnCollectionID, uint256 _tokenId, uint256 _mintCollectionID, uint256 _saltfun_o, address burner) to burn the collection A token and get a collection B token in return.
function burnToMint(uint256 mintIndex, uint256 _burnCollectionID, uint256 _tokenId, uint256 _mintCollectionID, uint256 _saltfun_o, address burner) external { require(msg.sender == minterContract, "Caller is not the Minter Contract"); require(_isApprovedOrOwner(burner, _tokenId), "ERC721: caller is not token owner or approved"); collectionAdditionalData[_mintCollectionID].collectionCirculationSupply = collectionAdditionalData[_mintCollectionID].collectionCirculationSupply + 1; 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; } }
New collection B token will be first minted to the user and then the collection A token will be burned.
_mintProcessing(mintIndex, ownerOf(_tokenId), tokenData[_tokenId], _mintCollectionID, _saltfun_o); // burn token _burn(_tokenId);
If in a secondary market, a buyer placed a buy order for a collection A token and deposited the funds, for seller to make a transaction to sell the collection token and receive the funds.
A malicious user who owns the collection A token can call function burnToMint
from a contract, the a collection B token will be minted to the contract and callback function onERC721Received
on the contract will be called.
The malicious user can then sell the collection A token to the buyer and get the funds, then the token is burned and the buyer gets nothing.
function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4) { // Sell the collection A token to the secondary market ... }
Manual Review
It's recommended that collection A token should be burned before collection B token is minted.
ERC721
#0 - c4-pre-sort
2023-11-20T02:13:48Z
141345 marked the issue as duplicate of #1198
#1 - c4-pre-sort
2023-11-20T09:25:01Z
141345 marked the issue as duplicate of #1597
#2 - c4-pre-sort
2023-11-26T14:00:10Z
141345 marked the issue as duplicate of #1742
#3 - c4-judge
2023-11-29T19:54:37Z
alex-ppg marked the issue as not a duplicate
#4 - c4-judge
2023-11-29T19:55:06Z
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:26:49Z
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
User may not be able to get their funds back after auction ends.
When an auction ends, the winner or admin calls function claimAuction(uint256 _tokenid) to claim the token. The token will be transferred to the winner.
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); }
Here, function safeTransferFrom(address from, address to, uint256 tokenId) and _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) will be called eventually.
If the winner is a contract, function _checkOnERC721Received
will call the callback function onERC721Received
in the receiving contract and it will fail unless the receiving contract properly implements the ERC721TokenReceiver
interface.
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)) } } } }
It's possible that the winner contract does not implement the ERC721TokenReceiver
interface properly, then the claiming will fail.
However, user can only get refund through the function claimAuction
.
} 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 {}
A failed claiming results in users being unable to get their funds back.
Manual Review
It's recommended that user should be able to get refund without function claimAuction
.
DoS
#0 - c4-pre-sort
2023-11-15T08:37:50Z
141345 marked the issue as duplicate of #843
#1 - c4-pre-sort
2023-11-16T13:35:58Z
141345 marked the issue as duplicate of #486
#2 - c4-judge
2023-12-01T22:44:58Z
alex-ppg marked the issue as not a duplicate
#3 - c4-judge
2023-12-01T22:45:24Z
alex-ppg marked the issue as duplicate of #1759
#4 - c4-judge
2023-12-08T22:15:06Z
alex-ppg marked the issue as partial-50
#5 - c4-judge
2023-12-09T00:23:13Z
alex-ppg changed the severity to 2 (Med Risk)