A malicious distressed/liquidatable-account holder can DOS the liquidator's TX of the PanopticPool#liquidate()
to prevent their account from a liquidation
#313
Labels
bug
Something isn't working
downgraded by judge
Judge downgraded the risk level of this issue
edited-by-warden
grade-c
primary issue
Highest quality submission among a set of duplicates
QA (Quality Assurance)
Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax
🤖_31_group
AI based duplicate group recommendation
sponsor disputed
Sponsor cannot duplicate the issue, or otherwise disagrees this is an issue
unsatisfactory
does not satisfy C4 submission criteria; not eligible for awards
Lines of code
https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L1032
https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L1035-L1036
Vulnerability details
Impact
Within the PanopticPool contract, the
MAX_TWAP_DELTA_LIQUIDATION
would be defined and513
(approximately5%
) is set to it like this:https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L156
Within the PanopticPool#
liquidate()
, it would be checked whether or not the price delta between two prices below would exceed theMAX_TWAP_DELTA_LIQUIDATION
:twapTick
, which is the TWAP and retrieved via thegetUniV3TWAP()
currentTick
, which is the spot price and retrieved via the UniswapV3Pool#slot0()
https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L1012
https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L1026
https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L1032
https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L1035-L1036
Based on the "slot0" in the UniswapV3's documentation, both the current tick (
currentTick
) and the current price (sqrtPriceX96
) would be returned from the UniswapV3Pool#slot0
.The current price (
sqrtPriceX96
) would be a spot price. And the current tick (currentTick
) would usually follow the current price (sqrtPriceX96
).According to the NatSpec of the PanopticPool#
liquidate()
, the TX of the PanopticPool#liquidate()
would be reverted - if TWAP tick (twapTick
) is too far away from the current tick (currentTick
) like this:Since the current tick (
currentTick
)-retrieved via the UniswapV3Pool#slot0()
would usually follow the current price (sqrtPriceX96
)-retrieved via the UniswapV3Pool#slot0()
, thecurrentTick
would usually follow a spot price (sqrtPriceX96
).Hence, a malicious actor would easily manipulate the
currentTick
by sandwiching a victim liquidator's TX of the PanopticPool#liquidate()
with directly swapping a large amount of token0/token1 on the target UniswapV3Pool in a single block.As a result, the victim liquidator's TX of the PanopticPool#
liquidate()
can be reverted in the validation below:https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L1035-L1036
Because, when the victim liquidator's TX of the PanopticPool#
liquidate()
would be executed right after the malicious actor would dramatically inflate/deflate the current price (sqrtPriceX96
) of the target UniswapV3 Pool, the current tick (currentTick
) of the target UniswapV3 Pool would also dramatically be moved in the same block, the price delta between two prices (currentTick - twapTick
) would exceed theMAX_TWAP_DELTA_LIQUIDATION
.Further more, according to the "Options Traders" part in the documentation, any ERC20 token can be used in the Panoptic Protocol.
It means that lower liquidity UniswapV3 Pool would be available on the Panoptic Protocol.
And therefore, lower liquidity UniswapV3 Pool would relatively be easier for a malicious actor to manipulate the
currentTick
(spot price) by exceeding theMAX_TWAP_DELTA_LIQUIDATION
(approximately5%
) at lower cost.NOTE:
The vulnerabllity that the current price (
sqrtPriceX96
) of the UniswapV3 Pool is easily manipulated would be proven in the past findings like this:Proof of Concept
NOTE:
Based on the "slot0" in the UniswapV3's documentation, both the current tick (
currentTick
) and the current price (sqrtPriceX96
) would be returned from the UniswapV3Pool#slot0
. And the current tick (currentTick
) would usually follow the current price (sqrtPriceX96
).Let's say there are 2 actors:
Attack scenario:
1/ Alice would call the PanopticPool#
liquidate()
to liquidate Bob's distressed/liquidatable account.2/ Bob would monitor the TX of the step 1/ and he would front-run it with directly swapping a large amount of token0 for token1 on the UniswapV3Pool, which is the target UniswapV3Pool that Alice attempted to liquidate when the step 1/.
3/ Right after, Bob would submit another TX that back-run the TX of the step 1/ with directly swapping a large amount of token1 for token0 on the UniswapV3Pool, which is as opposite direction of the swap to the step 2/ above.
4/ Bob's TX (step 2/) would be executed first.
sqrtPriceX96
) of the target UniswapV3 Pool has been dramatically moved (inflated /or deflated). Hence, the current tick (currentTick
) of the target UniswapV3 Pool has also been dramatically moved.5/ Alice's TX would be executed second.
currentTick
) has been dramatically moved when the step 4/, this her TX would be reverted - due to exceeding theMAX_TWAP_DELTA_LIQUIDATION
(approximately5%
) inside the validation in the PanopticPool#liquidate()
like this:sqrtPriceX96
) and the current tick (currentTick
) of the target UniswapV3 Pool has been back to the original price and tick when the step 1/.Tools Used
Recommended Mitigation Steps
Within the PanopticPool#
liquidate()
, consider increasing theMAX_TWAP_DELTA_LIQUIDATION
from approximately 5% (513
) to much higher value. (i.e. 30% (3000
)).Assessed type
DoS
The text was updated successfully, but these errors were encountered: