# L2 veCRV Verifiers

> L2 verifier contracts are used to securely synchronize veCRV and related state from Ethereum mainnet (L1) to supported L2s. They validate Merkle proofs and block data from L1, allowing trust-minimized updates of veCRV balances, total supply, and delegation state on L2.

# L2 veCRV Verifiers

L2 verifier contracts are used to securely synchronize veCRV and related state from Ethereum mainnet (L1) to supported L2s. They validate Merkle proofs and block data from L1, allowing trust-minimized updates of veCRV balances, total supply, and delegation state on L2.

---

## veCRV Verifier

The `VecrvVerifier` contract is used to verify and update the total supply and individual balances of veCRV on L2s by validating state proofs from L1. It enables trust-minimized synchronization of veCRV state by accepting Merkle proofs and block data, and updating the canonical veCRV oracle with supply and balance changes. This contract is typically called by relayers or bridges to reflect L1 veCRV state on L2.

:::solidity[`VecrvVerifier.sol`]

The source code for the `VecrvVerifier` contract is available on [GitHub](https://github.com/curvefi/storage-proofs/blob/main/contracts/vecrv/verifiers/VecrvVerifier.sol). The contract is written in [Solidity](https://soliditylang.org/) version `0.8.18`.

The `VecrvVerifier` contract is deployed at the following addresses:

- :logos-optimism: Optimism: [`0x4eee0D7F5C84EF30AEd22137EED4188ac778f97f`](https://optimistic.etherscan.io/address/0x4eee0D7F5C84EF30AEd22137EED4188ac778f97f)
- :logos-arbitrum: Arbitrum: [`0x852F32c22C5035EA12566EDFB4415625776D75d5`](https://arbiscan.io/address/0x852F32c22C5035EA12566EDFB4415625776D75d5)
- :logos-fraxtal: Fraxtal: [`0x4D1AF9911e4c19f64Be36c36EF39Fd026Bc9bb61`](https://fraxscan.com/address/0x4D1AF9911e4c19f64Be36c36EF39Fd026Bc9bb61)
- :logos-sonic: Sonic: [`0x38334e319D257d8f580f66393d25A6CD647A6AbC`](https://sonicscan.org/address/0x38334e319D257d8f580f66393d25A6CD647A6AbC)
- :logos-mantle: Mantle: [`0x820945D1E5759a57874846371F22b56b73c6AE85`](https://mantlescan.xyz/address/0x820945D1E5759a57874846371F22b56b73c6AE85)
- :logos-base: Base: [`0x3fE593E651Cd0B383AD36b75F4159f30BB0631A6`](https://basescan.org/address/0x3fE593E651Cd0B383AD36b75F4159f30BB0631A6)
- :logos-taiko: Taiko: [`0x3B519ae13D7CeB72CC922815f5dAaD741aD5087B`](https://taikoscan.io/address/0x3B519ae13D7CeB72CC922815f5dAaD741aD5087B)

<ContractABI>

```json
[{"inputs":[{"internalType":"address","name":"_block_hash_oracle","type":"address"},{"internalType":"address","name":"_vecrv_oracle","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BLOCK_HASH_ORACLE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_SLOPE_CHANGES_CNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VE_ORACLE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"bytes","name":"_block_header_rlp","type":"bytes"},{"internalType":"bytes","name":"_proof_rlp","type":"bytes"}],"name":"verifyBalanceByBlockHash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_block_number","type":"uint256"},{"internalType":"bytes","name":"_proof_rlp","type":"bytes"}],"name":"verifyBalanceByStateRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_block_header_rlp","type":"bytes"},{"internalType":"bytes","name":"_proof_rlp","type":"bytes"}],"name":"verifyTotalByBlockHash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_block_number","type":"uint256"},{"internalType":"bytes","name":"_proof_rlp","type":"bytes"}],"name":"verifyTotalByStateRoot","outputs":[],"stateMutability":"nonpayable","type":"function"}]
```

</ContractABI>

:::

### `BLOCK_HASH_ORACLE`
::::description[`VecrvVerifier.BLOCK_HASH_ORACLE() -> address: view`]

Getter for the block hash oracle contract, which is used to retrieve block hashes and state roots for verification.

Returns: block hash oracle (`address`).

<SourceCode>

```solidity title="VecrvVerifier.sol"
address public immutable BLOCK_HASH_ORACLE;
```

</SourceCode>

<Example>

This example returns the block hash oracle on Optimism.

```shell
>>> VecrvVerifier.BLOCK_HASH_ORACLE()
'0xeB896fB7D1AaE921d586B0E5a037496aFd3E2412'
```

</Example>

::::

### `MIN_SLOPE_CHANGES_CNT`
::::description[`VecrvVerifier.MIN_SLOPE_CHANGES_CNT() -> uint256: view`]

Returns the minimum number of slope changes required for a valid proof. This is set to 4, corresponding to 1 month (assuming 1 week per slope change).

Returns: minimum slope changes count (`uint256`).

<SourceCode>

```solidity title="VecrvVerifierCore.sol"
uint256 public constant MIN_SLOPE_CHANGES_CNT = 4; // 1 month
```

</SourceCode>

<Example>

This example returns the minimum slope change count.

```shell
>>> VecrvVerifier.MIN_SLOPE_CHANGES_CNT()
4
```

</Example>

::::

### `VE_ORACLE`
::::description[`VecrvVerifier.VE_ORACLE() -> address: view`]

Getter for the veCRV oracle contract, which is called to update the total supply and user balances after verification.

Returns: veCRV oracle (`address`).

<SourceCode>

```solidity title="VecrvVerifierCore.sol"
address public immutable VE_ORACLE;

constructor(address _ve_oracle) {
    VE_ORACLE = _ve_oracle;
}
```

</SourceCode>

<Example>

This example returns the VE_ORACLE address on Optimism.

```shell
>>> VecrvVerifier.VE_ORACLE()
'0xF1946D4879646e0FCD8F5bb32a5636ED8055176D'
```

</Example>

::::

### `verifyBalanceByBlockHash`
::::description[`VecrvVerifier.verifyBalanceByBlockHash(address _user, bytes memory _block_header_rlp, bytes memory _proof_rlp) external`]

Verifies a user's veCRV balance and updates the total veCRV supply using a block hash. This function is intended for use with RLP-encoded block headers and state proofs.

| Input               | Type      | Description                    |
| ------------------- | --------- | ------------------------------ |
| `_user`             | `address` | User to verify the balance for |
| `_block_header_rlp` | `bytes`   | RLP-encoded block header       |
| `_proof_rlp`        | `bytes`   | State proof of the parameters  |

<SourceCode>

<Tabs>
<TabItem value="VecrvVerifier.sol" label="VecrvVerifier.sol">

```solidity
/// @param _user User to verify balance for
/// @param _block_header_rlp The RLP-encoded block header
/// @param _proof_rlp The state proof of the parameters
function verifyBalanceByBlockHash(
        address _user,
        bytes memory _block_header_rlp,
        bytes memory _proof_rlp
    ) external {
        RLPReader.RLPItem[] memory proofs = _proof_rlp.toRlpItem().toList();
        require(proofs.length >= 1, "Invalid number of proofs");
        (bytes32 storage_root, uint256 block_number) = _extractAccountStorageRoot(_block_header_rlp, proofs[0]);

        _updateTotal(storage_root, block_number, proofs[1].toList());
        _updateBalance(_user, storage_root, block_number, proofs[2].toList());
    }

function _extractAccountStorageRoot(
        bytes memory _block_header_rlp,
        RLPReader.RLPItem memory account_proof
    ) internal returns (bytes32, 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"
        );
        return (_extractAccountStorageRoot(block_header.stateRootHash, account_proof), block_header.number);
    }
```

</TabItem>
<TabItem value="VecrvVerifierCore.sol" label="VecrvVerifierCore.sol">

```solidity
function _extractAccountStorageRoot(
        bytes32 state_root_hash,
        RLPReader.RLPItem memory account_proof
    ) internal returns (bytes32) {
        Verifier.Account memory account = Verifier.extractAccountFromProof(
            VOTING_ESCROW_HASH,
            state_root_hash,
            account_proof.toList()
        );
        require(account.exists, "VotingEscrow account does not exist");
        return account.storageRoot;
    }

    /// @dev Update total parameters with proofs
    function _updateTotal(
        bytes32 storageRoot,
        uint256 block_number,
        RLPReader.RLPItem[] memory proofs
    ) internal {
        require(proofs.length >= SLOPE_CHANGES_PROOF_I + MIN_SLOPE_CHANGES_CNT, "Invalid number of total proofs");

        // Extract slot values
        uint256 epoch = Verifier.extractSlotValueFromProof(
            keccak256(abi.encode(EPOCH_SLOT)),
            storageRoot,
            proofs[EPOCH_PROOF_I].toList()
        ).value;
        IVecrvOracle.Point memory point_history = _extract_point(
            POINT_HISTORY_PROOF_I,
            keccak256(abi.encode(uint256(keccak256(abi.encode(POINT_HISTORY_SLOT))) + epoch)),
            storageRoot,
            proofs
        );
        uint256 start = WEEK + point_history.ts / WEEK * WEEK;
        int128[] memory slope_changes = new int128[](proofs.length - SLOPE_CHANGES_PROOF_I);
        for (uint256 i = 0; i < proofs.length - SLOPE_CHANGES_PROOF_I; ++i) {
            slope_changes[i] = int128(int256(Verifier.extractSlotValueFromProof(
                keccak256(abi.encode(keccak256(abi.encode(SLOPE_CHANGES_SLOT, start + i * WEEK)))),
                storageRoot,
                proofs[SLOPE_CHANGES_PROOF_I + i].toList()
            ).value));
        }

        return IVecrvOracle(VE_ORACLE).update_total(
            epoch,
            point_history,
            slope_changes,
            block_number
        );
    }

/// @dev Update user's balance with proofs
    function _updateBalance(
        address user,
        bytes32 storageRoot,
        uint256 block_number,
        RLPReader.RLPItem[] memory proofs
    ) internal {
        require(proofs.length == LOCKED_BALANCE_PROOF_I + 2, "Invalid number of balance proofs");

        // Extract slot values
        uint256 user_point_epoch = Verifier.extractSlotValueFromProof(
            keccak256(
                abi.encode(keccak256(abi.encode(USER_POINT_EPOCH_SLOT, user)))
            ),
            storageRoot,
            proofs[USER_POINT_EPOCH_PROOF_I].toList()
        ).value;
        IVecrvOracle.Point memory user_point_history = _extract_point(
            USER_POINT_HISTORY_PROOF_I,
            keccak256(abi.encode(uint256(keccak256(abi.encode(keccak256(abi.encode(USER_POINT_HISTORY_SLOT, user))))) + user_point_epoch)),
            storageRoot,
            proofs
        );
        IVecrvOracle.LockedBalance memory locked = _extract_locked_balance(
            LOCKED_BALANCE_PROOF_I,
            keccak256(abi.encode(keccak256(abi.encode(LOCKED_BALANCE_SLOT, user)))),
            storageRoot,
            proofs
        );

        return IVecrvOracle(VE_ORACLE).update_balance(
            user,
            user_point_epoch,
            user_point_history,
            locked,
            block_number
        );
    }
```

</TabItem>
</Tabs>

</SourceCode>

<Example>

```shell
>>> soon
```

</Example>

::::

### `verifyBalanceByStateRoot`
::::description[`VecrvVerifier.verifyBalanceByStateRoot(address _user, uint256 _block_number, bytes memory _proof_rlp) external`]

Verifies a user's veCRV balance and updates the total veCRV supply using a state root obtained from the block hash oracle.

| Input               | Type      | Description                    |
| ------------------- | --------- | ------------------------------ |
| `_user`             | `address` | User to verify the balance for |
| `_block_number`     | `uint256` | Block number to use state root |
| `_proof_rlp`        | `bytes`   | State proof of the parameters  |

<SourceCode>

<Tabs>
<TabItem value="VecrvVerifier.sol" label="VecrvVerifier.sol">

```solidity
/// @param _user User to verify balance for
/// @param _block_number Number of the block to use state root hash
/// @param _proof_rlp The state proof of the parameters
function verifyBalanceByStateRoot(
    address _user,
    uint256 _block_number,
    bytes memory _proof_rlp
) external {
    RLPReader.RLPItem[] memory proofs = _proof_rlp.toRlpItem().toList();
    require(proofs.length >= 1, "Invalid number of proofs");
    bytes32 state_root = IBlockHashOracle(BLOCK_HASH_ORACLE).get_state_root(_block_number);
    bytes32 storage_root = _extractAccountStorageRoot(state_root, proofs[0]);

    _updateTotal(storage_root, _block_number, proofs[1].toList());
    _updateBalance(_user, storage_root, _block_number, proofs[2].toList());
}
```

</TabItem>
<TabItem value="VecrvVerifierCore.sol" label="VecrvVerifierCore.sol">

```solidity
function _extractAccountStorageRoot(
        bytes32 state_root_hash,
        RLPReader.RLPItem memory account_proof
    ) internal returns (bytes32) {
        Verifier.Account memory account = Verifier.extractAccountFromProof(
            VOTING_ESCROW_HASH,
            state_root_hash,
            account_proof.toList()
        );
        require(account.exists, "VotingEscrow account does not exist");
        return account.storageRoot;
    }
```

</TabItem>
</Tabs>

</SourceCode>

<Example>

```shell
>>> soon
```

</Example>

::::

### `verifyTotalByBlockHash`
::::description[`VecrvVerifier.verifyTotalByBlockHash(bytes memory _block_header_rlp, bytes memory _proof_rlp) external`]

Verifies and updates the total veCRV supply using a block hash and state proof. Intended for use with RLP-encoded block headers and state proofs. The proofs must be constructed off-chain and provided as input.

| Input         | Type      | Description                                 |
| ------------- | --------- | ------------------------------------------- |
| `_block_header_rlp` | `bytes` | RLP-encoded block header from L1           |
| `_proof_rlp`        | `bytes` | State proof of the parameters              |

<SourceCode>

```solidity title="VecrvVerifier.sol"
/// @param _block_header_rlp The RLP-encoded block header
/// @param _proof_rlp The state proof of the parameters
function verifyTotalByBlockHash(
    bytes memory _block_header_rlp,
    bytes memory _proof_rlp
) external {
    RLPReader.RLPItem[] memory proofs = _proof_rlp.toRlpItem().toList();
    require(proofs.length >= 1, "Invalid number of proofs");
    (bytes32 storage_root, uint256 block_number) = _extractAccountStorageRoot(_block_header_rlp, proofs[0]);

    _updateTotal(storage_root, block_number, proofs[1].toList());
}
```

</SourceCode>

<Example>

```shell
>>> soon
```

</Example>

::::

### `verifyTotalByStateRoot`
::::description[`VecrvVerifier.verifyTotalByStateRoot(uint256 _block_number, bytes memory _proof_rlp) external`]

Verifies and updates the total veCRV supply using a state root obtained from the block hash oracle. The proofs must be constructed off-chain and provided as input.

| Input         | Type      | Description                                 |
| ------------- | --------- | ------------------------------------------- |
| `_block_number` | `uint256` | Block number to use state root             |
| `_proof_rlp`    | `bytes`   | State proof of the parameters              |

<SourceCode>

```solidity title="VecrvVerifier.sol"
/// @param _block_number Number of the block to use state root hash
/// @param _proof_rlp The state proof of the parameters
function verifyTotalByStateRoot(
    uint256 _block_number,
    bytes memory _proof_rlp
) external {
    RLPReader.RLPItem[] memory proofs = _proof_rlp.toRlpItem().toList();
    require(proofs.length >= 1, "Invalid number of proofs");
    bytes32 state_root = IBlockHashOracle(BLOCK_HASH_ORACLE).get_state_root(_block_number);
    bytes32 storage_root = _extractAccountStorageRoot(state_root, proofs[0]);

    _updateTotal(storage_root, _block_number, proofs[1].toList());
}
```

</SourceCode>

<Example>

```shell
>>> soon
```

</Example>

::::

## Delegation Verifier

The `DelegationVerifier` contract is used to verify and update veCRV delegation state on L2s by validating state proofs from L1. It enables trust-minimized synchronization of delegated veCRV balances by accepting Merkle proofs and block data, and updating the canonical veCRV oracle with delegation changes. This contract is typically called by relayers or bridges to reflect L1 delegation state on L2.

:::solidity[`DelegationVerifier.sol`]

The source code for the `DelegationVerifier` contract is available on [GitHub](https://github.com/curvefi/storage-proofs/blob/main/contracts/vecrv/verifiers/DelegationVerifier.sol). The contract is written in [Solidity](https://soliditylang.org/) version `0.8.18`.

The `DelegationVerifier` contract is deployed at the following addresses:

- :logos-optimism: Optimism: [`0x1d04Fcb6293690D75E9262A89Ac3B816772E6841`](https://optimistic.etherscan.io/address/0x1d04Fcb6293690D75E9262A89Ac3B816772E6841)
- :logos-arbitrum: Arbitrum: [`0x820945D1E5759a57874846371F22b56b73c6AE85`](https://arbiscan.io/address/0x820945D1E5759a57874846371F22b56b73c6AE85)
- :logos-fraxtal: Fraxtal: [`0x852F32c22C5035EA12566EDFB4415625776D75d5`](https://fraxscan.com/address/0x852F32c22C5035EA12566EDFB4415625776D75d5)
- :logos-sonic: Sonic: [`0xC29229b477582CE810e8C261b2869b9d8c82F4a7`](https://sonicscan.org/address/0xC29229b477582CE810e8C261b2869b9d8c82F4a7)
- :logos-mantle: Mantle: [`0x1df9cEeE7aB8804749B795D64307A3CFE0e84905`](https://mantlescan.xyz/address/0x1df9cEeE7aB8804749B795D64307A3CFE0e84905)
- :logos-base: Base: [`0xAeB976BB02b5c36DcD57372a3B18326BfA4983C8`](https://basescan.org/address/0xAeB976BB02b5c36DcD57372a3B18326BfA4983C8)
- :logos-taiko: Taiko: [`0xD41f7CcB1e72e282b50b0f331944f8ea7D4CACB6`](https://taikoscan.io/address/0xD41f7CcB1e72e282b50b0f331944f8ea7D4CACB6)

<ContractABI>

```json
[{"inputs":[{"internalType":"address","name":"_block_hash_oracle","type":"address"},{"internalType":"address","name":"_vecrv_oracle","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BLOCK_HASH_ORACLE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VE_ORACLE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"bytes","name":"_block_header_rlp","type":"bytes"},{"internalType":"bytes","name":"_proof_rlp","type":"bytes"}],"name":"verifyDelegationByBlockHash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"uint256","name":"_block_number","type":"uint256"},{"internalType":"bytes","name":"_proof_rlp","type":"bytes"}],"name":"verifyDelegationByStateRoot","outputs":[],"stateMutability":"nonpayable","type":"function"}]
```

</ContractABI>

:::

### `BLOCK_HASH_ORACLE`
::::description[`DelegationVerifier.BLOCK_HASH_ORACLE() -> address: view`]

Getter for the block hash oracle contract, which is used to retrieve block hashes and state roots for verification.

Returns: block hash oracle (`address`).

<SourceCode>

```solidity title="DelegationVerifier.sol"
address public immutable BLOCK_HASH_ORACLE;

constructor(address _block_hash_oracle, address _vecrv_oracle)
{
    BLOCK_HASH_ORACLE = _block_hash_oracle;
    VE_ORACLE = _vecrv_oracle;
}
```

</SourceCode>

<Example>

```shell
>>> DelegationVerifier.BLOCK_HASH_ORACLE()
'0xeB896fB7D1AaE921d586B0E5a037496aFd3E2412'
```

</Example>

::::

### `VE_ORACLE`
::::description[`DelegationVerifier.VE_ORACLE() -> address: view`]

Getter for the veCRV oracle contract, which is called to update delegation state after verification.

Returns: veCRV oracle (`address`).

<SourceCode>

```solidity title="DelegationVerifier.sol"
address public immutable VE_ORACLE;

constructor(address _block_hash_oracle, address _vecrv_oracle)
{
    BLOCK_HASH_ORACLE = _block_hash_oracle;
    VE_ORACLE = _vecrv_oracle;
}
```

</SourceCode>

<Example>

```shell
>>> DelegationVerifier.VE_ORACLE()
'0xF1946D4879646e0FCD8F5bb32a5636ED8055176D'
```

</Example>

::::

### `verifyDelegationByBlockHash`
::::description[`DelegationVerifier.verifyDelegationByBlockHash(address _from, bytes memory _block_header_rlp, bytes memory _proof_rlp) external`]

Verifies and updates the delegation of veCRV balance from `_from` to the delegated address using a block hash. This function is intended for use with RLP-encoded block headers and state proofs.

| Input         | Type      | Description                                 |
| ------------- | --------- | ------------------------------------------- |
| `_from`       | `address` | Address from which balance is delegated     |
| `_block_header_rlp` | `bytes` | RLP-encoded block header from L1           |
| `_proof_rlp`        | `bytes` | State proof of the parameters              |

<SourceCode>

```solidity title="DelegationVerifier.sol"
interface IBlockHashOracle {
    function get_block_hash(uint256 _number) external view returns (bytes32);
    function get_state_root(uint256 _number) external view returns (bytes32);
}

interface IVecrvOracle {
    function update_delegation(
        address from,
        address to,
        uint256 block_number
    ) external;
}

function verifyDelegationByBlockHash(
    address _from,
    bytes memory _block_header_rlp,
    bytes memory _proof_rlp
) external {
    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"
    );

    return _updateDelegation(_from, block_header.number, block_header.stateRootHash, _proof_rlp);
}

/// @dev Update delegation using proof. `blockNumber` is used for updates linearization
function _updateDelegation(
    address from,
    uint256 blockNumber,
    bytes32 stateRoot,
    bytes memory proofRlp
) internal {
    RLPReader.RLPItem[] memory proofs = proofRlp.toRlpItem().toList();
    require(proofs.length == 2, "Invalid number of proofs");

    // Extract account proof
    Verifier.Account memory account = Verifier.extractAccountFromProof(
        VE_DELEGATE_HASH,
        stateRoot,
        proofs[0].toList()
    );
    require(account.exists, "Delegate account does not exist");

    // Extract slot values
    address to = address(uint160(Verifier.extractSlotValueFromProof(
        keccak256(abi.encode(
            keccak256(abi.encode(
                keccak256(abi.encode(1, block.chainid)), // slot of delegation_from[chain.id][]
                from
            ))
        )),
        account.storageRoot,
        proofs[1].toList()
    ).value));
    require(to != VE_DELEGATE, "Delegate not set");

    return IVecrvOracle(VE_ORACLE).update_delegation(from, to, blockNumber);
}
```

</SourceCode>

<Example>

```shell
>>> soon
```

</Example>

::::

### `verifyDelegationByStateRoot`
::::description[`DelegationVerifier.verifyDelegationByStateRoot(address _from, uint256 _block_number, bytes memory _proof_rlp) external`]

Verifies and updates the delegation of veCRV balance from `_from` to the delegated address using a state root obtained from the block hash oracle.

| Input         | Type      | Description                                 |
| ------------- | --------- | ------------------------------------------- |
| `_from`       | `address` | Address from which balance is delegated     |
| `_block_number` | `uint256` | Block number to use state root             |
| `_proof_rlp`    | `bytes`   | State proof of the parameters              |

<SourceCode>

```solidity title="DelegationVerifier.sol"
interface IBlockHashOracle {
    function get_block_hash(uint256 _number) external view returns (bytes32);
    function get_state_root(uint256 _number) external view returns (bytes32);
}

interface IVecrvOracle {
    function update_delegation(
        address from,
        address to,
        uint256 block_number
    ) external;
}

/// @param _from Address from which balance is delegated
/// @param _block_number Number of the block to use state root hash
/// @param _proof_rlp The state proof of the parameters
function verifyDelegationByStateRoot(
    address _from,
    uint256 _block_number,
    bytes memory _proof_rlp
) external {
    bytes32 state_root = IBlockHashOracle(BLOCK_HASH_ORACLE).get_state_root(_block_number);

    return _updateDelegation(_from, _block_number, state_root, _proof_rlp);
}

/// @dev Update delegation using proof. `blockNumber` is used for updates linearization
function _updateDelegation(
    address from,
    uint256 blockNumber,
    bytes32 stateRoot,
    bytes memory proofRlp
) internal {
    RLPReader.RLPItem[] memory proofs = proofRlp.toRlpItem().toList();
    require(proofs.length == 2, "Invalid number of proofs");

    // Extract account proof
    Verifier.Account memory account = Verifier.extractAccountFromProof(
        VE_DELEGATE_HASH,
        stateRoot,
        proofs[0].toList()
    );
    require(account.exists, "Delegate account does not exist");

    // Extract slot values
    address to = address(uint160(Verifier.extractSlotValueFromProof(
        keccak256(abi.encode(
            keccak256(abi.encode(
                keccak256(abi.encode(1, block.chainid)), // slot of delegation_from[chain.id][]
                from
            ))
        )),
        account.storageRoot,
        proofs[1].toList()
    ).value));
    require(to != VE_DELEGATE, "Delegate not set");

    return IVecrvOracle(VE_ORACLE).update_delegation(from, to, blockNumber);
}
```

</SourceCode>

<Example>

```shell
>>> soon
```

</Example>

::::
