Panoptic - tpiliposian's results

Effortless options trading on any token, any strike, any size.

General Information

Platform: Code4rena

Start Date: 27/11/2023

Pot Size: $60,500 USDC

Total HM: 7

Participants: 72

Period: 7 days

Judge: Picodes

Total Solo HM: 2

Id: 309

League: ETH

Panoptic

Findings Distribution

Researcher Performance

Rank: 70/72

Findings: 1

Award: $11.32

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

11.3163 USDC - $11.32

Labels

bug
downgraded by judge
grade-b
primary issue
QA (Quality Assurance)
sponsor disputed
Q-36

External Links

Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/f75d07c345fd795f907385868c39bafcd6a56624/contracts/SemiFungiblePositionManager.sol#L130-L135 https://github.com/code-423n4/2023-11-panoptic/blob/f75d07c345fd795f907385868c39bafcd6a56624/contracts/SemiFungiblePositionManager.sol#L1250-L1329

Vulnerability details

Impact

The SemiFungiblePositionManager.sol contract employs a fixed parameter, VEGOID, assigned a constant value of 2: uint128 private constant VEGOID = 2 used in calculating premiums associated with liquidity utilization. This parameter acts as a multiplier, adjusting the sensitivity of premium calculations to changes in liquidity, akin to Vega in options.

In financial terms, Vega (or VEGOID in this context) measures the sensitivity of an option's price to changes in implied volatility. However, the fixed nature of VEGOID overlooks the dynamic nature of market conditions where volatility (liquidity sensitivity) can vary significantly.

Implied volatility (IV) is a key determinant in options pricing, representing the market's estimation of an asset's future volatility. A higher IV signifies increased uncertainty regarding an asset's price, usually leading to higher option premiums. Vega, in turn, measures an option's price sensitivity concerning a 1% change in implied volatility. It is pivotal for assessing an option's potential to gain value before expiration.

https://www.sciencedirect.com/topics/mathematics/implied-volatility#:~:text=Implied%20volatility%20is%20calculated%20by,price%20of%20the%20option%2C%20for

https://corporatefinanceinstitute.com/resources/derivatives/vega/

So, the fixed VEGOID parameter overlooks the dynamic nature of implied volatility, leading to significant repercussions in options pricing:

  • Failure to adjust the VEGOID parameter for changing market conditions could result in mispriced premiums, potentially causing financial losses for users.
  • Inflexibility in accounting for varying implied volatility levels may lead to risks associated with options.
  • Market fluctuations may cause underestimation or overestimation of premiums, leading to potential mispricing of protocol or user assets.

Proof of Concept

    uint128 private constant VEGOID = 2;

The function that updates the Owed and Gross account liquidities:

    function _getPremiaDeltas(
        uint256 currentLiquidity,
        int256 collectedAmounts
    ) private pure returns (uint256 deltaPremiumOwed, uint256 deltaPremiumGross) {
        // extract liquidity values
        uint256 removedLiquidity = currentLiquidity.leftSlot();
        uint256 netLiquidity = currentLiquidity.rightSlot();

        // premia spread equations are graphed and documented here: https://www.desmos.com/calculator/mdeqob2m04
        // explains how we get from the premium per liquidity (calculated here) to the total premia collected and the multiplier
        // as well as how the value of VEGOID affects the premia
        // note that the "base" premium is just a common factor shared between the owed (long) and gross (short)
        // premia, and is only seperated to simplify the calculation
        // (the graphed equations include this factor without separating it)
        unchecked {
            uint256 totalLiquidity = netLiquidity + removedLiquidity;

            uint128 premium0X64_base;
            uint128 premium1X64_base;

            {
                uint128 collected0 = uint128(collectedAmounts.rightSlot());
                uint128 collected1 = uint128(collectedAmounts.leftSlot());

                // compute the base premium as collected * total / net^2 (from Eqn 3)
                premium0X64_base = Math
                    .mulDiv(collected0, totalLiquidity * 2 ** 64, netLiquidity ** 2)
                    .toUint128();
                premium1X64_base = Math
                    .mulDiv(collected1, totalLiquidity * 2 ** 64, netLiquidity ** 2)
                    .toUint128();
            }

            {
                uint128 premium0X64_owed;
                uint128 premium1X64_owed;
                {
                    // compute the owed premium (from Eqn 3)
                    uint256 numerator = netLiquidity + (removedLiquidity / 2 ** VEGOID);

                    premium0X64_owed = Math
                        .mulDiv(premium0X64_base, numerator, totalLiquidity)
                        .toUint128();
                    premium1X64_owed = Math
                        .mulDiv(premium1X64_base, numerator, totalLiquidity)
                        .toUint128();

                    deltaPremiumOwed = uint256(0).toRightSlot(premium0X64_owed).toLeftSlot(
                        premium1X64_owed
                    );
                }
            }

            {
                uint128 premium0X64_gross;
                uint128 premium1X64_gross;
                {
                    // compute the gross premium (from Eqn 4)
                    uint256 numerator = totalLiquidity ** 2 -
                        totalLiquidity *
                        removedLiquidity +
                        ((removedLiquidity ** 2) / 2 ** (VEGOID));
                    premium0X64_gross = Math
                        .mulDiv(premium0X64_base, numerator, totalLiquidity ** 2)
                        .toUint128();
                    premium1X64_gross = Math
                        .mulDiv(premium1X64_base, numerator, totalLiquidity ** 2)
                        .toUint128();
                    deltaPremiumGross = uint256(0).toRightSlot(premium0X64_gross).toLeftSlot(
                        premium1X64_gross
                    );
                }
            }
        }
    }

Tools Used

Manual review.

Short term: Implement an adjustable VEGOID within defined limits, allowing authorized entities to update it based on observed market conditions. Long Term: Develop algorithms to adaptively adjust VEGOID concerning observed liquidity changes or shifts in market volatility and establish transparent guidelines for VEGOID updates and educate stakeholders about its pivotal role in pricing accuracy, e.g here:

https://panoptic.xyz/research/streamia-vs-black-scholes

Assessed type

Other

#0 - c4-judge

2023-12-14T16:40:06Z

Picodes marked the issue as primary issue

#1 - c4-sponsor

2023-12-14T18:24:39Z

dyedm1 (sponsor) disputed

#2 - dyedm1

2023-12-14T18:29:26Z

This seems like a feature request rather than an actual issue. Although this mechanism is indeed described as similar to vega in options to help readers get an idea, the intended effect is to keep the liquidity utilization in check by increasing the premium multiplier when there is more demand for options buying than selling. Additionally, there is a disclaimer about the opinionated nature of this mechanism, noting that it may not be a fit for all protocols: https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L282

As long as the relationship between the premium multiplier and liquidity utilization holds as documented, there is no issue here.

#3 - c4-judge

2023-12-20T13:29:27Z

Picodes changed the severity to QA (Quality Assurance)

#4 - c4-judge

2023-12-26T23:14:51Z

Picodes marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter