Skip to content

Latest commit

 

History

History
54 lines (36 loc) · 3.98 KB

File metadata and controls

54 lines (36 loc) · 3.98 KB

Clumsy Pink Otter

High

Some accounts using Intents to trade might be liquidated while healthy or be unliquidatable while being unhealthy.

Summary

When user trades using signed intents, pending order is created using the price specified by the user. Upon settlement, there is some pnl realized due to difference of intent price and market price. The issue is that this difference is added to account only when checking margin to add pending order, but when protection (liquidation) is validated, collateral doesn't include pending intent's pnl from the difference of intent and market price. As such, user can be unfairly liquidated if price moves against him but he has enough collateral (with pnl from pending intent orders). If there is a loss from intent price difference, then this loss is not added when checking liquidation either, thus liquidation will revert as if account is healthy.

Root Cause

When protection is validated, only account's collateral is checked in InvariantLib.validate: https://github.com/sherlock-audit/2025-01-perennial-v2-4-update/blob/main/perennial-v2/packages/core/contracts/libs/InvariantLib.sol#L127

Note that when position is opened, intent price adjustment is added to account's collateral: https://github.com/sherlock-audit/2025-01-perennial-v2-4-update/blob/main/perennial-v2/packages/core/contracts/libs/InvariantLib.sol#L92

Internal Pre-conditions

  • User account is close to liquidation
  • Position is opened or closed via Intent with intent price far enough from the latest oracle price (far enough - meaning the difference in price can make account safe)
  • Price moves against the user

External Pre-conditions

None.

Attack Path

Happens by itself (unfair user liquidation) or can be crafted by the user (avoid liquidation).

  • Margin ratio is 0.5%, maintenence ratio is 0.3%
  • Trading fee is 0.1%
  • User has collateral = 350 and settled position of long = 1000 at the latest oracle price of 100 (account margin ratio is 350 / (1000 * 100) = 0.35%, very close to liquidation, but still healthy)
  • User signs intent to partially close position of the size 500 at the price of 101.
  • The intent is sent on-chain and user now has pending position of long = 500. His additional profit from the intent price is 500 * (101 - 100) = 500.
  • Liquidator commits unrequested price of 99.9
  • User's collateral is now 350 + 1000 * (99.9 - 100) = 250, however since the intent is guaranteed to be executed at the price of 101, his additional profit from the intent makes the expected margin ratio for liquidation reason = (250 + 500) / (1000 * 100) = 0.75%, so account should be safe.
  • However, liquidator immediately liquidates user account, because this additional profit from the intent is ignored.
  • Besides liquidation fee, upon settlement, user pays fees to close the position from liquidation, which is 500 * 100 * 0.1% = 50, so the final user collateral is 700 instead of 750.
  • User was unfairly liquidated (as his account was healthy) and user has lost 50 / 750 = 6.7% of his funds.

Impact

  • User can be unfairly and unexpectedly liquidated and lose 1%+ of his funds (because this happens only to users with low collateral and the pnl from intent price difference will mostly be small enough, the user loss relative to collateral will be above 1%).

Mitigation

Calculate sum of price adjustments for all pending guarantees (from latest + 1 to current), and add it to collateral when validating if account can be protected (liquidated).

Additionally, since intent updates are guaranteed and can not be invalidated, consider adding intent position updates to latest position when calculating the account health for liquidation reasons to make more fair liquidations. For example, when position is closed using intent (as in the example in the Attack Path):

  • latest = 1000
  • pending (intent) = -500 Use position of size (1000 - 500 = 500) to calculate health when liquidating, so min collateral should be below 150 instead of 300 to be able to liquidate the account.