xanoutas/solidity-security-findings huggingface dataset:
`cancelBid()` and `cancelAllBids()` functions are incorrectly implemented, resulting in partial/complete DoS-ing of bid cancelling functionality.
Audit: 2023-10-nextgen-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/a6f2397b68ef2865374c1bf7629349f25e71a44d/smart-contracts/AuctionDemo.sol#L122-L130
https://github.com/code-423n4/2023-10-nextgen/blob/a6f2397b68ef2865374c1bf7629349f25e71a44d/smart-contracts/AuctionDemo.sol#L132-L143


# Vulnerability details

## Impact

`cancelBid()` and `cancelAllBids()` functions are incorrectly implemented, resulting in partial/complete DoS-ing of bid cancelling functionality.

- bidder could lose funds when they change their mind and want to cancel their bid, imagine they were the top bidder and were going to be rewarded the NFT, and they change their mind, try to cancel but can't.
- for whatever reason it's discovered that the NFT up for auction is fake or problematic or the auction is problematic for some reason, and the auction should be cancelled for valid reasons. This cannot happen, because you will only cancel one bidder's bid, or each bidder will need to call this function individually.
- ability to cancel auctions/auction bids is a critical functionality, which if disabled, could have a serious impact.

## Proof of Concept

`cancelBid()` will only work/be useful if the user/msg.sender knows their `index` value in the `auctionInfoStru[]` array, which doesn't seem to be returned to them anywhere when they join an auction.
So the user would need to guess.

As far as my understanding goes, `cancelAllBids()` is meant to cancel all the bids for a specific auction, i.e. to cancel all the bids of bidders for a specific auction and NFT combo.
But it actually only cancels the bid for a specific bidder, because it uses `msg.sender` and then tries to loop through the bidders to cancel ALL bids for this auction... see the logic error?

## Tools Used
VSC. Manual.

## Recommended Mitigation Steps

The below recommended function implementation for `cancelBid()` is not fully gas optimized, so take that into account:
```diff
// cancel a single Bid
-   function cancelBid(uint256 _tokenid, uint256 index) public { 
+   function cancelBid(uint256 _tokenid) public {
        require(block.timestamp <= minter.getAuctionEndTime(_tokenid), "Auction ended"); 
+       uint256 auctionInfoStruLength = auctionInfoData[_tokenid].length;
+       for (uint256 i; i < auctionInfoStruLength;) { 
+           if (auctionInfoData[_tokenid][i].bidder == msg.sender && auctionInfoData[_tokenid][i].status == true) {
+               auctionInfoData[_tokenid][i].status = false;
+               (bool success, ) = payable(auctionInfoData[_tokenid][i].bidder).call{value: auctionInfoData[_tokenid][i].bid}("");
+				/// @audit add a boolean return value check here! Check my other finding/submission
+               emit CancelBid(msg.sender, _tokenid, i, success, auctionInfoData[_tokenid][i].bid);
+               return;
			}
+			unchecked {i++;}
		}
-       require(auctionInfoData[_tokenid][index].bidder == msg.sender && auctionInfoData[_tokenid][index].status == true);
-       auctionInfoData[_token
The NextGenRandomizerNXT contract's hash generating function flow, can be purposefully reverted in order to obtain a desired nft.
Audit: 2023-10-nextgen-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/RandomizerNXT.sol#L18


# Vulnerability details

## Impact
Due to the static nature of the ``NextGenRandomizerNXT`` contract as well as ``randomPool`` contract in XRandoms.sol. The final hashes which will be generated for collection nfts, making use of this randomizer, could be pouched by malicious actors in order to revert whenever minting a common nft. 

Contrary to the Chainlink randomizer, which sets the minted nfts via an unpredictable oracle callback after minting, the ``NextGenRandomizerNXT`` performs the minting & setting of said hash all in 1 call. Therefore making it easy to simulate the transaction off-chain.

## Proof of Concept
My POC will only consist of the written solidity scripts to pre-calculate the resulting hash generated by this randomizer. A real world application, would most likely imply some js scripts running the ``mint()`` function in a forked environment using an up to date rpc url. Finally, whenever a desired hash is produced, make the call on chain, with the final require() statement, therefore acting as its own protection to mev, block.timestamp manipulation & guaranteeing the desired hash or a revert.

Furthermore, because the rarity of said nft, isnt declared on chain, but rather by the admins via their front end. Said front-end components would have to be sanboxed & tested until the right modulo or formula for the nft rarity is found, prior of performing the exploit.
```
function testMintOnlyDesired() public {
        vm.warp(block.timestamp + 6 minutes);       // Skip to public nft sale.
        vm.prank(fixAddress("WL"));
        vm.deal(fixAddress("WL"), 1e18);
        bytes32[] memory merkle = new bytes32[](1);
        merkle[0] = keccak256(abi.encodePacked(fixAddress("WL")));
        /*
        _mintIndex: 10000000000 Of first minted nft.
        /

        minterContract.mint{value: 1e18}(
            1,
            1,
            10,
            "TokenData",
            fixAddress("WL"),
            merkle,
            address(delegation),
            0
        );

        bytes32 _resultingHash = nextGenCore.retrieveTokenHash(10000000000);
        /
        Metric that determines nft rarity:
        Lets say if _resultingHash is nicely divisible by 13, the nft is rare.
        */
        require(uint256(_resultingHash) % 13 == 0, "Common nft");
        emit EmitBytes32(_resultingHash);
        console.log(uint256(_resultingHash));
    }
```
To clarify: The ``require(uint256(_resultingHash) % 13 == 0, "Common nft");`` reverts my function call if the generated hash using prevRanDao did not generate any rare nfts.
## Tools Used
Foundry

## Recommended Mitigation Steps
Make use of an off-chain "oracle" which feeds off-chain data into the ``NextGenRandomizerNXT`` contract, to make the hashes 100% unpredictable by anyone.


## Assessed type

Oracle
User with multiple bid cannot claim reward
Audit: 2023-10-nextgen-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/main/smart-contracts/AuctionDemo.sol#L104


# Vulnerability details

## Impact
In the `claimAuction` function used to claim bids after a successful auction, it is seen that the looping of function used to return asset to user and refund other bidders is not well done. As a situation arises that if the highest bidder has done many bids, the claimAuction will always fail, leaving all bidders including other bidders funds stuck.


## Proof of Concept
We're walking through this function here
```solidity
for (uint256 i=0; i< auctionInfoData[_tokenid].length; i ++) {
            //@audit-check: users cannot claimauction as this function will always revert, due to multiple attempt to send same token to user if bidder bids more than once: high
            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) {
                //@audit-check: with enough bidders this might cause DOS due to not enough gas to process transaction; allow them to request for refund: high
                (bool success, ) = payable(auctionInfoData[_tokenid][i].bidder).call{value: auctionInfoData[_tokenid][i].bid}("");
                emit Refund(auctionInfoData[_tokenid][i].bidder, _tokenid, success, highestBid);
                //@audit-check: low: no need for else
            } else {}
        }
```
- The auctionInfoData[_tokenid] is used for looping
- There is a check to see if the caller is the highestBidder `if (auctionInfoData[_tokenid][i].bidder == highestBidder && auctionInfoData[_tokenid][i].bid == highestBid && auctionInfoData[_tokenid][i].status == true) {`
- owner of token was originally determined outside the loop and then there is a function to send the NFT to the highestBidder `IERC721(gencore).safeTransferFrom(ownerOfToken, highestBidder, _tokenid);` inside the loop meaning, there will be multiple attempts to send the NFT from originalOwner to highestBidder, the first attempt will be successful but on further attempt due to multiple bidding, the new `ownerOfToken` should be the highestBidder but still assumed to be the former owner, the function will still attempt to resend same token and cause a revert
- This would revert all calls and NFT will be stuck in contract, user will never get NFT and almost other bidders will not be refunded also.

## Tools Used
Manual Review

## Recommended Mitigation Steps
- Check to see if transfer of NFT have been done already by checking the owner of the NFT each time before attempting to transfer token


## Assessed type

ERC721
Critical Issues in Auction Refund Loop
Audit: 2023-10-nextgen-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/tree/main/smart-contracts/AuctionDemo.sol#L116


# Vulnerability details

## Impact
The vulnerability in the implementation of the refund loop within the AuctionDemo contract has several critical impacts that need to be addressed. The root cause of the vulnerability is that the refund process, when combined with the claimAuction function, results in multiple intertwined issues:

1. **Out-of-Gas Risk**: The contract's large `auctionInfoData[id]` array makes it likely to run out of gas during the execution of the refund loop. This could prevent users from claiming their refunds and significantly disrupt the functioning of the auction contract.

2. **Stuck Funds**: It's expected that all funds should be refunded. However, due to a lack of proper success return value checks, there is a risk of funds getting stuck in the contract. If claimAuction is called but fails without revert at Line 116, it would leave funds locked in the contract since claimAuction can only be called once, and there's no mechanism for emergency withdrawal of the ETH. The Bot Race did report this finding as [M-01](https://github.com/code-423n4/2023-10-nextgen/blob/main/bot-report.md#m-01-unchecked-return-value-of-low-level-calldelegatecall)

3. **Malicious Participant Lockout**: Fixing the issue no.2 via reverting on failed transfer would create a new vulnerability. A malicious participant contract can decide to revert when receiving an ETH refund in their fallback function. This can effectively lock the entire funds and the auctioned NFT. As a result, the winner cannot claim their NFT, the contract owner cannot receive the highest bid payment, and other participants cannot get their refunds.

## Proof of Concept
To illustrate this vulnerability, consider a scenario in which Alice participates in an auction via her contract. When Bob wins and attempts to claim the auction, the refund loop is executed, but Alice's contract maliciously reverts in its fallback function, leading to the locking of funds and the auctioned NFT.

1. Alice participates in an auction via her contract, which can call claimAuction if she wins but can also deny the refund in her fallback function if she loses.
2. Bob wins the auction and attempts to claim the auction.
3. The refund loop is executed to refund participants, including Alice, via `payable(Alice contract).call{value: bid}`.
4. Alice's contract reverts in its fallback function due to her malicious intention.
5. The entire auction pot and the NFT get locked because the claimAuction function consistently fails due to Alice's behavior.

The severity of these issues is extremely high, as they can lead to funds being permanently locked in the contract, the inability to claim auctioned NFTs, and potential disruption of the auction's integrity.

## Tools Used
Manual Review

## Recommended Mitigation Steps
To address these critical issues, it is recommended to separate the refund claim process
artist royalty calculated susceptible to wrong calculation
Audit: 2023-10-nextgen-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/MinterContract.sol#L415-L444


# Vulnerability details

## Impact
This can lead to for the artist during royalty payment.


## Proof of Concept
In the ```payArtist(...)``` function in the ```MinterContract.sol``` contract, the ```artistRoyalties1```, ```artistRoyalties2``` and ```artistRoyalties3``` are susceptible to errors due to missing validation checks.
For instance,
- ```add1Percentage```, ```add2Percentage```, ```add3Percentage``` are supposed to sum up to equal ```collectionRoyaltiesPrimarySplits[_collectionID].artistPercentage```

- ```_teamperc1```, ```_teamperc2``` are supposed to sum up to equal ```collectionRoyaltiesPrimarySplits[_collectionID].teamPercentage```.

But as shown below, these checke is not done and for that reason, the calculated values for ```artistRoyalties1```, ```artistRoyalties2``` and ```artistRoyalties3``` could be susceptible to errors

```solidity
function payArtist(uint256 _collectionID, address _team1, address _team2, uint256 _teamperc1, uint256 _teamperc2) public FunctionAdminRequired(this.payArtist.selector) {
        require(collectionArtistPrimaryAddresses[_collectionID].status == true, "Accept Royalties");
        require(collectionTotalAmount[_collectionID] > 0, "Collection Balance must be grater than 0");
        require(collectionRoyaltiesPrimarySplits[_collectionID].artistPercentage + _teamperc1 + _teamperc2 == 100, "Change percentages");
        uint256 royalties = collectionTotalAmount[_collectionID];
        ...
        artistRoyalties1 = royalties * collectionArtistPrimaryAddresses[colId].add1Percentage / 100;
        artistRoyalties2 = royalties * collectionArtistPrimaryAddresses[colId].add2Percentage / 100;
        artistRoyalties3 = royalties * collectionArtistPrimaryAddresses[colId].add3Percentage / 100;
        // @audit team royalties can be manipulated 
        teamRoyalties1 = royalties * _teamperc1 / 100;
        teamRoyalties2 = royalties * _teamperc2 / 100;
        ...
        emit PayTeam(tm2, success5, teamRoyalties2);
    }
```


## Tools Used
Manual review

## Recommended Mitigation Steps
Modify ```payArtist(...)``` to include checks to ensure this missing checks mentioned above are implemented as shown below

```solidity
function payArtist(uint256 _collectionID, address _team1, address _team2, uint256 _teamperc1, uint256 _teamperc2) public FunctionAdminRequired(this.payArtist.selector) {
        require(collectionArtistPrimaryAddresses[_collectionID].status == true, "Accept Royalties");
        require(collectionTotalAmount[_collectionID] > 0, "Collection Balance must be grater than 0");
        require(collectionRoyaltiesPrimarySplits[_collectionID].artistPercentage + _teamperc1 + _teamperc2 == 100, "Change percentages");
        require(add1Percentage + add2Percentage + add3Percentage == collectionRoyaltiesPrimarySplits[_collectionID].artistPercentage, "In
Auction Winner Can Retrieve Bid Amount Back Along With NFT
Audit: 2023-10-nextgen-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L111-L114
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L124-L130


# Vulnerability details

## Impact
Vulnerability allows the highest bidder (the auction winner) to reclaim their bid amount while also receiving the NFT.

## Proof of Concept
The vulnerability stems from the timing of checks in the claimAuction and cancelBid functions and reentrancy. 

- The `claimAuction` function only proceeds if the auction has ended, as verified by `require(block.timestamp >= minter.getAuctionEndTime(_tokenid))`
- The `cancelBid` function can be invoked as long as the auction hasn't ended, checked via `require(block.timestamp <= minter.getAuctionEndTime(_tokenid), "Auction ended")`
- The use of `safeTransferFrom` to transfer the NFT leads to a reentrancy attack. If the highestBidder is a contract with malicious logic in onERC721Received, it will invoke `cancelBid` and pass all `require` statements, and get his ETH back.
- Transaction won't fail when trying to send ETH to owner in next line, since `success` of `payable(owner()).call{value: highestBid}("")` didn't check.

## Tools Used
Manual code review

## Recommended Mitigation Steps
- Synchronize the timing checks in `claimAuction` and `cancelBid`.
- Review the sequence of operations and state changes in the `claimAuction` function to prevent reentrancy. eg add `auctionInfoData[_tokenid][index].status = false;` before `safeTransferFrom`


## Assessed type

Reentrancy
MinterContract::burnToMint() do not mint any token for the case where current circulationSupply greater than eq to totalSupply, make user pay ETH for nothing. 
Audit: 2023-10-nextgen-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/NextGenCore.sol#L213-L223


# Vulnerability details

## Impact
MinterContract::burnToMint() allows burning and minting a new token in the same function. Every time a new NFT get mint, the minter has to pay a price return by `getPrice(_mintCollectionID)`(in ETH). The issue is if the circulation supply for that collection exceed the totalSupply in NextgenCore contract. User would end up paying eth for nothing. As no nft minted to the user. 

```solidity 
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;
    // @audit below check will failed if circulation supply exceed current totalSupply

    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;
    }
}
```

## Proof of Concept
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/MinterContract.sol#L270
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/NextGenCore.sol#L217

## Tools Used
Manual review

## Recommended Mitigation Steps
Add an else block, which revert for the case where totalSupply reached for that collection.  





## Assessed type

ETH-Transfer
Collection can be frozen without data and without a randomizer
Audit: 2023-10-nextgen-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/NextGenCore.sol#L292-L295


# Vulnerability details

## Impact
Collection data can be ruined forever

## Proof of Concept
In the `NextGenCore.sol` contract, the admin can use the `freezeCollection(uint256 _collectionID)` function to "freeze" a collection.

When a collection is frozen, updating or setting the collection data through functions like `updateCollectionInfo()` and `setCollectionData()` becomes impossible.

It's crucial to note that once a collection is frozen, this action is irreversible. Unfreezing is not possible, meaning that once this function is called, altering the collection data becomes permanently prohibited.

However, it's worth mentioning that the function responsible for freezing a collection doesn't verify whether collection data has been added. In scenarios where a collection is added, and the `freezeCollection` function is mistakenly or intentionally called before adding data, the collection becomes "broken" and unusable.

## Tools Used
VSCode

## Recommended Mitigation Steps

Add the following checks to the `freezeCollection` function:

```solidity
require(wereDataAdded[_collectionID] == true, "Collection has no data");
require(collectionAdditionalData[_collectionID].randomizerContract != address(0), "randomizer not set");
```


## Assessed type

Invalid Validation
Possible DOS attack the moment any new bid is created.
Audit: 2023-10-nextgen-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L104


# Vulnerability details

## Impact
Due to the possibility of making 1 wei bids. Increasing the value of ``auctionInfoData[_tokenid].length`` is fairly accessible & could permanently lock users' ether & make the winning bidder unable to claim his nft, due to the function call consistently reverting.
The function bellow creates a newBid each time the msg.value is higher than the last. So even bids done all by 1 address, will make new instances in the ``auctionInfoData`` array.
```
function participateToAuction(uint256 _tokenid) public payable {
        require(msg.value > returnHighestBid(_tokenid) && block.timestamp <= minter.getAuctionEndTime(_tokenid) && minter.getAuctionStatus(_tokenid) == true);
        auctionInfoStru memory newBid = auctionInfoStru(msg.sender, msg.value, true);
        auctionInfoData[_tokenid].push(newBid);
    }
```
Here the claimAuction function makes use of the list's length, in order to transfer the nft to the highest bidder & repay other bidders with their ether.
Purposefully inflating this list, will guarantee this function to fail.
```
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 {}
        }
    }
```
## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
```
function testDOSAuction() public {
        _mintAndAuction();

        vm.prank(fixAddress("RegularUser1"));
        vm.deal(fixAddress("RegularUser1"), 1e16); // 0.01 ether
        uint256 gasLimit = 10000; // Ethereums gas limit is 15_000_000, so varies with foundry's setup.
        for(uint256 i