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

Missing Bounds Check in `update_profit_max_unlock_time` Function Allows Price Manipulation

Relevant GitHub Links

https://github.com/CodeHawks-Contests/2025-03-curve/blob/198820f0c30d5080f75073243677ff716429dbfd/contracts/scrvusd/oracles/ScrvusdOracleV2.vy#L334-L348

Summary

The update_profit_max_unlock_time function in ScrvusdOracleV2.vy lacks input validation, allowing the UNLOCK_TIME_VERIFIER role to drastically reduce the profit unlock time period. This can accelerate share unlocking rates by orders of magnitude, causing price calculations to become severely distorted. While the price smoothing mechanism provides some protection against sudden changes, it does not fully mitigate the economic impact of this vulnerability.

Vulnerability Details

The update_profit_max_unlock_time function allows changing a critical parameter that affects how quickly profit shares are unlocked:

@external
def update_profit_max_unlock_time(_profit_max_unlock_time: uint256, _block_number: uint256) -> bool:
access_control._check_role(UNLOCK_TIME_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
prev_value: uint256 = self.profit_max_unlock_time
self.profit_max_unlock_time = _profit_max_unlock_time
return prev_value != _profit_max_unlock_time

The function updates profit_max_unlock_time without any minimum value constraint. This parameter is critical as it directly affects the rate at which locked shares become unlocked, which in turn affects price calculations.

The vulnerability impacts price calculation through the following code path:

  1. _unlocked_shares() - calculates shares unlocked at a specific timestamp

  2. _total_supply() - subtracts unlocked shares from total supply

  3. _raw_price() - divides total assets by adjusted total supply

When profit_max_unlock_time is decreased, the calculation in _unlocked_shares() results in more shares being considered unlocked:

@view
def _unlocked_shares(
full_profit_unlock_date: uint256,
profit_unlocking_rate: uint256,
last_profit_update: uint256,
balance_of_self: uint256,
ts: uint256,
) -> uint256:
# ...
unlocked_shares: uint256 = profit_unlocking_rate * (ts - last_profit_update) // MAX_BPS_EXTENDED
# ...

Impact

The impact of changes in the unlock time is substantial:

  1. Acceleration of Share Unlocking: By changing the unlock time from the default 7 days (604,800 seconds) to a much shorter period like 1 hour (3,600 seconds), the unlocking rate would increase by approximately 168x.

  2. Price Distortion: The price calculation uses total supply minus unlocked shares as the denominator. A significant reduction in this denominator leads to an inflated price.

  3. Quantified Example:

    • If 1,000 locked shares are being unlocked over 7 days: normal rate ≈ 5.95 shares/hour

    • After change to 1 hour: rate ≈ 1,000 shares/hour

    • This dramatic acceleration significantly reduces the effective supply in price calculations

  4. Market Consequences:

    • Stableswap pools relying on this oracle would see price distortions

    • Arbitrageurs could exploit these price differences

    • LPs would suffer impermanent loss

    • If used as collateral elsewhere, it could trigger unwarranted liquidations

While the price smoothing mechanism (via _smoothed_price function) limits immediate exploitation by capping the rate of price change, it does not prevent the economic impact from materializing over time.

Tools Used

Manual code review

Recommendations

Implement bounds checking for the profit_max_unlock_time parameter.

Updates

Lead Judging Commences

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

[invalid] finding-missing-proof-content-validation

- See [here]([https://github.com/CodeHawks-Contests/2025-03-curve?tab=readme-ov-file#blockhash-oracle)](https://github.com/CodeHawks-Contests/2025-03-curve?tab=readme-ov-file#blockhash-oracle) on how it is used to verify storage variable - All state roots and proofs must be verified by the OOS `StateProofVerifier` inherited as `Verifier` (where the price values and params are extracted), so there is no proof that manipulating timestamp/inputs can affect a price update - It is assumed that the OOS prover will provide accurate data and the OOS verifier will verify the prices/max unlock time to be within an appropriate bound/values - There is a account existance check in L96 of `ScrvusdVerifierV1.sol`, in which the params for price updates are extracted from

Support

FAQs

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