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

Incorrect Storage Slot Mapping in Verifier Leading to Price Manipulation

Summary

The Solidity verifier for scrvUSD assumes hardcoded storage slot positions for the Vyper oracle’s PriceParams struct that do not necessarily match Vyper’s actual sequential storage layout. This misalignment can lead to the extraction of incorrect parameters, which in turn may cause the oracle to compute an inaccurate price, enabling attackers to manipulate the price feed and potentially drain stableswap pools.

Vulnerability Details

https://github.com/CodeHawks-Contests/2025-03-curve/blob/main/contracts/scrvusd/verifiers/ScrvusdVerifierV1.sol#L32-#L41

uint256[PROOF_CNT] internal PARAM_SLOTS = [
uint256(0), // filler for account proof
uint256(21), // total_debt
uint256(22), // total_idle
uint256(20), // totalSupply
uint256(38), // full_profit_unlock_date
uint256(39), // profit_unlocking_rate
uint256(40), // last_profit_update
uint256(keccak256(abi.encode(18, SCRVUSD))) // balanceOf(self) <- this being incorrect is a separate issue
];

The verifier’s PARAM_SLOTS array hardcodes specific slots for struct members. For example, it assumes:
total_debt is stored at slot 21
total_idle is stored at slot 22
total_supply is stored at slot 20

Other fields such as full_profit_unlock_date, profit_unlocking_rate, and last_profit_update are similarly mapped, and balance_of_self is derived via keccak256(abi.encode(18, SCRVUSD)).

In Vyper, a struct’s members are allocated sequentially in storage. If the PriceParams struct starts at slot n, its members will be in slots n (total_debt), n+1 (total_idle), n+2 (total_supply), and so on. The hardcoded values in the verifier may therefore not reflect the actual slot offsets in the deployed Vyper contract, especially when considering inherited variables or the ordering of state variables.

Reference: https://jtriley.substack.com/p/the-vyper-compiler?open=false#§storage-layout

If the verifier reads from incorrect storage slots, it will extract invalid parameter values. For instance, if the actual total_supply is located at slot n+2 but the verifier reads slot 20, the resulting value will be erroneous. Such inaccuracies directly affect the oracle’s raw_price computation.

Impact

The vulnerability directly undermines the integrity of the oracle’s price feed. Even if trusted roles are assumed, a wrong storage slot mapping leads to inaccurate price calculations and significant financial risk for dependent protocols.

Tools Used

Manual Review

Recommendations

Use Vyper’s storage inspection tools or compile the contract with detailed metadata to precisely determine the starting slot and sequential offsets for the PriceParams struct.

Modify the PARAM_SLOTS array in the verifier to reflect the actual storage layout. For example, if price_params starts at slot n, the mapping should be updated to:

PARAM_SLOTS = [
0, // filler for account proof
n, // total_debt
n+1, // total_idle
n+2, // total_supply
n+3, // full_profit_unlock_date
n+4, // profit_unlocking_rate
n+5, // last_profit_update
uint256(keccak256(abi.encode(n+6, SCRVUSD))) // balance_of_self (if applicable)
];
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.