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

Hardcoded Storage Slot Dependencies

Summary

The verifier contracts use hardcoded storage slots which makes them brittle to upstream contract change.

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

Vulnerability Detail

In ScrvusdVerifierV1.sol, the storage slots are hardcoded:

// 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)
];

also in ScrvusdVerifierV2.sol:

uint256 internal PERIOD_SLOT = 37; // profit_max_unlock_time

If the scrvUSD contract is upgraded and change its storage layout, these verifiers would continue to read from the wrong slots without any indication of failure.

Proof of Concept

If Curve deploys a new version of the scrvUSD vault with a different storage layout:

  1. The verifiers would continue to read from the old slots

  2. This would lead to incorrect parameters being passed to the oracle

  3. The resulting prices would be incorrect

Impact

This lead to incorrect pricing information being used across chains causing losses

Recommendation

Implement version checking, a more robust slot verification mechanism in ScrvusdVerifierV1

bytes32 constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
string public constant EXPECTED_VERSION = "3.0.4";
function verifyImplementation(bytes memory implementationProof) public view returns (bool) {
// extract the implementation address from the slot
bytes32 implementationSlot = keccak256(abi.encode(IMPLEMENTATION_SLOT));
// verify the implementation matches expected version
// implementation would need additional proof verification logic
return true; // if implementation match expected version
}
// then add a check in the verification functions:
require(verifyImplementation(implementationProof), "Invalid implementation version");

Optional:
Consider moving to a design that retrieves storage layout information directly or use a registry of known contract versions and their storage layouts.

Updates

Lead Judging Commences

0xnevi Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Out of scope
Assigned finding tags:

[invalid] finding-upgradeable-verifier-contracts

Invalid, - srCRVUSD is a minimal proxy, meaning it can never by upgraded, see [here](https://www.cyfrin.io/blog/upgradeable-proxy-smart-contract-pattern#:~:text=Minimal%20proxies%20are%20distinct%20from,provide%20upgrade%20or%20authorization%20functionality.) and [here](https://www.rareskills.io/post/eip-1167-minimal-proxy-standard-with-initialization-clone-pattern) for more info. - Even if srcrvUSD is migrated in the future via a new minimal proxy contract deployment (which is highly unlikely), the verifier contracts can be migrated along with it via revoking the access-control within the `ScrvusdOracleV2.vy` and then granting access to a new oracle. This is also not within the scope of this contest.

Support

FAQs

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