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

Inconsistent Handling of `total_idle` in `_obtain_price_params`

Summary

This report details a low-severity vulnerability identified in the ScrvusdOracleV2.vy contract, specifically within the _obtain_price_params function. The issue arises from the inconsistent handling of the total_idle variable, where it is not reduced when balance_of_self decreases during the unlocking process, leading to an overestimation of vault assets and a potential mismatch with real vault behavior.

Vulnerability Details

The vulnerability is located in the _obtain_price_params function of the ScrvusdOracleV2.vy contract, which calculates assumed price parameters for a given timestamp (parameters_ts) under the assumption of constant gains across distribution periods. During the for loop in this function, the balance_of_self (representing locked vault shares) is reduced as shares are unlocked, but the corresponding asset value is not subtracted from total_idle (representing liquid assets in the vault). This results in total_idle retaining a higher value than it should, misrepresenting the actual assets available in the vault.

Code Reference

@view
def _obtain_price_params(parameters_ts: uint256) -> PriceParams:
params: PriceParams = self.price_params
period: uint256 = self.profit_max_unlock_time
if params.last_profit_update + period >= parameters_ts:
return params
number_of_periods: uint256 = min(
(parameters_ts - params.last_profit_update) // period,
self.max_v2_duration,
)
gain: uint256 = (
params.balance_of_self * (params.total_idle + params.total_debt) // params.total_supply
)
params.total_idle += gain * number_of_periods
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
# ... (remaining code for updating unlocking parameters)

Issue: Before the loop, total_idle is increased by gain number_of_periods to account for profits becoming liquid. Within the loop, balance_of_self and total_supply are reduced as shares are unlocked, but total_idle remains unchanged. In a real vault (e.g., Yearn’s scrvUSD), when shares are unlocked and either burned or transferred, their equivalent asset value should be deducted from total_idle.

Expected Behavior

  • When balance_of_self decreases (e.g., from 100 to 74), the equivalent asset value of the freed shares (e.g., 26 units) should be subtracted from total_idle to reflect their removal from the vault.

Impact

  • Overestimation of Assets: The failure to reduce total_idle results in an inflated value of liquid assets in the vault. This affects the price calculation in _raw_price (total_assets / total_supply), leading to a higher price than the actual value.

  • Mismatch with Real Vault Logic: This behavior deviates from the expected operation of a vault like scrvUSD, where assets are removed from the vault when shares are unlocked, potentially misleading dependent contracts or users relying on accurate price feeds.

Tools Used

  • Manual code review

  • Vyper syntax analysis

  • No automated tools were used; the vulnerability was identified through logical inspection of variable updates and their alignment with vault mechanics.

Recommendations

Adjust total_idle in the Loop:

  • Modify the for loop to deduct the asset value of freed shares from total_idle when balance_of_self is reduced:

    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
    )
    freed_shares: uint256 = params.balance_of_self - new_balance_of_self
    params.total_idle -= freed_shares * (params.total_idle + params.total_debt) // 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
Updates

Lead Judging Commences

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

Support

FAQs

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