pragma solidity 0.8.18;
import {ScrvusdVerifierV1, IBlockHashOracle} from "./ScrvusdVerifierV1.sol";
import {RLPReader} from "hamdiallam/Solidity-RLP@2.0.7/contracts/RLPReader.sol";
import {StateProofVerifier as Verifier} from "../../xdao/contracts/libs/StateProofVerifier.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
interface IScrvusdOracleV2 {
function update_profit_max_unlock_time(
uint256 _profit_max_unlock_time,
uint256 _block_number
) external returns (bool);
}
contract ScrvusdVerifierV2 is ScrvusdVerifierV1, ReentrancyGuard, AccessControl {
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;
uint256 internal constant PERIOD_SLOT = 37;
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
event ProfitMaxUnlockTimeUpdated(uint256 period, uint256 blockNumber);
constructor(address _block_hash_oracle, address _scrvusd_oracle)
ScrvusdVerifierV1(_block_hash_oracle, _scrvusd_oracle)
{
_setupRole(ADMIN_ROLE, msg.sender);
}
function verifyPeriodByBlockHash(
bytes memory _block_header_rlp,
bytes memory _proof_rlp
) external onlyRole(ADMIN_ROLE) nonReentrant returns (bool) {
Verifier.BlockHeader memory block_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"
);
uint256 period = _extractPeriodFromProof(block_header.stateRootHash, _proof_rlp);
bool success = IScrvusdOracleV2(SCRVUSD_ORACLE).update_profit_max_unlock_time(period, block_header.number);
require(success, "Failed to update profit max unlock time");
emit ProfitMaxUnlockTimeUpdated(period, block_header.number);
return true;
}
function verifyPeriodByStateRoot(
uint256 _block_number,
bytes memory _proof_rlp
) external onlyRole(ADMIN_ROLE) nonReentrant returns (bool) {
bytes32 state_root = IBlockHashOracle(ScrvusdVerifierV1.BLOCK_HASH_ORACLE).get_state_root(_block_number);
uint256 period = _extractPeriodFromProof(state_root, _proof_rlp);
bool success = IScrvusdOracleV2(SCRVUSD_ORACLE).update_profit_max_unlock_time(period, _block_number);
require(success, "Failed to update profit max unlock time");
emit ProfitMaxUnlockTimeUpdated(period, _block_number);
return true;
}
function _extractPeriodFromProof(
bytes32 stateRoot,
bytes memory proofRlp
) internal view returns (uint256) {
RLPReader.RLPItem[] memory proofs = proofRlp.toRlpItem().toList();
require(proofs.length == 2, "Invalid number of proofs");
Verifier.Account memory account = Verifier.extractAccountFromProof(
ScrvusdVerifierV1.SCRVUSD_HASH,
stateRoot,
proofs[0].toList()
);
require(account.exists, "scrvUSD account does not exist");
Verifier.SlotValue memory slot = Verifier.extractSlotValueFromProof(
keccak256(abi.encode(PERIOD_SLOT)),
account.storageRoot,
proofs[1].toList()
);
require(slot.exists, "Slot does not exist");
return slot.value;
}
}