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

Exponential Total Supply Reduction in Price Projection

1. Summary

  • Severity: High

  • Category: Mathematical/Logic Error

  • Impact: Artificially inflated price calculations that can lead to pool draining

  • Likelihood: High for extended time projections


2. Affected Code

@view
def _obtain_price_params(parameters_ts: uint256) -> PriceParams:
# ...
# functions are reduced from `VaultV3._process_report()` given assumptions with constant gain
for _: uint256 in range(number_of_periods, bound=MAX_V2_DURATION):
new_balance_of_self: uint256 = (
params.balance_of_self
* (params.total_supply - params.balance_of_self) // params.total_supply
)
params.total_supply -= (
params.balance_of_self * params.balance_of_self // params.total_supply
)
params.balance_of_self = new_balance_of_self
# ...
  • Contract: ScrvusdOracleV2.vy

  • Function: _obtain_price_params


3. Vulnerability Details

Root Cause

The _obtain_price_params function progressively reduces total_supply in each iteration of the loop without proper bounds or sanity checks. With each period, the total_supply is reduced by (params.balance_of_self * params.balance_of_self // params.total_supply), which can cause it to decrease exponentially over multiple periods.

Attack Scenario

  1. Over extended projection periods, the total_supply can become very small

  2. When _raw_price later calculates self._total_assets(parameters) * 10**18 // self._total_supply(parameters, ts), the diminished denominator causes an artificially inflated price

  3. An attacker can exploit the inaccurate price by trading against the pool that relies on this oracle

  4. The pool can be drained through arbitrage due to the mispricing, causing significant loss to liquidity providers


4. Proof of Concept (PoC)

def demonstrate_supply_reduction():
# Initial values
total_supply = 10**18
balance_of_self = 10**17 # 10% of total supply
periods = 20
print(f"Initial: total_supply={total_supply}, balance_of_self={balance_of_self}")
for i in range(periods):
new_balance_of_self = (
balance_of_self * (total_supply - balance_of_self) // total_supply
)
total_supply -= (
balance_of_self * balance_of_self // total_supply
)
balance_of_self = new_balance_of_self
print(f"Period {i+1}: total_supply={total_supply}, balance_of_self={balance_of_self}")
# Calculate price assuming constant assets of 10**18
price = 10**18 * 10**18 // total_supply
print(f"Price: {price} (vs initial 1.0: {price/10**18}x)")
# Results will show exponential decrease in total_supply and increase in price

5. Recommended Fix

Proposed Solution

@view
def _obtain_price_params(parameters_ts: uint256) -> PriceParams:
# ...
original_total_supply = params.total_supply
min_allowed_supply = original_total_supply // 10 # Prevent reduction below 10% of original
for _: uint256 in range(number_of_periods, bound=MAX_V2_DURATION):
new_balance_of_self: uint256 = (
params.balance_of_self
* (params.total_supply - params.balance_of_self) // params.total_supply
)
supply_reduction = min(
(params.balance_of_self * params.balance_of_self // params.total_supply),
(params.total_supply - min_allowed_supply) # Prevent going below minimum
)
params.total_supply -= supply_reduction
params.balance_of_self = new_balance_of_self
# ...

Alternative Mitigation Strategies

  • Implement a hard minimum cap on total_supply to prevent division by very small numbers

  • Reduce max_v2_duration significantly to limit the compounding effect

  • Use a different mathematical model that doesn't reduce the supply exponentially


6. Severity Justification

  • Impact: High - The vulnerability can lead to drastically incorrect price calculations, enabling malicious actors to drain pools relying on this oracle. As stated in the README: "If not precise enough, this can lead to MEV in the liquidity pool, at a loss for the liquidity providers. Even worse, if someone is able to manipulate this rate, it can lead to the pool being drained from one side."

  • Likelihood: High - The issue will manifest deterministically when projecting prices for longer durations, which is exactly what the price_v2 function is designed to do.

Updates

Lead Judging Commences

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

Support

FAQs

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