Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect price calculation for first token of each generation due to delayed generation increment #609

Closed
howlbot-integration bot opened this issue Aug 9, 2024 · 1 comment
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working downgraded by judge Judge downgraded the risk level of this issue duplicate-564 🤖_03_group AI based duplicate group recommendation satisfactory satisfies C4 submission criteria; eligible for awards sufficient quality report This report is of sufficient quality

Comments

@howlbot-integration
Copy link

Lines of code

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

Vulnerability details

Impact

After incrementing each generation, the priceIncrement is incremented by priceIncrementByGen in _incrementGeneration. The generation incrementation occurs when minting the first token of the new generation, not when minting the last token of the current generation. This leads to the issue that when minting the first token of each new generation, the priceIncrementByGen is not included in the price because calculateMintPrice is called before the generation is incremented:

function mintToken(
    bytes32[] calldata proof
  )
    public
    payable
    whenNotPaused
    nonReentrant
    onlyWhitelisted(proof, keccak256(abi.encodePacked(msg.sender)))
  {
@>    uint256 mintPrice = calculateMintPrice(); // @audit getting the mint price
    require(msg.value >= mintPrice, 'Insufficient ETH send for minting.');

@>    _mintInternal(msg.sender, mintPrice); // @audit minting token and incrementing generation if necessary

    uint256 excessPayment = msg.value - mintPrice;
    if (excessPayment > 0) {
      (bool refundSuccess, ) = msg.sender.call{ value: excessPayment }('');
      require(refundSuccess, 'Refund of excess payment failed.');
    }
  }

When minting the first token of a new generation, the current generation is incremented after calculating the minting price because the increment occurs inside _mintInternal:

function _mintInternal(address to, uint256 mintPrice) internal {
    if (generationMintCounts[currentGeneration] >= maxTokensPerGen) {
      _incrementGeneration();
    }
    //...
}

As a result, the required mint price for the first token of each new generation does not include the priceIncrementByGen.

Tools Used

Manual Review

Recommended Mitigation Steps

The root cause of this issue is that the generation incrementation occurs when minting the first token of the new generation, rather than when minting the last token of the current generation. A simple fix is to move the generation incrementation check to the end of the mint process. Below is a suggestion for updating the code in mintToken and _mintInternal

// mintToken

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);

    uint256 excessPayment = msg.value - mintPrice;
    if (excessPayment > 0) {
      (bool refundSuccess, ) = msg.sender.call{ value: excessPayment }('');
      require(refundSuccess, 'Refund of excess payment failed.');
    }

+    if (generationMintCounts[currentGeneration] == maxTokensPerGen) {
+       _incrementGeneration();
+    }
  }

// _mintInternal
function _mintInternal(address to, uint256 mintPrice) internal {
-    if (generationMintCounts[currentGeneration] >= maxTokensPerGen) {
-      _incrementGeneration();
-   }
    //...
}

Assessed type

Context

@howlbot-integration howlbot-integration bot added 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value 🤖_03_group AI based duplicate group recommendation bug Something isn't working duplicate-210 sufficient quality report This report is of sufficient quality labels Aug 9, 2024
howlbot-integration bot added a commit that referenced this issue Aug 9, 2024
@c4-judge c4-judge added satisfactory satisfies C4 submission criteria; eligible for awards 3 (High Risk) Assets can be stolen/lost/compromised directly upgraded by judge Original issue severity upgraded from QA/Gas by judge 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value downgraded by judge Judge downgraded the risk level of this issue and removed 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value 3 (High Risk) Assets can be stolen/lost/compromised directly upgraded by judge Original issue severity upgraded from QA/Gas by judge labels Aug 20, 2024
@c4-judge
Copy link
Contributor

c4-judge commented Sep 5, 2024

koolexcrypto marked the issue as duplicate of #564

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working downgraded by judge Judge downgraded the risk level of this issue duplicate-564 🤖_03_group AI based duplicate group recommendation satisfactory satisfies C4 submission criteria; eligible for awards sufficient quality report This report is of sufficient quality
Projects
None yet
Development

No branches or pull requests

1 participant