A logic flaw in the _runSwap()
function of PerpetualVault.sol
can cause the protocol to become unusable when a user attempts to deposit funds. The issue prevents further deposits, withdrawals, and automated execution by the keeper, effectively denying service to the protocol.
The protocol supports two trading execution methods:
1. Trades executed on both GMX and ParaSwap
2. Trades executed on either GMX or ParaSwap exclusively
Scenario Leading to Failure
The vulnerability manifests when the vault operates at 1x leverage long and exclusively executes trades on ParaSwap. The following sequence leads to a protocol-wide failure:
Vault Setup:
• The protocol administrators deploy a vault.
• They deposit funds and open a 1x long position on ParaSwap.
User Deposit Initiates an Increase Action:
• A user deposits funds into the vault.
• Since a position is already open (positionIsClosed == false), the execution enters the else block in deposit().
• Execution fees are paid, and the next action is set to INCREASE_ACTION, with the data encoding the long position.
Keeper Executes runNextAction():
• The keeper calls runNextAction()
, which deletes the nextAction state variable after caching its value.
• Since nextAction.selector == INCREASE_ACTION, the execution proceeds to _runSwap()
.
Issue in _runSwap()
Leaves Protocol in a Stuck State:
• The vault executes the trade on ParaSwap.
• The function enters the if (flow == FLOW.DEPOSIT)
branch and mints shares to the depositor.
• However, the flow state is never reset, it remains stuck on DEPOSIT.
Resulting Failure
• flow is stuck on DEPOSIT: This blocks all further deposit(), withdraw(), and run() calls, as they require flow == NO_FLOW
.
• nextAction is empty (selector = NO_ACTION): This means runNextAction()
immediately reverts, making it impossible for the keeper to proceed.
This issue halts protocol operations because:
1. New deposits cannot be processed (deposit() fails due to ongoing DEPOSIT flow).
2. Withdrawals cannot be executed (withdraw() fails for the same reason).
3. The keeper cannot continue executing automated actions (runNextAction() always reverts wasting keeper gas).
manual review
Properly reset flow after swap completion
Likelihood: Medium/High, - Leverage = 1x - beenLong = True - positionIsClosed = False - Metadata → 1 length and Dex Swap Impact: Medium/High, DoS on any new action before the admin uses setVaultState Since this seems to be the most probable path for a 1x PerpVault, this one deserves a High.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.