The flow state transitions in PerpetualVault can be manipulated to bypass expected state management. It's specifies that flow states should only change from NONE, to NONE, or through cancelFlow()
, but there are paths where this is violated. This breaks the safety assumptions about position management and fund security.
flow state transitions must follow strict patterns
The flow state (NONE, DEPOSIT, SIGNAL_CHANGE, WITHDRAW, COMPOUND, LIQUIDATION) tracks critical vault operations.
The PerpetualVault contract manages these flows through various functions like deposit(), withdraw(), and run(). Each operation should:
Start from FLOW.NONE
Execute its logic
Return to FLOW.NONE
However, the current implementation allows state transitions that violate this pattern. For example, during position management, a flow could transition directly between states without proper resolution.
Core Vulnerability Surface (Primary)
Integration Points (Secondary)
Supporting Infrastructure (Tertiary)
The vulnerability flow typically starts in PerpetualVault's state management functions, propagates through GmxProxy's callbacks, and can affect position data reading through VaultReader. The interface contracts define the boundaries where these interactions occur.
When flow states transition incorrectly:
Multiple operations could execute simultaneously
Funds could be locked in incomplete states
Position management could become corrupted
User deposits/withdrawals might be blocked
The Perpetual Vault system manages complex trading operations through state flows. The flow states (NONE, DEPOSIT, SIGNAL_CHANGE, WITHDRAW, COMPOUND, LIQUIDATION) represent different critical operations in the vault. Each operation should be like a complete transaction, start from idle (NONE), execute, and return to idle. But there's a catch in how these transitions are managed.
We can see in the interactions between these state transitions, particularly in runNextAction()
where multiple state changes can occur without proper NONE state resolution.
What Actually Happens is that the PerpetualVault contract allows users to deposit USDC and take leveraged positions through GMX. When a user initiates an operation, the contract should:
Start from FLOW.NONE
Execute the operation (like deposit or position management)
Return to FLOW.NONE
But here's the error, the contract can transition between active states without properly completing operations. This means a deposit flow could directly transition to a signal change flow, breaking the core safety assumption that operations must complete atomically.
Think of it like multiple people trying to drive through an intersection simultaneously when the traffic light malfunctions. In the vault's case, this could mean:
A deposit operation could collide with position management
Withdrawal locks could be bypassed
Position leverage could be manipulated during state transitions
In this case the vault can enter multiple operational states without proper resolution.
The vulnerability manifests in the interaction between runNextAction() and position management functions. When a user deposits funds or requests a position change, the keeper executes a series of actions through GMX. During these operations, the flow state can transition incorrectly, bypassing the NONE state that should separate distinct operations.
This means that invariants around share accounting and position management can be violated. For example, during a deposit flow, if the state transitions directly to SIGNAL_CHANGE without proper resolution, the protocol's assumption about atomic operations breaks down. The keeper system, which executes these actions asynchronously, compounds this risk by introducing timing complexities.
Users' share values could be miscalculated during these improper transitions, violating the "Depositor Share Value Preservation" invariant. With leveraged positions up to 3x, even small accounting errors can have amplified effects on user positions.
Implementing strict state machine controls that enforce proper transitions through the NONE state, ensuring the protocol maintains its core invariants around share accounting and position management.
Supporting Contract Roles:
VaultReader.sol: Position data validation
GmxProxy.sol: Order execution callbacks
Interface contracts: Define strict state transition boundaries
There is no real proof, concrete root cause, specific impact, or enough details in those submissions. Examples include: "It could happen" without specifying when, "If this impossible case happens," "Unexpected behavior," etc. Make a Proof of Concept (PoC) using external functions and realistic parameters. Do not test only the internal function where you think you found something.
There is no real proof, concrete root cause, specific impact, or enough details in those submissions. Examples include: "It could happen" without specifying when, "If this impossible case happens," "Unexpected behavior," etc. Make a Proof of Concept (PoC) using external functions and realistic parameters. Do not test only the internal function where you think you found something.
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.