DeFiFoundrySolidity
16,653 OP
View results
Submission Details
Severity: medium
Valid

Incorrect Asset Accounting Due to Assumed 1:1 Exchange Rate Between Underlying and Asset

Description:

In the StrategyMainnet contract, several functions assume a 1:1 exchange rate between the underlying token (e.g., WETH) and the asset token (e.g., alETH) when calculating the total assets and reporting profits or losses. This assumption can lead to inaccurate asset valuation, especially if the exchange rate between the underlying and asset deviates from 1:1.

Affected Functions:

  1. _harvestAndReport Function:

    function _harvestAndReport() internal override returns (uint256 _totalAssets) {
    uint256 claimable = transmuter.getClaimableBalance(address(this));
    if (claimable > 0) {
    // transmuter.claim(claimable, address(this));
    }
    uint256 unexchanged = transmuter.getUnexchangedBalance(address(this));
    uint256 underlyingBalance = underlying.balanceOf(address(this));
    _totalAssets = unexchanged + asset.balanceOf(address(this)) + underlyingBalance;
    }
    • The function sums up unexchanged (which is in terms of the asset), asset.balanceOf (asset), and underlyingBalance (underlying token) without considering the exchange rate between the underlying and the asset.

    • If the underlying token's value differs from the asset token's value, the _totalAssets calculation will be inaccurate, leading to incorrect profit/loss reporting.

  2. balanceDeployed Function:

    function balanceDeployed() public view returns (uint256) {
    return transmuter.getUnexchangedBalance(address(this)) + underlying.balanceOf(address(this)) + asset.balanceOf(address(this));
    }
    • Similar to _harvestAndReport, this function sums balances of the asset and underlying tokens directly, assuming a 1:1 exchange rate.

    • This can misrepresent the actual value of deployed assets if the exchange rate is not 1:1.

  3. availableWithdrawLimit Function:

    function availableWithdrawLimit(address /*_owner*/) public view override returns (uint256) {
    return asset.balanceOf(address(this)) + transmuter.getUnexchangedBalance(address(this));
    }
    • This function excludes the underlyingBalance from the calculation.

    • If there are significant underlying tokens held by the strategy, they are not considered in the withdraw limit, potentially restricting withdrawals unnecessarily.

Impact:

  • Inaccurate Reporting: Investors and stakeholders may receive incorrect information about the strategy's performance, leading to misplaced trust or investment decisions.

  • Potential Losses: Miscalculations in total assets can result in incorrect profit/loss reporting, potentially causing unintended distributions or losses to depositors.

  • Withdrawal Issues: Users may be unable to withdraw their rightful share if the availableWithdrawLimit does not accurately reflect the total assets.

Recommendation:

  • Implement Exchange Rate Integration:

    • Incorporate real-time exchange rates between the underlying and the asset when calculating total assets.

    • Adjust the unexchanged, asset.balanceOf, and underlyingBalance values according to their respective exchange rates before summing them up.

  • Use Price Oracles:

    • Integrate a trusted price oracle to fetch the latest exchange rates between the underlying and asset tokens.

    • Ensure that the oracle source is reliable and resistant to manipulation.

  • Update Affected Functions:

    • Modify the _harvestAndReport, balanceDeployed, and availableWithdrawLimit functions to account for exchange rates.

    • Example adjustment in _harvestAndReport:

      function _harvestAndReport() internal override returns (uint256 _totalAssets) {
      uint256 unexchanged = transmuter.getUnexchangedBalance(address(this));
      uint256 assetBalance = asset.balanceOf(address(this));
      uint256 underlyingBalance = underlying.balanceOf(address(this));
      uint256 underlyingValueInAsset = underlyingBalance * getUnderlyingToAssetRate();
      _totalAssets = unexchanged + assetBalance + underlyingValueInAsset;
      }
      function getUnderlyingToAssetRate() internal view returns (uint256) {
      // Fetch the exchange rate from a trusted oracle
      }
  • Consider Edge Cases:

    • Ensure that edge cases, such as extreme market conditions or oracle failures, are handled gracefully.

    • Implement fallback mechanisms if the price oracle is unavailable.

Updates

Appeal created

inallhonesty Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Incorrect accounting in `_harvestAndReport` claimable should be included

inallhonesty Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Incorrect accounting in `_harvestAndReport` claimable should be included

galturok Submitter
5 months ago
inallhonesty Lead Judge
5 months ago
inallhonesty Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

balanceDeployed() and _harvestAndReport() add WETH and alETH, but they have different prices

Support

FAQs

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