Summary
The _validatePrice function is responsible for validating market prices before allowing operations in the perpetual vault. However, the function incorrectly uses prices.indexTokenPrice for validating marketData.longToken, instead of using the correct prices.longTokenPrice.
This issue results in incorrect price validation and could lead to unsafe or invalid operations due to mismatched token prices.
Vulnerability Details
function _validatePrice(address perpVault, MarketPrices memory prices) internal view {
(
,
int256 answer,
uint256 startedAt,
,
) = AggregatorV2V3Interface(sequencerUptimeFeed).latestRoundData();
bool isSequencerUp = answer == 0;
require(isSequencerUp, "sequencer is down");
uint256 timeSinceUp = block.timestamp - startedAt;
require(timeSinceUp > GRACE_PERIOD_TIME, "Grace period is not over");
address market = IPerpetualVault(perpVault).market();
IVaultReader reader = IPerpetualVault(perpVault).vaultReader();
MarketProps memory marketData = reader.getMarket(market);
_check(marketData.indexToken, prices.indexTokenPrice.min);
_check(marketData.indexToken, prices.indexTokenPrice.max);
@> _check(marketData.longToken, prices.indexTokenPrice.min);
@> _check(marketData.longToken, prices.indexTokenPrice.max);
_check(marketData.shortToken, prices.shortTokenPrice.min);
_check(marketData.shortToken, prices.shortTokenPrice.max);
}
The function mistakenly applies prices.indexTokenPrice when validating marketData.longToken instead of using prices.longTokenPrice. The long token should be validated using prices.longTokenPrice.min and prices.longTokenPrice.max, but the function mistakenly references prices.indexTokenPrice.min and prices.indexTokenPrice.max.
Impact
If the index token price significantly differs from the actual long token price, the contract may allow invalid trades or reject valid ones.
This can result in incorrect liquidations or potential loss of funds.
Tools Used
Manual Review
Recommendations
Fix the incorrect price reference in _validatePrice by replacing:
function _validatePrice(address perpVault, MarketPrices memory prices) internal view {
// L2 Sequencer check
(
/*uint80 roundID*/,
int256 answer,
uint256 startedAt,
/*uint256 updatedAt*/,
/*uint80 answeredInRound*/
) = AggregatorV2V3Interface(sequencerUptimeFeed).latestRoundData();
bool isSequencerUp = answer == 0;
require(isSequencerUp, "sequencer is down");
// Make sure the grace period has passed after the sequencer is back up.
uint256 timeSinceUp = block.timestamp - startedAt;
require(timeSinceUp > GRACE_PERIOD_TIME, "Grace period is not over");
address market = IPerpetualVault(perpVault).market();
IVaultReader reader = IPerpetualVault(perpVault).vaultReader();
MarketProps memory marketData = reader.getMarket(market);
_check(marketData.indexToken, prices.indexTokenPrice.min);
_check(marketData.indexToken, prices.indexTokenPrice.max);
- _check(marketData.longToken, prices.indexTokenPrice.min);
- _check(marketData.longToken, prices.indexTokenPrice.max);
+ _check(marketData.longToken, prices.longTokenPrice.min);
+ _check(marketData.longToken, prices.longTokenPrice.max);
_check(marketData.shortToken, prices.shortTokenPrice.min);
_check(marketData.shortToken, prices.shortTokenPrice.max);
}