DeFiLayer 1Layer 2
14,723 OP
View results
Submission Details
Severity: medium
Invalid

Oracle updates prices with stale data, leading to incorrect smoothening

Summary

The update_price function in ScrvusdOracleV2.vy calculates and stores last_prices based on old parameters before updating to new parameters. However, it sets the last_update timestamp to the current block timestamp. This inconsistency causes the smoothening mechanism to use incorrect baseline prices with current timestamps, potentially allowing faster-than-intended price movements and creating arbitrage opportunities.

Vulnerability Details

In the update_price function, the contract stores the last prices for smoothening before updating the price parameters:

@external
def update_price(
_parameters: uint256[ALL_PARAM_CNT], _ts: uint256, _block_number: uint256
) -> uint256:
access_control._check_role(PRICE_PARAMETERS_VERIFIER, msg.sender)
# Allowing same block updates for fixing bad blockhash provided (if possible)
assert self.last_block_number <= _block_number, "Outdated"
self.last_block_number = _block_number
self.last_prices = [self._price_v0(), self._price_v1(), self._price_v2()]
self.last_update = block.timestamp
ts: uint256 = self.price_params_ts
current_price: uint256 = self._raw_price(ts, ts)
self.price_params = PriceParams(
total_debt=_parameters[0],
total_idle=_parameters[1],
total_supply=_parameters[2],
full_profit_unlock_date=_parameters[3],
profit_unlocking_rate=_parameters[4],
last_profit_update=_parameters[5],
balance_of_self=_parameters[6],
)
self.price_params_ts = _ts

The issue is that:

  1. The contract calculates last_prices using old parameters

  2. It sets last_update to the current timestamp

  3. It then updates the parameters

This creates a mismatch where old prices are stored with a current timestamp. When the smoothening function calculates the allowed change, it uses this timestamp to determine how much time has passed:

@view
def _smoothed_price(last_price: uint256, raw_price: uint256) -> uint256:
max_change: uint256 = (
self.max_price_increment * (block.timestamp - self.last_update) * last_price // 10**18
)
if unsafe_sub(raw_price + max_change, last_price) > 2 * max_change:
return last_price + max_change if raw_price > last_price else last_price - max_change
return raw_price

Impact

This inconsistency can lead to:

  1. Incorrect Price Transitions: The smoothening mechanism will allow larger price movements than intended because it calculates the time difference from an incorrect starting point.

  2. Potential Arbitrage Opportunities: Systems that rely on this oracle for pricing (like stableswap-ng pools) could be exploited through arbitrage when prices move faster than intended.

  3. Compounding Errors: Multiple rapid updates could exacerbate this issue, potentially causing significant price drift in a short time.

The impact is medium because it affects core oracle functionality and could indirectly put funds at risk in systems that rely on this oracle's accuracy, though it requires specific conditions to exploit.

Tools Used

Manual code review

Recommendations

Update the order of operations to calculate last_prices after updating parameters:

# First update parameters
self.price_params = PriceParams(...)
self.price_params_ts = _ts
# Then calculate and store last prices
self.last_prices = [self._price_v0(), self._price_v1(), self._price_v2()]
self.last_update = block.timestamp
Updates

Lead Judging Commences

0xnevi Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-last_profit_update-used-instead-timestamp

- Sponsor Comments - State root oracles usually do not provide block.timestamp, so it's simply not available. That is why last_profit_update is intended. - In `update_price`, this value must be a future block, meaning this update is a state checked and allowed by the OOS verifier contracts. The impact is also increasingly limited given price is smoothen and any updates via the block hash `verifyScrvusdByBlockHash` can also update the prices appropriately, meaning the price will likely stay within safe arbitrage range aligning with protocol logic

Appeal created

bauchibred Auditor
5 months ago
0xnevi Lead Judge
4 months ago
0xnevi Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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