DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: low
Invalid

Missing ERC-4626 Implementation in _mint() Function Leading to Inaccurate Share Calculation

Summary

The _mint() function in the PerpetualVault contract manually calculates shares when users deposit assets. However, it does not follow the ERC-4626 standard, leading to inaccurate share allocations, potential rounding errors, and reduced composability with DeFi protocols. Implementing ERC-4626 would ensure standardized and accurate share issuance.

Vulnerability Details

The _mint() function is responsible for issuing shares to users when they deposit assets into the vault:

function _mint(uint256 depositId, uint256 amount, bool refundFee, MarketPrices memory prices) internal {
uint256 _shares;
if (totalShares == 0) {
_shares = depositInfo[depositId].amount * 1e8;
} else {
uint256 totalAmountBefore;
if (positionIsClosed == false && _isLongOneLeverage(beenLong)) {
totalAmountBefore = IERC20(indexToken).balanceOf(address(this)) - amount;
} else {
totalAmountBefore = _totalAmount(prices) - amount;
}
if (totalAmountBefore == 0) totalAmountBefore = 1;
_shares = amount * totalShares / totalAmountBefore;
}
depositInfo[depositId].shares = _shares;
totalShares = totalShares + _shares;
}

Issues in _mint()

  1. Manual Share Calculation Instead of ERC-4626’s Built-in Functions

    • The function manually computes:

      _shares = amount * totalShares / totalAmountBefore;
    • This introduces potential rounding errors and inaccuracies.

    • ERC-4626 provides convertToShares(amount), which should be used instead.

  2. Arbitrary Fallback to totalAmountBefore = 1

    • If totalAmountBefore == 0, the function sets it to 1, leading to incorrect share minting:

      if (totalAmountBefore == 0) totalAmountBefore = 1;
    • This artificially inflates the share count, which can over-reward early depositors.

  3. No Standardized Minting Process

    • ERC-4626 includes deposit(), mint(), and redeem() functions, ensuring accurate asset-to-share conversion and withdrawal calculations.

    • Without it, the vault cannot integrate with DeFi protocols expecting ERC-4626 compliance.

Impact

  1. Inaccurate Share Allocations – Users may receive incorrect shares, leading to potential over- or under-minting.

  2. Withdrawal Issues – Incorrect share calculations may cause unexpected behavior when redeeming assets, leading to incorrect payouts.

  3. DeFi Incompatibility – The vault cannot integrate with yield aggregators, lending platforms, or protocols that expect ERC-4626 compliance, limiting its adoption.

Tools Used

Manuel Review

OpenZeppelin ERC-4626 standard documentation

Recommendations

Recommendations

  1. Inherit ERC-4626 to standardize minting and withdrawals:

    import {ERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";
    contract PerpetualVault is ERC4626, Ownable2StepUpgradeable, ReentrancyGuardUpgradeable {
    constructor(IERC20 _collateralToken) ERC4626(_collateralToken) {}
    }
  2. Replace manual _mint() logic with ERC-4626 functions:

    uint256 shares = convertToShares(amount);
    _mint(depositId, shares);
  3. Use convertToAssets() for withdrawals:

    uint256 assets = convertToAssets(shares);

By implementing ERC-4626, the vault will eliminate unnecessary complexity, improve accuracy, and increase composability across DeFi protocols.

Updates

Lead Judging Commences

n0kto Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

invalid_totalAmountBefore_is_1_incorrect_calculation_supposition

No proof when this can happen: Most of the time totalAmountBefore equals 0 (balance minus amount sent), it means totalShares equals 0. If it could happen with very specific conditions, report with that tag didn't add the needed details to be validated.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!