{"id":31911544,"url":"https://github.com/evmts/primitives","last_synced_at":"2025-10-13T17:19:26.089Z","repository":{"id":317614784,"uuid":"1068142078","full_name":"evmts/primitives","owner":"evmts","description":"Unopinionated low level utilities for Ethereum","archived":false,"fork":false,"pushed_at":"2025-10-02T01:02:51.000Z","size":97,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-02T01:18:50.260Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/evmts.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-01T23:21:57.000Z","updated_at":"2025-10-02T01:02:55.000Z","dependencies_parsed_at":"2025-10-02T01:18:52.069Z","dependency_job_id":"4f17b8e0-ae7e-4b0d-9778-a78d8e0f81db","html_url":"https://github.com/evmts/primitives","commit_stats":null,"previous_names":["evmts/primitives"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/evmts/primitives","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evmts%2Fprimitives","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evmts%2Fprimitives/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evmts%2Fprimitives/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evmts%2Fprimitives/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/evmts","download_url":"https://codeload.github.com/evmts/primitives/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evmts%2Fprimitives/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279016297,"owners_count":26085828,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-13T02:00:06.723Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-13T17:19:24.547Z","updated_at":"2025-10-13T17:19:26.076Z","avatar_url":"https://github.com/evmts.png","language":"Zig","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ethereum Primitives (Zig)\n\nModern, type-safe Zig library providing fundamental Ethereum primitives and utilities. Zero external dependencies, comptime-optimized, and designed for high-performance EVM implementations.\n\n## Design Philosophy\n\n1. **Type Safety** - Distinct types prevent mixing addresses, hashes, and raw bytes\n2. **Comptime Everything** - Maximum use of compile-time computation and validation\n3. **Zero Allocations** - Stack-first design; heap only when necessary\n4. **Explicit Errors** - Comprehensive error types with clear semantics\n5. **EIP Compliance** - Strict adherence to Ethereum specifications\n6. **Performance** - Optimized for EVM hot paths (addressing, hashing, RLP)\n\n---\n\n## Core Types\n\n### `Address`\n\n20-byte Ethereum addresses with checksum validation and contract address computation.\n\n```zig\npub const Address = struct {\n    bytes: [20]u8,\n\n    pub const ZERO: Address = .{ .bytes = [_]u8{0} ** 20 };\n\n    pub fn fromHex(hex: []const u8) !Address;\n    pub fn fromBytes(bytes: []const u8) !Address;\n    pub fn fromPublicKey(x: u256, y: u256) Address;\n    pub fn fromU256(value: u256) Address;\n\n    pub fn isValid(str: []const u8) bool;\n    pub fn isValidChecksum(str: []const u8) bool;\n\n    pub fn toHex(self: Address) [42]u8;\n    pub fn toChecksum(self: Address) [42]u8;\n    pub fn toU256(self: Address) u256;\n\n    pub fn create(deployer: Address, nonce: u64) Address;\n    pub fn create2(deployer: Address, salt: [32]u8, init_code_hash: [32]u8) Address;\n\n    pub fn isZero(self: Address) bool;\n    pub fn eql(self: Address, other: Address) bool;\n\n    pub fn format(\n        self: Address,\n        comptime fmt: []const u8,\n        options: std.fmt.FormatOptions,\n        writer: anytype,\n    ) !void;\n};\n```\n\n#### Usage\n\n```zig\nconst addr = try Address.fromHex(\"0x742d35Cc6641C91B6E4bb6ac9e3ff2958c94E676\");\n\nconst valid = Address.isValid(\"0x...\");\nconst valid_checksum = Address.isValidChecksum(\"0x...\");\n\nconst hex = addr.toChecksum();\nconst hex_lower = addr.toHex();\n\nconst contract = Address.create(deployer, nonce);\nconst create2_addr = Address.create2(deployer, salt, init_code_hash);\n\nif (addr.isZero()) { ... }\nif (addr.eql(other)) { ... }\n\nconst value = addr.toU256();\nconst addr_from_int = Address.fromU256(value);\n```\n\n---\n\n### `Hash`\n\n32-byte cryptographic hashes (Keccak256, transaction hashes, storage keys, etc).\n\n```zig\npub const Hash = struct {\n    bytes: [32]u8,\n\n    pub const ZERO: Hash = .{ .bytes = [_]u8{0} ** 32 };\n    pub const EMPTY_CODE_HASH: Hash = .{ .bytes = .{\n        0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c,\n        0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0,\n        0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b,\n        0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70,\n    } };\n    pub const EMPTY_TRIE_ROOT: Hash = .{ .bytes = .{\n        0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6,\n        0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e,\n        0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0,\n        0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21,\n    } };\n\n    pub fn fromHex(hex: []const u8) !Hash;\n    pub fn fromBytes(bytes: []const u8) !Hash;\n    pub fn keccak256(data: []const u8) Hash;\n\n    pub fn toHex(self: Hash) [66]u8;\n    pub fn toU256(self: Hash) u256;\n\n    pub fn eql(self: Hash, other: Hash) bool;\n\n    pub fn format(\n        self: Hash,\n        comptime fmt: []const u8,\n        options: std.fmt.FormatOptions,\n        writer: anytype,\n    ) !void;\n};\n```\n\n#### Usage\n\n```zig\nconst hash = try Hash.fromHex(\"0x1234...\");\nconst hash_from_data = Hash.keccak256(data);\nconst hash_from_bytes = Hash.fromBytes(\u0026bytes);\n\nconst hex = hash.toHex();\nif (hash.eql(other)) { ... }\nconst value = hash.toU256();\n```\n\n---\n\n### `Hex`\n\nHexadecimal encoding/decoding utilities with validation.\n\n```zig\npub const Hex = struct {\n    pub fn encode(allocator: Allocator, bytes: []const u8) ![]u8;\n    pub fn encodeUpper(allocator: Allocator, bytes: []const u8) ![]u8;\n    pub fn encodeFixed(comptime N: usize, bytes: [N]u8) [2 + N * 2]u8;\n\n    pub fn decode(allocator: Allocator, hex: []const u8) ![]u8;\n    pub fn decodeFixed(comptime N: usize, hex: []const u8) ![N]u8;\n\n    pub fn toU256(hex: []const u8) !u256;\n    pub fn toU64(hex: []const u8) !u64;\n\n    pub fn fromU256(allocator: Allocator, value: u256) ![]u8;\n    pub fn fromU64(allocator: Allocator, value: u64) ![]u8;\n\n    pub fn isValid(str: []const u8) bool;\n    pub fn byteLength(hex: []const u8) usize;\n\n    pub fn padLeft(allocator: Allocator, bytes: []const u8, target_length: usize) ![]u8;\n    pub fn padRight(allocator: Allocator, bytes: []const u8, target_length: usize) ![]u8;\n\n    pub fn trimLeft(bytes: []const u8) []const u8;\n    pub fn trimRight(bytes: []const u8) []const u8;\n\n    pub fn concat(allocator: Allocator, arrays: []const []const u8) ![]u8;\n    pub fn slice(bytes: []const u8, start: usize, end: usize) []const u8;\n};\n```\n\n#### Usage\n\n```zig\nconst hex = try Hex.encode(allocator, bytes);\ndefer allocator.free(hex);\n\nconst hex_fixed = Hex.encodeFixed(20, bytes);\n\nconst bytes = try Hex.decode(allocator, \"0x1234\");\ndefer allocator.free(bytes);\n\nconst bytes_fixed = try Hex.decodeFixed(20, \"0x1234...\");\n\nconst value = try Hex.toU256(\"0x1234\");\n\nif (Hex.isValid(\"0x1234\")) { ... }\nconst len = Hex.byteLength(\"0x1234\");\n\nconst padded = try Hex.padLeft(allocator, bytes, 32);\nconst trimmed = Hex.trimLeft(bytes);\nconst result = try Hex.concat(allocator, \u0026[_][]const u8{ bytes1, bytes2 });\n```\n\n---\n\n### `Numeric`\n\nEthereum unit conversions and number utilities.\n\n```zig\npub const Numeric = struct {\n    pub const WEI: u256 = 1;\n    pub const KWEI: u256 = 1_000;\n    pub const MWEI: u256 = 1_000_000;\n    pub const GWEI: u256 = 1_000_000_000;\n    pub const SZABO: u256 = 1_000_000_000_000;\n    pub const FINNEY: u256 = 1_000_000_000_000_000;\n    pub const ETHER: u256 = 1_000_000_000_000_000_000;\n\n    pub const Unit = enum {\n        wei,\n        kwei,\n        mwei,\n        gwei,\n        szabo,\n        finney,\n        ether,\n\n        pub fn toMultiplier(self: Unit) u256;\n        pub fn fromString(str: []const u8) ?Unit;\n        pub fn toString(self: Unit) []const u8;\n    };\n\n    pub fn parseEther(ether_str: []const u8) !u256;\n    pub fn parseGwei(gwei_str: []const u8) !u256;\n    pub fn parseUnits(value_str: []const u8, unit: Unit) !u256;\n\n    pub fn formatEther(allocator: Allocator, wei_value: u256) ![]u8;\n    pub fn formatGwei(allocator: Allocator, wei_value: u256) ![]u8;\n    pub fn formatUnits(allocator: Allocator, wei_value: u256, unit: Unit, decimals: ?u8) ![]u8;\n\n    pub fn convertUnits(value: u256, from_unit: Unit, to_unit: Unit) !u256;\n\n    pub fn calculateGasCost(gas_used: u64, gas_price_gwei: u256) u256;\n    pub fn formatGasCost(allocator: Allocator, gas_used: u64, gas_price_gwei: u256) ![]u8;\n};\n```\n\n#### Usage\n\n```zig\nconst wei = try Numeric.parseEther(\"1.5\");\nconst wei_gwei = try Numeric.parseGwei(\"20\");\nconst wei_custom = try Numeric.parseUnits(\"1.5\", .ether);\n\nconst str = try Numeric.formatEther(allocator, wei_value);\ndefer allocator.free(str);\n\nconst str_gwei = try Numeric.formatGwei(allocator, wei_value);\ndefer allocator.free(str_gwei);\n\nconst converted = try Numeric.convertUnits(1, .ether, .gwei);\n\nconst cost = Numeric.calculateGasCost(gas_used, gas_price_gwei);\n```\n\n---\n\n## Encoding \u0026 Serialization\n\n### `RLP`\n\nRecursive Length Prefix encoding/decoding.\n\n```zig\npub const RLP = struct {\n    pub const Data = union(enum) {\n        String: []const u8,\n        List: []Data,\n\n        pub fn deinit(self: Data, allocator: Allocator) void;\n    };\n\n    pub const Decoded = struct {\n        data: Data,\n        remainder: []const u8,\n    };\n\n    pub fn encode(allocator: Allocator, input: anytype) ![]u8;\n    pub fn encodeBytes(allocator: Allocator, bytes: []const u8) ![]u8;\n    pub fn encodeList(allocator: Allocator, items: []const []const u8) ![]u8;\n\n    pub fn decode(allocator: Allocator, input: []const u8, stream: bool) !Decoded;\n\n    pub fn encodedLength(input: anytype) usize;\n    pub fn isList(data: []const u8) bool;\n};\n```\n\n#### Usage\n\n```zig\nconst encoded = try RLP.encode(allocator, \"hello\");\ndefer allocator.free(encoded);\n\nconst list = [_][]const u8{ \"cat\", \"dog\" };\nconst encoded_list = try RLP.encode(allocator, list);\ndefer allocator.free(encoded_list);\n\nconst decoded = try RLP.decode(allocator, data, false);\ndefer decoded.data.deinit(allocator);\n\nswitch (decoded.data) {\n    .String =\u003e |s| std.debug.print(\"String: {s}\\n\", .{s}),\n    .List =\u003e |l| std.debug.print(\"List with {} items\\n\", .{l.len}),\n}\n\nconst decoded_stream = try RLP.decode(allocator, data, true);\ndefer decoded_stream.data.deinit(allocator);\n```\n\n---\n\n### `ABI`\n\nApplication Binary Interface encoding/decoding for smart contracts.\n\n```zig\npub const ABI = struct {\n    pub const Type = enum {\n        uint8, uint16, uint32, uint64, uint128, uint256,\n        int8, int16, int32, int64, int128, int256,\n        address,\n        bool,\n        bytes1, bytes2, bytes3, bytes4, bytes8, bytes16, bytes32,\n        bytes,\n        string,\n        uint256_array,\n        bytes32_array,\n        address_array,\n        string_array,\n\n        pub fn isDynamic(self: Type) bool;\n        pub fn size(self: Type) ?usize;\n        pub fn getType(self: Type) []const u8;\n    };\n\n    pub const Value = union(Type) {\n        uint8: u8,\n        uint16: u16,\n        uint32: u32,\n        uint64: u64,\n        uint128: u128,\n        uint256: u256,\n        int8: i8,\n        int16: i16,\n        int32: i32,\n        int64: i64,\n        int128: i128,\n        int256: i256,\n        address: Address,\n        bool: bool,\n        bytes1: [1]u8,\n        bytes2: [2]u8,\n        bytes3: [3]u8,\n        bytes4: [4]u8,\n        bytes8: [8]u8,\n        bytes16: [16]u8,\n        bytes32: [32]u8,\n        bytes: []const u8,\n        string: []const u8,\n        uint256_array: []const u256,\n        bytes32_array: []const [32]u8,\n        address_array: []const Address,\n        string_array: []const []const u8,\n\n        pub fn getType(self: Value) Type;\n    };\n\n    pub const Selector = [4]u8;\n\n    pub fn computeSelector(signature: []const u8) Selector;\n    pub fn createSignature(allocator: Allocator, name: []const u8, types: []const Type) ![]u8;\n\n    pub fn encodeParameters(allocator: Allocator, values: []const Value) ![]u8;\n    pub fn decodeParameters(allocator: Allocator, data: []const u8, types: []const Type) ![]Value;\n\n    pub fn encodeFunctionCall(allocator: Allocator, signature: []const u8, values: []const Value) ![]u8;\n    pub fn encodeEventTopic(signature: []const u8) Hash;\n};\n```\n\n#### Usage\n\n```zig\nconst selector = ABI.computeSelector(\"transfer(address,uint256)\");\n\nconst sig = try ABI.createSignature(\n    allocator,\n    \"transfer\",\n    \u0026[_]ABI.Type{ .address, .uint256 }\n);\ndefer allocator.free(sig);\n\nconst values = [_]ABI.Value{\n    .{ .address = recipient },\n    .{ .uint256 = amount },\n};\n\nconst encoded = try ABI.encodeParameters(allocator, \u0026values);\ndefer allocator.free(encoded);\n\nconst calldata = try ABI.encodeFunctionCall(\n    allocator,\n    \"transfer(address,uint256)\",\n    \u0026values\n);\ndefer allocator.free(calldata);\n\nconst types = [_]ABI.Type{ .address, .uint256 };\nconst decoded = try ABI.decodeParameters(allocator, data, \u0026types);\ndefer allocator.free(decoded);\n\nconst topic0 = ABI.encodeEventTopic(\"Transfer(address,address,uint256)\");\n```\n\n---\n\n## Transactions\n\n### Transaction Types\n\n```zig\npub const TransactionType = enum(u8) {\n    legacy = 0x00,\n    eip2930 = 0x01,\n    eip1559 = 0x02,\n    eip4844 = 0x03,\n    eip7702 = 0x04,\n};\n```\n\n### `LegacyTransaction`\n\n```zig\npub const LegacyTransaction = struct {\n    nonce: u64,\n    gas_price: u256,\n    gas_limit: u64,\n    to: ?Address,\n    value: u256,\n    data: []const u8,\n    v: u64,\n    r: [32]u8,\n    s: [32]u8,\n\n    pub fn sign(self: *LegacyTransaction, private_key: [32]u8, chain_id: u64) !void;\n    pub fn serialize(self: LegacyTransaction, allocator: Allocator) ![]u8;\n    pub fn deserialize(allocator: Allocator, data: []const u8) !LegacyTransaction;\n    pub fn hash(self: LegacyTransaction, allocator: Allocator) !Hash;\n    pub fn recoverSender(self: LegacyTransaction) !Address;\n};\n```\n\n### `EIP1559Transaction`\n\n```zig\npub const EIP1559Transaction = struct {\n    chain_id: u64,\n    nonce: u64,\n    max_priority_fee_per_gas: u256,\n    max_fee_per_gas: u256,\n    gas_limit: u64,\n    to: ?Address,\n    value: u256,\n    data: []const u8,\n    access_list: []const AccessListEntry,\n    v: u64,\n    r: [32]u8,\n    s: [32]u8,\n\n    pub fn effectiveGasPrice(self: EIP1559Transaction, base_fee: u256) u256;\n    pub fn validate(self: EIP1559Transaction) !void;\n    pub fn sign(self: *EIP1559Transaction, private_key: [32]u8) !void;\n    pub fn serialize(self: EIP1559Transaction, allocator: Allocator) ![]u8;\n    pub fn deserialize(allocator: Allocator, data: []const u8) !EIP1559Transaction;\n    pub fn hash(self: EIP1559Transaction, allocator: Allocator) !Hash;\n    pub fn recoverSender(self: EIP1559Transaction) !Address;\n};\n```\n\n### `BlobTransaction` (EIP-4844)\n\n```zig\npub const BlobTransaction = struct {\n    chain_id: u64,\n    nonce: u64,\n    max_priority_fee_per_gas: u256,\n    max_fee_per_gas: u256,\n    gas_limit: u64,\n    to: Address,\n    value: u256,\n    data: []const u8,\n    access_list: []const AccessListEntry,\n    max_fee_per_blob_gas: u64,\n    blob_versioned_hashes: []const Hash,\n    v: u64,\n    r: [32]u8,\n    s: [32]u8,\n\n    pub const BYTES_PER_BLOB = 131_072;\n    pub const MAX_BLOBS_PER_TX = 6;\n    pub const BLOB_GAS_PER_BLOB = 131_072;\n\n    pub fn blobGas(self: BlobTransaction) u64;\n    pub fn blobFee(self: BlobTransaction, blob_base_fee: u64) u64;\n    pub fn validate(self: BlobTransaction) !void;\n    pub fn sign(self: *BlobTransaction, private_key: [32]u8) !void;\n    pub fn serialize(self: BlobTransaction, allocator: Allocator) ![]u8;\n    pub fn deserialize(allocator: Allocator, data: []const u8) !BlobTransaction;\n};\n\npub const Blob = [131_072]u8;\npub const BlobCommitment = [48]u8;\npub const BlobProof = [48]u8;\n\npub fn commitmentToVersionedHash(commitment: BlobCommitment) Hash;\npub fn calculateBlobBaseFee(excess_blob_gas: u64) u64;\n```\n\n### `SetCodeTransaction` (EIP-7702)\n\n```zig\npub const SetCodeTransaction = struct {\n    chain_id: u64,\n    nonce: u64,\n    max_priority_fee_per_gas: u256,\n    max_fee_per_gas: u256,\n    gas_limit: u64,\n    to: ?Address,\n    value: u256,\n    data: []const u8,\n    access_list: []const AccessListEntry,\n    authorization_list: []const Authorization,\n    v: u64,\n    r: [32]u8,\n    s: [32]u8,\n\n    pub fn validate(self: SetCodeTransaction) !void;\n    pub fn sign(self: *SetCodeTransaction, private_key: [32]u8) !void;\n    pub fn serialize(self: SetCodeTransaction, allocator: Allocator) ![]u8;\n    pub fn deserialize(allocator: Allocator, data: []const u8) !SetCodeTransaction;\n};\n\npub const Authorization = struct {\n    chain_id: u64,\n    address: Address,\n    nonce: u64,\n    v: u64,\n    r: [32]u8,\n    s: [32]u8,\n\n    pub fn create(chain_id: u64, address: Address, nonce: u64, private_key: [32]u8) !Authorization;\n    pub fn authority(self: Authorization) !Address;\n    pub fn validate(self: Authorization) !void;\n    pub fn signingHash(self: Authorization) !Hash;\n};\n```\n\n---\n\n### `AccessList` (EIP-2930)\n\n```zig\npub const AccessListEntry = struct {\n    address: Address,\n    storage_keys: []const Hash,\n};\n\npub const AccessList = []const AccessListEntry;\n\npub const ACCESS_LIST_ADDRESS_COST = 2400;\npub const ACCESS_LIST_STORAGE_KEY_COST = 1900;\n\npub fn calculateGas(list: AccessList) u64;\npub fn hasAddress(list: AccessList, address: Address) bool;\npub fn hasStorageKey(list: AccessList, address: Address, key: Hash) bool;\npub fn deduplicate(allocator: Allocator, list: AccessList) !AccessList;\npub fn serialize(allocator: Allocator, list: AccessList) ![]u8;\n```\n\n#### Usage\n\n```zig\nconst entry = AccessListEntry{\n    .address = addr,\n    .storage_keys = \u0026[_]Hash{ key1, key2 },\n};\n\nconst list = [_]AccessListEntry{ entry };\n\nconst gas_cost = calculateGas(\u0026list);\nconst has_addr = hasAddress(\u0026list, addr);\nconst has_key = hasStorageKey(\u0026list, addr, key);\n\nconst deduped = try deduplicate(allocator, \u0026list);\ndefer allocator.free(deduped);\n\nconst serialized = try serialize(allocator, \u0026list);\ndefer allocator.free(serialized);\n```\n\n---\n\n## System Contracts\n\n### `BeaconRoots` (EIP-4788)\n\nTrust-minimized access to consensus layer (beacon chain) block roots from within the EVM. Beacon roots are stored in a ring buffer for recent block access without unbounded storage growth.\n\n```zig\npub const BEACON_ROOTS_ADDRESS = Address{\n    .bytes = [_]u8{\n        0x00, 0x0F, 0x3d, 0xf6, 0xD7, 0x32, 0x80, 0x7E,\n        0xf1, 0x31, 0x9f, 0xB7, 0xB8, 0xbB, 0x85, 0x22,\n        0xd0, 0xBe, 0xac, 0x02,\n    },\n};\n\npub const SYSTEM_ADDRESS = Address{\n    .bytes = [_]u8{0xff} ** 18 ++ [_]u8{0xff, 0xfe},\n};\n\npub const HISTORY_BUFFER_LENGTH: u64 = 8191;\npub const BEACON_ROOT_READ_GAS: u64 = 4200;\npub const BEACON_ROOT_WRITE_GAS: u64 = 20000;\n\npub const BeaconRootsContract = struct {\n    database: *Database,\n    allocator: Allocator,\n\n    pub fn execute(\n        self: *Self,\n        caller: Address,\n        input: []const u8,\n        gas_limit: u64,\n    ) !struct { output: []const u8, gas_used: u64 };\n\n    pub fn processBeaconRootUpdate(\n        database: *Database,\n        block_info: *const BlockInfo,\n    ) !void;\n};\n\npub fn computeSlots(timestamp: u64) struct {\n    timestamp_slot: u64,\n    root_slot: u64\n};\n```\n\n#### Usage\n\n```zig\nvar contract = BeaconRootsContract{\n    .database = \u0026database,\n    .allocator = allocator,\n};\n\n// System call to store beacon root (called at block start)\nconst timestamp: u64 = 1710338135;\nconst beacon_root = [_]u8{0xAB} ** 32;\n\nvar input: [64]u8 = undefined;\nstd.mem.writeInt(u256, input[0..32], timestamp, .big);\n@memcpy(input[32..64], \u0026beacon_root);\n\nconst write_result = try contract.execute(\n    SYSTEM_ADDRESS,\n    \u0026input,\n    100000,\n);\ndefer allocator.free(write_result.output);\n\n// Read beacon root for a timestamp\nvar read_input: [32]u8 = undefined;\nstd.mem.writeInt(u256, \u0026read_input, timestamp, .big);\n\nconst read_result = try contract.execute(\n    caller_address,\n    \u0026read_input,\n    10000,\n);\ndefer allocator.free(read_result.output);\n\nif (read_result.output.len == 32) {\n    // Root found\n    const beacon_root_bytes = read_result.output[0..32];\n} else {\n    // Root not available (timestamp too old or not found)\n}\n\n// Process beacon root update at block start\ntry BeaconRootsContract.processBeaconRootUpdate(\u0026database, \u0026block_info);\n\n// Compute ring buffer slots for a timestamp\nconst slots = computeSlots(timestamp);\n// slots.timestamp_slot -\u003e location for beacon root\n// slots.root_slot -\u003e location for timestamp verification\n```\n\n---\n\n## Logs \u0026 Events\n\n### `EventLog`\n\n```zig\npub const EventLog = struct {\n    address: Address,\n    topics: []const Hash,\n    data: []const u8,\n    block_number: ?u64,\n    transaction_hash: ?Hash,\n    transaction_index: ?u32,\n    log_index: ?u32,\n    removed: bool,\n\n    pub fn eventSignature(self: EventLog) ?Hash;\n};\n\npub fn filterLogs(logs: []const EventLog, topics: []const ?Hash) []const EventLog;\n```\n\n#### Usage\n\n```zig\nconst log = EventLog{\n    .address = contract_addr,\n    .topics = \u0026[_]Hash{ topic0, topic1, topic2 },\n    .data = event_data,\n    .block_number = 12345,\n    .transaction_hash = tx_hash,\n    .transaction_index = 0,\n    .log_index = 0,\n    .removed = false,\n};\n\nconst sig = log.eventSignature();\n\nconst filtered = filterLogs(logs, \u0026[_]?Hash{ topic0, null, null });\n```\n\n---\n\n## Gas Constants\n\n```zig\npub const Gas = struct {\n    pub const TX = 21_000;\n    pub const TX_CREATE = 32_000;\n    pub const TX_DATA_ZERO = 4;\n    pub const TX_DATA_NONZERO = 16;\n\n    pub const COLD_ACCOUNT_ACCESS = 2_600;\n    pub const COLD_SLOAD = 2_100;\n    pub const WARM_STORAGE_READ = 100;\n\n    pub const SSTORE_SET = 20_000;\n    pub const SSTORE_RESET = 5_000;\n    pub const SSTORE_CLEAR_REFUND = 15_000;\n\n    pub const CALL = 700;\n    pub const CALL_VALUE = 9_000;\n    pub const CALL_STIPEND = 2_300;\n    pub const NEW_ACCOUNT = 25_000;\n\n    pub const ECRECOVER = 3_000;\n    pub const SHA256_BASE = 60;\n    pub const SHA256_WORD = 12;\n    pub const RIPEMD160_BASE = 600;\n    pub const RIPEMD160_WORD = 120;\n    pub const IDENTITY_BASE = 15;\n    pub const IDENTITY_WORD = 3;\n\n    pub fn memoryExpansion(byte_size: u64) u64;\n    pub fn intrinsic(params: struct {\n        data: []const u8,\n        is_creation: bool,\n        access_list: ?AccessList,\n    }) u64;\n};\n```\n\n#### Usage\n\n```zig\nconst base_cost = Gas.TX;\nconst create_cost = Gas.TX_CREATE;\n\nconst memory_cost = Gas.memoryExpansion(1024);\n\nconst intrinsic_gas = Gas.intrinsic(.{\n    .data = calldata,\n    .is_creation = false,\n    .access_list = null,\n});\n```\n\n---\n\n## Opcodes\n\nEVM opcode enumeration with categorization and utility methods.\n\n```zig\npub const Opcode = enum(u8) {\n    STOP = 0x00,\n    ADD = 0x01,\n    MUL = 0x02,\n    SUB = 0x03,\n    DIV = 0x04,\n    SDIV = 0x05,\n    MOD = 0x06,\n    SMOD = 0x07,\n    ADDMOD = 0x08,\n    MULMOD = 0x09,\n    EXP = 0x0a,\n    SIGNEXTEND = 0x0b,\n    LT = 0x10,\n    GT = 0x11,\n    SLT = 0x12,\n    SGT = 0x13,\n    EQ = 0x14,\n    ISZERO = 0x15,\n    AND = 0x16,\n    OR = 0x17,\n    XOR = 0x18,\n    NOT = 0x19,\n    BYTE = 0x1a,\n    SHL = 0x1b,\n    SHR = 0x1c,\n    SAR = 0x1d,\n    KECCAK256 = 0x20,\n    ADDRESS = 0x30,\n    BALANCE = 0x31,\n    ORIGIN = 0x32,\n    CALLER = 0x33,\n    CALLVALUE = 0x34,\n    CALLDATALOAD = 0x35,\n    CALLDATASIZE = 0x36,\n    CALLDATACOPY = 0x37,\n    CODESIZE = 0x38,\n    CODECOPY = 0x39,\n    GASPRICE = 0x3a,\n    EXTCODESIZE = 0x3b,\n    EXTCODECOPY = 0x3c,\n    RETURNDATASIZE = 0x3d,\n    RETURNDATACOPY = 0x3e,\n    EXTCODEHASH = 0x3f,\n    BLOCKHASH = 0x40,\n    COINBASE = 0x41,\n    TIMESTAMP = 0x42,\n    NUMBER = 0x43,\n    PREVRANDAO = 0x44,\n    GASLIMIT = 0x45,\n    CHAINID = 0x46,\n    SELFBALANCE = 0x47,\n    BASEFEE = 0x48,\n    BLOBHASH = 0x49,\n    BLOBBASEFEE = 0x4a,\n    POP = 0x50,\n    MLOAD = 0x51,\n    MSTORE = 0x52,\n    MSTORE8 = 0x53,\n    SLOAD = 0x54,\n    SSTORE = 0x55,\n    JUMP = 0x56,\n    JUMPI = 0x57,\n    PC = 0x58,\n    MSIZE = 0x59,\n    GAS = 0x5a,\n    JUMPDEST = 0x5b,\n    TLOAD = 0x5c,\n    TSTORE = 0x5d,\n    MCOPY = 0x5e,\n    PUSH0 = 0x5f,\n    PUSH1 = 0x60,\n    PUSH2 = 0x61,\n    // ... PUSH3-PUSH31\n    PUSH32 = 0x7f,\n    DUP1 = 0x80,\n    DUP2 = 0x81,\n    // ... DUP3-DUP15\n    DUP16 = 0x8f,\n    SWAP1 = 0x90,\n    SWAP2 = 0x91,\n    // ... SWAP3-SWAP15\n    SWAP16 = 0x9f,\n    LOG0 = 0xa0,\n    LOG1 = 0xa1,\n    LOG2 = 0xa2,\n    LOG3 = 0xa3,\n    LOG4 = 0xa4,\n    CREATE = 0xf0,\n    CALL = 0xf1,\n    CALLCODE = 0xf2,\n    RETURN = 0xf3,\n    DELEGATECALL = 0xf4,\n    CREATE2 = 0xf5,\n    AUTH = 0xf6,\n    AUTHCALL = 0xf7,\n    STATICCALL = 0xfa,\n    REVERT = 0xfd,\n    INVALID = 0xfe,\n    SELFDESTRUCT = 0xff,\n\n    pub fn isPush(self: Opcode) bool;\n    pub fn pushSize(self: Opcode) u8;\n    pub fn isDup(self: Opcode) bool;\n    pub fn dupPosition(self: Opcode) u8;\n    pub fn isSwap(self: Opcode) bool;\n    pub fn swapPosition(self: Opcode) u8;\n    pub fn isLog(self: Opcode) bool;\n    pub fn logTopics(self: Opcode) u8;\n    pub fn isTerminating(self: Opcode) bool;\n    pub fn isStateModifying(self: Opcode) bool;\n    pub fn isArithmetic(self: Opcode) bool;\n    pub fn isComparison(self: Opcode) bool;\n    pub fn isBitwise(self: Opcode) bool;\n    pub fn name(self: Opcode) []const u8;\n};\n```\n\n### Opcode Categories\n\n#### PUSH Operations (0x5f-0x7f)\n\n```zig\nif (opcode.isPush()) {\n    const size = opcode.pushSize(); // 0-32 bytes\n}\n```\n\n#### DUP Operations (0x80-0x8f)\n\n```zig\nif (opcode.isDup()) {\n    const position = opcode.dupPosition(); // 1-16\n}\n```\n\n#### SWAP Operations (0x90-0x9f)\n\n```zig\nif (opcode.isSwap()) {\n    const position = opcode.swapPosition(); // 1-16\n}\n```\n\n#### LOG Operations (0xa0-0xa4)\n\n```zig\nif (opcode.isLog()) {\n    const topics = opcode.logTopics(); // 0-4\n}\n```\n\n### Opcode Classification\n\n```zig\n// Terminating opcodes\nif (opcode.isTerminating()) {\n    // STOP, RETURN, REVERT, INVALID, SELFDESTRUCT\n}\n\n// State-modifying opcodes\nif (opcode.isStateModifying()) {\n    // SSTORE, TSTORE, LOG*, CREATE*, CALL*, SELFDESTRUCT\n}\n\n// Arithmetic opcodes\nif (opcode.isArithmetic()) {\n    // ADD, MUL, SUB, DIV, SDIV, MOD, SMOD, ADDMOD, MULMOD, EXP, SIGNEXTEND\n}\n\n// Comparison opcodes\nif (opcode.isComparison()) {\n    // LT, GT, SLT, SGT, EQ, ISZERO\n}\n\n// Bitwise opcodes\nif (opcode.isBitwise()) {\n    // AND, OR, XOR, NOT, BYTE, SHL, SHR, SAR\n}\n```\n\n#### Usage\n\n```zig\nconst opcode = Opcode.PUSH1;\n\nif (opcode.isPush()) {\n    const bytes_to_read = opcode.pushSize();\n    // Read bytes_to_read bytes following the opcode\n}\n\nconst name = opcode.name(); // \"PUSH1\"\n\n// Check opcode properties\nif (Opcode.SSTORE.isStateModifying()) {\n    // Handle state modification\n}\n\nif (Opcode.RETURN.isTerminating()) {\n    // End execution\n}\n\n// Categorize opcodes\nswitch (opcode) {\n    .ADD, .MUL, .SUB =\u003e {\n        // Arithmetic operations\n    },\n    .PUSH1...PUSH32 =\u003e {\n        const size = opcode.pushSize();\n        // Handle PUSH operations\n    },\n    .DUP1...DUP16 =\u003e {\n        const pos = opcode.dupPosition();\n        // Handle DUP operations\n    },\n    else =\u003e {},\n}\n```\n\n---\n\n## EIP Configuration\n\n### `Eips`\n\nEthereum Improvement Proposal (EIP) configuration system that consolidates all EIP-specific logic for the EVM. Provides hardfork-based feature detection and gas cost calculations with support for custom EIP overrides.\n\n```zig\npub const Hardfork = enum {\n    FRONTIER,\n    HOMESTEAD,\n    DAO,\n    TANGERINE_WHISTLE,\n    SPURIOUS_DRAGON,\n    BYZANTIUM,\n    CONSTANTINOPLE,\n    PETERSBURG,\n    ISTANBUL,\n    MUIR_GLACIER,\n    BERLIN,\n    LONDON,\n    ARROW_GLACIER,\n    GRAY_GLACIER,\n    MERGE,\n    SHANGHAI,\n    CANCUN,\n    PRAGUE,\n};\n\npub const EipOverride = struct {\n    eip: u16,\n    enabled: bool,\n};\n\npub const Eips = struct {\n    hardfork: Hardfork,\n    overrides: []const EipOverride = \u0026.{},\n\n    // Feature detection\n    pub fn is_eip_active(self: Self, eip: u16) bool;\n    pub fn get_active_eips(self: Self) []const u16;\n\n    // Opcode availability\n    pub fn eip_3855_push0_enabled(self: Self) bool;\n    pub fn eip_3198_basefee_opcode_enabled(self: Self) bool;\n    pub fn eip_1153_transient_storage_enabled(self: Self) bool;\n    pub fn eip_5656_has_mcopy(self: Self) bool;\n\n    // Transaction types\n    pub fn eip_1559_is_enabled(self: Self) bool;\n    pub fn eip_4844_blob_transactions_enabled(self: Self) bool;\n    pub fn eip_7702_eoa_code_enabled(self: Self) bool;\n\n    // Gas costs\n    pub fn eip_2929_cold_sload_cost(self: Self) u64;\n    pub fn eip_2929_warm_storage_read_cost(self: Self) u64;\n    pub fn eip_2929_cold_account_access_cost(self: Self) u64;\n    pub fn eip_2929_warm_account_access_cost(self: Self) u64;\n    pub fn eip_3529_gas_refund_cap(self: Self, gas_used: u64, refund_counter: u64) u64;\n    pub fn eip_2028_calldata_gas_cost(self: Self, is_zero: bool) u64;\n    pub fn eip_160_exp_byte_gas_cost(self: Self) u64;\n    pub fn sstore_gas_cost(self: Self, current: u256, new: u256, original: u256) SstoreGasCost;\n\n    // Code limits\n    pub fn eip_170_max_code_size(self: Self) u32;\n    pub fn eip_3860_size_limit(self: Self) u64;\n    pub fn eip_3860_word_cost(self: Self) u64;\n\n    // Behavior changes\n    pub fn eip_6780_selfdestruct_same_transaction_only(self: Self) bool;\n    pub fn eip_3541_should_reject_ef_bytecode(self: Self) bool;\n    pub fn eip_4399_use_prevrandao(self: Self) bool;\n\n    // Warming \u0026 access lists\n    pub fn pre_warm_transaction_addresses(\n        self: Self,\n        access_list: *AccessList,\n        origin: Address,\n        target: ?Address,\n        coinbase: Address,\n    ) !void;\n};\n```\n\n### Major EIP Groups by Hardfork\n\n#### Berlin (EIP-2929, EIP-2930)\n- **EIP-2929**: Gas cost increases for state access opcodes\n- **EIP-2930**: Optional access lists in transactions\n\n#### London (EIP-1559, EIP-3198, EIP-3529, EIP-3541)\n- **EIP-1559**: Fee market change with base fee per gas\n- **EIP-3198**: BASEFEE opcode\n- **EIP-3529**: Reduction in gas refunds (1/5 instead of 1/2)\n- **EIP-3541**: Reject contracts starting with 0xEF\n\n#### Shanghai (EIP-3651, EIP-3855, EIP-3860)\n- **EIP-3651**: Warm COINBASE address\n- **EIP-3855**: PUSH0 instruction\n- **EIP-3860**: Limit and meter initcode (48KB limit, 2 gas/word)\n\n#### Cancun (EIP-1153, EIP-4788, EIP-4844, EIP-5656, EIP-6780)\n- **EIP-1153**: Transient storage opcodes (TLOAD/TSTORE)\n- **EIP-4788**: Beacon block root in the EVM\n- **EIP-4844**: Shard blob transactions\n- **EIP-5656**: MCOPY instruction\n- **EIP-6780**: SELFDESTRUCT only in same transaction\n\n#### Prague (EIP-2537, EIP-2935, EIP-6110, EIP-7002, EIP-7702)\n- **EIP-2537**: BLS12-381 precompile operations\n- **EIP-2935**: Historical block hashes from state\n- **EIP-6110**: Validator deposits on chain\n- **EIP-7002**: Execution layer triggerable exits\n- **EIP-7702**: Set EOA account code\n\n### Usage\n\n#### Basic Configuration\n\n```zig\nconst eips = Eips{ .hardfork = .CANCUN };\n\n// Check if specific EIPs are active\nif (eips.is_eip_active(1559)) {\n    // EIP-1559 fee market is active\n}\n\nif (eips.eip_4844_blob_transactions_enabled()) {\n    // Handle blob transactions\n}\n\n// Get all active EIPs for current hardfork\nconst active_eips = eips.get_active_eips();\nfor (active_eips) |eip_num| {\n    std.debug.print(\"EIP-{}: active\\n\", .{eip_num});\n}\n```\n\n#### Gas Cost Calculations\n\n```zig\nconst berlin = Eips{ .hardfork = .BERLIN };\nconst istanbul = Eips{ .hardfork = .ISTANBUL };\n\n// EIP-2929: Cold storage costs\nconst cold_sload = berlin.eip_2929_cold_sload_cost(); // 2100\nconst warm_sload = berlin.eip_2929_warm_storage_read_cost(); // 100\n\n// Pre-Berlin costs\nconst old_sload = istanbul.eip_2929_cold_sload_cost(); // 200\n\n// EIP-3529: Gas refund cap\nconst london = Eips{ .hardfork = .LONDON };\nconst gas_used: u64 = 100_000;\nconst refund_counter: u64 = 50_000;\n\n// London: refund capped at 1/5 of gas used\nconst refund = london.eip_3529_gas_refund_cap(gas_used, refund_counter); // 20,000\n\n// Pre-London: capped at 1/2\nconst old_refund = istanbul.eip_3529_gas_refund_cap(gas_used, refund_counter); // 50,000\n\n// EIP-2028: Calldata gas costs\nconst zero_byte_cost = london.eip_2028_calldata_gas_cost(true); // 4\nconst nonzero_byte_cost = london.eip_2028_calldata_gas_cost(false); // 16\n```\n\n#### SSTORE Gas Costs\n\n```zig\nconst eips = Eips{ .hardfork = .LONDON };\n\nconst current: u256 = 0;\nconst new: u256 = 1;\nconst original: u256 = 0;\n\nconst cost = eips.sstore_gas_cost(current, new, original);\n// cost.gas = 20000 (setting from zero)\n// cost.refund = 0\n\n// Clearing storage\nconst clear_cost = eips.sstore_gas_cost(1, 0, 1);\n// clear_cost.gas = 5000\n// clear_cost.refund = 4800 (reduced by EIP-3529 in London)\n```\n\n#### Code Size Limits\n\n```zig\nconst spurious = Eips{ .hardfork = .SPURIOUS_DRAGON };\nconst shanghai = Eips{ .hardfork = .SHANGHAI };\n\n// EIP-170: Contract code size limit\nconst max_code = spurious.eip_170_max_code_size(); // 24,576 (0x6000)\n\n// EIP-3860: Initcode size limits\nconst init_limit_pre = spurious.size_limit(); // 24,576 bytes\nconst init_limit_post = shanghai.size_limit(); // 49,152 bytes (48KB)\n\nconst word_cost = shanghai.word_cost(); // 2 gas per word\n```\n\n#### Transaction Address Warming\n\n```zig\nconst shanghai = Eips{ .hardfork = .SHANGHAI };\nvar access_list = AccessList.init(allocator);\ndefer access_list.deinit();\n\n// Pre-warm addresses for transaction execution\n// Includes: origin, target, and coinbase (EIP-3651)\ntry shanghai.pre_warm_transaction_addresses(\n    \u0026access_list,\n    tx_origin,\n    tx_target,\n    block_coinbase,\n);\n```\n\n#### Custom EIP Overrides\n\n```zig\n// Enable future EIPs on older hardfork for testing\nconst custom = Eips{\n    .hardfork = .LONDON,\n    .overrides = \u0026[_]EipOverride{\n        .{ .eip = 3855, .enabled = true }, // Enable PUSH0\n        .{ .eip = 3860, .enabled = true }, // Enable initcode metering\n    },\n};\n\nif (custom.eip_3855_push0_enabled()) {\n    // PUSH0 is now available on London\n}\n\n// Disable specific EIPs for testing\nconst restricted = Eips{\n    .hardfork = .CANCUN,\n    .overrides = \u0026[_]EipOverride{\n        .{ .eip = 4844, .enabled = false }, // Disable blob transactions\n        .{ .eip = 1153, .enabled = false }, // Disable transient storage\n    },\n};\n\nif (!restricted.eip_4844_blob_transactions_enabled()) {\n    // Blob transactions disabled despite Cancun hardfork\n}\n```\n\n#### Behavior Checks\n\n```zig\nconst cancun = Eips{ .hardfork = .CANCUN };\nconst london = Eips{ .hardfork = .LONDON };\nconst merge = Eips{ .hardfork = .MERGE };\n\n// EIP-6780: Restrict SELFDESTRUCT behavior\nif (cancun.eip_6780_selfdestruct_same_transaction_only()) {\n    // Only destroy contracts created in same transaction\n}\n\n// EIP-3541: Reject 0xEF bytecode\nconst bytecode = [_]u8{ 0xEF, 0x00, 0x01 };\nif (london.eip_3541_should_reject_ef_bytecode()) {\n    if (london.eip_3541_should_reject_create_with_ef_bytecode(\u0026bytecode)) {\n        return error.InvalidBytecode; // Reject EIP-3540 magic\n    }\n}\n\n// EIP-4399: PREVRANDAO vs DIFFICULTY\nif (merge.eip_4399_use_prevrandao()) {\n    // Use PREVRANDAO opcode instead of DIFFICULTY\n}\n```\n\n---\n\n## Error Handling\n\nAll fallible operations return Zig error unions:\n\n```zig\npub const AddressError = error{\n    InvalidFormat,\n    InvalidLength,\n    InvalidChecksum,\n};\n\npub const HexError = error{\n    InvalidFormat,\n    InvalidLength,\n    InvalidCharacter,\n    OddLength,\n    ValueTooLarge,\n};\n\npub const NumericError = error{\n    InvalidInput,\n    InvalidUnit,\n    InvalidFormat,\n    ValueTooLarge,\n};\n\npub const RLPError = error{\n    InputTooShort,\n    InputTooLong,\n    InvalidLength,\n    NonCanonical,\n    InvalidRemainder,\n    LeadingZeros,\n};\n\npub const ABIError = error{\n    InvalidSelector,\n    InvalidType,\n    InvalidData,\n    DataTooSmall,\n    OutOfBounds,\n    InvalidAddress,\n};\n\npub const TransactionError = error{\n    InvalidSignature,\n    InvalidChainId,\n    InvalidType,\n};\n```\n\n#### Usage\n\n```zig\nconst addr = Address.fromHex(hex_str) catch |err| switch (err) {\n    error.InvalidFormat =\u003e return error.BadInput,\n    error.InvalidChecksum =\u003e {\n        std.log.warn(\"Checksum failed\\n\", .{});\n        return error.InvalidChecksum;\n    },\n    else =\u003e return err,\n};\n```\n\n---\n\n## State Management\n\n### `StorageKey`\n\nComposite key for EVM storage (address + slot).\n\n```zig\npub const StorageKey = struct {\n    address: Address,\n    slot: u256,\n\n    pub fn hash(self: StorageKey, hasher: anytype) void;\n    pub fn eql(a: StorageKey, b: StorageKey) bool;\n};\n```\n\n#### Usage\n\n```zig\nvar storage = std.AutoHashMap(StorageKey, u256).init(allocator);\ndefer storage.deinit();\n\nconst key = StorageKey{ .address = addr, .slot = 0 };\ntry storage.put(key, value);\n\nconst stored_value = storage.get(key);\n```\n\n### State Constants\n\n```zig\npub const EMPTY_CODE_HASH: [32]u8 = .{\n    0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c,\n    0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0,\n    0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b,\n    0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70,\n};\n\npub const EMPTY_TRIE_ROOT: [32]u8 = .{\n    0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6,\n    0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e,\n    0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0,\n    0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21,\n};\n```\n\n---\n\n## Module Structure\n\n```zig\nconst primitives = @import(\"primitives\");\n\npub const Address = primitives.Address;\npub const Hash = primitives.Hash;\n\npub const Hex = primitives.Hex;\npub const RLP = primitives.RLP;\npub const ABI = primitives.ABI;\n\npub const Numeric = primitives.Numeric;\npub const Gas = primitives.Gas;\n\npub const LegacyTransaction = primitives.LegacyTransaction;\npub const EIP1559Transaction = primitives.EIP1559Transaction;\npub const BlobTransaction = primitives.BlobTransaction;\npub const SetCodeTransaction = primitives.SetCodeTransaction;\n\npub const AccessList = primitives.AccessList;\npub const AccessListEntry = primitives.AccessListEntry;\npub const EventLog = primitives.EventLog;\n\npub const StorageKey = primitives.StorageKey;\npub const EMPTY_CODE_HASH = primitives.EMPTY_CODE_HASH;\npub const EMPTY_TRIE_ROOT = primitives.EMPTY_TRIE_ROOT;\n```\n\n---\n\n## Examples\n\n### Creating and Signing a Transaction\n\n```zig\nconst std = @import(\"std\");\nconst primitives = @import(\"primitives\");\n\npub fn main() !void {\n    var gpa = std.heap.GeneralPurposeAllocator(.{}){};\n    defer _ = gpa.deinit();\n    const allocator = gpa.allocator();\n\n    var tx = primitives.EIP1559Transaction{\n        .chain_id = 1,\n        .nonce = 42,\n        .max_priority_fee_per_gas = try primitives.Numeric.parseGwei(\"2\"),\n        .max_fee_per_gas = try primitives.Numeric.parseGwei(\"20\"),\n        .gas_limit = 21_000,\n        .to = try primitives.Address.fromHex(\"0x742d35Cc6641C91B6E4bb6ac9e3ff2958c94E676\"),\n        .value = try primitives.Numeric.parseEther(\"1.5\"),\n        .data = \u0026[_]u8{},\n        .access_list = \u0026[_]primitives.AccessListEntry{},\n        .v = 0,\n        .r = [_]u8{0} ** 32,\n        .s = [_]u8{0} ** 32,\n    };\n\n    const private_key = try primitives.Hex.decodeFixed(32, \"0x...\");\n    try tx.sign(private_key);\n\n    const serialized = try tx.serialize(allocator);\n    defer allocator.free(serialized);\n\n    const tx_hash = try tx.hash(allocator);\n\n    std.debug.print(\"Transaction hash: {}\\n\", .{tx_hash});\n}\n```\n\n### Encoding a Contract Call\n\n```zig\nconst primitives = @import(\"primitives\");\n\nconst recipient = try primitives.Address.fromHex(\"0x...\");\nconst amount = try primitives.Numeric.parseEther(\"100\");\n\nconst values = [_]primitives.ABI.Value{\n    .{ .address = recipient },\n    .{ .uint256 = amount },\n};\n\nconst calldata = try primitives.ABI.encodeFunctionCall(\n    allocator,\n    \"transfer(address,uint256)\",\n    \u0026values\n);\ndefer allocator.free(calldata);\n```\n\n### Decoding Event Logs\n\n```zig\nconst event_signature = primitives.ABI.encodeEventTopic(\n    \"Transfer(address,address,uint256)\"\n);\n\nfor (logs) |log| {\n    if (log.topics.len \u003e 0 and log.topics[0].eql(event_signature)) {\n        const from = primitives.Address.fromBytes(log.topics[1].bytes[12..32]);\n        const to = primitives.Address.fromBytes(log.topics[2].bytes[12..32]);\n\n        const types = [_]primitives.ABI.Type{.uint256};\n        const values = try primitives.ABI.decodeParameters(allocator, log.data, \u0026types);\n        defer allocator.free(values);\n\n        const amount = values[0].uint256;\n\n        std.debug.print(\"Transfer from {} to {}: {}\\n\", .{ from, to, amount });\n    }\n}\n```\n\n---\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevmts%2Fprimitives","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fevmts%2Fprimitives","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevmts%2Fprimitives/lists"}