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

[M-1] In ScrvusdOracleV2.vy:: update_price function External calls before updating the contract's state allows price manipulation during execution leading to incorrect stored price values.

Summary

In ScrvusdOracleV2.vy:: update_price function the external calls for prices execute first before updating the contract's state for the price leads to price manipulation.

Vulnerability Details

In update_price function the self.last_prices was executed before self.last_update, meanwhile an MeV bot or an authorized user with PRICE_PARAMETERS_VERIFIER role can manipulate the price sources to their advantages by changing the price through the change of the state of Oracle before self.last_update is called. Since the state update (last_update) happens after the external prices is fetched the contract just records block.timestamp after the prices are already retrieved.

def __init__(_initial_price: uint256):
"""
@param _initial_price Initial price of asset per share (10**18)
"""
self.last_prices = [_initial_price, _initial_price, _initial_price]
self.last_update = block.timestamp
@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

Impact

Price can be exploited for personal gain and contract can operate with incorrect price

Tools Used

Manual Code Review

Recommendations

Update state first before fetching the external prices.

def __init__(_initial_price: uint256):
"""
@param _initial_price Initial price of asset per share (10**18)
"""
self.last_update = block.timestamp
self.last_prices = [_initial_price, _initial_price, _initial_price]
@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_update = block.timestamp
self.last_prices = [self._price_v0(), self._price_v1(), self._price_v2()]
Updates

Lead Judging Commences

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

Support

FAQs

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