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
over 1 year ago
0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!