Skip to content

Latest commit

 

History

History
123 lines (106 loc) · 5.23 KB

061.md

File metadata and controls

123 lines (106 loc) · 5.23 KB

Powerful Honeysuckle Anteater

High

The volatility field for borrowing can be any arbitrary value and could be used to reduce option fees

Summary

The volatility field can be arbitrarily passed into the deposit function in the borrowing.sol contract, allowing borrowers to manipulate or impact the pricing of option fees.

Root Cause

The volatility field used in option fee calculations measures the collateral's (e.g., ETH's) historical price fluctuations. Specifically, it represents the statistical standard deviation of the asset's historical returns over a chosen timeframe.

In this case, it should represent a 30-day timeframe, as noted in the NatSpec in Options.sol:

    /**
     * @dev Function to calculate option fees
     * @param _ethPrice ETH price
@>>  * @param _ethVolatility ETH volatility for 30days
     * @param _amount Depositing amount
     * @param _strikePrice Strike price chosen by user
     */
    function calculateOptionPrice(uint128 _ethPrice, uint256 _ethVolatility, uint256 _amount, StrikePrice _strikePrice)
        public
        view
        returns (uint256)
    {

The root cause is that, we could arbitrary specify that value in borrowing.sol deposit() function:

    function depositTokens(
@>>     BorrowDepositParams memory depositParam
    ) external payable nonReentrant whenNotPaused(IMultiSign.Functions(0)) {

Definition of BorrowDepositParams struct:

    struct BorrowDepositParams {
        IOptions.StrikePrice strikePercent;
        uint64 strikePrice;
@>>     uint256 volatility;
        AssetName assetName;
        uint256 depositingAmount;
    }

Then in BorrowingLib.sol:

    function deposit(
        IBorrowing.BorrowLibDeposit_Params memory libParams,
        IBorrowing.BorrowDepositParams memory params,
        IBorrowing.Interfaces memory interfaces,
        mapping(IBorrowing.AssetName => address assetAddress) storage assetAddress
    ) public returns (uint256) {
........Skip Code....
        // Call calculateOptionPrice in options contract to get options fees
        uint256 optionFees = interfaces.options.calculateOptionPrice(
@>>        libParams.ethPrice, params.volatility, params.depositingAmount, params.strikePercent
        );

And in Options.sol, we can see how it directly impacts the calculation, by directly affecting baseOptionPrice and the optionPrice variables.

  function calculateOptionPrice(uint128 _ethPrice, uint256 _ethVolatility, uint256 _amount, StrikePrice _strikePrice)
        public
        view
        returns (uint256)
    {
@>>     uint256 a = _ethVolatility;
        uint256 ethPrice = _ethPrice; /*getLatestPrice();*/
        // Get global omnichain data
        IGlobalVariables.OmniChainData memory omniChainData = globalVariables.getOmniChainData();
        // Calculate the current ETH vault value
        uint256 E = omniChainData.totalVolumeOfBorrowersAmountinUSD + (_amount * _ethPrice); 
        require(E != 0, "No borrowers in protocol");
        uint256 cdsVault;
        if (omniChainData.noOfBorrowers == 0) {
            cdsVault = omniChainData.totalCdsDepositedAmount * USDA_PRECISION;
        } else {
            // Else, get the cds vault current value from omnichain global data
            cdsVault = omniChainData.cdsPoolValue * USDA_PRECISION;
        }
        require(cdsVault != 0, "CDS Vault is zero");

        uint256 b = (cdsVault * 1e2 * OPTION_PRICE_PRECISION) / E; //1e18 * 1e2 * 1e5 / 1e8 = 1e17
@>>     uint256 baseOptionPrice = ((sqrt(10 * a * ethPrice)) * PRECISION) / OPTION_PRICE_PRECISION
            + ((3 * PRECISION * OPTION_PRICE_PRECISION) / b); 
        uint256 optionPrice;
        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;
    }

Internal pre-conditions

No response

External pre-conditions

No response

Attack Path

  1. User wants to create a deposit
  2. User specifies lower volatility value, such as 1, which is the lowest valid one. This leads to significantly less optionFees.

Impact

OptionFees are impacted and borrowers pay less, than they should.

Mitigation

Don't allow users to arbitrary pass that value, rather retrieve it from trusted source.