# Missing hook call will lead to incorrect oracle results

Omnipool remove_token withdraws protocol liquidity without calling on_liquidity_changed, leaving oracle and circuit breaker stale.

- Payout: Contest reward
- Severity: medium
- Program: HydraDX
- Credited: J4X
- Disclosed: 2024-04-10
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2024-02-hydradx-findings/issues/51
- Library page: https://bugbountylibrary.com/r/c4%3A2024-02-hydradx%3AM-09
- Tags: domain:web3 impact:integrity kind:contest platform:code4rena sev:medium tech:rust technique:source-review vuln:business-logic vuln:oracle-manipulation

**Target:** Substrate pallet omnipool remove_token extrinsic
**Root cause:** remove_token transfers protocol-owned reserve out of the pool but never invokes T::OmnipoolHooks::on_liquidity_changed, unlike withdraw_protocol_liquidity and add_token.
**Steps:**
1. Read pallet omnipool lib.rs remove_token (call_index 12).
2. Note it transfers asset_state.reserve to beneficiary and removes asset.
3. Compare with withdraw_protocol_liquidity which calls `T::OmnipoolHooks::on_liquidity_changed(origin, info)?`.
4. Observe no hook call in remove_token, so oracle/circuit breaker never see the liquidity decrease.
**How found:** Code-reading comparison: author noticed withdraw_protocol_liquidity calls the liquidity hook while remove_token, which also moves protocol liquidity, does not; sponsor disputed, judge re-reviewed and confirmed add_token calls the hook but remove_token does not.
**Escalation:** Stale circuit-breaker accounting: a prior 90-token removal unrecorded lets a later 20-token withdrawal pass a 100-token per-block limit, undermining the safety limit; oracle prices computed on removed liquidity.
**Insight:** When auditing hook-based oracle/limit systems, diff every state-mutating function against siblings that call the same hook; functions that move value but skip the notification (especially admin-only removal paths) silently desync accounting.
