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

Oracle Halts on Revert

Summary

The oracle stop when invalid parameters trigger a revert freezing price updates.

Vulnerability Detail

This vulnerability occur when the oracle revert during the price update leaving the oracle in a stale state.

https://github.com/CodeHawks-Contests/2025-03-curve/blob/198820f0c30d5080f75073243677ff716429dbfd/contracts/scrvusd/verifiers/ScrvusdVerifierV1.sol#L83-L111

The verifier in ScrvusdVerifierV1 fetches scrvUSD vault parameters (total_supply, balance_of_self) from Ethereum state but does not validate them:

function \_extractParametersFromProof(.....) internal view returns (uint256\[PARAM\_CNT] memory) {
// Extracts parameters from storage slots (total\_supply at slot 20)
for (uint256 i = 1; i < PROOF\_CNT; i++) {
params\[i - 1] = slot.value; // No check for balance\_of\_self <= total\_supply
}
return params; // Invalid parameters passed to oracle
}

The oracle update_price function attempt to compute the new price using the verifier parameters:

@external
def update\_price(\_parameters: uint256\[ALL\_PARAM\_CNT], \_ts: uint256, \_block\_number: uint256) -> uint256:
# Use parameters like \_parameters\[2] (total\_supply) and \_parameters\[6] (balance\_of\_self)
self.price\_params = PriceParams() # Update state if no revert

The oracle calculate _total_supply as total_supply - unlocked_shares. If balance_of_self > total_supply, this subtraction underflow causing unnecessary revert:

@view
def \_total\_supply(p: PriceParams, ts: uint256) -> uint256:
unlocked: uint256 = self.\_unlocked\_shares(...) # Depends on balance\_of\_self
return p.total\_supply - unlocked # Revert if unlocked > total\_supply (Vyper underflow check)

The entire update_price transaction revert and the oracle retains it last valid state (price_params, last_update).

then functions like price_v0/price_v1 return outdated values.

Impact

- Outdated prices allow arbitrageurs to drain liquidity.

- Lending protocols using the oracle misprice collateral.

Tool used

Manual review

Recommendation

1. Add Parameter Validation in Verifier:

function \_extractParametersFromProof() internal view {
require(params\[6] <= params\[2], "balance\_of\_self exceeds total\_supply");
}

2. though optional but design the oracle to retain the last valid price if updates fail.

Updates

Lead Judging Commences

0xnevi Lead Judge
6 months ago
0xnevi Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Out of scope
Assigned finding tags:

[invalid] finding-division-by-zero

Note that `total_supply` and `profit_unlocking_rate` is initially set to 1 and 0 respectively when the `ScrvusdOracleV2.vy` is deployed 1. `total_supply` and `profit_unlocking_rate` is part of the price param updates within `update_price`, which must have gone through verification via the OOS `StateProofVerifier` contract, so there is no evidence that a 0 supply is allowed either via a 0 supply update or an extremely high `profit_unlocking_rate`. 2. Since price is retrieved via values retrived from the V3Vault, if there is no supply, there is arguably no price to be posted. As such, reverting is arguably the correct choice since a 0 price value is not expected from scrvUSD, which is a stable coin.

Support

FAQs

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