Last active
May 8, 2025 15:53
-
-
Save pcaversaccio/64c7829e0c7ed4eeb1e5a07e2177836c to your computer and use it in GitHub Desktop.
Historical block hashes oracle Vyper contract.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # pragma version ~=0.4.1 | |
| """ | |
| @title Historical Block Hashes Oracle | |
| @custom:contract-name block_hash_oracle | |
| @license GNU Affero General Public License v3.0 only | |
| @author pcaversaccio | |
| @notice The contract function `block_hash` can be used to access the | |
| historical block hashes beyond the default 256-block limit. | |
| We use the EIP-2935 (https://eips.ethereum.org/EIPS/eip-2935) | |
| history contract, which maintains a ring buffer of the last | |
| 8,191 block hashes stored in state: | |
| - For blocks within the last 256 blocks, the function `block_hash` | |
| uses the native `BLOCKHASH` opcode. | |
| - For blocks between 257 and 8,191 blocks ago, the function | |
| `block_hash` queries the EIP-2935 history contract using the | |
| specified `get` method: https://eips.ethereum.org/EIPS/eip-2935#get. | |
| - For blocks older than 8,191 blocks or future blocks (including the | |
| current one), it returns zero, matching the `BLOCKHASH` behavior. | |
| Please note that after EIP-2935 is activated, it takes 8,191 | |
| blocks to fully populate the history. Before that, only block | |
| hashes from the fork block onward are available. | |
| """ | |
| from snekmate.utils import block_hash as bh | |
| @external | |
| @view | |
| def block_hash(block_number: uint256) -> bytes32: | |
| """ | |
| @dev Returns the block hash for block number `block_number`. | |
| @notice For blocks older than 8,191 or future blocks (including | |
| the current one), returns zero, matching the `BLOCKHASH` | |
| behaviour. Furthermore, this function does verify if the | |
| history contract is deployed. If the history contract is | |
| undeployed, the function will fallback to the `BLOCKHASH` | |
| behaviour. | |
| @param block_number The 32-byte block number. | |
| @return bytes32 The 32-byte block hash for block number `block_number`. | |
| """ | |
| return bh._block_hash(block_number) |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Compilation Versions
Use Vyper version
0.4.1and π snekmate commit96b0845:After, save the gist code to a file called
block_hash_oracle.vyand run:vyper block_hash_oracle.vyContract Creation Code
0x61015961001161000039610159610000f35f3560e01c63b509f4ef81186100315760243610341761015557602060043560e05261002c61014061009a565b610140f35b5f5ffd5b5a6040516080526020606052606050602060c0606051608071f90827f1c53a10cb7a02335b17532000293585fa9050610070573d5f5f3e3d5ffd5b3d602081183d602010021860a05260a0805160200360031b6020820151811c811b90509050815250565b4360e051106100ac575f815250610153565b60e0514303610100526101006101005111156101385761200061010051101561010b577f6e49e66782037c0555897870e29fa5e552daf4719552131a0abce779daec0a5d71f90827f1c53a10cb7a02335b1753200029353f141561010e565b60015b61012f5760e051604052610123610120610035565b61012051815250610153565b5f815250610153565b60e05161010043038112610155574381101561015557408152505b565b5f80fd8558205faae5cedd8d4098fc2e7250264ec7db93b093963b3583c968a0353969b14b2d1901598000a1657679706572830004010035Standard
JSONInputThe standard
JSONinput file can be retrieved via:vyper -f solc_json block_hash_oracle.vy > block_hash_oracle.json{ "language": "Vyper", "sources": { "venv/Lib/site-packages/snekmate/utils/block_hash.vy": { "content": "# pragma version ~=0.4.1\n\"\"\"\n@title Utility Functions to Access Historical Block Hashes\n@custom:contract-name block_hash\n@license GNU Affero General Public License v3.0 only\n@author pcaversaccio\n@notice These functions can be used to access the historical block\n hashes beyond the default 256-block limit. We use the EIP-2935\n (https://eips.ethereum.org/EIPS/eip-2935) history contract,\n which maintains a ring buffer of the last 8,191 block hashes\n stored in state. For the blocks within the last 256 blocks,\n we use the native `BLOCKHASH` opcode. For blocks between 257\n and 8,191 blocks ago, the function `_block_hash` queries via\n the specified `get` (https://eips.ethereum.org/EIPS/eip-2935#get)\n method the EIP-2935 history contract. For blocks older than\n 8,191 or future blocks (including the current one), we return\n zero, matching the `BLOCKHASH` behaviour.\n\n Please note that after EIP-2935 is activated, it takes 8,191\n blocks to fully populate the history. Before that, only block\n hashes from the fork block onward are available.\n\"\"\"\n\n\n# @dev The `HISTORY_STORAGE_ADDRESS` contract address.\n# @notice See the EIP-2935 specifications here: https://eips.ethereum.org/EIPS/eip-2935#specification.\n_HISTORY_STORAGE_ADDRESS: constant(address) = 0x0000F90827F1C53a10cb7A02335B175320002935\n\n\n# @dev The `keccak256` hash of the runtime bytecode of the\n# history contract deployed at `HISTORY_STORAGE_ADDRESS`.\n_HISTORY_STORAGE_RUNTIME_BYTECODE_HASH: constant(bytes32) = (\n 0x6e49e66782037c0555897870e29fa5e552daf4719552131a0abce779daec0a5d\n)\n\n\n@deploy\n@payable\ndef __init__():\n \"\"\"\n @dev To omit the opcodes for checking the `msg.value`\n in the creation-time EVM bytecode, the constructor\n is declared as `payable`.\n \"\"\"\n pass\n\n\n@internal\n@view\ndef _block_hash(block_number: uint256) -> bytes32:\n \"\"\"\n @dev Returns the block hash for block number `block_number`.\n @notice For blocks older than 8,191 or future blocks (including\n the current one), returns zero, matching the `BLOCKHASH`\n behaviour. Furthermore, this function does verify if the\n history contract is deployed. If the history contract is\n undeployed, the function will fallback to the `BLOCKHASH`\n behaviour.\n @param block_number The 32-byte block number.\n @return bytes32 The 32-byte block hash for block number `block_number`.\n \"\"\"\n # For future blocks (including the current one), we already return\n # an empty `bytes32` value here in order not to iterate through the\n # remaining code.\n if block_number >= block.number:\n return empty(bytes32)\n\n delta: uint256 = unsafe_sub(block.number, block_number)\n\n if delta <= 256:\n return blockhash(block_number)\n elif delta > 8191 or _HISTORY_STORAGE_ADDRESS.codehash != _HISTORY_STORAGE_RUNTIME_BYTECODE_HASH:\n # The Vyper built-in function `blockhash` reverts if the block number\n # is more than `256` blocks behind the current block. We explicitly\n # handle this case (i.e. `delta > 8191`) to ensure the function returns\n # an empty `bytes32` value rather than reverting (i.e. exactly matching\n # the `BLOCKHASH` opcode behaviour).\n return empty(bytes32)\n else:\n return self._get_history_storage(block_number)\n\n\n@internal\n@view\ndef _get_history_storage(block_number: uint256) -> bytes32:\n \"\"\"\n @dev Returns the block hash for block number `block_number` by\n calling the `HISTORY_STORAGE_ADDRESS` contract address.\n @notice Please note that for any request outside the range of\n `[block.number - 8191, block.number - 1]`, this function\n reverts (see https://eips.ethereum.org/EIPS/eip-2935#get).\n Furthermore, this function does not verify if the history\n contract is deployed. If the history contract is undeployed,\n the function will return an empty `bytes32` value.\n @param block_number The 32-byte block number.\n @return bytes32 The 32-byte block hash for block number `block_number`.\n \"\"\"\n return convert(\n raw_call(\n _HISTORY_STORAGE_ADDRESS,\n abi_encode(block_number),\n max_outsize=32,\n is_static_call=True,\n ),\n bytes32,\n )\n", "sha256sum": "5c2da0de4147e4cc317c92ca0beb0ccf13f4485f35688e29832f34179decf02c" }, "block_hash_oracle.vy": { "content": "# pragma version ~=0.4.1\n\"\"\"\n@title Historical Block Hashes Oracle\n@custom:contract-name block_hash_oracle\n@license GNU Affero General Public License v3.0 only\n@author pcaversaccio\n@notice The contract function `block_hash` can be used to access the\n historical block hashes beyond the default 256-block limit.\n We use the EIP-2935 (https://eips.ethereum.org/EIPS/eip-2935)\n history contract, which maintains a ring buffer of the last\n 8,191 block hashes stored in state:\n - For blocks within the last 256 blocks, the function `block_hash`\n uses the native `BLOCKHASH` opcode.\n - For blocks between 257 and 8,191 blocks ago, the function\n `block_hash` queries the EIP-2935 history contract using the\n specified `get` method: https://eips.ethereum.org/EIPS/eip-2935#get.\n - For blocks older than 8,191 blocks or future blocks (including the\n current one), it returns zero, matching the `BLOCKHASH` behavior.\n\n Please note that after EIP-2935 is activated, it takes 8,191\n blocks to fully populate the history. Before that, only block\n hashes from the fork block onward are available.\n\"\"\"\n\nfrom snekmate.utils import block_hash as bh\n\n\n@external\n@view\ndef block_hash(block_number: uint256) -> bytes32:\n \"\"\"\n @dev Returns the block hash for block number `block_number`.\n @notice For blocks older than 8,191 or future blocks (including\n the current one), returns zero, matching the `BLOCKHASH`\n behaviour. Furthermore, this function does verify if the\n history contract is deployed. If the history contract is\n undeployed, the function will fallback to the `BLOCKHASH`\n behaviour.\n @param block_number The 32-byte block number.\n @return bytes32 The 32-byte block hash for block number `block_number`.\n \"\"\"\n return bh._block_hash(block_number)\n", "sha256sum": "c2d0fd1295f106bb1c081bba1992d27e4a7606eb98c75fcda22d96e69e779628" } }, "settings": { "outputSelection": { "block_hash_oracle.vy": ["*"] }, "search_paths": ["venv/Lib/site-packages", "venv", "."] }, "compiler_version": "v0.4.1+commit.8a93dd27", "integrity": "5faae5cedd8d4098fc2e7250264ec7db93b093963b3583c968a0353969b14b2d" }Deployment via
CreateXCall
deployCreate2(bytes32,bytes)with the arguments:{ "salt": "0xecc539188a01ae980fcdf7e561679dfda0b504b6e017309f2c72d8e6f182e3bf", "initCode": "0x61015961001161000039610159610000f35f3560e01c63b509f4ef81186100315760243610341761015557602060043560e05261002c61014061009a565b610140f35b5f5ffd5b5a6040516080526020606052606050602060c0606051608071f90827f1c53a10cb7a02335b17532000293585fa9050610070573d5f5f3e3d5ffd5b3d602081183d602010021860a05260a0805160200360031b6020820151811c811b90509050815250565b4360e051106100ac575f815250610153565b60e0514303610100526101006101005111156101385761200061010051101561010b577f6e49e66782037c0555897870e29fa5e552daf4719552131a0abce779daec0a5d71f90827f1c53a10cb7a02335b1753200029353f141561010e565b60015b61012f5760e051604052610123610120610035565b61012051815250610153565b5f815250610153565b60e05161010043038112610155574381101561015557408152505b565b5f80fd8558205faae5cedd8d4098fc2e7250264ec7db93b093963b3583c968a0353969b14b2d1901598000a1657679706572830004010035" }Deployments
0x0e04bDF212088405D1EF8abE507F644a87c17a840x0e04bDF212088405D1EF8abE507F644a87c17a84Testing
You can test it on e.g. Sepolia by running: