xanoutas/solidity-security-findings huggingface dataset:
Upgraded Q -> 2 from #1067 [1725542544973]
Audit: 2024-07-traitforge-findings
Severity: HIGH

Judge has assessed an item in Issue #1067 as 2 risk. The relevant finding follows:

 [QA-11] Potential overflow of currentGeneration beyond maxGeneration in _incrementGeneration function
Impact
The _incrementGeneration function can increment the currentGeneration beyond the maxGeneration limit, leading to an inconsistent state in the contract. This inconsistency can cause issues, especially in functions that rely on the currentGeneration being within the allowed range.

Proof of Concept
The _incrementGeneration function currently does not check if the currentGeneration has reached the maxGeneration before incrementing it: https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/TraitForgeNft/TraitForgeNft.sol#L345-L355

function _incrementGeneration() private {
    require(
        generationMintCounts[currentGeneration] >= maxTokensPerGen,
        'Generation limit not yet reached'
    );
    currentGeneration++;
    generationMintCounts[currentGeneration] = 0;
    priceIncrement = priceIncrement + priceIncrementByGen;
    entropyGenerator.initializeAlphaIndices();
    emit GenerationIncremented(currentGeneration);
}
In contrast, other parts of the contract, such as the forge function, include checks to ensure the new generation does not exceed maxGeneration:

require(newGeneration <= maxGeneration, "can't be over max generation");
Without a similar check in _incrementGeneration, the currentGeneration can exceed the maxGeneration, leading to an inconsistent state.

Recommended Mitigation Steps
Add a check in the _incrementGeneration function to ensure that the currentGeneration does not exceed the maxGeneration.
Upgraded Q -> 2 from #32 [1725448114436]
Audit: 2024-07-traitforge-findings
Severity: HIGH

Judge has assessed an item in Issue #32 as 2 risk. The relevant finding follows:

 [L-01] TraitForgeNft::mintWithBudWithBudget() will revert when users try to mint NFTs with ids bigger than 10_000
The mintWithBudget() function is used to allow users to mint multiple NFTs within a single transaction. However there is a problem within the while loop. As can be seen from the below code snippet, the condition in the while loop requires that the _tokenIds < maxTokensPerGen, maxTokensPerGen is set to 10_000 and cannot be changed. However _tokenIds increases with each minted NFT. When the first generation of NFTs is minted, the mintWithBudget() function will always revert.

  function mintWithBudget(
    bytes32[] calldata proof
  )
    public
    payable
    whenNotPaused
    nonReentrant
    onlyWhitelisted(proof, keccak256(abi.encodePacked(msg.sender)))
  {
    uint256 mintPrice = calculateMintPrice();
    uint256 amountMinted = 0;
    uint256 budgetLeft = msg.value;

    while (budgetLeft >= mintPrice && _tokenIds < maxTokensPerGen) {
      _mintInternal(msg.sender, mintPrice);
      amountMinted++;
      budgetLeft -= mintPrice;
      mintPrice = calculateMintPrice();
    }
    if (budgetLeft > 0) {
      (bool refundSuccess, ) = msg.sender.call{ value: budgetLeft }('');
      require(refundSuccess, 'Refund failed.');
    }
  }
Recommended Mitigation Steps
Consider removing the _tokenIds < maxTokensPerGen from the while loop.
Potential Denial of Service (DoS) by Forger in the forgeWithListed()
Audit: 2024-07-traitforge-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/EntityForging/EntityForging.sol#L158
https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/TraitForgeNft/TraitForgeNft.sol#L367-L396
https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/TraitForgeNft/TraitForgeNft.sol#L141-L151
https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/EntityForging/EntityForging.sol#L177-L191


# Vulnerability details

## Impact
In the EntityForging contract, there are multiple scenarios where a listed forger can potentially cause a Denial of Service (DoS) attack against a merger during the forging process. The forger can exploit several vectors to prevent the forging process from completing, such as:
1. Reverting When Funds are Sent: If the forger’s address is a contract with a fallback function that reverts when receiving funds, it can cause the transaction to fail.
2. Canceling Beforehand: The forger can monitor the blockchain for a merger’s transaction and cancel the listing before it completes.
3. Burning the Token: The forger can burn their token, which will cancel the listing.
4. Transferring the Token: The forger can transfer their token to another address, which automatically cancels the listing.

## Proof of Concept
1. The forger lists a token for forging.
2. A merger initiates the forging process by calling forgeWithListed.
3. The forger can use one of the aforementioned methods to cause the transaction to fail, leading to a denial of service for the merger.

## Tools Used
Manual Code Review

## Recommended Mitigation Steps
Implement a claim mechanism where the forger can withdraw their fees rather than transferring them immediately. This can be done by maintaining a mapping of forgers to their owed fees. Forgers can claim their fees through the claimFee function.
```js
mapping(address => uint256) public pendingFees;
function claimFees() external nonReentrant {
    uint256 amount = pendingFees[msg.sender];
    require(amount > 0, "No fees to claim");

    pendingWithdrawals[msg.sender] = 0;

    (bool success, ) = msg.sender.call{ value: amount }("");
    require(success, "Claim failed");

    emit FeesClaimed(msg.sender, amount);
}
```


## Assessed type

DoS
Upgraded Q -> 2 from #107 [1724083523856]
Audit: 2024-07-traitforge-findings
Severity: HIGH

Judge has assessed an item in Issue #107 as 2 risk. The relevant finding follows:

 [06] maxGeneration check included for forging a new NFT but not included when minting a new NFT
TraitForgeNft::forge includes the following check to ensure that maxGeneration is not exceeded:

 require(newGeneration <= maxGeneration, "can't be over max generation");
However, this check is not explicitly included in the TraitForgeNft::mintToken , TraitForgeNft::mintWithBudget and TraitForgeNft::_mintInternal functions. Without the explicit checks, the maxGeneration could be exceed for mints.

Recommended Mitigation Steps
Consider including the maxGeneration check explicitly for mint functions.
Upgraded Q -> 2 from #1014 [1724082614295]
Audit: 2024-07-traitforge-findings
Severity: HIGH

Judge has assessed an item in Issue #1014 as 2 risk. The relevant finding follows:

 [QA-3] mintWithBudget() has an inadequate check that will be invalid after generation 1
Description:

TraitForgeNft::mintWithBudget() allows minting until the maximum number of tokens per generation is minted (or the funds are spent), meaning that this function can’t be used to mint NFTs of 2 generations.

while (budgetLeft >= mintPrice && _tokenIds < maxTokensPerGen)

However this check is wrong because tokenIds don’t restart after the generation is incremented, meaning this check will be ineffective after generation 1 because _tokenIds will be way higher than maxTokensPerGen = 10_000.
Upgraded Q -> 2 from #1017 [1724082393626]
Audit: 2024-07-traitforge-findings
Severity: HIGH

Judge has assessed an item in Issue #1017 as 2 risk. The relevant finding follows:

 [QA-05] mintWithBuget will stop working after the first generation
Impact
The mintWithBudget function’s while loop condition will fail after the first generation of tokens has been minted. This breaks the core functionality of the function, preventing it from being used as intended for subsequent generations.

Instances
Found in contracts/TraitForgeNft/TraitForgeNft.sol at Line 215
@>: while loop condition of _tokenIds < maxTokensPerGen would fail after the first generation has been minted. I would argue it’s not intended design since mintWithBudget is designed as an alternative to mintToken which allows user to mint multiple entities within one transaction to save gas and effort

202:  function mintWithBudget(
    ...
214:
215:@>     while (budgetLeft >= mintPrice && _tokenIds < maxTokensPerGen) { 
216:      _mintInternal(msg.sender, mintPrice);
      ...
225:  }
Upgraded Q -> 3 from #1020 [1724082189816]
Audit: 2024-07-traitforge-findings
Severity: HIGH

Judge has assessed an item in Issue #1020 as 3 risk. The relevant finding follows:

 [L-09] TraitForgeNft: When nuking/buring an NFT, the original owner loses his aidrop amount
File: contracts/TraitForgeNft/TraitForgeNft.sol
Contract: TraitForgeNft.sol
Function: burn
Line: 148
Link: https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/TraitForgeNft/TraitForgeNft.sol#L146-L149
The burn function burns the airdrop share of the original owner as mapping is never updated when selling an NFT. Verify if this is intended, as this adds a huge disadvantage at trading NFTs. Also a user might not be aware and feels like he was robbed airdrop shares.
Upgraded Q -> 2 from #1023 [1724081645126]
Audit: 2024-07-traitforge-findings
Severity: HIGH

Judge has assessed an item in Issue #1023 as 2 risk. The relevant finding follows:

 [L-02] Lack max generation check
The generation of Entity can be over preset 10.

    function _incrementGeneration() private {
        require(
            generationMintCounts[currentGeneration] >= maxTokensPerGen,
            'Generation limit not yet reached'
        );
        currentGeneration++;
+       require(currentGeneration <= maxGeneration, "can't be over max generation");
        generationMintCounts[currentGeneration] = 0;
        priceIncrement = priceIncrement + priceIncrementByGen;
        entropyGenerator.initializeAlphaIndices();
        emit GenerationIncremented(currentGeneration);
    }
Can’t increment generation due to wrong function modifier
Audit: 2024-07-traitforge-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/EntropyGenerator/EntropyGenerator.sol#L206-L216
https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/TraitForgeNft/TraitForgeNft.sol#L345-L355


# Vulnerability details

Checking the `EntropyGenerator` contract it shows that during deployment the `traitForgeNft` contract is set as an allowedCaller, but the method `initializeAlphaIndices` has the onlyOwner modifier. 

When a NFT is minted there is a check if the generation limit is reached, and when the limit is reached it would increment to the next generation. However the line

`entropyGenerator.initializeAlphaIndices();` would always revert because it uses the wrong modifier.

This results in that the `traitForgeNft` contract can’t increment generations and when this cap is reached the contract will always revert.

```
modifier onlyAllowedCaller() {
    require(msg.sender == allowedCaller, 'Caller is not allowed');
    _;
  }
  
  constructor(address _traitForgetNft) {
    allowedCaller = _traitForgetNft;
    initializeAlphaIndices();
  }
  
  // must be onlyAllowedCaller modifier
function initializeAlphaIndices() public whenNotPaused onlyOwner {
    uint256 hashValue = uint256(
      keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp))
    );

    uint256 slotIndexSelection = (hashValue % 258) + 512;
    uint256 numberIndexSelection = hashValue % 13;

    slotIndexSelectionPoint = slotIndexSelection;
    numberIndexSelectionPoint = numberIndexSelection;
}
```
## Proof of Concept

For this POC we’ve set the `maxTokensPerGen` to 1 instead of 10K so that we simulate what happens when the NFT must increment the generations and the limit is reached. 

It will reverts because the `onlyOwner` modifier is used instead of the `onlyAllowedCaller` 

```tsx
import { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers';
import { expect } from 'chai';
import { ethers } from 'hardhat';
import {
  Trait,
  Airdrop,
  DevFund,
  EntityForging,
  EntropyGenerator,
  EntityTrading,
  NukeFund,
  TraitForgeNft,
} from '../typechain-types';

import generateMerkleTree from '../scripts/genMerkleTreeLib';

describe('POC:', function () {
  let token: Trait;

  let entityForging: EntityForging;
  let nft: TraitForgeNft;
  let owner: HardhatEthersSigner;
  let user1: HardhatEthersSigner;
  let user2: HardhatEthersSigner;

  let merkleInfo: any;

  before(async () => {
    [owner, user1, user2] = await ethers.getSigners();
    // Deploy TraitForgeNft contract
    const TraitForgeNft = await ethers.getContractFactory('TraitForgeNft');
    nft = (await TraitForgeNft.deploy()) as TraitForgeNft;

    // Deploy Airdrop contract
    const airdropFactory = await ethers.getContractFactory('Airdrop');
    const airdrop = (await airdropFactory.deploy()) as Airdrop;

    await nft.setAirdropContract(await airdrop.getAddress());
    // TraitForgeNft will be owner of Airdrop Contract
    await air
No maximum generation check for mintToken and mintWithBudget functions 
Audit: 2024-07-traitforge-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/TraitForgeNft/TraitForgeNft.sol#L345


# Vulnerability details

## Impact
The value of `currentGeneration` storage variable can be more than `maxGeneration`.



## Proof of Concept


## Tools Used
Manual review

## Recommended Mitigation Steps
Replace `_incrementGeneration` function with below code
```
function _incrementGeneration() private {
    require(
      generationMintCounts[currentGeneration] >= maxTokensPerGen,
      'Generation limit not yet reached'
    );
    require(currentGeneration < maxGeneration, 'Maximum generation limit reached');
    currentGeneration++;
    generationMintCounts[currentGeneration] = 0;
    priceIncrement = priceIncrement + priceIncrementByGen;
    entropyGenerator.initializeAlphaIndices();
    emit GenerationIncremented(currentGeneration);
}
```


## Assessed type

Invalid Validation
Unchecked generation increment can exceed max generation limit, breaking contract invariants
Audit: 2024-07-traitforge-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/TraitForgeNft/TraitForgeNft.sol#L345-L355


# Vulnerability details

## Impact
`_incrementGeneration()` function can increment the `currentGeneration` beyond the `maxGeneration` limit. Specifically, it contradicts checks in other parts of the contract (e.g., the `forge()` function) that assume `currentGeneration` never exceeds `maxGeneration`. This could allow minting of tokens in non-existent generations.

## Proof of Concept
Look at the `_incrementGeneration()` function: https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/TraitForgeNft/TraitForgeNft.sol#L345-L355

```solidity
345:   function _incrementGeneration() private {
346:     require(
347:       generationMintCounts[currentGeneration] >= maxTokensPerGen,
348:       'Generation limit not yet reached'
349:     );
350:     currentGeneration++;
351:     generationMintCounts[currentGeneration] = 0;
352:     priceIncrement = priceIncrement + priceIncrementByGen;
353:     entropyGenerator.initializeAlphaIndices();
354:     emit GenerationIncremented(currentGeneration);
355:   }
```

This function increments `currentGeneration` without checking if it has reached `maxGeneration`. In contrast, other parts of the contract, such as the [`forge()`](https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/TraitForgeNft/TraitForgeNft.sol#L153-L179) function, assume that `currentGeneration` never exceeds `maxGeneration`:

```solidity
153:   function forge(
154:     address newOwner,
155:     uint256 parent1Id,
156:     uint256 parent2Id,
157:     string memory
158:   ) external whenNotPaused nonReentrant returns (uint256) {
159:     require(
160:       msg.sender == address(entityForgingContract),
161:       'unauthorized caller'
162:     );
163:     uint256 newGeneration = getTokenGeneration(parent1Id) + 1;    ❌ 
164: 
165:     /// Check new generation is not over maxGeneration
166:     require(newGeneration <= maxGeneration, "can't be over max generation"); ❌ 
167: 
168:     // Calculate the new entity's entropy
169:     (uint256 forgerEntropy, uint256 mergerEntropy) = getEntropiesForTokens(
170:       parent1Id,
171:       parent2Id
172:     );
173:     uint256 newEntropy = (forgerEntropy + mergerEntropy) / 2;
174: 
175:     // Mint the new entity
176:     uint256 newTokenId = _mintNewEntity(newOwner, newEntropy, newGeneration);
177: 
178:     return newTokenId;
179:   }
```

This could can lead to a state where `currentGeneration > maxGeneration`, violating the contract's intended behavior.


## Tools Used
Manual review

## Recommended Mitigation Steps
Add a check in the `_incrementGeneration()` function to ensure that `currentGeneration` does not exceed `maxGeneration`. 

```diff
function _incrementGeneration() private {
    require(
        generationMintCounts[currentGeneration
Cannot mint NFT in batch if `_tokenIds >= maxTokensPerGen`
Audit: 2024-07-traitforge-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/TraitForgeNft/TraitForgeNft.sol#L215


# Vulnerability details

## Impact
The `mintWithBudget()` function is designed for users to buy NFTs in batch. However, if `_tokenIds` reaches or exceeds `maxTokensPerGen`, this function becomes useless.

## Proof of Concept
The `mintWithBudget()` function incorrectly uses the `_tokenIds` variable to enforce the minting limit.

This leads to an issue where, if `_tokenIds >= maxTokensPerGen`, the function becomes ineffective, and users cannot mint additional NFTs in a batch. The `amountMinted` variable tracks the number of NFTs minted in the current batch, is not used to limit the number of NFTs minted per batch. Instead, the `_tokenIds` is used mistakenly in the condition `_tokenIds < maxTokensPerGen`:

```solidity
File: contracts\TraitForgeNft\TraitForgeNft.sol
202:   function mintWithBudget(
...
212:     uint256 amountMinted = 0;
213:     uint256 budgetLeft = msg.value;
214: 
215:>    while (budgetLeft >= mintPrice && _tokenIds < maxTokensPerGen) {
216:       _mintInternal(msg.sender, mintPrice);
217:>      amountMinted++;
218:       budgetLeft -= mintPrice;
219:       mintPrice = calculateMintPrice();
220:     }
```

## Tools Used
vscode

## Recommended Mitigation Steps
```diff
-    while (budgetLeft >= mintPrice && _tokenIds < maxTokensPerGen) {
+    while (budgetLeft >= mintPrice && amountMinted < maxTokensPerGen) {
```











## Assessed type

DoS
EntityTrading.sol :: nukeFundContribution in the buyNFT() is not calculated correctly, leading to incorrect fees being contributed to Nuke Fund.
Audit: 2024-07-traitforge-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityTrading/EntityTrading.sol#L72
https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityTrading/EntityTrading.sol#L33-L35


# Vulnerability details

## Impact
**`buyNFT()`** allows users to purchase a listed NFT. However, there is an issue with the calculation of the **`nukeFundContribution`**, which represents the funds sent to the Nuke Fund. Due to this miscalculation, the fees intended to fund the project are incorrect.
## Proof of Concept
**`buyNFT()`** is implemented as follows:
```Solidity
function buyNFT(uint256 tokenId) external payable whenNotPaused nonReentrant {
    Listing memory listing = listings[listedTokenIds[tokenId]];
    require(
      msg.value == listing.price,
      'ETH sent does not match the listing price'
    );
    require(listing.seller != address(0), 'NFT is not listed for sale.');

    //transfer eth to seller (distribute to nukefund)
@>  uint256 nukeFundContribution = msg.value / taxCut;
    uint256 sellerProceeds = msg.value - nukeFundContribution;
    transferToNukeFund(nukeFundContribution); // transfer contribution to nukeFund

    // transfer NFT from contract to buyer
    (bool success, ) = payable(listing.seller).call{ value: sellerProceeds }(
      ''
    );
    require(success, 'Failed to send to seller');
    nftContract.transferFrom(address(this), msg.sender, tokenId); // transfer NFT to the buyer

    delete listings[listedTokenIds[tokenId]]; // remove listing

    emit NFTSold(
      tokenId,
      listing.seller,
      msg.sender,
      msg.value,
      nukeFundContribution
    ); // emit an event for the sale
  }
```
As it stands, the calculation **`nukeFundContribution = msg.value / taxCut`** is incorrect because **`taxCut`** represents a percentage and cannot be used directly as a divisor. While this approach works in first instance because **`taxCut`** is hardcoded to 10% (as indicated in the documentation), it becomes problematic when **`taxCut`** is modified using the **`setTaxCut()`**.
```Solidity
function setTaxCut(uint256 _taxCut) external onlyOwner {
    taxCut = _taxCut;
  }
```
If **`taxCut`** is changed to a percentage other than 10%, the calculation will become incorrect. For instance, let’s consider two examples: one with a **`taxCut`** of 10% and another with 20%.


1. 10% Example

**`taxCut = 10%`**
**`msg.value = 1e18`**

**`nukeFundContribution = msg.value / taxCut =  1e18 / 10 = 1e17`**
**`Percentatge = (1e17 / 1e18) * 100 = 10%`**

In this case is correct, **`taxCut == Percentatge`**.

2. 20% Example

**`taxCut = 20%`**
**`msg.value = 1e18`**

**`nukeFundContribution = msg.value / taxCut =  1e18 / 20 = 5e16`**
**`Percentatge = (5e16 / 1e18) * 100 = 5%`**

As you can see, **`taxCut`** is not equal **`Percentage`** (20% != 5%), in this case is incorrect. This cause the protocol to receive less funding than
whitelist cannot  mint NFT via mintWithBudget() when generation is larger than 1.
Audit: 2024-07-traitforge-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/TraitForgeNft/TraitForgeNft.sol#L202-L225


# Vulnerability details

## Impact
In mintWithBudget(), there is one limitation `_tokenIds` must be less than `maxTokensPerGen`. Whitelist users cannot mint NFT via mintWithBudget() if current generation is not 1.

## Proof of Concept
Function `mintWithBudget()` aims to help users mint multiple NFTs in one transaction compared with mintToken(). However, in function mintWithBudget(), when current tokenId is not less than `maxTokensPerGen`, all operations will be reverted. This is not one expected behaviour.
```javascript
  function mintWithBudget(
    bytes32[] calldata proof
  )
    public
    payable
    whenNotPaused
    nonReentrant
    onlyWhitelisted(proof, keccak256(abi.encodePacked(msg.sender)))
  {
    uint256 mintPrice = calculateMintPrice();
    uint256 amountMinted = 0;
    uint256 budgetLeft = msg.value;
@==>while (budgetLeft >= mintPrice && _tokenIds < maxTokensPerGen) {
      _mintInternal(msg.sender, mintPrice);
      amountMinted++;
      budgetLeft -= mintPrice;
      mintPrice = calculateMintPrice();
    }
    ...
}
```
## Tools Used
Manual

## Recommended Mitigation Steps
Delete the related limitation in mintWithBudget().


## Assessed type

DoS