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

Having a fixed inaccurate initial price value enables price manipulation and breaks cross-chain price equality for a while

Summary

The ScrvusdOracleV2 contract initializes the price at a fixed value of 1 (10^18) regardless of the actual scrvUSD price on Ethereum mainnet at deployment time. This creates a significant discrepancy between the initial oracle price on the destination chain and the actual price on Ethereum, enabling price manipulation and breaking the fundamental security invariant of maintaining consistent prices across chains.

Vulnerability Details

From ScrvusdOracleV2::init()

@deploy
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
# initial raw_price is 1
self.profit_max_unlock_time = 7 * 86400 # Week by default
self.price_params = PriceParams(
total_debt=0,
total_idle=1,
total_supply=1,
full_profit_unlock_date=0,
profit_unlocking_rate=0,
last_profit_update=0,
balance_of_self=0,
)

While the init accepts an _initial_price parameter, the comment "initial raw_price is 1" and the initialization of price_params with total_idle=1 and total_supply=1 effectively sets the raw price to 1, regardless of the actual scrvUSD price on Ethereum at the time of deployment.

According to the project README, the primary purpose of this oracle is to ensure consistent pricing of scrvUSD across chains:

"To address this problem, we opted to have secondary scrvUSD markets on all chains where scrvUSD can be redeemed. Since the price of the asset is not stable, we cannot use a 'simple' stableswap-ng pool as the price of the asset would go up as the yield accrues."

The oracle's role is to "fetch scrvUSD vault parameters from Ethereum, and provide them on other chains, with the goal of being able to compute the growth rate in a safe (non-manipulable) and precise (no losses due to approximation) way."

But this has then been broken and we then have to wait until a prover sends in a valid block hash or stateroot to then update the price, this is unlike what's done in the case of the profit_max_unlock time, since it's set correctly to 7 days.

Impact

Since the actual scrvUSD price on Ethereum is not exactly 1 at deployment time (which is highly likely as scrvUSD accrues yield), there will be an immediate price discrepancy between chains, which would then allow for arbitrage opportunities where attackers can exploit the price difference between chains, potentially draining liquidity from pools on the destination chain.

The impact is particularly severe cause protocol intends to deploy on any/all EVM known chains, so now post the initial deployment period on any new EVM chain that's to be supported by the and before the first price update occurs, we have a window where the price on the destination chain is completely disconnected from the actual price on Ethereum and this all depends on how long it takes the prover to send in the update.

Tools Used

Manual review

Recommendations

The initialization should use the actual scrvUSD price from Ethereum at deployment time which the deployer should in this case be trusted and correctly provide, this can also be easily done by passing in a params array during deployment.

Alternatively, pricing should not be updated during init, and instead there should be a flag that allows for querying prices only after the first price update has occurred, in the price getters.

Updates

Lead Judging Commences

0xnevi Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

finding-hardcoded-initial-price

Appeal created

0xnevi Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

finding-hardcoded-initial-price

Support

FAQs

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