Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: low
Invalid

Insufficient off-chain price validation potentially results in incorrect asset valuation when fulfilling swaps

Summary

The verifyOffchainPrice() function does not currently check whether the price fetched from the PremiumReport is greater than zero. This omission can lead to the use of invalid price data, resulting in incorrect asset valuations during swaps.

Vulnerability Details

The verifyOffchainPrice() function decodes a PremiumReport from verified price data and performs the following checks:

  • It ensures that the report is not expired by verifying that block.timestamp is not greater than premiumReport.validFromTimestamp + self.maxVerificationDelay.

function verifyOffchainPrice(Data storage self, bytes memory priceData) internal returns (UD60x18 priceX18) {
---SNIP---
PremiumReport memory premiumReport = abi.decode(verifiedPricetData, (PremiumReport));
// @audit-info Missing check for block.timestamp < premiumReport.validFromTimestamp
if (block.timestamp > premiumReport.validFromTimestamp + self.maxVerificationDelay) {
revert Errors.DataStreamReportExpired();
}
priceX18 = ud60x18(int256(premiumReport.price).toUint256());
}

However, it lacks a validation step to ensure that the price is valid (greater than zero) before proceeding with further calculations.

Now, notice that in fulfillSwap(), this function is invoked to get price from report and used as shown here:

// @audit-info Price fetched
>> ctx.priceX18 = stabilityConfiguration.verifyOffchainPrice(priceData);
// @audit-info Calculation here is based on the fetched price
ctx.amountIn = request.amountIn;
>> ctx.amountOutBeforeFeesX18 = getAmountOfAssetOut(ctx.vaultId, ud60x18(ctx.amountIn), ctx.priceX18);
// @audit-info gets the base fee and swap fee based on that price
>> (ctx.baseFeeX18, ctx.swapFeeX18) = getFeesForAssetsAmountOut(ctx.amountOutBeforeFeesX18, ctx.priceX18);
---SNIP---
// subtract the fees and convert the UD60x18 value to the collateral's decimals value
ctx.amountOut =
collateral.convertUd60x18ToTokenAmount(ctx.amountOutBeforeFeesX18.sub(ctx.baseFeeX18.add(ctx.swapFeeX18)));
// @audit-info slippage check
ctx.minAmountOut = request.minAmountOut;
>> if (ctx.amountOut < ctx.minAmountOut) {
revert Errors.SlippageCheckFailed(ctx.minAmountOut, ctx.amountOut);
}
---SNIP---
// @audit-info Finally, the output token are sent to the user
>> IERC20(ctx.asset).safeTransfer(user, ctx.amountOut);

If the price used in the swap is bogous, the calculated ctx.amountOutBeforeFeesX18 will be based on an incorrect price. This means that the amount of collateral assets the user receives in exchange for their USD tokens could be significantly different from what they expect.

Impact

  • The slippage check compares the expected output (ctx.amountOut) against the minimum acceptable amount (request.minAmountOut). If the price is incorrect and results in a higher ctx.amountOut, it may pass the slippage check even though the actual market conditions do not support such a price.

  • Users could end up receiving less collateral than they should have based on the valid market price.

Tools Used

Manual Review

Recommendations

Add a check to ensure that block.timestamp is not earlier than premiumReport.validFromTimestamp.

+ // @audit Check if the price is valid
+ if (premiumReport.price <= 0) {
+ revert Errors.InvalidPrice(); // New error for invalid price
+ }
// Check if the report has expired
if (block.timestamp > premiumReport.validFromTimestamp + self.maxVerificationDelay) {
revert Errors.DataStreamReportExpired();
}
priceX18 = ud60x18(int256(premiumReport.price).toUint256());
Updates

Lead Judging Commences

inallhonesty Lead Judge
6 months ago
inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

[INVALID] The `verifyOffchainPrice()` function does not currently check whether the price fetched from the `PremiumReport` is greater than zero

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.