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: 75/243
Findings: 3
Award: $36.08
🌟 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/a6f2397b68ef2865374c1bf7629349f25e71a44d/smart-contracts/AuctionDemo.sol#L134-L143 https://github.com/code-423n4/2023-10-nextgen/blob/a6f2397b68ef2865374c1bf7629349f25e71a44d/smart-contracts/AuctionDemo.sol#L57-L61 https://github.com/code-423n4/2023-10-nextgen/blob/a6f2397b68ef2865374c1bf7629349f25e71a44d/smart-contracts/AuctionDemo.sol#L124-L130
The current auction mechanism has undermined the motivation of the auction and turned it into a mev game. User may don't have enough motivation to participate in the auction.
Bidder can cancel his bid and get his token before auction end.
function cancelBid(uint256 _tokenid, uint256 index) public { require(block.timestamp <= minter.getAuctionEndTime(_tokenid), "Auction ended"); require(auctionInfoData[_tokenid][index].bidder == msg.sender && auctionInfoData[_tokenid][index].status == true); auctionInfoData[_tokenid][index].status = false; (bool success, ) = payable(auctionInfoData[_tokenid][index].bidder).call{value: auctionInfoData[_tokenid][index].bid}(""); emit CancelBid(msg.sender, _tokenid, index, success, auctionInfoData[_tokenid][index].bid); }
In participateToAuction
, user need to take a bid higher than current highest bid.
function participateToAuction(uint256 _tokenid) public payable { require(msg.value > returnHighestBid(_tokenid) && block.timestamp <= minter.getAuctionEndTime(_tokenid) && minter.getAuctionStatus(_tokenid) == true); ..
Suppose a situation, at the beginning of an auction, malicious user take bid at a extreme high value. During the auction ,other user see the bidder price is so high, and he can not participate ,because lower bid is not allowed.Nearly at the end of auction time,malicious user can cancel the bid ,and take bid with a very small value , due to there is no other competitors,he will become the final winner. Or another situation, other mev player notice the malicious user's small value bid ,and front run,this auction will finally become a mev game.
manual
consider redesign the auction mechanism
MEV
#0 - c4-pre-sort
2023-11-15T08:53:40Z
141345 marked the issue as duplicate of #962
#1 - c4-judge
2023-12-02T15:12:52Z
alex-ppg marked the issue as not a duplicate
#2 - c4-judge
2023-12-02T15:16:13Z
alex-ppg marked the issue as duplicate of #1784
#3 - c4-judge
2023-12-07T11:50:05Z
alex-ppg marked the issue as duplicate of #1323
#4 - c4-judge
2023-12-08T17:25:11Z
alex-ppg marked the issue as partial-25
#5 - c4-judge
2023-12-08T17:28:03Z
alex-ppg marked the issue as satisfactory
#6 - c4-judge
2023-12-08T18:16:08Z
alex-ppg marked the issue as partial-25
#7 - c4-judge
2023-12-09T00:20:29Z
alex-ppg changed the severity to 3 (High Risk)
35.614 USDC - $35.61
https://github.com/code-423n4/2023-10-nextgen/blob/2467db02cc446374ab9154dde98f7e931d71584d/smart-contracts/MinterContract.sol#L540 https://github.com/code-423n4/2023-10-nextgen/blob/2467db02cc446374ab9154dde98f7e931d71584d/smart-contracts/MinterContract.sol#L566
"getPrice" returns an unexpectedly high value at the end time, it's very unfair to users who mint at the end time.
When users mint, getPrice
is called to get current nft price.
In saleoption 2
,with time goes by, the price will decrease.The issue is at the moment of endtime,the price will become extremely high.
block.timestamp < collectionPhases[_collectionId].publicEndTime
Notice this is <
,which should be <=
.
Due to this logic, the price at end will become return collectionPhases[_collectionId].collectionMintCost;
,which suddenly become high.
poc:
Create a new file and paste it.
const { loadFixture,time } = require("@nomicfoundation/hardhat-toolbox/network-helpers") const { expect } = require("chai") const { ethers } = require("hardhat") const fixturesDeployment = require("../scripts/fixturesDeployment.js") let signers let contracts describe("NextGen Tests", function () { before(async function () { ;({signers, contracts} = await loadFixture(fixturesDeployment)) }) context("Get Price decrease", () => { it.only("#Get Price decrease", async function () { await setEnviorment(); const priceAtWei = await contracts.hhMinter.getPrice( 2, // _collectionID ) const priceCurrent = ethers.formatEther(priceAtWei); console.log("priceCurrent",priceCurrent); const currenttime = await time.latest(); console.log("currenttime",currenttime); await time.increaseTo(1796931278 -1) const priceAtNearlyEndWei = await contracts.hhMinter.getPrice( 2, // _collectionID ) const priceAtNearlyEnd = ethers.formatEther(priceAtNearlyEndWei); console.log("priceAtNearlyEnd",priceAtNearlyEnd); await time.increaseTo(1796931278) const priceAtEndWei = await contracts.hhMinter.getPrice( 2, // _collectionID ) const priceAtEnd = ethers.formatEther(priceAtEndWei); console.log("priceAtEnd",priceAtEnd); }) }) }) async function setEnviorment() { await contracts.hhCore.createCollection( "Test Collection 1", "Artist 1", "For testing", "www.test.com", "CCO", "https://ipfs.io/ipfs/hash/", "", ["desc"], ) await contracts.hhCore.createCollection( "Test Collection 2", "Artist 2", "For testing", "www.test.com", "CCO", "https://ipfs.io/ipfs/hash/", "", ["desc"], ) await contracts.hhCore.createCollection( "Test Collection 3", "Artist 3", "For testing", "www.test.com", "CCO", "https://ipfs.io/ipfs/hash/", "", ["desc"], ) await contracts.hhAdmin.registerCollectionAdmin( 1, signers.addr1.address, true, ) await contracts.hhAdmin.registerCollectionAdmin( 3, signers.addr1.address, true, ) await contracts.hhCore.connect(signers.addr1).setCollectionData( 1, // _collectionID signers.addr1.address, // _collectionArtistAddress 2, // _maxCollectionPurchases 10000, // _collectionTotalSupply 0, // _setFinalSupplyTimeAfterMint ) await contracts.hhCore.setCollectionData( 2, // _collectionID signers.addr1.address, // _collectionArtistAddress 1, // _maxCollectionPurchases 100, // _collectionTotalSupply 1000, // _setFinalSupplyTimeAfterMint ) // await contracts.hhCore.setCollectionData( 3, // _collectionID signers.addr1.address, // _collectionArtistAddress 1, // _maxCollectionPurchases 100, // _collectionTotalSupply 1000, // _setFinalSupplyTimeAfterMint ) await contracts.hhCore.addMinterContract( contracts.hhMinter, ) await contracts.hhCore.addRandomizer( 2, contracts.hhRandomizer, ) await contracts.hhCore.addRandomizer( 3, contracts.hhRandomizer, ) await contracts.hhMinter.setCollectionCosts( 2, // _collectionID BigInt(1000000000000000000), // _collectionMintCost 1 eth BigInt(100000000000000000), // _collectionEndMintCost 0.1 eth BigInt(100000000000000000), // _rate 200, // _timePeriod 2, // _salesOptions '0xD7ACd2a9FD159E69Bb102A1ca21C9a3e3A5F771B', // delAddress ) await contracts.hhMinter.setCollectionPhases( 2, // _collectionID 1698138500, // _allowlistStartTime 1698138500, // _allowlistEndTime 1698138500, // _publicStartTime 1796931278, // _publicEndTime "0x8e3c1713145650ce646f7eccd42c4541ecee8f07040fc1ac36fe071bbfebb870", // _merkleRoot ) }
logs:
NextGen Tests Get Price decrease priceCurrent 0.1 currenttime 1699414629 priceAtNearlyEnd 0.1 priceAtEnd 1.0 ✔ #Get Price decrease (229ms) 1 passing (2s)
compare prices at 1796931278 -1
and 1796931278
, One is 1
another is '0.1`
manual
change <' to
<=`
Context
#0 - c4-pre-sort
2023-11-16T01:41:15Z
141345 marked the issue as duplicate of #1391
#1 - c4-judge
2023-12-08T21:42:13Z
alex-ppg marked the issue as partial-50
#2 - c4-judge
2023-12-09T01:50:25Z
alex-ppg changed the severity to 2 (Med Risk)
🌟 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
erc721 safeTransferFrom
may dos claimAuction
. It is unknown if the receiving address is contract or not and implements the onERC721Received function or not. If it is smart contract, it will dos the claim logic
claimAuction
is to transfer nft to the auction winner. When participating in aution, it never check bidder is eoa or smart-contract.
If the winner is smart-contract and do not have _onERC721Received()
, according to the erc721 safeTransferFrom
logic, it will revert. The claim logic, not only contains the transfer logic, but also repay token to other bidder, will dos. I think it's a serious problem.
manual
check bidder in participateToAuction
, if the bidder is smart contract ,check it has _onERC721Received()
or not
ERC721
#0 - c4-pre-sort
2023-11-16T14:11:23Z
141345 marked the issue as duplicate of #486
#1 - c4-judge
2023-12-01T22:49:27Z
alex-ppg marked the issue as not a duplicate
#2 - c4-judge
2023-12-01T22:49:45Z
alex-ppg marked the issue as duplicate of #1759
#3 - c4-judge
2023-12-08T22:15:50Z
alex-ppg marked the issue as partial-50
#4 - c4-judge
2023-12-09T00:23:12Z
alex-ppg changed the severity to 2 (Med Risk)