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.
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:
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.
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.
Manual Review
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.
- 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.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.