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

Missing State Update for Price in update_price

Summary

The update_price function in the ScrvusdOracleV2 contract calculates a new price based on the _parameters passed to it. However, the calculated new_price is not stored in the contract’s state, meaning that the contract does not persist the latest price after each update. This causes a situation where the contract will continue using outdated price data from previous price updates.

Vulnerability Details

The update_price function computes a new price (new_price) based on the input _parameters. It compares new_price with the current price (current_price) and logs a PriceUpdate event. However, the calculated new_price is not saved to any state variables like last_prices. The last_prices array, which stores the previous prices, is not updated with the new price value. As a result, future calculations that rely on these prices will use outdated data, leading to inconsistencies in price feeds.

@external
def update_price(
_parameters: uint256[ALL_PARAM_CNT], _ts: uint256, _block_number: uint256
) -> uint256:
"""
@notice Update price using `_parameters`
@param _parameters Parameters of Yearn Vault to calculate scrvUSD price
@param _ts Timestamp at which these parameters are true
@param _block_number Block number of parameters to linearize updates
@return Absolute relative price change of final price with 10^18 precision
"""
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
@> new_price: uint256 = self._raw_price(_ts, _ts)
log PriceUpdate(new_price, _ts, _block_number)
if new_price > current_price:
return (new_price - current_price) * 10**18 // current_price
return (current_price - new_price) * 10**18 // current_price

last_prices is updated with the results of _price_v0(), _price_v1(), and _price_v2(), but the actual new_price calculated in the function is not stored in last_prices. This means that the contract will continue to use the old price values instead of the newly computed price.

Impact

The absence of a persistent record of the new price means that subsequent price calculations (such as those performed by _price_v0, _price_v1, and _price_v2) will use stale prices.

This can lead to incorrect price data being fed to external systems, potentially causing price manipulations, incorrect financial calculations, or vulnerabilities in any system relying on this contract for accurate price feeds.

Without an updated price, the contract cannot provide a consistent or accurate price for scrvUSD, which undermines the reliability of the oracle.

Tools Used

Manual Review

Recommendations

Store the calculated new_price in a state variable like last_prices so that it can be used in future calculations. For example:

self.last_prices = [new_price, new_price, new_price]
Updates

Lead Judging Commences

0xnevi Lead Judge
3 months ago
0xnevi Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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