DeFiLayer 1Layer 2
14,723 OP
View results
Submission Details
Severity: low
Valid

Timestamp Discrepancy in Verification Methods of ScrvusdVerifierV1

Summary

In ScrvusdVerifierV1.sol, the two verification methods verifyScrvusdByBlockHash and verifyScrvusdByStateRoot pass different timestamps to the update_price function of ScrvusdOracleV2. The former uses the Ethereum block header timestamp (block_header.timestamp), while the latter uses the scrvUSD vault’s last_profit_update (params[5]). This discrepancy can lead to minor variations in the oracle’s price calculations due to differences in how unlocked shares and parameter adjustments are computed, particularly when the block time and last_profit_update diverge.

Vulnerability Details

The issue arises from inconsistent timestamp arguments passed to the oracle:

  • In verifyScrvusdByBlockHash :

function verifyScrvusdByBlockHash(
bytes memory _block_header_rlp,
bytes memory _proof_rlp
) external returns (uint256) {
Verifier.BlockHeader memory block_header = Verifier.parseBlockHeader(_block_header_rlp);
require(block_header.hash != bytes32(0), "Invalid blockhash");
require(
block_header.hash == IBlockHashOracle(BLOCK_HASH_ORACLE).get_block_hash(block_header.number),
"Blockhash mismatch"
);
uint256[PARAM_CNT] memory params = _extractParametersFromProof(block_header.stateRootHash, _proof_rlp);
return _updatePrice(params, block_header.timestamp, block_header.number);
}

Timestamp: block_header.timestamp - the Ethereum block’s timestamp when the scrvUSD state was captured.

  • In verifyScrvusdByStateRoot :

function verifyScrvusdByStateRoot(
uint256 _block_number,
bytes memory _proof_rlp
) external returns (uint256) {
bytes32 state_root = IBlockHashOracle(BLOCK_HASH_ORACLE).get_state_root(_block_number);
uint256[PARAM_CNT] memory params = _extractParametersFromProof(state_root, _proof_rlp);
return _updatePrice(params, params[5], _block_number); // Use last_profit_update as the timestamp surrogate
}

Timestamp: params[5] - the last_profit_update value from the scrvUSD vault’s storage (slot 40), indicating the last profit update time

Impact

  • Price Variation

Tools Used

  • Manual Code Analysis

Recommendations

  • Use block_header.timestamp universally in the code.

Updates

Lead Judging Commences

0xnevi Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-last_profit_update-used-instead-timestamp

- Sponsor Comments - State root oracles usually do not provide block.timestamp, so it's simply not available. That is why last_profit_update is intended. - In `update_price`, this value must be a future block, meaning this update is a state checked and allowed by the OOS verifier contracts. The impact is also increasingly limited given price is smoothen and any updates via the block hash `verifyScrvusdByBlockHash` can also update the prices appropriately, meaning the price will likely stay within safe arbitrage range aligning with protocol logic

Support

FAQs

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