Closed c4-bot-4 closed 5 months ago
Picodes marked the issue as primary issue
True, this is the intended design. Those tokens are not completely useless - they can serve as temporary liquidity for option sellers to withdraw without needing to call settleLongPremium, and could compensate for haircut tokens (allowing them to be withdrawn) if a haircut in that chunk occured. Don't see a failure case here (where to send the tokens is a subjective question).
Picodes changed the severity to QA (Quality Assurance)
Hi @Picodes, I'd like to defend my issue.
The leftover tokens, as the report states, in most use case scenarios would serve as liquidity for future option sellers or haircut tokens. However, that is to assume this pool would be used and run forever. If the pool starts with users initially engaging but then gradually dies out, it is almost certain that some tokens will remain trapped in the pool.
I understand that the sponsors may not want to fix this issue, but I think this issue itself should be a medium severity because it would indeed cause lock of funds. The mitigation would be allowing an admin to pull out these tokens to the collateral tracker when they think this pool is dead.
@pkqs90 I still think this falls within QA as these tokens are indeed "lost" but do not really belong to anyone as they have been forfeited, so there is no real "loss of funds" here. I see the fact that these tokens cannot be saved as a consequence of the permissionless design
Lines of code
https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L1974 https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L1650-L1652
Vulnerability details
Impact
Premium payed by long option holders may be locked in PanopticPool.
Bug Description
First, we need to understand how the premium works in PanopticPool.
Short option holders earn premium by: 1. Uniswap fees, 2. Long option holder paid premium.
However, long option holders pay premium when long options are closed, or by a manual
settleLongPremium()
call. If a short option is closed before ALL the corresponding long options (minted after the short option) have paid their premiums, the short option holder won't receive the full premium they're due (unless there were unclaimed premium before). That is why short option holders are expected to callsettleLongPremium()
for some long options to increase the available premium before closing their positions.The issue here is not all short option holders will call
settleLongPremium()
before closing their positions, as it is very likely someone may simply forget to call it before closing, or choose to do so to save gas. If that happens, the short option holder will leave with some premium unclaimed, despite it having been paid by the long option holders. This unclaimed premium then accumulates within the protocol. If no future short options are created, these funds remain locked in the protocol forever.Note: This is a medium severity instead of high because the premium would be locked only if no future options are created, which is an assumption. However, if this scenario occurs, it would lead to a loss of funds, which should certainly raise attention.
Proof of Concept
In PanopticPool,
s_settledTokens
is used to keep track of the amount of collected uniswap fees and paid long option premium. We will slightly modify the unit testtest_success_settledPremiumDistribution
, to show how premium paid by long option holders are locked within the protocol.In this unit test, the following steps happen:
Modify
Misc.t.sol
to burn all positions:Modify
PanopticPool.sol
to see console logs:Running the unit test, we can see it logs
UPDATED s_settledTokens[chunkKey]= 41667
when all positions are closed, which means premium is locked in PanopticPool.Tools Used
Foundry
Recommended Mitigation Steps
A simple way to mitigate this would be to create a function in PanopticPool to move the extra
s_settledTokens
back toCollateralTracker
(have it tracked ins_poolAssets
). The extra premium could be calculated by:s_settledTokens - (grossPremiumCurrent - grossPremiumLast) * totalLiquidity
.Assessed type
Token-Transfer