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

Missing Slot Existence Check in ScrvusdVerifierV1

Summary

The ScrvusdVerifierV1 contract, specifically in the _extractParametersFromProof function, does not verify whether storage slots exist before assigning their values to the params array.

The StateProofVerifier.extractSlotValueFromProof function returns a SlotValue struct with an exists flag, but V1 only uses slot.value without checking slot.exists. In Ethereum, uninitialized or missing storage slots default to 0. If a slot is missing due to an incorrect proof, a vault storage layout change, or an error, the contract silently assigns 0 to the corresponding parameter (e.g., totalSupply, total_debt). This contrasts with ScrvusdVerifierV2, which explicitly checks slot.exists for the profit_max_unlock_time slot.

Vulnerability Details

In the ScrvusdVerifierV1 contract, there’s a function called _extractParametersFromProof that pulls specific data (like total_debt, total_idle, or totalSupply) from storage slots in the scrvUSD vault. It does this using a helper library called StateProofVerifier, which checks the vault’s storage based on state proof.

The library returns a SlotValue structure for each slot, which has two key pieces of information:

  • value: The actual data stored in the slot (e.g., a number like 100 for total_debt).

  • exists: A flag (true or false) that tells us whether the slot was actually found in the vault’s storage.

Here’s the problem: in Ethereum, if a storage slot has never been used or written to, its value is automatically 0. The ScrvusdVerifierV1 code takes the value from each slot and uses it without checking the exists flag. For example:

// Extract slot values
uint256[PARAM_CNT] memory params;
for (uint256 i = 1; i < PROOF_CNT; i++) {
Verifier.SlotValue memory slot = Verifier.extractSlotValueFromProof(
keccak256(abi.encode(PARAM_SLOTS[i])),
account.storageRoot,
proofs[i].toList()
);
// Slots might not exist, but typically we just read them.
@> params[i - 1] = slot.value;
}

This means:

  • If the slot exists and has a real value (say, 50), it works fine and assigns 50.

  • But if the slot doesn’t exist (because it’s not part of the vault’s storage or the proof is incomplete), the value will still be 0, and the code will blindly assign 0 to the parameter.

This affects all calls to verifyScrvusdByBlockHash and verifyScrvusdByStateRoot, potentially compromising the reliability of the scrvUSD oracle system.

Impact

The contract doesn’t stop or throw an error when a slot is missing. It quietly assigns 0 and keeps going. This makes it hard to spot the problem until something breaks downstream like users noticing weird prices.

Tools Used

Manual Review

Recommendations

Add explicit checks for slot existence in the _extractParametersFromProof function, similar to ScrvusdVerifierV2. This ensures the contract only proceeds with verified data and reverts if any slot is missing.

Updates

Lead Judging Commences

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

[invalid] finding-slot-not-check-verifierv1-v2

- Looking at the OOS `StateProofVerifier` and `MerklePatriciaProofVerifier` contract that extracts the slot, the `exists` flag will be flagged as true as long as a non-zero length value is returned as seen [here](https://github.com/curvefi/curve-xdao/blob/3ff77bd2ccc9c88d50ee42d2a746fc7648c7ff2c/contracts/libs/StateProofVerifier.sol#L133C13-L136). From the `MerklePatriciaProofVerifier.extractProofValue`, the minimum length returned will be 1 as represenetd by `bytes(0)`. So this seems to be purely a sanity check that might not even be required. - A slot with zero values is only allowed when the proof provided by the prover correctly proofs that such values are included within the Merkle-Patricia-Tree. The values fetched from mainnet from the V3Vault stored in the merkle trie is likely checked before hand and aggregated into the MerkleTree.

Support

FAQs

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