xanoutas/solidity-security-findings huggingface dataset:
Protocol Contracts does not have access to pause and unpause functionality
Audit: 2024-07-traitforge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/DevFund/DevFund.sol#L9
https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityForging/EntityForging.sol#L10
https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityTrading/EntityTrading.sol#L11
https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntropyGenerator/EntropyGenerator.sol#L9
https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/NukeFund/NukeFund.sol#L11
https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/TraitForgeNft/TraitForgeNft.sol#L14


# Vulnerability details

## Impact
The protocol contracts inherits from `Pausable` contract of `openzeppelin`, but the  feature to pause and unpause the contract will be available only if the inheriting contract expose the `_pause()` and `_unpause()` functions as external. Since these functions in the library are internal functions, it is the responsibility of the inheriting contract to extend and expose these functions with proper access restrictions.

As all the below contract did not expose the `_pause()` and `_unpause()` functions, none of these contracts are pausable when required.


- DevFund.sol
- EntityForging.sol
- EntityTrading.sol
- EntropyGenerator.sol
- NukeFund.sol
- TraitForgeNft.sol

As such, none of the above contracts are pausable.

## Proof of Concept

The _pause() and _unpause() are internal functions and hence are available to the inheriting contracts. But, since this functionality is not exposed by the inheriting contracts, the contracts cannot be paused or unpaused when required.

```
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }
   
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
```

## Tools Used
Manual review

## Recommended Mitigation Steps
All the contracts listed above that extends the `Pausable` contract should exposed the below functions. It is important to apply proper access control to the two functions so that only account with correct entitlement can pause and unpause the respective contracts.

`onlyOwner()` is used an example to demonstrate the need for access control.

```solidity
function pause() public onlyOwner() {
        _pause();
    }

function unpause() public onlyOwner() {
        _unpause();
}
```





## Assessed type

Library
The contract cannot be paused
Audit: 2024-07-traitforge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/DevFund/DevFund.sol#L61
https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityForging/EntityForging.sol#L179
https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityForging/EntityForging.sol#L70
https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityTrading/EntityTrading.sol#L41
https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityTrading/EntityTrading.sol#L63


# Vulnerability details

## Impact
The `EntityForging`, `EntityTrading` and `DevFund` contracts all inherits `Pausable` contract from Openzeppelin to allow the contracts to be paused in the event of emergencies. To this effect, these aforementioned contracts all implement a `whenNotPaused` modifier on their various functions. 

For example, in `EntityTrading` the functions `listNFTForSale`, `buyNFT` and `cancelListing` all have the `whenNotPaused` modifier. The same is true for `listForForging`, forgeWithlisted` and `cancelListingForForging` functions in `EntityForging` contract as well as `claim` function in `DevFund` contract. 

It is noted that neither of the three contracts have implemented the `pause()` and `unpause ()` functions which means that the contracts can never enter a pause state . There is absolutely no mechanism for the contract to be paused. 

Openzeppelin requires contracts inheriting from `Pausable` contract to implement an external/public `pause` and `unpause` function which would in turn call their internal `_pause` and `_unpause` functions. 

Effectively, if there is any form of emergency, the contracts can never be paused and critical actions such as canceling Listing, buyingNFT, listing NFT for sale are never paused. 

## Proof of Concept
1. https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/DevFund/DevFund.sol#L61
2. https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityForging/EntityForging.sol#L179
3. https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityForging/EntityForging.sol#L70
4. https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityTrading/EntityTrading.sol#L41
5. https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityTrading/EntityTrading.sol#L63


## Tools Used
Manual Review
## Recommended Mitigation Steps
Consider implementing `pause` and `unpause` functionality in the aforementioned contracts. 


## Assessed type

Access Control
Incomplete implementation of `pause` functionality
Audit: 2024-07-traitforge-findings
Severity: MEDIUM

# Lines of code

(https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/DevFund/DevFund.sol#L9


# Vulnerability details

## Impact
The contracts within the codebase are supposed to be `pausable`. However, they do not correctly exposes the `_pause()` and `_unpause()` internal functions through access restricted external functions.

Therefore, there will be no mechanism to `pause/unpause` the contracts as required.

## Proof of Concept
Let`s use [DevFund](https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/DevFund/DevFund.sol#L9) contract for example:
```solidity
import '@openzeppelin/contracts/security/Pausable.sol';
contract DevFund is IDevFund, Ownable, ReentrancyGuard, Pausable {
```
A seen this contract is `pausable`.

The issue here however is that the contract does not exposes the `_pause()` and `_unpause()` `internal` functions through access restricted `external` functions.
In [Pausable.sol](https://github.com/smiley159/f2p/blob/master/node_modules/@openzeppelin/contracts/security/Pausable.sol), the pause functionalities are all `internal` functions with `whenNotPaused` and `whenPaused` modifiers:
```solidity
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
```
But to actually enable these functionalities, there has to be an `external/public` functions configured for it which is missing in this case.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Implement `external/public` functions with access control for these functionalities.
```diff
+   function pause() external onlyOwner {
+       _pause();
+   }

+   function unpause() external onlyOwner {
+       _unpause();
+   }
```


## Assessed type

Other
`Forgers` can forge up to `forgePotential + 1` for a given `tokenId`
Audit: 2024-07-traitforge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityForging/EntityForging.sol#L87-L90
https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityForging/EntityForging.sol#L131-L133


# Vulnerability details

## Impact
The `EntityForging` contract has a vulnerability allowing the forging limit to be exceeded.

## Proof of Concept
[`EntityForging::listForForging()`](https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityForging/EntityForging.sol#L87-L90) requires that `forgePotential` for a given `tokenId` is not exceeded. However this is implemented as follows:
```solidity
    require(
>>    forgePotential > 0 && forgingCounts[tokenId] <= forgePotential,
      'Entity has reached its forging limit'
    );
```
As seen, the `<=` operator allows listing even when the `forging count` is already equal to the `forgePotential`. 

This count is again incremented in [`EntityForging::forgeWithListed()`](https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityForging/EntityForging.sol#L131-L133), as shown here:
```solidity
    // Check forger's breed count increment but do not check forge potential here
    // as it is already checked in listForForging for the forger
>>  forgingCounts[forgerTokenId]++;
```
Therefore, the limit is exceeded.

### Example Scenario:
**Initial State**:
- `forgePotential` = 5
- `forgingCounts[tokenId]` = 5 --> This means that this `tokenId` has been forged `5 times` already

**listForForging()**:
- `Bob` calls this function and the condition passes since (`5 <= 5`).
- This `tokenId` is then listed for forging.

**forgeWithListed()**
- During forging, `forgingCounts[tokenId]` is again incremented
- New `forgingCounts[tokenId] = 6`. ---> This exceeds the `forgePotential`  since this is the `6th time forging` but the function just allows it.

As seen, `forgePotential` is exceeded: 
---> Users can forge more than the intended limit and still [receive `forgerShare`](https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityForging/EntityForging.sol#L158-L159).
```solidity
    address payable forgerOwner = payable(nftContract.ownerOf(forgerTokenId));
>>  (bool success_forge, ) = forgerOwner.call{ value: forgerShare }(''); // @udit-info forgerShare sent to user
    require(success_forge, 'Failed to send to Forge Owner');
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Adjust Condition in `listForForging()` as follows:
```diff
    require(
-     forgePotential > 0 && forgingCounts[tokenId] <= forgePotential,
+     forgePotential > 0 && forgingCounts[tokenId] < forgePotential,
      'Entity has reached its forging limit'
    );
```
The use of the `<` operator ensures that the token can only be listed if `forgingCounts[tokenId]` is stri
User can maximize profits by front-running
Audit: 2024-07-traitforge-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
A user can maximize his profits by front-running other nuke transactions

## Proof of Concept

Let's assume that there are currently 1000 active entities, out of which a portion can be nuked. Until time T, all users are waiting for their entities to mature more and produce better profits. At time T, a user X decides to nuke his entity and withdraw a part of the honeypot. With an average nuke factor of ~14% (calculated from the average entropy, 555555), this nuke would eliminate the contribution of the last 86 minted entities, and would require 79 new ones to bring back the pot to the same value.

A malicious user can decide to front-run the transaction of user X and withdraw first, therefore getting a bigger chunk of the reward pool. Two average entities nuking - user X and the malicious user - would remove ~4 out of ~15.5 ETH from the pot. If a second malicious user attempts the same thing, it would already remove ~5.5 out of the 15.5 ETH.

If more than one user shares the same strategy, this could potentially hurt the protocol. If this triggers around 4% of the player base to nuke their entities, a nuke would not even be profitable anymore and would most likely cause all users to attempt to recover what they can from the investment and kill the game.

## Tools Used
Manual inspection

## Recommended Mitigation Steps
Use flashbots to hide user intentions and prevent front-running.


## Assessed type

Context
Non-functional pause function
Audit: 2024-07-traitforge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityForging/EntityForging.sol#L10-L11


# Vulnerability details

## Impact
In TraitForgeNft, NukeFund, EntityTrading contract, we have one pause function. This function aims to pause the key functions to protect the funds if one vulnerability is found or some emergency scenarios. Currently, the pause function is not implemented and all functions cannot be pause. The emergency protection does not work.

## Proof of Concept
Take TraitForgeNft as one example, we inherit `Pausable` from openzepplin library, but openzepplin library only provide the internal version `_pause()`. If we want to support pause function, we should to add one external/public function to support pause function.
```javascript
contract TraitForgeNft is
  ITraitForgeNft,
  ERC721Enumerable,
  ReentrancyGuard,
  Ownable,
  Pausable
{
}
```
```javascript
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }
```

## Tools Used
Manual

## Recommended Mitigation Steps
Add external/public pause()/unpause() function.


## Assessed type

Context
The Pausable feature is ineffective
Audit: 2024-07-traitforge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/DevFund/DevFund.sol#L9
https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/EntityForging/EntityForging.sol#L10
https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/EntityTrading/EntityTrading.sol#L11
https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/EntropyGenerator/EntropyGenerator.sol#L9
https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/NukeFund/NukeFund.sol#L11
https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/TraitForgeNft/TraitForgeNft.sol#L14


# Vulnerability details

## Impact
Missing functions for pausing and unpausing contract operations.

## Proof of Concept
The contracts include the OZ pausable feature to pause or resume certain functions with `whenNotPaused` modifier. However, they are missing public privileged functions that allow authorized users to call `_pause()` and `_unpause()` to enable this OZ pausable feature.

## Tools Usedf
vscode

## Recommended Mitigation Steps
Add public privileged functions that allow authorized users to call `_pause()` and `_unpause()`.





## Assessed type

DoS
Exceeded forge for forger NFTs
Audit: 2024-07-traitforge-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The forger NFT can be listed once more time after it has already been forged `forgePotential` times, leading to exceeded NFT forge.

## Proof of Concept
The function `listForForging()` does not correctly account for the current forge count during the listing process. If `forgingCounts[tokenId]` of a forger NFT reaches `forgePotential`, it can still be listed for forging (L88), allowing the NFT to be listed even when it has already been forged `forgePotential` times. The actual forge count is only incremented in the `forgeWithListed()` function (line 133), after the listing process.

```solidity
File: EntityForging.sol
67:   function listForForging(
	...
87:     require(
88:>      forgePotential > 0 && forgingCounts[tokenId] <= forgePotential,
89:       'Entity has reached its forging limit'
90:     );

102:   function forgeWithListed(
	...
131:     // Check forger's breed count increment but do not check forge potential here
132:     // as it is already checked in listForForging for the forger
133:>    forgingCounts[forgerTokenId]++;
```

For example, given

- `forgePotential` of NFT = 1

The 1st time forge: `forgingCounts[tokenId]` = 0 when `listForForging()`, forge succeed.
The 2nd time forge: as `forgingCounts[tokenId]` = `forgePotential` = 1 when `listForForging()`, the NFT can still be `listForForging()`, forge succeed.

## Tools Used
vscode

## Recommended Mitigation Steps
```diff
     require(
-      forgePotential > 0 && forgingCounts[tokenId] <= forgePotential,
+      forgePotential > 0 && forgingCounts[tokenId] + 1 <= forgePotential,
       'Entity has reached its forging limit'
     );
```














## Assessed type

Invalid Validation
Incorrect price calculation for edge case
Audit: 2024-07-traitforge-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
According to the `calculateMintPrice()`, the first NFT in each generation will be minted with price `startPrice`. But the actual price for first NFT in each generation is `startPrice + priceIncrement * maxTokensPerGen`. The miner has to pay more funds than expected. 

## Proof of Concept
The same vulnerability happens in mintToken() and mintWithBudget(). Let's take mintToken() as one example:
The first NFT in generation 0 price: startPrice
The second NFT in generation 0 price: startPrice + priceIncrement
...
The 100000th NFT in generation 0 price: startPrice + priceIncrement * 99999
The next one should be the first NFT in generation 2, expected price should be startPrice, but actual price is `startPrice + priceIncrement * 100000`

The vulnerability is that we calculate this new NFT's price and then we calculated which generation this NFT belongs to.
We should calculate this newly NFT's generation, and then calculate this NFT's price.
```javascript
  function mintToken(
    bytes32[] calldata proof
  )
    public payable whenNotPaused nonReentrant
    onlyWhitelisted(proof, keccak256(abi.encodePacked(msg.sender)))
  {
    uint256 mintPrice = calculateMintPrice();
    require(msg.value >= mintPrice, 'Insufficient ETH send for minting.');

    _mintInternal(msg.sender, mintPrice);
    ...
   }
  function calculateMintPrice() public view returns (uint256) {
    uint256 currentGenMintCount = generationMintCounts[currentGeneration];
    uint256 priceIncrease = priceIncrement * currentGenMintCount;
    uint256 price = startPrice + priceIncrease;
    return price;
  }
  function _mintInternal(address to, uint256 mintPrice) internal {
    if (generationMintCounts[currentGeneration] >= maxTokensPerGen) {
      _incrementGeneration();
    }
    _tokenIds++;
    uint256 newItemId = _tokenIds;
    _mint(to, newItemId);
    uint256 entropyValue = entropyGenerator.getNextEntropy();

    tokenCreationTimestamps[newItemId] = block.timestamp;
    tokenEntropy[newItemId] = entropyValue;
    tokenGenerations[newItemId] = currentGeneration;
    // This generation's mint count.
    generationMintCounts[currentGeneration]++;
    initialOwners[newItemId] = to;
    if (!airdropContract.airdropStarted()) {
      airdropContract.addUserAmount(to, entropyValue);
    }
    ...
}
```


## Tools Used
Manual
## Recommended Mitigation Steps
Calculate newly NFT's generation and then calculate newly NFT's price.


## Assessed type

Error
Zero age for NFTs with entrophy whose last digit is zero
Audit: 2024-07-traitforge-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The age of NFT could constantly being zero, results in smaller nuke factor and nuke less funds.

## Proof of Concept
The NFT age could be constantly zero, as the `perfomanceFactor` in `calculateAge()` function is zero if last digit of token entropy is zero:

```solidity
File: contracts\NukeFund\NukeFund.sol
118:   function calculateAge(uint256 tokenId) public view returns (uint256) {
     ...
126:     uint256 perfomanceFactor = nftContract.getTokenEntropy(tokenId) % 10; // <= perfomanceFactor could be zero
127: 
128:     uint256 age = (daysOld *
129:       perfomanceFactor *
130:       MAX_DENOMINATOR *
131:       ageMultiplier) / 365; // add 5 digits for decimals
132:     return age;
133:   }
```

The `perfomanceFactor` is derived from the last digit of the token entropy, which can be zero. This results in the `age` being calculated as zero, regardless of how long the user holds NFT.

Therefore, the calculated nuke factor is smaller than intended because it can not be adjusted [based on the NFT age](https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/NukeFund/NukeFund.sol#L147-L148).

As the claimable funds is calculated [based on the nuke factor](https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/NukeFund/NukeFund.sol#L166-L167), users receive less funds.

## Tools Used
vscode

## Recommended Mitigation Steps
```diff
     uint256 perfomanceFactor = nftContract.getTokenEntropy(tokenId) % 10;
+    perfomanceFactor = perfomanceFactor == 0? 1 : perfomanceFactor;
```











## Assessed type

Math
Incorrect forgingCounts process in forge
Audit: 2024-07-traitforge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntityForging/EntityForging.sol#L87-L90


# Vulnerability details

## Impact
The forge NFT can be used to forge with more than `forgePotential` times.

## Proof of Concept
If one NFT is one forger, this NFT can be used to listForForging. Each forger NFT's potential is different. If NFT's potential is 0, we cannot use this forger NFT to forge because of insufficient potential. If one forger NFT's potential is x, we can list this NFT for forging x times in one year.

The problem is that the check `forgingCounts[tokenId] <= forgePotential` in listForForging is improper.
Consider the below scenario:
- Alice has one forger NFT Token with potential 1. So this NFT is expected to be used for forging only one time within one year.
- Alice calls listForForging() at first time, `forgingCounts[tokenId]` is zero, we can pass the require check.
- Bob forge this forger NFT via forgeWithListed(). Now Alice's NFT `forgingCounts[tokenId]` equals 1.
- Alice can call listForForging() for the second time, we still can pass the require check.

Although our NFT's potential is 1, we can use this NFT twice within one year for forging. This is not one expected behaviour and break the invariant.
```javascript
  function listForForging(
    uint256 tokenId,
    uint256 fee
  ) public whenNotPaused nonReentrant {
    ...
    uint256 entropy = nftContract.getTokenEntropy(tokenId); // Retrieve entropy for tokenId
    uint8 forgePotential = uint8((entropy / 10) % 10); // Extract the 5th digit from the entropy
    require(
      forgePotential > 0 && forgingCounts[tokenId] <= forgePotential,
      'Entity has reached its forging limit'
    );

    bool isForger = (entropy % 3) == 0; // Determine if the token is a forger based on entropy
    require(isForger, 'Only forgers can list for forging');
    ...
}

```


## Tools Used
Manual
## Recommended Mitigation Steps
Change `forgingCounts[tokenId] <= forgePotential` to `forgingCounts[tokenId] < forgePotential` in listForForging()


## Assessed type

Context