2024-12-30
*issue*
================================================================================

Borrowers can choose any volatility in order to pay less fees

================================================================================

*issue-contents*

0 CONTENTS

*issue-metadata*

1 METADATA

Number 792
Severity Medium
Author John44
Contest Autonomint
Platform Sherlock
*issue-summary*

2 SUMMARY

When borrowers deposit into borrowing.sol they have to specify a volatility parameter. It is used when calculating how much option fees they will be charged. This is problematic as depositors can choose any value in order to be charged the lowest possible number of fees.

*issue-root-cause*

3 ROOT CAUSE

In Borrowing.depositTokens the volatility parameter is porvided by the user, thus users will always set it to an amount that results in less option fees.

*issue-internal-pre-conditions*

4 INTERNAL PRE-CONDITIONS

No response

*issue-external-pre-conditions*

5 EXTERNAL PRE-CONDITIONS

No response

*issue-attack-path*

6 ATTACK PATH

  1. User deposits tokens into borrowing.sol.
  2. Before depositing they calculate which value of volatility will reduce their option fees the most.
  3. They use that value and are charged less fees then they should have been.

For example, in the protocol’s tests the following value is used for the volatility: 50622665 https://github.com/sherlock-audit/2024-11-autonomint/blob/0d324e04d4c0ca306e1ae4d4c65f0cb9d681751b/Blockchain/Blockchian/test/foundry/Borrowing.t.sol#L47

However, we can test that by setting the volatility to 10000000 the fees are significantly less.

*issue-impact*

7 IMPACT

Users can reduce the number of option fees they have to pay.

*issue-poc*

8 POC

This is a mock-up function that calculates the option fee, exactly how it is currently calculated in the protocol. Standard parameters are set in order to isolate the effects of the volatility variable. It can be ran in any environment, just make sure to do the necessary imports.

By using the paramerter for volatility used in the tests 50622665, we receive 165738482. By using 10000000, we receive 101927611.

    function calculateOptionPrice(uint256 _ethVolatility)
        public
        view
        returns (uint256)
    {
        uint128 _ethPrice = 4000e2;
        uint256 _amount = 10e18;
        StrikePrice _strikePrice = StrikePrice(1);
        uint256 a = _ethVolatility;
        uint256 ethPrice = _ethPrice; /*getLatestPrice();*/
        // Get global omnichain data
        // Calculate the current ETH vault value
        uint256 totalVolumeOfBorrowersAmountinUSD = 10e18 * _ethPrice / 1e14;

        uint256 E = totalVolumeOfBorrowersAmountinUSD + (_amount * _ethPrice);
        // Check the ETH vault is not zero
        require(E != 0, "No borrowers in protocol");
        uint256 cdsVault;
        // If the there is no borrowers in borrowing, then the cdsVault value is deposited value itself
        // Else, get the cds vault current value from omnichain global data
        cdsVault = 1000e6 * USDA_PRECISION;

        // Check cds vault is non zero
        require(cdsVault != 0, "CDS Vault is zero");
        // Calculate b, cdsvault to eth vault ratio
        uint256 b = (cdsVault * 1e2 * OPTION_PRICE_PRECISION) / E;
        uint256 baseOptionPrice = ((sqrt(10 * a * ethPrice)) * PRECISION) /
            OPTION_PRICE_PRECISION +
            ((3 * PRECISION * OPTION_PRICE_PRECISION) / b); // 1e18 is used to handle division precision //18 * 5 /

        uint256 optionPrice;
        // Calculate option fees based on strike price chose by user
        if (_strikePrice == StrikePrice.FIVE) {
            // constant has extra 1e3 and volatility have 1e8
            optionPrice =
                baseOptionPrice +
                (400 * OPTION_PRICE_PRECISION * baseOptionPrice) /
                (3 * a);
        } else if (_strikePrice == StrikePrice.TEN) {
            optionPrice =
                baseOptionPrice +
                (100 * OPTION_PRICE_PRECISION * baseOptionPrice) /
                (3 * a);
        } else if (_strikePrice == StrikePrice.FIFTEEN) {
            optionPrice =
                baseOptionPrice +
                (50 * OPTION_PRICE_PRECISION * baseOptionPrice) /
                (3 * a);
        } else if (_strikePrice == StrikePrice.TWENTY) {
            optionPrice =
                baseOptionPrice +
                (10 * OPTION_PRICE_PRECISION * baseOptionPrice) /
                (3 * a);
        } else if (_strikePrice == StrikePrice.TWENTY_FIVE) {
            optionPrice =
                baseOptionPrice +
                (5 * OPTION_PRICE_PRECISION * baseOptionPrice) / //5 * x /
                (3 * a);
        } else {
            revert("Incorrect Strike Price");
        }
        return ((optionPrice * _amount) / PRECISION) / USDA_PRECISION; //x*18/30 = 6 -> x = 18
    }

    function testCalculateOptionPrice(
        uint256 _ethVolatility
    ) public view returns (uint256) {
        //set up mockup omni chain data
        uint256 noOfBorrowers = 1;
        uint256 totalCdsDepositedAmount = 10000e6;
        uint256 cdsPoolValue = 10000e6;
        StrikePrice _strikePrice = StrikePrice(1);
        uint256 _amount = 1e18;
        uint256 ethPrice = 4000e2;
        uint256 totalVolumeOfBorrowersAmountinUSD = 10e18 * ethPrice;
        uint256 a = _ethVolatility;

        // Calculate the current ETH vault value
        uint256 E = totalVolumeOfBorrowersAmountinUSD + (_amount * ethPrice);
        // Check the ETH vault is not zero
        require(E != 0, "No borrowers in protocol");
        uint256 cdsVault;
        // If the there is no borrowers in borrowing, then the cdsVault value is deposited value itself
        if (noOfBorrowers == 0) {
            cdsVault = totalCdsDepositedAmount * USDA_PRECISION;
        } else {
            // Else, get the cds vault current value from omnichain global data
            cdsVault = cdsPoolValue * USDA_PRECISION;
        }

        // Check cds vault is non zero
        require(cdsVault != 0, "CDS Vault is zero");
        // Calculate b, cdsvault to eth vault ratio
        uint256 b = (cdsVault * 1e2 * OPTION_PRICE_PRECISION) / E;
        uint256 baseOptionPrice = ((sqrt(10 * a * ethPrice)) * PRECISION) / OPTION_PRICE_PRECISION + ((3 * PRECISION * OPTION_PRICE_PRECISION) / b); // 1e18 is used to handle division precision

        uint256 optionPrice;
        // Calculate option fees based on strike price chose by user
        if (_strikePrice == StrikePrice.FIVE) {
            // constant has extra 1e3 and volatility have 1e8
            optionPrice = baseOptionPrice + (400 * OPTION_PRICE_PRECISION * baseOptionPrice) / (3 * a);
        } else if (_strikePrice == StrikePrice.TEN) {
            optionPrice = baseOptionPrice + (100 * OPTION_PRICE_PRECISION * baseOptionPrice) / (3 * a);
        } else if (_strikePrice == StrikePrice.FIFTEEN) {
            optionPrice = baseOptionPrice + (50 * OPTION_PRICE_PRECISION * baseOptionPrice) / (3 * a);
        } else if (_strikePrice == StrikePrice.TWENTY) {
            optionPrice = baseOptionPrice + (10 * OPTION_PRICE_PRECISION * baseOptionPrice) / (3 * a);
        } else if (_strikePrice == StrikePrice.TWENTY_FIVE) {
            optionPrice = baseOptionPrice + (5 * OPTION_PRICE_PRECISION * baseOptionPrice) / (3 * a);
        } else {
            revert("Incorrect Strike Price");
        }
        return ((optionPrice * _amount) / PRECISION) / USDA_PRECISION;
    }

    function sqrt(uint256 y) internal pure returns (uint256 z) {
        if (y > 3) {
            z = y;
            uint256 x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }
}
*issue-mitigation*

9 MITIGATION

volatility should not be derived from user-input, but instead be set by an admin depending on the actual ETH volatility.

================================================================================

LINKS

*issue-links*