The two verifier contracts work together to securely update and maintain the scrvUSD oracle using on-chain state proofs.
ScrvusdVerifierV1 extracts scrvUSD vault parameters (such as total debt, idle funds, supply, and profit unlocking metrics) from state proofs. It validates these parameters by verifying the block header or state root against the BlockHashOracle and then updates the scrvUSD oracle’s price via its update_price (see oracle documentation) function.
ScrvusdVerifierV2 focuses specifically on updating the profit unlocking duration (profit_max_unlock_time). It uses similar state proof techniques—verifying either an RLP-encoded block header or a state root—to extract the period value. This period is then sent to the scrvUSD oracle via the update_profit_max_unlock_time function.
Together, these contracts ensure that the scrvUSD oracle remains accurate by securely integrating verified on-chain data.
This function verifies scrvUSD parameters using an RLP-encoded block header and a corresponding state proof. It parses the block header to ensure the BlockHash is valid and matches the expected value from the BlockHashOracle, then extracts the scrvUSD vault parameters from the state proof. It then updates the scrvUSD oracle with these parameters, returning the absolute relative price change scaled to precision.
Returns: absolute relative price change of the scrvUSD price.
Input
Type
Description
_block_header_rlp
bytes
RLP-encoded block header containing block details
_proof_rlp
bytes
RLP-encoded state proof for the scrvUSD parameters
Source code
// SPDX-License-Identifier: MITpragma solidity0.8.18;import{RLPReader}from"hamdiallam/Solidity-RLP@2.0.7/contracts/RLPReader.sol";import{StateProofVerifierasVerifier}from"../../xdao/contracts/libs/StateProofVerifier.sol";uint256constantPARAM_CNT=2+5;uint256constantPROOF_CNT=1+PARAM_CNT;interfaceIScrvusdOracle{functionupdate_price(uint256[PARAM_CNT]memory_parameters,uint256_ts,uint256_block_number)externalreturns(uint256);}interfaceIBlockHashOracle{functionget_block_hash(uint256_number)externalviewreturns(bytes32);functionget_state_root(uint256_number)externalviewreturns(bytes32);}contractScrvusdVerifierV1{usingRLPReaderforbytes;usingRLPReaderforRLPReader.RLPItem;// Common constantsaddressconstantSCRVUSD=0x0655977FEb2f289A4aB78af67BAB0d17aAb84367;bytes32constantSCRVUSD_HASH=keccak256(abi.encodePacked(SCRVUSD));// Storage slots of parametersuint256[PROOF_CNT]internalPARAM_SLOTS=[uint256(0),// filler for account proofuint256(21),// total_debtuint256(22),// total_idleuint256(20),// totalSupplyuint256(38),// full_profit_unlock_dateuint256(39),// profit_unlocking_rateuint256(40),// last_profit_updateuint256(keccak256(abi.encode(18,SCRVUSD)))// balanceOf(self)];addresspublic immutableSCRVUSD_ORACLE;addresspublic immutableBLOCK_HASH_ORACLE;constructor(address_block_hash_oracle,address_scrvusd_oracle){BLOCK_HASH_ORACLE=_block_hash_oracle;SCRVUSD_ORACLE=_scrvusd_oracle;}/// @param _block_header_rlp The RLP-encoded block header/// @param _proof_rlp The state proof of the parametersfunctionverifyScrvusdByBlockHash(bytesmemory_block_header_rlp,bytesmemory_proof_rlp
)externalreturns(uint256){Verifier.BlockHeadermemoryblock_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]memoryparams=_extractParametersFromProof(block_header.stateRootHash,_proof_rlp);return_updatePrice(params,block_header.timestamp,block_header.number);}/// @dev Extract parameters from the state proof using the given state root.function_extractParametersFromProof(bytes32stateRoot,bytesmemoryproofRlp
)internalviewreturns(uint256[PARAM_CNT]memory){RLPReader.RLPItem[]memoryproofs=proofRlp.toRlpItem().toList();require(proofs.length==PROOF_CNT,"Invalid number of proofs");// Extract account proofVerifier.Accountmemoryaccount=Verifier.extractAccountFromProof(SCRVUSD_HASH,stateRoot,proofs[0].toList());require(account.exists,"scrvUSD account does not exist");// Extract slot valuesuint256[PARAM_CNT]memoryparams;for(uint256i=1;i<PROOF_CNT;i++){Verifier.SlotValuememoryslot=Verifier.extractSlotValueFromProof(keccak256(abi.encode(PARAM_SLOTS[i])),account.storageRoot,proofs[i].toList());// Slots might not exist, but typically we just read them.params[i-1]=slot.value;}returnparams;}/// @dev Calls the oracle to update the price parameters./// Both child contracts use the same oracle call, differing only in how they obtain the timestamp.function_updatePrice(uint256[PARAM_CNT]memoryparams,uint256ts,uint256number)internalreturns(uint256){returnIScrvusdOracle(SCRVUSD_ORACLE).update_price(params,ts,number);}}
This function verifies scrvUSD parameters by retrieving the state root for a given block number from the block hash oracle and then extracting the scrvUSD vault parameters using a state proof. The extracted parameters are used to update the scrvUSD oracle, returning the absolute relative price change scaled to 10^18 precision.
Returns: The absolute relative price change of the scrvUSD price.
Input
Type
Description
_block_number
uint256
Block number for which to retrieve the state root
_proof_rlp
bytes
RLP-encoded state proof for the scrvUSD parameters
Source code
// SPDX-License-Identifier: MITpragma solidity0.8.18;import{RLPReader}from"hamdiallam/Solidity-RLP@2.0.7/contracts/RLPReader.sol";import{StateProofVerifierasVerifier}from"../../xdao/contracts/libs/StateProofVerifier.sol";uint256constantPARAM_CNT=2+5;uint256constantPROOF_CNT=1+PARAM_CNT;interfaceIScrvusdOracle{functionupdate_price(uint256[PARAM_CNT]memory_parameters,uint256_ts,uint256_block_number)externalreturns(uint256);}interfaceIBlockHashOracle{functionget_block_hash(uint256_number)externalviewreturns(bytes32);functionget_state_root(uint256_number)externalviewreturns(bytes32);}contractScrvusdVerifierV1{usingRLPReaderforbytes;usingRLPReaderforRLPReader.RLPItem;// Common constantsaddressconstantSCRVUSD=0x0655977FEb2f289A4aB78af67BAB0d17aAb84367;bytes32constantSCRVUSD_HASH=keccak256(abi.encodePacked(SCRVUSD));// Storage slots of parametersuint256[PROOF_CNT]internalPARAM_SLOTS=[uint256(0),// filler for account proofuint256(21),// total_debtuint256(22),// total_idleuint256(20),// totalSupplyuint256(38),// full_profit_unlock_dateuint256(39),// profit_unlocking_rateuint256(40),// last_profit_updateuint256(keccak256(abi.encode(18,SCRVUSD)))// balanceOf(self)];addresspublic immutableSCRVUSD_ORACLE;addresspublic immutableBLOCK_HASH_ORACLE;constructor(address_block_hash_oracle,address_scrvusd_oracle){BLOCK_HASH_ORACLE=_block_hash_oracle;SCRVUSD_ORACLE=_scrvusd_oracle;}/// @param _block_number Number of the block to use state root hash/// @param _proof_rlp The state proof of the parametersfunctionverifyScrvusdByStateRoot(uint256_block_number,bytesmemory_proof_rlp
)externalreturns(uint256){bytes32state_root=IBlockHashOracle(BLOCK_HASH_ORACLE).get_state_root(_block_number);uint256[PARAM_CNT]memoryparams=_extractParametersFromProof(state_root,_proof_rlp);// Use last_profit_update as the timestamp surrogatereturn_updatePrice(params,params[5],_block_number);}/// @dev Extract parameters from the state proof using the given state root.function_extractParametersFromProof(bytes32stateRoot,bytesmemoryproofRlp
)internalviewreturns(uint256[PARAM_CNT]memory){RLPReader.RLPItem[]memoryproofs=proofRlp.toRlpItem().toList();require(proofs.length==PROOF_CNT,"Invalid number of proofs");// Extract account proofVerifier.Accountmemoryaccount=Verifier.extractAccountFromProof(SCRVUSD_HASH,stateRoot,proofs[0].toList());require(account.exists,"scrvUSD account does not exist");// Extract slot valuesuint256[PARAM_CNT]memoryparams;for(uint256i=1;i<PROOF_CNT;i++){Verifier.SlotValuememoryslot=Verifier.extractSlotValueFromProof(keccak256(abi.encode(PARAM_SLOTS[i])),account.storageRoot,proofs[i].toList());// Slots might not exist, but typically we just read them.params[i-1]=slot.value;}returnparams;}/// @dev Calls the oracle to update the price parameters./// Both child contracts use the same oracle call, differing only in how they obtain the timestamp.function_updatePrice(uint256[PARAM_CNT]memoryparams,uint256ts,uint256number)internalreturns(uint256){returnIScrvusdOracle(SCRVUSD_ORACLE).update_price(params,ts,number);}}
This function verifies the period using an RLP-encoded block header and a corresponding state proof. It parses the block header to ensure the block hash is valid and matches the expected value from the block hash oracle, then extracts the period from the state proof. Finally, it uses the extracted period to update the scrvUSD oracle's profit_max_unlock_time.
Returns: a boolean indicating whether the update was successful.
Input
Type
Description
_block_header_rlp
bytes
RLP-encoded block header containing block information
_proof_rlp
bytes
RLP-encoded state proof for the period
Source code
// SPDX-License-Identifier: MITpragma solidity0.8.18;import{ScrvusdVerifierV1,IBlockHashOracle}from"./ScrvusdVerifierV1.sol";import{RLPReader}from"hamdiallam/Solidity-RLP@2.0.7/contracts/RLPReader.sol";import{StateProofVerifierasVerifier}from"../../xdao/contracts/libs/StateProofVerifier.sol";interfaceIScrvusdOracleV2{functionupdate_profit_max_unlock_time(uint256_profit_max_unlock_time,uint256_block_number)externalreturns(bool);}contractScrvusdVerifierV2isScrvusdVerifierV1{usingRLPReaderforbytes;usingRLPReaderforRLPReader.RLPItem;uint256internal PERIOD_SLOT=37;// profit_max_unlock_timeconstructor(address_block_hash_oracle,address_scrvusd_oracle)ScrvusdVerifierV1(_block_hash_oracle,_scrvusd_oracle){}/// @param _block_header_rlp The RLP-encoded block header/// @param _proof_rlp The state proof of the periodfunctionverifyPeriodByBlockHash(bytesmemory_block_header_rlp,bytesmemory_proof_rlp
)externalreturns(bool){Verifier.BlockHeadermemoryblock_header=Verifier.parseBlockHeader(_block_header_rlp);require(block_header.hash!=bytes32(0),"Invalid blockhash");require(block_header.hash==IBlockHashOracle(ScrvusdVerifierV1.BLOCK_HASH_ORACLE).get_block_hash(block_header.number),"Blockhash mismatch");uint256period=_extractPeriodFromProof(block_header.stateRootHash,_proof_rlp);returnIScrvusdOracleV2(SCRVUSD_ORACLE).update_profit_max_unlock_time(period,block_header.number);}/// @dev Extract period from the state proof using the given state root.function_extractPeriodFromProof(bytes32stateRoot,bytesmemoryproofRlp
)internalviewreturns(uint256){RLPReader.RLPItem[]memoryproofs=proofRlp.toRlpItem().toList();require(proofs.length==2,"Invalid number of proofs");// Extract account proofVerifier.Accountmemoryaccount=Verifier.extractAccountFromProof(ScrvusdVerifierV1.SCRVUSD_HASH,stateRoot,proofs[0].toList());require(account.exists,"scrvUSD account does not exist");Verifier.SlotValuememoryslot=Verifier.extractSlotValueFromProof(keccak256(abi.encode(PERIOD_SLOT)),account.storageRoot,proofs[1].toList());require(slot.exists);returnslot.value;}}
This function verifies the period by retrieving the state root for a given block number from the block hash oracle and then using a state proof to extract the period. The extracted period is used to update the scrvUSD oracle's profit_max_unlock_time.
Returns: a boolean indicating whether the update was successful.
Input
Type
Description
_block_number
uint256
Block number for which to retrieve the state root
_proof_rlp
bytes
RLP-encoded state proof for the period
Source code
// SPDX-License-Identifier: MITpragma solidity0.8.18;import{ScrvusdVerifierV1,IBlockHashOracle}from"./ScrvusdVerifierV1.sol";import{RLPReader}from"hamdiallam/Solidity-RLP@2.0.7/contracts/RLPReader.sol";import{StateProofVerifierasVerifier}from"../../xdao/contracts/libs/StateProofVerifier.sol";interfaceIScrvusdOracleV2{functionupdate_profit_max_unlock_time(uint256_profit_max_unlock_time,uint256_block_number)externalreturns(bool);}contractScrvusdVerifierV2isScrvusdVerifierV1{usingRLPReaderforbytes;usingRLPReaderforRLPReader.RLPItem;uint256internal PERIOD_SLOT=37;// profit_max_unlock_timeconstructor(address_block_hash_oracle,address_scrvusd_oracle)ScrvusdVerifierV1(_block_hash_oracle,_scrvusd_oracle){}/// @param _block_number Number of the block to use state root hash/// @param _proof_rlp The state proof of the periodfunctionverifyPeriodByStateRoot(uint256_block_number,bytesmemory_proof_rlp
)externalreturns(bool){bytes32state_root=IBlockHashOracle(ScrvusdVerifierV1.BLOCK_HASH_ORACLE).get_state_root(_block_number);uint256period=_extractPeriodFromProof(state_root,_proof_rlp);returnIScrvusdOracleV2(SCRVUSD_ORACLE).update_profit_max_unlock_time(period,_block_number);}/// @dev Extract period from the state proof using the given state root.function_extractPeriodFromProof(bytes32stateRoot,bytesmemoryproofRlp
)internalviewreturns(uint256){RLPReader.RLPItem[]memoryproofs=proofRlp.toRlpItem().toList();require(proofs.length==2,"Invalid number of proofs");// Extract account proofVerifier.Accountmemoryaccount=Verifier.extractAccountFromProof(ScrvusdVerifierV1.SCRVUSD_HASH,stateRoot,proofs[0].toList());require(account.exists,"scrvUSD account does not exist");Verifier.SlotValuememoryslot=Verifier.extractSlotValueFromProof(keccak256(abi.encode(PERIOD_SLOT)),account.storageRoot,proofs[1].toList());require(slot.exists);returnslot.value;}}