# Perpetual Protocol Curie: unguarded funding update drains vault

Unguarded OrderBook funding-growth updater let anyone inject a fabricated 1e70 twPremium into their own order's cached funding, inflating PnL to drain vault USDC.

- Payout: Real-world hack
- Severity: high
- Program: PerpetualProtocol
- Credited: DeFiHackLabs
- Disclosed: 2026-07-16
- Write-up quality: 8/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2026-10/PerpetualProtocol_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20260716-perpetualprotocol
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity technique:incomplete-fix technique:source-review vuln:access-control vuln:accounting-error vuln:business-logic

**Target:** OrderBook.updateFundingGrowthAndLiquidityCoefficientInFundingPayment on Optimism
**Root cause:** The external OrderBook.updateFundingGrowthAndLiquidityCoefficientInFundingPayment lacks the _requireOnlyClearingHouse() guard that every other state-changing entrypoint has, and applies caller-supplied fundingGrowthGlobal.twPremiumX96 to the trader's cached funding growth.
**Steps:**
1. Call `ClearingHouse.addLiquidity()` with dust order (base=2, quote=1 wei) to create a cached open order
2. Call `OrderBook.updateFundingGrowthAndLiquidityCoefficientInFundingPayment(self, baseToken, Growth(1e70, 0))` directly to poison cached funding growth
3. Call `Vault.withdraw(USDC, vault.balance)`; withdraw triggers settleAllFunding, converting poisoned cache into huge realized PnL/free collateral
4. Repeat against second market deployment in same tx, then sweep USDC
**How found:** Source review of verified OrderBook implementation: noticed every state-changing entrypoint calls _requireOnlyClearingHouse() except updateFundingGrowthAndLiquidityCoefficientInFundingPayment, whose body applies caller-supplied fundingGrowthGlobal to cached funding with no caller check.
**Exploitation:** Fresh attacker contract uses only public user paths: dust addLiquidity, direct unguarded OrderBook call with fabricated 1e70 twPremiumX96, then Vault.withdraw of full balance. settleAllFunding turns the poisoned cache into bogus funding payment inflating free collateral, letting attacker withdraw other LPs' USDC (~3,062.21 USDC across two vaults).
**Escalation:** Same attack applied to two perp deployments in one transaction, summing drained balances.
**Insight:** When auditing a contract, enumerate every external state-changing function and diff their access-control guards; a single missing _requireOnlyClearingHouse() among many guarded siblings is a high-signal anomaly, especially when the function accepts caller-supplied accounting values (funding growth, PnL) that later settle into withdrawable collateral.
