The vault keeps track of the current flow via the flow state variable. Due to the PerpetualVault:_noneFlow:L1258-1262 function, deposit, withdraw, and run functions cannot be called while flow is not NONE (0).
The flow is set to none after a current flow is finalized, and this can happen in different ways depending on the vault and which flow is the current one. For example, a PerpetualVault:deposit call will run _finalize if the positionIsClosed variable is true, meaning the vault has no active position. PerpetualVault:_finalize:L1208-1218 will reset the flow for a deposit like this:
However if the vault uses DEX swaps only for a deposit, the flow will not be reset to none. And there is no next action to run for the keeper, resulting in the _noneFlow function limiting the actions that can be performed on the vault by users and keepers, until the keeper can clear the flow via setting the state manually using either of the functions: PerpetualVault:cancelFlow:L1220-1242 or PerpetualVault:setVaultState:L681-697
PerpetualVault:afterOrderCancellation:L592-623 which is a callback function for gmx positions so not relevant for deposits using dex only.
PerpetualVault:_handleReturn:L1129-1156 which is only called in withdrawal cases (flow == FLOW.WITHDRAW) so not relevant either.
PerpetualVault:_finalize:L1208-1218 which are only called from deposit if there is no current position. Or runNextAction if the next action is FINALIZE, and the afterOrderExecution which again is a gmx callback function.
PerpetualVault:_updateState:L1186-1206 which is called from afterOrderExecution function which again is a gmx callback. It is also done in the _runSwap function but only if the flow is not withdraw or deposit.
The _runSwap function which is referenced above, will be called (in the case of a deposit in a vault with an active position) by the runNextAction function. The withdraw flow will be cleared as it calls, _handleReturn, but the deposit flow will just call _mint which does not clear the flow. As an example we can see how in the PerpetualVault:deposit:L215-243 function, in the case there is no active position, and we can complete the deposit without any next action, the call to _mint is followed by calling _finalize to clear the flow.
As the _runSwap is a result of the keeper calling the runNextAction and the execution ends with _mint without any clearing of flow state/finalizing, or setting any nextAction for keeper to call again to finalize, the vault will be stuck in the deposit flow with no next action for keeper to run.
Add this in test/PerpetualVault.t.sol
The likelihood of this is high as every deposit into a 1x vault where the keeper will use only DEX swaps (ParaSwap) will result in a flow not being reset.
The impact is medium as this needs to be manually resolved by either calling the cancelFlow function to reset it, but this will cancel the deposit and the depositor will need to try again, resulting in the same outcome if the keeper does not decide to use GMX in addition. The other way to handle this is to call setVaultState, but doing this every time this happens can be error-prone as so much of the state for the vault is set using this single call. Also, taking into account the time it can take the keeper to notice this, this issue will cause a significant disruption to the functioning of the vaults where only DEX swaps are used frequently.
Manual review
Call finalize after mint in the _runSwap function to finalize the flow.
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.