When a position is open and a user deposits tokens, if the keeper’s off-chain metadata consists of a single element (i.e. metadata.length == 1
) with protocol set to DEX
, the deposit flow proceeds through the _runSwap() function. In this branch, the contract calls _mint()
without subsequently finalizing the flow (i.e. without calling _finalize()
). As a result, the flow remains active and is never reset, so any future call to deposit, which begins by checking _noneFlow() will revert
. This effectively creates a denial-of-service (DoS) condition for deposit, withdrawal, and run() calls.
During positionIsClosed == false
, user deposits some tokens, now keeper runs- runNextAction()
, then flow goes into- _runSwap(metadata, true, prices)
, from there if metadata of length==1, then it goes into below else block-
here it mints tokens, but never calls _finalize()
- which is responsible to set flow again to None. And it creates a denial-of-service state.
DOS
since keeper do many transcations and can make any length of metadata from 0 - 2, so its much likely that signals can make metadata.length==1 with protocol==DEX.
Manual Review
add a _finalize()
call immediately after _mint(...)
in the branch handling metadata.length == 1
.
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.