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: 116/243
Findings: 1
Award: $13.39
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: The_Kakers
Also found by: 00xSEV, 0x3b, Arabadzhiev, DeFiHackLabs, Fulum, Madalad, MrPotatoMagic, SpicyMeatball, Tadev, ZanyBonzy, ZdravkoHr, alexfilippov314, audityourcontracts, cheatc0d3, devival, dy, evmboi32, immeas, lsaudit, mrudenko, oakcobalt, oualidpro, pipidu83, r0ck3tz, rishabh, rotcivegaf, tpiliposian, xAriextz
13.3948 USDC - $13.39
constructor (address _minter, address _gencore, address _adminsContract) public {
else {}
returnhighestbidder
sets the highbid to 0. The available bids are then compared against it, presumably to check if they're higher. However, the highbid is not set to the last checked bid. This means that that bids are compared against 0 and not the actual bids. COnsider fixing this for as good practice.function returnHighestBidder(uint256 _tokenid) public view returns (address) { uint256 highBid = 0; uint256 index; for (uint256 i=0; i< auctionInfoData[_tokenid].length; i++) { if (auctionInfoData[_tokenid][i].bid > highBid && auctionInfoData[_tokenid][i].status == true) { //@note all bids are compared against 0 index = i; } } ...
returnHighestBidder
function can be refactored.
To participate in an auction, the amount bid must be greater than the current highest bid, and each newbid
is pushed into the auctionInfoData
array. This ensures that every newbid
(which is greater than the previous) becomes the last element of the auctionInfoData
array. Thererfore, the last element of the array belonds to the highest bidder.The returnHighestBidder
can be made to count down from the last element instead of the first, and checks their auction status. If true, then it is selected. It seems faster and less gas intensive as the loop is more likely to run lesser iterations.
e.g
function returnHighestBidder(uint256 _tokenid) public view returns (address) { uint256 index; uint256 refactor = auctionInfoData[_tokenid].length; for (uint i = refactor; i > 0;) { if (auctionInfoData[_tokenid][i - 1].status == true) { index = i - 1; break; unchecked{ --i; } else { revert("No Active Bidder"); } } return auctionInfoData[_tokenid][index].bidder; }
participateToAuction
, cancelBid
, cancelAllBids
functions can be called when block.timestamp <= minter.getAuctionEndTime(_tokenid)
i.e when auction ends.The claimAuction
function can be claimed when block.timestamp >= minter.getAuctionEndTime(_tokenid)
.
This introduces a potenctial race condition in which users and admins can all call the functions at the exact same time for the exact same token, when block.timestamp >= minter.getAuctionEndTime(_tokenid)
.
Depending on which gets executed first, undersired outcomes may occur. For instance, If the highest bidder decides to cancel his auction, he might find out that the admin already claimed on his behalf, or that another user outbidded him at the last second.
Consider changing the condition in claimAuction
to block.timestamp > minter.getAuctionEndTime(_tokenid)
.
registerCollectionAdmin
should check for collection existence before registering admin so as to prevent setting admin for non-existent collections;function registerCollectionAdmin(uint256 _collectionID, address _address, bool _status) public AdminRequired { require(_collectionID > 0, "Collection Id must be larger than 0"); //@note inexistent collection?? collectionAdmin[_address][_collectionID] = _status; }
function registerAdmin(address _admin, bool _status) public onlyOwner {
function registerFunctionAdmin(address _address, bytes4 _selector, bool _status) public AdminRequired {
function registerBatchFunctionAdmin(address _address, bytes4[] memory _selector, bool _status) public AdminRequired {
function registerCollectionAdmin(uint256 _collectionID, address _address, bool _status) public AdminRequired {
abi.encodePacked
should not be used with dynamic types when passing the result to a hash function such as keccak256
. Use abi.encode
instead, which will pad items to 32 bytes, to prevent any hash collisions.uint256 randomNum = uint(keccak256(abi.encodePacked(block.prevrandao, blockhash(block.number - 1), block.timestamp))) % 1000;
L36
uint256 randomNum = uint(keccak256(abi.encodePacked(block.prevrandao, blockhash(block.number - 1), block.timestamp))) % 100;
L41
External call in unbounded loop
uint256 mintIndex = gencore.viewTokensIndexMin(_collectionID) + gencore.viewCirSupply(_collectionID);
L188
Typo - "greater" not "grater"
require(collectionTotalAmount[_collectionID] > 0, "Collection Balance must be grater than 0");
Misleading revert string - "at least 1 mint/period" better
require(tDiff>=1, "1 mint/period");
function updateAdminsContract(address _admin) public FunctionAdminRequired(this.updateAdminsContract.selector) { adminsContract = INextGenAdmins(_admin); }
#0 - 141345
2023-11-25T08:07:08Z
1386 ZanyBonzy l r nc 2 2 4
L 1 n L 2 n L 3 d dup of https://github.com/code-423n4/2023-10-nextgen-findings/issues/586 L 4 r L 5 d dup of https://github.com/code-423n4/2023-10-nextgen-findings/issues/962 L 6 l L 7 r L 8 i bot L 9 i bot L 10 n L 11 n L 12 l
#1 - c4-pre-sort
2023-11-25T08:11:20Z
141345 marked the issue as sufficient quality report
#2 - alex-ppg
2023-12-08T15:15:34Z
The Warden's QA report has been graded B based on a score of 10 combined with a manual review per the relevant QA guideline document located here.
The Warden's submission's score was assessed based on the following accepted findings:
#3 - c4-judge
2023-12-08T15:15:43Z
alex-ppg marked the issue as grade-b
#4 - alex-ppg
2023-12-09T11:27:21Z
Hey @ZanyBonzy, thanks for following up on this! When evaluating QA reports, I have performed my independent judgment and the QA Judgment response above reflects this as I only considered 5
as a valid submission of a QA (L) issue.
#5 - alex-ppg
2023-12-12T14:40:43Z
Hey @ZanyBonzy, thanks for your feedback. Per the document linked in the initial response to this submission as well as the main PJQA discussion page:
At my discretion, if a Warden has sufficiently argued in the QA report that the submission may be upgrade-able and they have focused adequately on the issue, I may reward them a reduced percentage out of the report's pool.
Your 5
statement makes no mention of the potential impact of #1323 and the impact detailed in your QA report is of a QA (L)
grade or lower. To ensure transparency in the grade of your QA report, I will provide rationale for the grade of each of the report's points:
status
of true
. The bids are in a strictly ascending order (when we factor in their status
) meaning that no functionality needs to change. As such, I considered this a gas optimization.QA (L)
abi.encode
equivalent to abi.encodePacked
I
point at bestAs a result of the above evaluation, I will maintain my grade B
assignment which could have arguably been a grade C
. Keep in mind that the A
grade was awarded to a report that had at minimum 23
points which is substantially higher than yours. Even considering 3
and 6
as a QA (NC)
and all I
exhibits, it would bring your total to 10 + 6 + 2 = 18
which is far less than the fifth and final A
report with a score of 23
(#1794).