Panoptic - JecikPo's results

Permissionless, perpetual options trading on any token, any strike, any size.

General Information

Platform: Code4rena

Start Date: 01/04/2024

Pot Size: $120,000 USDC

Total HM: 11

Participants: 55

Period: 21 days

Judge: Picodes

Total Solo HM: 6

Id: 354

League: ETH

Panoptic

Findings Distribution

Researcher Performance

Rank: 13/55

Findings: 1

Award: $2,050.64

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: pkqs90

Also found by: 0xStalin, Aymen0909, DanielArmstrong, JecikPo, bin2chen

Labels

bug
3 (High Risk)
satisfactory
upgraded by judge
:robot:_98_group
duplicate-497

Awards

2050.6445 USDC - $2,050.64

External Links

Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/PanopticPool.sol#L1635

Vulnerability details

Impact

When PanopticPool.settleLongPremium() is called it supposed to change the accounting of the shares on the CollateralTracker so that the long holders` shares (based on the calculated premium) are burned. Instead due to incorrect sign they are minted.

Proof of Concept

The settleLongPremium() function calculates the would-be-accumulated premium for a long option based on the premiumAccumulatorX obtained through SFPM.getAccountPremium(). Those accumulators are then adjusted by the s_options[owner][tokenId][legIndex] value which holds the last used accumulators. The difference is multiplied by the liquidity:

            LeftRightSigned realizedPremia = LeftRightSigned
                .wrap(0)
                .toRightSlot(int128(int256((accumulatedPremium.rightSlot() * liquidity) / 2 ** 64)))
                .toLeftSlot(int128(int256((accumulatedPremium.leftSlot() * liquidity) / 2 ** 64)));

This realizedPremia is then used to exercise is on each CollateralToken using s_collateralTokenX.exercise().

The problem is that it is a positive value, and the positive value on exercise() will cause the shares to be minted instead of burned, because exercise() reverts the sign:

int256 tokenToPay = -realizedPremium;

Then the following happens:

           } else if (tokenToPay < 0) {
                // if user must receive tokens, mint them
                uint256 sharesToMint = convertToShares(uint256(-tokenToPay));
                _mint(optionOwner, sharesToMint);
            }

Tools Used

Manual Review

The realizedPremium should be coming to the exercise() as negative:

PanopticPool.sol:

-            s_collateralToken0.exercise(owner, 0, 0, 0, realizedPremia.rightSlot());
-            s_collateralToken1.exercise(owner, 0, 0, 0, realizedPremia.leftSlot());
+            s_collateralToken0.exercise(owner, 0, 0, 0, -realizedPremia.rightSlot());
+            s_collateralToken1.exercise(owner, 0, 0, 0, -realizedPremia.leftSlot());

Assessed type

Math

#0 - c4-judge

2024-04-23T11:44:59Z

Picodes marked the issue as duplicate of #376

#1 - c4-judge

2024-04-30T21:47:40Z

Picodes changed the severity to 3 (High Risk)

#2 - c4-judge

2024-04-30T21:47:44Z

Picodes marked the issue as satisfactory

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