VaultReader contract allows positions to have non-zero USD size while having zero token size, breaking a fundamental size consistency. This can lead to incorrect position valuations and potentially affect the entire vault's accounting. The issue occurs in the VaultReader's position size tracking where GMX storage can end up with inconsistent state between USD and token denominated position sizes. When a position is created or modified, the contract assumes that if sizeInUsd is positive, sizeInTokens must also be positive but this invariant can be violated due to missing validation in the position management logic. We expects that any position with sizeInUsd > 0 must have sizeInTokens > 0. However, there's a scenario where shows
The question is - How can the position size in tokens be zero while having positive USD value?
The issue stems from the position size tracking in GMX's storage where:
SIZE_IN_USD and SIZE_IN_TOKENS are tracked separately
No invariant enforcement between these values at the storage level
Position updates can modify one value without properly updating the other
Imagine an account showing $1000 in value but 0 actual dollars in the vault, this is essentially what's happening in our VaultReader contract. Positions can end up with non-zero USD size but zero token size, which should be mathematically impossible.
When users interact with the PerpetualVault, they're expecting their position sizes to be consistently tracked both in USD and tokens. The VaultReader contract serves as the source of truth for these values, pulling data from GMX storage. However, there's a critical gap in the validation chain. getPositionSizeInUsd(), getPositionInfo()
The contract expects that any position with value (sizeInUsd > 0) must have actual tokens backing it (sizeInTokens > 0). But because SIZE_IN_USD and SIZE_IN_TOKENS are tracked independently in GMX's storage, they can become desynchronized.
When PerpetualVault calculates position values or executes trades, it relies on VaultReader for accurate position data. With inconsistent sizes:
Position PnL calculations could return incorrect values
Leverage calculations become unreliable
Risk management checks could be bypassed
For example, if a vault has $10,000 in sizeInUsd but 0 sizeInTokens, the getPositionInfo() function would attempt calculations using these inconsistent values, potentially affecting all users in that vault.
The PerpetualVault relies on VaultReader for critical calculations like PnL and leverage. With inconsistent sizes, these calculations become unreliable or can revert.
The whitepaper's position management section assumes atomic updates
GMX's storage layout allows independent size updates
The VaultReader was trusted to always return consistent values
The VaultReader contract, which serves as the system's eyes into GMX positions, can report positions having USD value without any actual tokens backing them creating phantom value in the system.
When a position is opened through the PerpetualVault, it should always maintain consistency between its USD size and token size. Think of it like a forex trading account, if you have $1000 worth of EUR/USD position, you must have some actual euros in your position. But our VaultReader allows a state where sizeInUsd shows 1000 while sizeInTokens is 0, mathematically impossible in a healthy system.
The consequences are precise and severe. When calculating position values in getPositionInfo(), the contract uses these sizes to determine PnL and collateral requirements. With phantom positions, a vault showing $100,000 in positions might have zero actual backing tokens, leading to incorrect share price calculations and potentially allowing users to withdraw more value than the vault actually holds.
Enforce size consistency at the validation level
This validation should be integrated into all functions in PerpetualVault.sol that modify or read position data through VaultReader, ensuring consistent position sizing throughout the protocol's operations.
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.