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

The Calculation of SCRVUSD_HASH Does Not Comply with Ethereum State Trie

Summary

Vulnerability Details

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

A more subtle, common, yet frequently overlooked error is that "the accountKey of a contract address within Ethereum's State Trie is not simply keccak256(abi.encodePacked(address))."

You might see something similar to the following implementation in your ScrvusdVerifierV1 contract:

constructor(address _block_hash_oracle, address _scrvusd_oracle) {
BLOCK_HASH_ORACLE = _block_hash_oracle;
SCRVUSD_ORACLE = _scrvusd_oracle;
SCRVUSD_HASH = keccak256(abi.encodePacked(_scrvusd_oracle));
}

Then, when verifying a proof, you might use:

account = Verifier.extractAccountFromProof(
SCRVUSD_HASH, // <-- Using the "hash" computed above
stateRoot,
proofs[0].toList()
);

However, in Ethereum’s State Trie (Merkle-Patricia Trie), the key corresponding to a specific address is calculated differently. Specifically, it's obtained by first RLP-encoding the address (with the prefix 0x94 followed by the 20-byte address) and then computing its keccak256 hash. It's NOT computed directly via abi.encodePacked(address).

A classic and authoritative example is documented in the Ethereum Yellow Paper’s description of how accounts' Merkle-Patricia Trie keys are generated. Or, alternatively, you can refer to numerous tutorials on State Proofs. They consistently demonstrate that when constructing an Account Proof for an address 0xABCD…, the corresponding key in the trie is:

keccak256( RLP.encode(0xABCD...) )

Moreover, RLP.encode(0xABCD...) is NOT equal to abi.encodePacked(0xABCD...). The RLP encoding adds a length prefix (typically 0x94 followed by the 20-byte address itself), whereas abi.encodePacked simply concatenates these 20 bytes directly without any additional metadata.

Therefore, if ScrvusdVerifierV1.SCRVUSD_HASH is not computed according to Ethereum's official RLP encoding method, the subsequent call to extractAccountFromProof(...) would almost certainly fail or, worse, yield insecure results. It fundamentally attempts to access an account at an incorrect path in the State Trie.

Impact

Tools Used

Recommendations

Updates

Lead Judging Commences

0xnevi Lead Judge
3 months ago
0xnevi Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Lack of quality

Support

FAQs

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