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

Precision Inconsistency in max_change Calculation Due to Variable self.max_price_increment

Summary

The _smoothed_price function calculates max_change using self.max_price_increment, which is initialized as 2 * 10**12 (10^12 precision) but can be set between 10**8 and 10**18 via set_max_price_increment. The formula max_change = (self.max_price_increment * (block.timestamp - self.last_update) * last_price) // 10**18

assumes last_price has 10^18 precision and divides by 10**18 to normalize, but self.max_price_increment’s variable precision (10^8 to 10^18) disrupts this alignment, leading to inconsistent max_change values that don’t match the intended smoothing behavior.

Vulnerability Details

def set_max_price_increment(_max_price_increment: uint256):
"""
@notice Set maximum price increment of scrvUSD.
Must be less than StableSwap's minimum fee.
fee / (2 * block_time) is considered to be safe.
@param _max_price_increment Maximum acceleration (per sec)
"""
access_control._check_role(access_control.DEFAULT_ADMIN_ROLE, msg.sender)
@> assert 10**8 <= _max_price_increment and _max_price_increment <= 10**18
self.max_price_increment = _max_price_increment
log SetMaxPriceIncrement(_max_price_increment)

If self.max_price_increment is set to 10**8, the calculated max_change will be much smaller than expected.

  • If self.max_price_increment is set to 10**18, the division by 10**18 ensures correct scaling, but this does not hold for all values between 10**8 and 10**18. (e.g 10**12)

  • This could cause unexpectedly low or high max_change values, leading to incorrect price smoothing behavior.

def _smoothed_price(last_price: uint256, raw_price: uint256) -> uint256:
# Ideally should be (max_price_increment / 10**18) ** (block.timestamp - self.last_update)
# Using linear approximation to simplify calculations
@> max_change: uint256 = (
self.max_price_increment * (block.timestamp - self.last_update) * last_price // 10**18
)
# -max_change <= (raw_price - last_price) <= max_change
if unsafe_sub(raw_price + max_change, last_price) > 2 * max_change:
return last_price + max_change if raw_price > last_price else last_price - max_change
return raw_price

self.max_price_increment’s precision (10^8 to 10^18) directly affects max_change’s scale.

  • Initial value (2 * 10**12) is 10^6 times smaller than 10^18, reducing max_change’s magnitude significantly compared to an intended 10^18-based rate.

  • The comment suggests an exponential form (max_price_increment / 10**18) ** time, implying max_price_increment should be a rate normalized to 10^18, but the linear formula doesn’t enforce this.

Impact

  • Incorrect Smoothing: When self.max_price_increment is less than 10^18 (e.g., 2 * 10**12), max_change is scaled down by a factor of self.max_price_increment / 10**18 (e.g., 2 * 10^-6), making price changes much smaller than intended, potentially freezing prices or under-smoothing.

  • Over-Smoothing Risk: If set to 10**8, the effect is 10^10 times smaller, rendering smoothing ineffective.

  • Behavioral Variability: Different admins setting different values (e.g., 10**12 vs. 10**18) cause unpredictable smoothing, undermining reliability.

Tools Used

Manual

Recommendations

enforce a fixed precision for self.max_price_increment (e.g., always store it as 10**18-scaled values).

Updates

Lead Judging Commences

0xnevi Lead Judge
3 months ago
0xnevi Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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