# Self-settlement via dispatchFrom bypasses refund mechanism allowing underfunded debt settlement

Self-settlement via dispatchFrom lets a position owner inflate balance with phantom delegate shares, so refund becomes a self-transfer and underfunded debt settles without real coverage.

- Payout: Contest reward
- Severity: medium
- Program: Panoptic
- Credited: merlinboii
- Disclosed: 2026-03-09
- Write-up quality: 7/10
- Original: https://code4rena.com/reports/2025-12-panoptic-next-core#m-12
- Library page: https://bugbountylibrary.com/r/c4%3A2025-12-panoptic-next-core%3AM-12
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:solidity technique:chained technique:source-review vuln:access-control vuln:accounting-error vuln:business-logic

**Target:** PanopticPool settlement/forceExercise with CollateralTracker delegate/refund
**Root cause:** Settlement refund logic assumes refunder != refundee; when account == msg.sender, delegate() phantom shares (~2^248) pass balance checks and refund() self-transfer injects no real value, so shortages are never funded.
**Steps:**
1. Call _settlePremium or _forceExercise with account == msg.sender via dispatchFrom.
2. CollateralTracker.delegate() inflates balanceOf[account] by ~type(uint248).max phantom shares.
3. Settlement burns X shares where X > real balance B; inflated balance passes checks.
4. refund(account, account, ...) executes as self-transfer, injecting no value.
5. revoke() repairs supply: _internalSupply += s, balanceOf[account] = 0; net supply change -B, shortage s never funded.
**How found:** Code reading of refund()'s documented assumption that refunder has enough money; noticed account == msg.sender path via dispatchFrom breaks the distinct-third-party assumption, then traced delegate/revoke supply accounting.
**Exploitation:** Position owner self-settles an underfunded debt (interest + premium) by exploiting delegate phantom shares and self-refund, extracting settlement without covering the shortage.
**Insight:** When a protocol documents an assumption in a function's natspec (e.g. 'refunder has enough money'), check whether any caller path makes refunder == refundee or otherwise violates it; self-referential calls turn transfers into no-ops.
