Platform: Code4rena
Start Date: 13/12/2023
Pot Size: $36,500 USDC
Total HM: 18
Participants: 110
Period: 8 days
Judge: 0xTheC0der
Id: 311
League: ETH
Rank: 91/110
Findings: 2
Award: $8.70
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: jnforja
Also found by: 0x175, 0xCiphky, 0xDING99YA, 0xmystery, ArmedGoose, Aymen0909, Franklin, KupiaSec, McToady, MrPotatoMagic, Ocean_Sky, PNS, Pechenite, TermoHash, Topmark, _eperezok, alexbabits, deth, hals, imare, jeff, ktg, leegh, mahdirostami, marqymarq10, mojito_auditor, neocrao, ptsanev, twcctop, zraxx
1.337 USDC - $1.34
An auction's parameters (timeBuffer
, reservePrice
and minBidIncrementPercentage
) can be changed anytime by contract owner, even when the auction is still in active. The changes will be effective immediately in createBid
and _settleAuction
, but users may not realize that. Possible impacts include:
reservePrice
and minBidIncrementPercentage
changes result bidding transactions fail.reservePrice
change results in unsuccessful auction if the new reservePrice
is larger than current highest bid, and no more bids after that.The set
functions for timeBuffer
, reservePrice
and minBidIncrementPercentage
are only limited to called by owner, and have no timing limit. Owner can change them anytime, even when an active auction exists. These changes take effect immediately in createBid
and _settleAuction
, which may create undefined behaviors, like fail of bidding, change of price.
/** * @notice Set the auction time buffer. * @dev Only callable by the owner. */ function setTimeBuffer(uint256 _timeBuffer) external override onlyOwner { timeBuffer = _timeBuffer; emit AuctionTimeBufferUpdated(_timeBuffer); } /** * @notice Set the auction reserve price. * @dev Only callable by the owner. */ function setReservePrice(uint256 _reservePrice) external override onlyOwner { reservePrice = _reservePrice; emit AuctionReservePriceUpdated(_reservePrice); } /** * @notice Set the auction minimum bid increment percentage. * @dev Only callable by the owner. */ function setMinBidIncrementPercentage(uint8 _minBidIncrementPercentage) external override onlyOwner { minBidIncrementPercentage = _minBidIncrementPercentage; emit AuctionMinBidIncrementPercentageUpdated(_minBidIncrementPercentage); }
vscode
Protect the set
functions with modifier whenPaused
, or cache the parameters of auction for an ongoing auction.
Other
#0 - c4-pre-sort
2023-12-22T04:18:24Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-12-22T04:18:37Z
raymondfam marked the issue as duplicate of #55
#2 - c4-pre-sort
2023-12-24T14:17:57Z
raymondfam marked the issue as duplicate of #495
#3 - c4-judge
2024-01-06T18:14:50Z
MarioPoneder changed the severity to QA (Quality Assurance)
#4 - c4-judge
2024-01-07T16:03:14Z
MarioPoneder marked the issue as grade-c
#5 - c4-judge
2024-01-10T17:32:52Z
This previously downgraded issue has been upgraded by MarioPoneder
#6 - c4-judge
2024-01-10T17:33:18Z
MarioPoneder marked the issue as duplicate of #515
#7 - c4-judge
2024-01-10T17:35:22Z
MarioPoneder marked the issue as partial-50
#8 - c4-judge
2024-01-11T18:03:12Z
MarioPoneder changed the severity to 2 (Med Risk)
🌟 Selected for report: 0xmystery
Also found by: 00xSEV, 0xCiphky, 0xDING99YA, 0xhitman, ABAIKUNANBAEV, Aamir, BARW, IllIllI, King_, MrPotatoMagic, Pechenite, SovaSlava, SpicyMeatball, Topmark, Ward, ZanyBonzy, ast3ros, bart1e, cheatc0d3, deth, developerjordy, hals, imare, kaveyjoe, ktg, leegh, osmanozdemir1, peanuts, roland, rvierdiiev, shaka, sivanesh_808, spacelord47
7.359 USDC - $7.36
validateMediaType
does not check the metadata of AUDIO art piece.The metadata of all art piece types are defined and validated except AUDIO type.
File: packages/revolution/src/CultureIndex.sol 159: function validateMediaType(ArtPieceMetadata calldata metadata) internal pure { 160: require(uint8(metadata.mediaType) > 0 && uint8(metadata.mediaType) <= 5, "Invalid media type"); 161: 162: if (metadata.mediaType == MediaType.IMAGE) 163: require(bytes(metadata.image).length > 0, "Image URL must be provided"); 164: else if (metadata.mediaType == MediaType.ANIMATION) 165: require(bytes(metadata.animationUrl).length > 0, "Animation URL must be provided"); 166: else if (metadata.mediaType == MediaType.TEXT) 167: require(bytes(metadata.text).length > 0, "Text must be provided"); // @audit no checking for AUDIO type 168: }
File: packages/revolution/src/interfaces/ICultureIndex.sol 88: struct ArtPieceMetadata { 89: string name; 90: string description; 91: MediaType mediaType; 92: string image; 93: string text; 94: string animationUrl; // @audit no metadata for AUDIO type 95: }
The comment for metadata is incorrect (L205
). The animation URL is mandatory and not optional according to validateMediaType(L164-L165
).
File: packages/revolution/src/CultureIndex.sol 164: else if (metadata.mediaType == MediaType.ANIMATION) 165: require(bytes(metadata.animationUrl).length > 0, "Animation URL must be provided"); 205: * - `metadata` must include name, description, and image. Animation URL is optional. // @audit incorrect comment
newQuorumVotesBPS
.The hardcoded quorum votes bps (MAX_QUORUM_VOTES_BPS
) is max instead of min.
File: packages/revolution/src/CultureIndex.sol 495: * @dev newQuorumVotesBPS must be greater than the hardcoded min
First check if the token is minted before returning the token's metadata, avoiding return invalid data for non-existing token.
File: packages/revolution/src/VerbsToken.sol 193: function tokenURI(uint256 tokenId) public view override returns (string memory) { 194: return descriptor.tokenURI(tokenId, artPieces[tokenId].metadata); // @audit `_requireOwned(tokenId)` 195: } 200: function dataURI(uint256 tokenId) public view override returns (string memory) { 201: return descriptor.dataURI(tokenId, artPieces[tokenId].metadata); // @audit `_requireOwned(tokenId)` 202: }
VerbsToken.sol#L193-L195,VerbsToken.sol#L200-L202
unlock
functions for minter
, descriptor
and cultureIndex
.The lock functions (lockMinter
, lockDescriptor
, lockCultureIndex
) are risky when no unlock functions are provided. Once locked, there's no way to unlock it, which means it is unable to upgrade minter
, descriptor
and cultureIndex
.
File: packages/revolution/src/VerbsToken.sol 220: function lockMinter() external override onlyOwner whenMinterNotLocked 242: function lockDescriptor() external override onlyOwner whenDescriptorNotLocked { 262: function lockCultureIndex() external override onlyOwner whenCultureIndexNotLocked {
VerbsToken.sol#L220, VerbsToken.sol#L242, VerbsToken.sol#L262
reservePrice
.Use _auction.amount < reservePrice
instead of address(this).balance < reservePrice
, as _auction.amount
is the real bid amount of the auction and it is possible that address(this).balance != _auction.amount
.
File: packages/revolution/src/AuctionHouse.sol 348: if (address(this).balance < reservePrice) { // @audit use `_auction.amount < reservePrice` 349: // If contract balance is less than reserve price, refund to the last bidder 350: if (_auction.bidder != address(0)) { 351: _safeTransferETHWithFallback(_auction.bidder, _auction.amount); 352: }
File: packages/revolution/src/CultureIndex.sol 185: for (uint i; i < creatorArrayLength; i++) { 186: require(creatorArray[i].creator != address(0), "Invalid creator address"); 187: totalBps += creatorArray[i].bps; // @audit require `creatorArray[i].bps != 0` 188: }
#0 - c4-pre-sort
2023-12-24T17:48:38Z
raymondfam marked the issue as sufficient quality report
#1 - c4-judge
2024-01-07T19:37:21Z
MarioPoneder marked the issue as grade-b