code-423n4 / 2022-06-notional-coop-findings

1 stars 1 forks source link

Division round down 2 times may cause `convertToShares` calculation incorrect if underlying token with decimals less than 8. #143

Closed code423n4 closed 2 years ago

code423n4 commented 2 years ago

Lines of code

https://github.com/code-423n4/2022-06-notional-coop/blob/6f8c325f604e2576e2fe257b6b57892ca181509a/notional-wrapped-fcash/contracts/wfCashERC4626.sol#L40 https://github.com/code-423n4/2022-06-notional-coop/blob/6f8c325f604e2576e2fe257b6b57892ca181509a/notional-wrapped-fcash/contracts/wfCashERC4626.sol#L52

Vulnerability details

Impact

Proof Of Concept

  1. Assume underlyingToken is USDC with decimals = 6 so precision = 1e6. Wrapped fCash has totalSupply() = 2000000, has not matured and pvInternal = 2111111. We try to get amount of share with assets = 1e9.

  2. Use 2nd formula

    pvExternal = (pvInternal * precision) / INTERNAL_TOKEN_PRECISION
    = (2111111 * 1e6) / 1e8
    = 21111
    => totalAssets() = 21111
  3. Use first formula, we get

    (assets * totalSupply()) / totalAssets()
    = (1e9 * 2000000) / 21111
    = 94737340722
  4. But if we didn’t round down in first calculation, we will get

    (assets * totalSupply()) / totalAssets() 
    = (assets * totalSupply()) / ((pvInternal * precision) / INTERNAL_TOKEN_PRECISION)
    = (assets * totalSupply() * INTERNAL_TOKEN_PRECISION) / (pvInternal * precision)
    = (1e9 * 2000000 * 1e8) / (2111111 * 1e6)
    = 94736847091

Tools Used

Manual Review

Recommended Mitigation Steps

Merge 2 formula into 1 calculation to improve precision.

jeffywu commented 2 years ago

Good find and agree with the mitigation.

jeffywu commented 2 years ago

Duplicate of #155 (I think this is the better written description of all the duplicates).

I believe this should be Med Severity due to the size of the potential effect.

jeffywu commented 2 years ago

My understanding of the ERC4626 specification (and this comes via Alberto who is one of the authors of the specification), is that convertToShares and convertToAssets are intended to be oracle values that do not take into account slippage for an actual exit (that is the job of previewMint/previewWithdraw, etc). Therefore, convertToShares and convertToAssets must be resistant to flash loan manipulation (which they are here) and usable as view methods for UIs and display purposes, but should not be used by smart contracts to determine actual trading amounts.

If that is the case then I would think that the severity would be reduced for this particular report. I think that rounding in the previewMint/etc cases could be left as High.

 convertToShares

The amount of shares that the Vault would exchange for the amount of assets provided, in an ideal scenario where all the conditions are met.

MUST NOT be inclusive of any fees that are charged against assets in the Vault.

MUST NOT show any variations depending on the caller.

MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.

MUST NOT revert unless due to integer overflow caused by an unreasonably large input.