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

Incorrect Storage Slot Calculation for `balance_of_self` in Verifier

Summary

The ScrvusdVerifierV1 contract computes the storage slot for the vault's balance_of_self incorrectly by swapping the order of the mapping key and the mapping slot.

Instead of using keccak256(abi.encode(SCRVUSD, 18)) (where SCRVUSD is the vault's address and 18 is the storage slot), the code uses keccak256(abi.encode(18, SCRVUSD)). This error leads to the extraction of an incorrect balance, which in turn miscalculates the total supply and reported price.

Vulnerability Details

In Solidity, the storage location for a mapping value is computed using keccak256(abi.encode(key, slot)), where key is the mapping key (in this case, the vault's address, SCRVUSD) and slot is the designated storage slot number (here, assumed to be 18 for the balance mapping).

// Storage slots of parameters
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)
];

The verifier defines the slot for balance_of_self as follows:

uint256(keccak256(abi.encode(18, SCRVUSD))) // balanceOf(self)

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

According to storage layout for mappings, the correct slot should be computed as:

uint256(keccak256(abi.encode(SCRVUSD, 18)))

where the key (vault address) comes first and the mapping slot second.

By swapping the order, the verifier reads from an incorrect storage location, causing it to obtain a wrong value for balance_of_self. This value is subsequently used to derive critical parameters such as total_supply for the vault, directly affecting the computation of the oracle's raw_price.

Impact

In protocols like stableswap pools that rely on the oracle's price, such mispricing can be exploited to execute profitable arbitrage attacks or even drain the pool, resulting in significant financial losses for liquidity providers. Given the direct influence of the oracle price on asset redemption rates, the miscalculation poses a risk of irreversible economic damage even if all privileged roles are assumed to be trusted.

Tools Used

Manual Review

Recommendations

Update the storage slot computation in the verifier to:

// Storage slots of parameters
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)
+ uint256(keccak256(abi.encode(SCRVUSD, 18)))
];
Updates

Lead Judging Commences

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

[invalid] finding-ScrvusdVerifierV1-incorrect-storage-slot-balanceOf-compute

- Per sponsor comments, verified slot is vyper, solidity contract only verifies it. - Vyper computes storage slots different from solidity as seen [here](https://ethereum.stackexchange.com/questions/149311/storage-collision-in-vyper-hashmap)

Support

FAQs

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