xanoutas/solidity-security-findings huggingface dataset:
NextGenMinterContract::mint can be reentered for sales option 3 to mint many NFTs in a single period and bypass viewMaxAllowance for any sales option
Audit: 2023-10-nextgen-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/hardhat/smart-contracts/MinterContract.sol#L196
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/hardhat/smart-contracts/NextGenCore.sol#L231


# Vulnerability details

## Impact

The reentrancy vulnerability in the `NextGenMinterContract::mint` function allows an attacker to bypass the restriction of minting only one NFT per period. The reentrencies can be achieved from the `_safeMint` in the function `NextGenCore::_mintProcessing` to call the function `NextGenMinterContract::mint` again and again. The attacker can also bypass the `viewMaxAllowance` check for any sales option. Consequently, an attacker could exploit this to mint many NFTs within a single period and for any max allowance set. 

This could distrupt the collection supply, and bypass any intended restriction and allowance that was set by the function admin of the `NextGenCore::createCollection`.

## Proof of Concept
The issue arises because the following parameters do not update after each call. Basically, after reentering, `gencore.retrieveTokensMintedPublicPerAddress(col, msg.sender)` does not update while `_numberOfTokens` and `gencore.viewMaxAllowance(col)` stay the same.

```javascript
            require(_numberOfTokens <= gencore.viewMaxAllowance(col), "Change no of tokens");
            require(gencore.retrieveTokensMintedPublicPerAddress(col, msg.sender) + _numberOfTokens <= 
            gencore.viewMaxAllowance(col), "Max");
```

Following the same logic, for the sales Option == 3, the following conditions do not revert because :
- `_numberOfTokens ` stays the same (=1)
- `collectionPhases[col].allowlistStartTime + (collectionPhases[col].timePeriod * (gencore.viewCirSupply(col) - 1))` also stays the same. Since this code will run after the last iteration, the circulating supply will not change (and it will be equal to the number of iteration or reentrencies). As long as `tDiff>1`, the reentrency will work.

```javascript
            uint tDiff = (block.timestamp - timeOfLastMint) / collectionPhases[col].timePeriod;
            require(tDiff>=1 && _numberOfTokens == 1, "1 mint/period");
            lastMintDate[col] = collectionPhases[col].allowlistStartTime + (collectionPhases[col].timePeriod * 
            (gencore.viewCirSupply(col) - 1));
```

### POC
You can add this test to the file nextGen.t.sol of our foundry setup in gist [C4 nextGen foundry setup](https://gist.github.com/Maroutis/d49da271eafbdfacc638c966883386ad). And execute it with the command forge test --mt testCanReenterMintForSaleOption3 -vvvv

```javascript
    function testCanReenterMintForSaleOption3() public {
        createAndSetCollections();
        Attack attacker = new Attack(address(hhMinter));
        vm.deal(address(attacker), 10 ether);
        vm.deal(address(user), 10 ether);

        bytes32[] memory
Reentrancy in mint function allows minting above the limit allowed per address / allowlisted address
Audit: 2023-10-nextgen-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/08a56bacd286ee52433670f3bb73a0e4a4525dd4/smart-contracts/NextGenCore.sol#L189-L200


# Vulnerability details

## Impact

The `mint` function in NextGenCore.sol doesn't follow the checks-effects-interactions pattern and can be reentered through the `onERC721Received` function, if the receiver is a contract.  
The state variables written after the call are `tokensMintedAllowlistAddress` (during allowlist phase) or `tokensMintedPerAddress` (during public mint).

```solidity

function mint(uint256 mintIndex, address _mintingAddress , address _mintTo, string memory _tokenData, uint256 _saltfun_o, uint256 _collectionID, uint256 phase) external {
    // checks
    require(msg.sender == minterContract, "Caller is not the Minter Contract");
    // effects
    collectionAdditionalData[_collectionID].collectionCirculationSupply = collectionAdditionalData[_collectionID].collectionCirculationSupply + 1;
    if (collectionAdditionalData[_collectionID].collectionTotalSupply >= collectionAdditionalData[_collectionID].collectionCirculationSupply) {

        // interactions => external call to "onERC721Received", if receiver is contract
        _mintProcessing(mintIndex, _mintTo, _tokenData, _collectionID, _saltfun_o);

        // effects
        if (phase == 1) {
            tokensMintedAllowlistAddress[_collectionID][_mintingAddress] = tokensMintedAllowlistAddress[_collectionID][_mintingAddress] + 1;
        } else {
            tokensMintedPerAddress[_collectionID][_mintingAddress] = tokensMintedPerAddress[_collectionID][_mintingAddress] + 1;
        }
    }
}
```

[NextGenCore 189-200](https://github.com/code-423n4/2023-10-nextgen/blob/08a56bacd286ee52433670f3bb73a0e4a4525dd4/smart-contracts/NextGenCore.sol#L189-L200)

As a result, it is possible to bypass the following checks and mint more tokens than allowed per address, during the public mint phase:

```solidity
require(_maxAllowance >= gencore.retrieveTokensMintedALPerAddress(col, msg.sender) + _numberOfTokens, "AL limit");
```

[MinterContract 213](https://github.com/code-423n4/2023-10-nextgen/blob/08a56bacd286ee52433670f3bb73a0e4a4525dd4/smart-contracts/MinterContract.sol#L213)

and during the allowlist phase:

```solidity
require(gencore.retrieveTokensMintedPublicPerAddress(col, msg.sender) + _numberOfTokens <= gencore.viewMaxAllowance(col), "Max");
```

[MinterContract 224](https://github.com/code-423n4/2023-10-nextgen/blob/08a56bacd286ee52433670f3bb73a0e4a4525dd4/smart-contracts/MinterContract.sol#L224)

## Proof of Concept

This test shows that on a collection where the total supply is 50 and the maximum allowed per address during the public mint phase is 3, a single buyer could buy up all the supply.

```solidity
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "./contracts/IERC721Receiver.sol"; // OpenZeppelin IERC721Receiver interface

import {NextGenMinterC
Missing highBid value update returnHighestBidder would return the wrong HighestBidder causing nft to be minted to the wrong winner
Audit: 2023-10-nextgen-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/main/hardhat/smart-contracts/AuctionDemo.sol#L92

# Vulnerability details

## Impact

returnHighestBidder is used in checking who the winner is, however the code
implemented in doing this was flawed as it missed updating highBid variable
for the current higest bid found so far before next iteration. Missing this
would cost function to simply return whoever was the last valid bidder
which is not what's intended

## Proof of Concept

AuctionDemo:returnHighestBidder()

```solidity
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) {
                index = i; // @audit-issue missing highBid update
            }
        }
        if (auctionInfoData[_tokenid][index].status == true) {
                return auctionInfoData[_tokenid][index].bidder;
            } else {
                revert("No Active Bidder");
        }
    }
```

## Tools Used

Manaul Review

## Recommended Mitigation Steps

Change function to account for the bid amount

```diff
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) {
                index = i;
              + highBid = auctionInfoData[_tokenid][i].bid;
            }
        }
        if (auctionInfoData[_tokenid][index].status == true) {
                return auctionInfoData[_tokenid][index].bidder;
            } else {
                revert("No Active Bidder");
        }
    }
```

## Assessed type

Other
DoS: Auction May Be Made Unusable By An Attacker
Audit: 2023-10-nextgen-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/main/hardhat/smart-contracts/AuctionDemo.sol#L60

# Vulnerability details

## Impact

When the items of `auctionInfoData` array is sufficiently high,
claimAuction, cancelAllBids(), returnHighestBid() will all not be able to
be called due to the block gas limit rendering all funds locked and auction
unusable.

auctionInfoData can only ever be add to and thus will always increase. This
array will naturally increase as new bids are made.

Notice an attacker can simply send additional 1 WEI worth of ETH to pass
the required checks to creating new Bids, cancel bid to get funds back and
preform steps continually to maliciously increase array size with lossing
bigged funds.

## Proof of Concept

AuctionDemo:participateToAuction() where newBid can be added to
auctionInfoData and grow indefinitely

```solidity
    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);
    }
```

Example function that would break

```solidity
function returnHighestBid(uint256 _tokenid) public view returns (uint256) {
        uint256 index;
        if (auctionInfoData[_tokenid].length > 0) {
            uint256 highBid = 0;
            for (uint256 i=0; i< auctionInfoData[_tokenid].length; i++) {
                if (auctionInfoData[_tokenid][i].bid > highBid &&
auctionInfoData[_tokenid][i].status == true) {
                    highBid = auctionInfoData[_tokenid][i].bid;
                    index = i;
                }
            }
            if (auctionInfoData[_tokenid][index].status == true) {
                return highBid;
            } else {
                return 0;
            }
        } else {
            return 0;
        }
    }
```

## Tools Used

Visual Studio Code

## Recommended Mitigation Steps

Consider modifying the newBids creation approach

## Assessed type

DOS
The reentrancy vulnerability in NextGenCore can allow an attacker to manipulate minting execution
Audit: 2023-10-nextgen-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/main/hardhat/smart-contracts/NextGenCore.sol#L194

# Vulnerability details

## Impact

Function _mintProcessing() has been used in mint() and airDropTokens() and
both doesn't follow check-effect-interaction pattern and code updates the
values of tokensAirdropPerAddress, tokensMintedAllowlistAddress and
tokensMintedPerAddress variables after making external call by using
safeMint(). This would give attacker opportunity to reenter the Minter
contract logics and perform malicious action while contract storage state
is wrong.

Attacker can perform this action:

- call MinterContract::mint() specifying his maxAllowance.
- Function executes and checks attacker isn't exceeding limit, i.e line
[[224](https://github.com/code-423n4/2023-10-nextgen/blob/main/hardhat/smart-contracts/MinterContract.sol#L224)](https://github.com/code-423n4/2023-10-nextgen/blob/main/hardhat/smart-contracts/MinterContract.sol#L224)
in the case of public phase and line [[217](https://github.com/code-423n4/2023-10-nextgen/blob/main/hardhat/smart-contracts/MinterContract.sol#L217)](https://github.com/code-423n4/2023-10-nextgen/blob/main/hardhat/smart-contracts/MinterContract.sol#L217)
for the allowList phase - ( Notice this function performs this checks using
the values returned from retrieveTokensMintedALPerAddress and
retrieveTokensMintedALPerAddress)
- amount is valid and check passes, proceeds minting to attacker on core
contract
- _safeMint() triggers a callBack into attacker contract before updating
this crucial varaible on line [[194](https://github.com/code-423n4/2023-10-nextgen/blob/main/hardhat/smart-contracts/NextGenCore.sol#L194)](https://github.com/code-423n4/2023-10-nextgen/blob/main/hardhat/smart-contracts/NextGenCore.sol#L194).
- calls MinterContract::mint() again with same valid inputs while
transaction has not yet settled reenter the minting ( Note - bypasses limit
check because neither tokensMintedAllowlistAddress or
tokensMintedPerAddress has been updated ) and mint passed limit.
- perform this multiple times in a single transaction before transactions
settle, minting possibly ALL collection's totalSupply

## Proof of Concept

This is mint() code in NextGenCore contract:

```solidity
function mint(uint256 mintIndex, address _mintingAddress , address _mintTo,
string memory _tokenData, uint256 _saltfun_o, uint256 _collectionID,
uint256 phase) external {
        require(msg.sender == minterContract, "Caller is not the Minter
Contract");
        collectionAdditionalData[_collectionID].collectionCirculationSupply
= collectionAdditionalData[_collectionID].collectionCirculationSupply + 1;
        if (collectionAdditionalData[_collectionID].collectionTotalSupply
>= collectionAdditionalData[_collectionID].collectionCirculationSupply) {
            _mintProcessing(mintIndex, _mintTo, _tokenData, _collectionID,
_saltfun_o); // @audit-issue
            if
Bidder Can Retrieve Bid Amount Twice in `claimAuction`
Audit: 2023-10-nextgen-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
This vulnerability enables a bidder to recover their bid amount twice during the execution of the claimAuction function.

## Proof of Concept
A race condition between the claimAuction and cancelBid functions allows a non-winning bidder to claim their bid twice:

- In claimAuction, the function proceeds if the auction has ended, as checked by `require(block.timestamp >= minter.getAuctionEndTime(_tokenid))`.
- In cancelBid, the function is accessible as long as the auction hasn't ended, verified by `require(block.timestamp <= minter.getAuctionEndTime(_tokenid), "Auction ended");`.
- A bidder's contract, in the line `(bool success, ) = payable(auctionInfoData[_tokenid][i].bidder).call{value: auctionInfoData[_tokenid][i].bid}("")`, can use its `receive()` function to re-enter the `cancelBid` function. This reentrancy can occur within the same block timestamp.
- The bidder can thus bypass the require checks in `cancelBid` and reclaim their bid, effectively receiving their bid amount twice.

## Tools Used
manual review

## Recommended Mitigation Steps
Fix timing checks in `claimAuction` and `cancelBid`


## Assessed type

Reentrancy
Multiple instances of reentrancy
Audit: 2023-10-nextgen-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/NextGenCore.sol#L231
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/hardhat/smart-contracts/ERC721.sol#L193
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/MinterContract.sol#L434-L438
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/MinterContract.sol#L464
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/hardhat/smart-contracts/AuctionDemo.sol#L112-L116


# Vulnerability details

## Impact
This can lead to massive loss of funds and asset in Nextgen.

## Proof of Concept
There are multiple instances of reentrancy in ```NextGenCore.sol```, ```MinterContract.sol``` and ```AuctionMemo.sol```. 
These reentrancy is due to 
- the use of ERC721 [```_safeMint(...)```](https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/NextGenCore.sol#L231) for token transfer which implements a ```_checkOnERC721Received(...)``` and [```safeTransferFrom```](https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/hardhat/smart-contracts/ERC721.sol#L193) return value check on the ```recipient``` contract thereby handing transfer over to the recipient contract. 

```solidity
        ....
        require(
            _checkOnERC721Received(address(0), to, tokenId, data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
        ...
```
- the use of solidity ```call()``` function to transfer value to external recipients thereby handing transfer over tot these contracts. for example in the ```payArtist(...)``` function of the ```MinterContract.sol``` contract.

```solidity
        ...
        (bool success1, ) = payable(collectionArtistPrimaryAddresses[colId].primaryAdd1).call{value: artistRoyalties1}("");
        (bool success2, ) = payable(collectionArtistPrimaryAddresses[colId].primaryAdd2).call{value: artistRoyalties2}("");
        (bool success3, ) = payable(collectionArtistPrimaryAddresses[colId].primaryAdd3).call{value: artistRoyalties3}("");
        (bool success4, ) = payable(tm1).call{value: teamRoyalties1}("");
        (bool success5, ) = payable(tm2).call{value: teamRoyalties2}("");
        ...
```

In both scenarios, a malicious user can perform malicious tasks that could lead to huge loss for the protocol


## Tools Used
Manual

## Recommended Mitigation Steps
Implement a reentrancy guard in all state altering and state reading functions in the ```NextGenCore.sol```, ```MinterContract.sol``` and ```AuctionMemo.sol``` contracts respectively




## Assessed type

Reentrancy
Winner of auction status is not set to false after claim so eligible for refund 
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-L120


# Vulnerability details

## Impact
Bidders funds will get stuck 

## Proof of Concept
After Auction winner claims token, his/her status is not set to false so he is eligible for a refund because the refund logic checks for all  indexes with status set to true, which not supposed to be so.
So the refund transaction will not be successful because the funds to be refunded are less than the amount of users  that are eligible to receive  a refund.
And users cannot cancel bid because Auction is over, so the function wont work

  function cancelBid(uint256 _tokenid, uint256 index) public {
        require(
            block.timestamp <= minter.getAuctionEndTime(_tokenid),
            "Auction ended"

 function cancelAllBids(uint256 _tokenid) public {
        require(
            block.timestamp <= minter.getAuctionEndTime(_tokenid),
            "Auction ended"
        );


The claim/refund function is in the link description 

## Tools Used
Manual Review 
## Recommended Mitigation Steps
Add a line of code that sets that Auctionwinner bid index to false.


## Assessed type

Other
Last token of maximum supply can be paid, but it isn't minted nor reverted.
Audit: 2023-10-nextgen-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/main/smart-contracts/NextGenCore.sol#L181
https://github.com/code-423n4/2023-10-nextgen/blob/main/smart-contracts/NextGenCore.sol#L192
https://github.com/code-423n4/2023-10-nextgen/blob/main/smart-contracts/NextGenCore.sol#L217


# Vulnerability details

## Description

`collectionCirculationSupply` is incremented in each mint and it's used to check if the mint don't overtakes the collection's max supply. However, it increments before the check, which makes that although last token is in the max supply range, the `collectionCirculationSupply` becomes a value higher than max supply, not allowing the mint. Even further, the code doesn't revert or refund funds if that happens.

## Proof of Concept 

1. Let's exemplify:
- I want to mint the last token of the collection (the token 200) 
- `collectionTotalSupply = 200`
- `collectionCirculationSupply = 200`. The variable is designed to be the next ID to mint, which is 200 in the example case because we will mint the token 200 of the collection. The following line is used everytime the protocol wants to know the next ID to mint: 
```solidity
uint256 mintIndex = gencore.viewTokensIndexMin(_collectionID) + gencore.viewCirSupply(_collectionID);
```


2. I call `mint`, which calls `mint` from `NextGenCore.sol`: 
```solidity
    function mint(uint256 mintIndex, address _mintingAddress , address _mintTo, string memory _tokenData, uint256 _saltfun_o, uint256 _collectionID, uint256 phase) external {
        require(msg.sender == minterContract, "Caller is not the Minter Contract");
        collectionAdditionalData[_collectionID].collectionCirculationSupply = collectionAdditionalData[_collectionID].collectionCirculationSupply + 1;
        if (collectionAdditionalData[_collectionID].collectionTotalSupply >= collectionAdditionalData[_collectionID].collectionCirculationSupply) {
            _mintProcessing(mintIndex, _mintTo, _tokenData, _collectionID, _saltfun_o);
            if (phase == 1) {
                tokensMintedAllowlistAddress[_collectionID][_mintingAddress] = tokensMintedAllowlistAddress[_collectionID][_mintingAddress] + 1;
            } else {
                tokensMintedPerAddress[_collectionID][_mintingAddress] = tokensMintedPerAddress[_collectionID][_mintingAddress] + 1;
            }
        }
    }
```
3. First, it increments the `collectionCirculationSupply`: 
```solidity
collectionAdditionalData[_collectionID].collectionCirculationSupply = collectionAdditionalData[_collectionID].collectionCirculationSupply + 1;
```
- `collectionCirculationSupply = 200 + 1 = 201` 

4. After, it checks if it's higher than the `collectionTotalSupply`: 
```solidity
if (collectionAdditionalData[_collectionID].collectionTotalSupply >= collectionAdditionalData[_collectionID].collectionCirculationSupply)
```
- `collectionTotalSupply (200) >= collectionCirculationSupply (201)`, which is `FALSE`

5. So, the last token of the supply will not be minted. How