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

Current `MAX_V2_DURATION` curbs price growth projection at ~ 10% earlier than it should

Summary

The ScrvusdOracleV2 contract incorrectly calculates the MAX*V2_DURATION constant, which is intended to represent 4 years in terms of weekly periods. The current implementation uses 192 weeks (4 * 12 _ 4), which assumes 48 weeks per year instead of the standard 52 weeks. This discrepancy results in premature limitation of price growth projection, affecting the accuracy of long-term price calculations.

Vulnerability Details

Take a look at ScrvusdOracleV2.vy#L57

MAX_V2_DURATION: constant(uint256) = 4 * 12 * 4 # 4 years

The comment indicates that this constant is meant to represent 4 years, but the calculation uses 4 _ 12 _ 4 = 192 weeks. A standard year has approximately 52 weeks (365 days / 7 days per week), so 4 years should be represented as 4 * 52 = 208 weeks.

This constant is used in the _obtain_price_params function to limit how far into the future the price projection can go:

number_of_periods: uint256 = min(
(parameters_ts - params.last_profit_update) // period,
self.max_v2_duration,
)

The max_v2_duration value is set in the constructor to "half a year" (4 * 6 = 24 weeks), but can be updated through the set_max_v2_duration function, which enforces that the new value doesn't exceed MAX_V2_DURATION.

Impact

The incorrect calculation of MAX_V2_DURATION means that price growth projection is limited to 192 weeks instead of the intended 208 weeks (4 years) when we inted to use the real max and this results in:

  • Premature limitation of price growth projection by approximately 16 weeks ~ 10% of the whole duration

  • Most crucially is the fact that this would cause for a deviation of the growth pattern on the destination chain than what is on mainnet which is a security invariant for this protocol.

Tools Used

Manual review

Recommendations

- MAX_V2_DURATION: constant(uint256) = 4 * 12 * 4 # 4 years
+ MAX_V2_DURATION: constant(uint256) = 4 * 52 # 4 years (52 weeks per year)

In the same light the current v2 duration if intended to be 6 months should be set to the below:

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

@deploy
def __init__(_initial_price: uint256):
#snip
# 2 * 10 ** 12 is equivalent to
# 1) 0.02 bps per second or 0.24 bps per block on Ethereum
# 2) linearly approximated to max 63% APY
self.max_price_increment = 2 * 10**12
- self.max_v2_duration = 4 * 6 # half a year
+ self.max_v2_duration = 26 # half a year
access_control.__init__()
access_control._set_role_admin(PRICE_PARAMETERS_VERIFIER, access_control.DEFAULT_ADMIN_ROLE)
access_control._set_role_admin(UNLOCK_TIME_VERIFIER, access_control.DEFAULT_ADMIN_ROLE)
Updates

Lead Judging Commences

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

[invalid] finding-MAX_V2_DURATION

This is simply an approximation. I don't believe there is any incorrect logic here, given as long as this duration of growth is consistently applied, there will arguably be no incorrect oracle prices here. Additionally, I highly doubt there will be a instance where 48 weeks has passed since the last update.

Appeal created

bauchibred Submitter
5 months ago
0xnevi Lead Judge
5 months ago
0xnevi Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Design choice
Assigned finding tags:

[invalid] finding-MAX_V2_DURATION

This is simply an approximation. I don't believe there is any incorrect logic here, given as long as this duration of growth is consistently applied, there will arguably be no incorrect oracle prices here. Additionally, I highly doubt there will be a instance where 48 weeks has passed since the last update.

Support

FAQs

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