In the scrvUSD Verifier contract (V1), one of the critical parameters needed to update the cross-chain oracle is the scrvUSD contract’s own balance (i.e., “balanceOf(self)”). This value is obtained from a mapping stored in the scrvUSD contract. However, the verifier incorrectly computes the storage key for this mapping by reversing the order of parameters.
Faulty Code:
The verifier defines the storage slot for retrieving its own balance as:
Here, 18
is intended to be the mapping’s slot number and SCRVUSD
is the key (the contract’s address). According to Solidity’s standard, the correct storage key for a mapping is computed as:
In this case, the correct computation should be:
By swapping the order, the verifier is looking up a completely different key in the scrvUSD contract’s storage.
For a mapping declared in Solidity such as:
stored at slot p
(here, p = 18
), the value for a key k
(in our case, SCRVUSD
) is stored at:
That is, the key (k
) comes first, followed by the slot (p
). Any deviation from this order will yield an incorrect storage location.
In the verifierV1.sol contract, the array of storage slot identifiers is defined as:
For plain variables (such as total_debt
, etc.), this contract should use the direct padded representation. For a mapping entry like balanceOf(self)
, the key must be computed as:
instead of
Incorrect Balance Extraction:
Because the computed key does not match the actual key where the scrvUSD balance is stored, the state proof will not locate the correct balance value. In a typical proof (e.g., using eth_getProof
), the balance is stored under keccak256(abi.encode(SCRVUSD, 18))
. The verifier, however, will look for a storage entry under the incorrect key, leading it to return a default value (often zero).
Impact on Oracle Price Computation:
The scrvUSD balance is a vital parameter in calculating the vault’s total assets and, ultimately, the price per share. An incorrect balance (e.g., zero instead of the actual balance) would lead to a miscalculation of the scrvUSD price. This can cause either:
Denial of Update: The oracle may reject the proof if critical parameters are missing.
Price Manipulation: An attacker might intentionally supply a crafted state proof for the incorrect key to force an arbitrarily low (or otherwise manipulated) balance, causing the oracle to update with erroneous data.
Severity: High.
The scrvUSD balance is integral to computing the overall vault valuation. An error here directly affects the oracle’s price, which in turn can lead to significant financial losses in any dependent pool.
Exploitation Complexity:
An attacker does not need to compromise any cryptographic primitives; they simply need to provide a state proof and benefit from the verifier’s incorrect storage key lookup. Even if the honest prover supplies correct data, the verifier will still extract an incorrect value (likely 0).
Impact:
Mispricing in the oracle can lead to significant arbitrage opportunities.
A manipulated price could destabilize the stableswap pool, draining liquidity or causing cascading economic failures.
State of the scrvUSD Vault:
Assume the scrvUSD contract stores its own balance in a mapping at slot 18. The correct key for the balance of scrvUSD itself is:
and let’s say the actual balance is a nonzero value XX.
Verifier’s Incorrect Key Calculation:
The verifier instead calculates:
This yields a different 32‑byte value that does not exist in the scrvUSD contract’s storage. Therefore, when the state proof is processed, the extracted balance value defaults to 0.
Oracle Update with Incorrect Data:
The verifier then calls the oracle’s update_price
function with the array of parameters, where the balance value is now incorrect (zero). The oracle, using these parameters, computes the price per share using a formula similar to:
If the balance component is part of total_assets
(or affects profit calculations), the computed price will be misrepresented.
Exploitation by an Attacker:
An attacker (or a malicious prover) could intentionally craft a state proof that supplies an artificially low balance value (or simply rely on the verifier’s error to return zero) to force the oracle into updating the scrvUSD price to a manipulated value.
For example, if the oracle updates the price using the faulty balance, the scrvUSD price could be set much lower than intended, allowing an attacker to buy scrvUSD at a discounted rate on a stableswap pool and then later sell it when the price corrects.
Expected Correct Proof:
A legitimate state proof (from an Ethereum archive node) returns the balance value XX under the key:
The verifier should extract XX if it used the correct key.
Verifier’s Behavior:
The verifier uses:
Since the scrvUSD contract’s storage does not contain an entry under faultyKey
, the RLP proof extraction returns a default value (0).
Outcome:
The oracle receives parameters with the scrvUSD balance set to 0.
This leads to an incorrect price computation (for instance, if total assets or profit-related calculations rely on the balance).
Demonstration:
An attacker could supply a valid state proof that—when processed by the verifier’s flawed logic—yields a zero balance.
The resulting mispricing on the target chain can be exploited via arbitrage on a stableswap pool. For example, the pool might temporarily reflect a scrvUSD price so low that a trader could buy large quantities at an underpriced rate, profiting when the price corrects.
Update the computation of the storage key for the scrvUSD balance. Change:
to:
This correction ensures that the state proof correctly retrieves the actual balance stored under the proper key.
- 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)
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.