[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-deep-evm-11-huff-jump-tables-function-dispatch":3},{"article":4,"author":59},{"id":5,"category_id":6,"title":7,"slug":8,"excerpt":9,"content_md":10,"content_html":11,"locale":12,"author_id":13,"published":14,"published_at":15,"meta_title":16,"meta_description":17,"focus_keyword":18,"og_image":19,"canonical_url":19,"robots_meta":20,"created_at":15,"updated_at":15,"tags":21,"category_name":39,"related_articles":40},"d0000000-0000-0000-0000-000000000111","a0000000-0000-0000-0000-000000000002","Deep EVM #11: Huff Jump Tables — O(1) Function Dispatch Without Solidity Overhead","deep-evm-11-huff-jump-tables-function-dispatch","Build an O(1) function dispatcher in Huff using packed jump tables. Compare Solidity's if-else chain with a hand-crafted jump table that saves thousands of gas.","## The Problem with Solidity's Dispatcher\n\nWhen you call a Solidity contract, the first thing the EVM executes is the function dispatcher. Solidity generates a linear if-else chain that compares the calldata's first 4 bytes (the function selector) against each known selector:\n\n```\nCALLDATALOAD 0x00\nSHR 224\nDUP1\nPUSH4 0x70a08231    \u002F\u002F balanceOf(address)\nEQ\nPUSH2 dest1\nJUMPI\nDUP1\nPUSH4 0xa9059cbb    \u002F\u002F transfer(address,uint256)\nEQ\nPUSH2 dest2\nJUMPI\n...\n```\n\nFor a contract with N functions, this is O(N) — the worst case checks all N selectors before finding a match. Each comparison costs roughly 22 gas (DUP1 + PUSH4 + EQ + PUSH2 + JUMPI). A contract with 20 functions wastes up to 440 gas just on dispatch. Solidity's compiler (since 0.8.22) sometimes uses a binary search tree for large interfaces, bringing it to O(log N), but this is still suboptimal.\n\nFor an MEV bot contract that gets called millions of times, those 400+ gas units per call add up to real ETH.\n\n## The Jump Table Approach: O(1)\n\nA jump table maps a function selector directly to a code offset using arithmetic, not comparison. The idea is borrowed from CPU architecture — computed GOTOs have been used in systems programming since the 1960s.\n\nThe concept:\n\n1. Extract the function selector from calldata (4 bytes).\n2. Use arithmetic to compute a jump destination from the selector.\n3. JUMP directly to that destination.\n\nNo comparisons, no branching, constant time regardless of how many functions exist.\n\n### Approach 1: Minimal Selector Encoding\n\nIf your contract has a small number of functions (1-8), you can assign selectors manually by mining vanity selectors where the first byte or two encode a unique small integer:\n\n```huff\n#define macro DISPATCHER() = takes(0) returns(0) {\n    0x00 calldataload       \u002F\u002F [calldata_word]\n    0xe0 shr                \u002F\u002F [selector]\n\n    \u002F\u002F Extract routing byte — first byte of selector\n    0x18 shr                \u002F\u002F [first_byte]\n\n    \u002F\u002F Each entry in our table is 2 bytes (PUSH2 offset)\n    \u002F\u002F Table start + first_byte * 2 = jump destination pointer\n    0x02 mul                \u002F\u002F [offset_in_table]\n    __tablestart(JumpTable) \u002F\u002F [table_start, offset_in_table]\n    add                     \u002F\u002F [entry_address]\n    \n    \u002F\u002F Load the 2-byte jump destination from the table\n    codecopy_dest:\n    0x00 codecopy           \u002F\u002F copy 2 bytes from code to memory\n    0x00 mload              \u002F\u002F [jump_dest (padded to 32 bytes)]\n    0xf0 shr                \u002F\u002F [jump_dest] — shift right to get 2-byte value\n    jump                    \u002F\u002F GOTO function body\n}\n\n#define jumptable JumpTable {\n    swap_exact   \u002F\u002F selector 0x00...\n    add_liq      \u002F\u002F selector 0x01...\n    remove_liq   \u002F\u002F selector 0x02...\n    flash_loan   \u002F\u002F selector 0x03...\n}\n```\n\n### Approach 2: Packed Code Table\n\nFor maximum density, you can pack the jump table directly into the bytecode using `__tablestart` and `__tablesize`. Huff natively supports jump tables as first-class constructs:\n\n```huff\n#define jumptable__packed SELECTOR_TABLE {\n    fn_swap\n    fn_transfer\n    fn_approve\n    fn_balance\n}\n\n#define macro MAIN() = takes(0) returns(0) {\n    0x00 calldataload 0xe0 shr  \u002F\u002F [selector]\n\n    \u002F\u002F Map selector to index (0-3)\n    \u002F\u002F This depends on your selector values — you mine them\n    \u002F\u002F so the top byte is the index\n    0x18 shr                    \u002F\u002F [index]\n\n    \u002F\u002F Bounds check\n    dup1 0x04 lt                \u002F\u002F [in_bounds?, index]\n    valid jumpi\n    0x00 0x00 revert\n\n    valid:\n    \u002F\u002F Load from packed table (2 bytes per entry)\n    __tablestart(SELECTOR_TABLE)\n    swap1 0x02 mul add          \u002F\u002F [table_entry_ptr]\n\n    \u002F\u002F Read 2-byte code offset\n    0x1e mload                  \u002F\u002F trick: mload from code via codecopy\n    jump\n\n    fn_swap:\n        SWAP_IMPL()\n    fn_transfer:\n        TRANSFER_IMPL()\n    fn_approve:\n        APPROVE_IMPL()\n    fn_balance:\n        BALANCE_IMPL()\n}\n```\n\n## Calldata Bit-Shifting for Selector Extraction\n\nThe standard selector extraction is:\n\n```huff\n0x00 calldataload   \u002F\u002F loads 32 bytes from calldata position 0\n0xe0 shr            \u002F\u002F shift right 224 bits (256 - 32) to isolate top 4 bytes\n```\n\nThis costs: PUSH1 (3) + CALLDATALOAD (3) + PUSH1 (3) + SHR (3) = 12 gas.\n\nA cheaper alternative when you only need 1-2 bytes of the selector:\n\n```huff\n0x00 calldataload   \u002F\u002F [calldata_word]\n0xf8 shr            \u002F\u002F [first_byte] — shift right 248 bits to get just byte 0\n```\n\nSame gas, but now your routing key is 1 byte (256 possible values). If your contract has \u003C= 256 functions, one byte is enough to uniquely identify each. You mine vanity selectors (using `cast selectors` or a custom script) so that each function's first selector byte is unique.\n\n## Real Example: A 61-Byte DEX Router\n\nHere is a minimal swap contract that routes between two operations — `swapExactIn` and `swapExactOut` — in just 61 bytes of runtime bytecode:\n\n```huff\n#define constant POOL = 0x...       \u002F\u002F Uniswap V2 pair address\n#define constant TOKEN0 = 0x...     \u002F\u002F token0 address\n#define constant TOKEN1 = 0x...     \u002F\u002F token1 address\n\n#define macro MAIN() = takes(0) returns(0) {\n    \u002F\u002F Selector: 1 byte at calldata[0]\n    0x00 calldataload 0xf8 shr  \u002F\u002F [route_byte]\n\n    \u002F\u002F route_byte == 0 → swapExactIn, else → swapExactOut\n    swap_out jumpi\n\n    \u002F\u002F --- swapExactIn ---\n    SWAP_EXACT_IN()\n    stop\n\n    swap_out:\n    SWAP_EXACT_OUT()\n    stop\n}\n\n#define macro SWAP_EXACT_IN() = takes(0) returns(0) {\n    \u002F\u002F Read amount from calldata[1..33]\n    0x01 calldataload           \u002F\u002F [amountIn]\n\n    \u002F\u002F Transfer token0 to pool\n    \u002F\u002F ... ERC20 transfer calldata assembly ...\n\n    \u002F\u002F Call pool.swap(amountOut, 0, address(this), \"\")\n    \u002F\u002F ... swap calldata assembly ...\n}\n```\n\nThe entire dispatcher is 7 bytes: `PUSH1 0x00 CALLDATALOAD PUSH1 0xf8 SHR PUSH2 dest JUMPI`. Compare that to Solidity's 40+ byte dispatcher.\n\n## Gas Comparison\n\nLet us benchmark dispatch cost for varying function counts:\n\n| Functions | Solidity (if-else) | Solidity (binary) | Huff Jump Table |\n|-----------|-------------------|-------------------|------------------|\n| 2         | 22-44 gas         | 22-44 gas         | 15 gas           |\n| 4         | 22-88 gas         | 22-66 gas         | 15 gas           |\n| 8         | 22-176 gas        | 22-88 gas         | 15 gas           |\n| 16        | 22-352 gas        | 22-110 gas        | 15 gas           |\n| 32        | 22-704 gas        | 22-132 gas        | 15 gas           |\n\nThe jump table cost is constant: CALLDATALOAD (3) + SHR (3) + arithmetic (3-6) + JUMP (8) = ~15-18 gas. It never changes regardless of function count.\n\n## Mining Vanity Selectors\n\nFor the jump table approach to work, you need function selectors with predictable routing bytes. You can mine these:\n\n```python\nimport hashlib\nimport itertools\n\ntarget_byte = 0x00  # desired first byte of selector\nbase_name = \"swap\"\n\nfor suffix in itertools.count():\n    name = f\"{base_name}{suffix}(uint256,address)\"\n    selector = hashlib.sha3_256(name.encode()).digest()[:4]  # keccak256\n    if selector[0] == target_byte:\n        print(f\"Found: {name} -> 0x{selector.hex()}\")\n        break\n```\n\nIn practice, we use `cast sig` from Foundry or a Rust tool that brute-forces function names to find selectors with desired prefixes. For a contract with 8 functions, mining 8 compatible selectors takes milliseconds.\n\n## Bytecode Size Impact\n\nBytecode size directly affects deployment cost (200 gas per byte via CREATE, 32,000 base). A comparison:\n\n| Approach | Runtime Bytecode | Deploy Gas (runtime only) |\n|----------|-----------------|---------------------------|\n| Solidity (8 funcs) | ~800 bytes | 160,000 gas |\n| Huff jump table (8 funcs) | ~200 bytes | 40,000 gas |\n| Huff minimal (2 funcs) | ~61 bytes | 12,200 gas |\n\nFor MEV bots that redeploy contracts frequently (to rotate addresses or update parameters via CREATE2), smaller bytecode directly translates to lower operational costs.\n\n## Limitations\n\n1. **Non-standard ABI** — External tools (Etherscan, wallets) cannot decode your calldata without custom ABI definitions.\n2. **Selector mining** — Requires upfront work and constrains function naming.\n3. **Maintenance cost** — Huff is harder to audit and modify than Solidity.\n4. **Packed tables** — The `__tablestart` and `__tablesize` builtins have edge cases with the Huff compiler; test thoroughly.\n\n## Summary\n\nJump tables replace Solidity's O(N) dispatch chain with O(1) computed jumps. The gas savings compound across millions of calls — a meaningful advantage for high-frequency contracts like MEV bots and DEX routers. Combined with vanity selector mining, you can build contracts where the dispatch overhead is under 20 gas regardless of interface size. In the next article, we will explore advanced Huff patterns: adaptive execution, multi-operator auth, and memory layout tricks.","\u003Ch2 id=\"the-problem-with-solidity-s-dispatcher\">The Problem with Solidity’s Dispatcher\u003C\u002Fh2>\n\u003Cp>When you call a Solidity contract, the first thing the EVM executes is the function dispatcher. Solidity generates a linear if-else chain that compares the calldata’s first 4 bytes (the function selector) against each known selector:\u003C\u002Fp>\n\u003Cpre>\u003Ccode>CALLDATALOAD 0x00\nSHR 224\nDUP1\nPUSH4 0x70a08231    \u002F\u002F balanceOf(address)\nEQ\nPUSH2 dest1\nJUMPI\nDUP1\nPUSH4 0xa9059cbb    \u002F\u002F transfer(address,uint256)\nEQ\nPUSH2 dest2\nJUMPI\n...\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>For a contract with N functions, this is O(N) — the worst case checks all N selectors before finding a match. Each comparison costs roughly 22 gas (DUP1 + PUSH4 + EQ + PUSH2 + JUMPI). A contract with 20 functions wastes up to 440 gas just on dispatch. Solidity’s compiler (since 0.8.22) sometimes uses a binary search tree for large interfaces, bringing it to O(log N), but this is still suboptimal.\u003C\u002Fp>\n\u003Cp>For an MEV bot contract that gets called millions of times, those 400+ gas units per call add up to real ETH.\u003C\u002Fp>\n\u003Ch2 id=\"the-jump-table-approach-o-1\">The Jump Table Approach: O(1)\u003C\u002Fh2>\n\u003Cp>A jump table maps a function selector directly to a code offset using arithmetic, not comparison. The idea is borrowed from CPU architecture — computed GOTOs have been used in systems programming since the 1960s.\u003C\u002Fp>\n\u003Cp>The concept:\u003C\u002Fp>\n\u003Col>\n\u003Cli>Extract the function selector from calldata (4 bytes).\u003C\u002Fli>\n\u003Cli>Use arithmetic to compute a jump destination from the selector.\u003C\u002Fli>\n\u003Cli>JUMP directly to that destination.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>No comparisons, no branching, constant time regardless of how many functions exist.\u003C\u002Fp>\n\u003Ch3>Approach 1: Minimal Selector Encoding\u003C\u002Fh3>\n\u003Cp>If your contract has a small number of functions (1-8), you can assign selectors manually by mining vanity selectors where the first byte or two encode a unique small integer:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">#define macro DISPATCHER() = takes(0) returns(0) {\n    0x00 calldataload       \u002F\u002F [calldata_word]\n    0xe0 shr                \u002F\u002F [selector]\n\n    \u002F\u002F Extract routing byte — first byte of selector\n    0x18 shr                \u002F\u002F [first_byte]\n\n    \u002F\u002F Each entry in our table is 2 bytes (PUSH2 offset)\n    \u002F\u002F Table start + first_byte * 2 = jump destination pointer\n    0x02 mul                \u002F\u002F [offset_in_table]\n    __tablestart(JumpTable) \u002F\u002F [table_start, offset_in_table]\n    add                     \u002F\u002F [entry_address]\n    \n    \u002F\u002F Load the 2-byte jump destination from the table\n    codecopy_dest:\n    0x00 codecopy           \u002F\u002F copy 2 bytes from code to memory\n    0x00 mload              \u002F\u002F [jump_dest (padded to 32 bytes)]\n    0xf0 shr                \u002F\u002F [jump_dest] — shift right to get 2-byte value\n    jump                    \u002F\u002F GOTO function body\n}\n\n#define jumptable JumpTable {\n    swap_exact   \u002F\u002F selector 0x00...\n    add_liq      \u002F\u002F selector 0x01...\n    remove_liq   \u002F\u002F selector 0x02...\n    flash_loan   \u002F\u002F selector 0x03...\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Approach 2: Packed Code Table\u003C\u002Fh3>\n\u003Cp>For maximum density, you can pack the jump table directly into the bytecode using \u003Ccode>__tablestart\u003C\u002Fcode> and \u003Ccode>__tablesize\u003C\u002Fcode>. Huff natively supports jump tables as first-class constructs:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">#define jumptable__packed SELECTOR_TABLE {\n    fn_swap\n    fn_transfer\n    fn_approve\n    fn_balance\n}\n\n#define macro MAIN() = takes(0) returns(0) {\n    0x00 calldataload 0xe0 shr  \u002F\u002F [selector]\n\n    \u002F\u002F Map selector to index (0-3)\n    \u002F\u002F This depends on your selector values — you mine them\n    \u002F\u002F so the top byte is the index\n    0x18 shr                    \u002F\u002F [index]\n\n    \u002F\u002F Bounds check\n    dup1 0x04 lt                \u002F\u002F [in_bounds?, index]\n    valid jumpi\n    0x00 0x00 revert\n\n    valid:\n    \u002F\u002F Load from packed table (2 bytes per entry)\n    __tablestart(SELECTOR_TABLE)\n    swap1 0x02 mul add          \u002F\u002F [table_entry_ptr]\n\n    \u002F\u002F Read 2-byte code offset\n    0x1e mload                  \u002F\u002F trick: mload from code via codecopy\n    jump\n\n    fn_swap:\n        SWAP_IMPL()\n    fn_transfer:\n        TRANSFER_IMPL()\n    fn_approve:\n        APPROVE_IMPL()\n    fn_balance:\n        BALANCE_IMPL()\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"calldata-bit-shifting-for-selector-extraction\">Calldata Bit-Shifting for Selector Extraction\u003C\u002Fh2>\n\u003Cp>The standard selector extraction is:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">0x00 calldataload   \u002F\u002F loads 32 bytes from calldata position 0\n0xe0 shr            \u002F\u002F shift right 224 bits (256 - 32) to isolate top 4 bytes\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>This costs: PUSH1 (3) + CALLDATALOAD (3) + PUSH1 (3) + SHR (3) = 12 gas.\u003C\u002Fp>\n\u003Cp>A cheaper alternative when you only need 1-2 bytes of the selector:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">0x00 calldataload   \u002F\u002F [calldata_word]\n0xf8 shr            \u002F\u002F [first_byte] — shift right 248 bits to get just byte 0\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Same gas, but now your routing key is 1 byte (256 possible values). If your contract has &lt;= 256 functions, one byte is enough to uniquely identify each. You mine vanity selectors (using \u003Ccode>cast selectors\u003C\u002Fcode> or a custom script) so that each function’s first selector byte is unique.\u003C\u002Fp>\n\u003Ch2 id=\"real-example-a-61-byte-dex-router\">Real Example: A 61-Byte DEX Router\u003C\u002Fh2>\n\u003Cp>Here is a minimal swap contract that routes between two operations — \u003Ccode>swapExactIn\u003C\u002Fcode> and \u003Ccode>swapExactOut\u003C\u002Fcode> — in just 61 bytes of runtime bytecode:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">#define constant POOL = 0x...       \u002F\u002F Uniswap V2 pair address\n#define constant TOKEN0 = 0x...     \u002F\u002F token0 address\n#define constant TOKEN1 = 0x...     \u002F\u002F token1 address\n\n#define macro MAIN() = takes(0) returns(0) {\n    \u002F\u002F Selector: 1 byte at calldata[0]\n    0x00 calldataload 0xf8 shr  \u002F\u002F [route_byte]\n\n    \u002F\u002F route_byte == 0 → swapExactIn, else → swapExactOut\n    swap_out jumpi\n\n    \u002F\u002F --- swapExactIn ---\n    SWAP_EXACT_IN()\n    stop\n\n    swap_out:\n    SWAP_EXACT_OUT()\n    stop\n}\n\n#define macro SWAP_EXACT_IN() = takes(0) returns(0) {\n    \u002F\u002F Read amount from calldata[1..33]\n    0x01 calldataload           \u002F\u002F [amountIn]\n\n    \u002F\u002F Transfer token0 to pool\n    \u002F\u002F ... ERC20 transfer calldata assembly ...\n\n    \u002F\u002F Call pool.swap(amountOut, 0, address(this), \"\")\n    \u002F\u002F ... swap calldata assembly ...\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>The entire dispatcher is 7 bytes: \u003Ccode>PUSH1 0x00 CALLDATALOAD PUSH1 0xf8 SHR PUSH2 dest JUMPI\u003C\u002Fcode>. Compare that to Solidity’s 40+ byte dispatcher.\u003C\u002Fp>\n\u003Ch2 id=\"gas-comparison\">Gas Comparison\u003C\u002Fh2>\n\u003Cp>Let us benchmark dispatch cost for varying function counts:\u003C\u002Fp>\n\u003Ctable>\u003Cthead>\u003Ctr>\u003Cth>Functions\u003C\u002Fth>\u003Cth>Solidity (if-else)\u003C\u002Fth>\u003Cth>Solidity (binary)\u003C\u002Fth>\u003Cth>Huff Jump Table\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\n\u003Ctr>\u003Ctd>2\u003C\u002Ftd>\u003Ctd>22-44 gas\u003C\u002Ftd>\u003Ctd>22-44 gas\u003C\u002Ftd>\u003Ctd>15 gas\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>4\u003C\u002Ftd>\u003Ctd>22-88 gas\u003C\u002Ftd>\u003Ctd>22-66 gas\u003C\u002Ftd>\u003Ctd>15 gas\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>8\u003C\u002Ftd>\u003Ctd>22-176 gas\u003C\u002Ftd>\u003Ctd>22-88 gas\u003C\u002Ftd>\u003Ctd>15 gas\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>16\u003C\u002Ftd>\u003Ctd>22-352 gas\u003C\u002Ftd>\u003Ctd>22-110 gas\u003C\u002Ftd>\u003Ctd>15 gas\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>32\u003C\u002Ftd>\u003Ctd>22-704 gas\u003C\u002Ftd>\u003Ctd>22-132 gas\u003C\u002Ftd>\u003Ctd>15 gas\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Cp>The jump table cost is constant: CALLDATALOAD (3) + SHR (3) + arithmetic (3-6) + JUMP (8) = ~15-18 gas. It never changes regardless of function count.\u003C\u002Fp>\n\u003Ch2 id=\"mining-vanity-selectors\">Mining Vanity Selectors\u003C\u002Fh2>\n\u003Cp>For the jump table approach to work, you need function selectors with predictable routing bytes. You can mine these:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-python\">import hashlib\nimport itertools\n\ntarget_byte = 0x00  # desired first byte of selector\nbase_name = \"swap\"\n\nfor suffix in itertools.count():\n    name = f\"{base_name}{suffix}(uint256,address)\"\n    selector = hashlib.sha3_256(name.encode()).digest()[:4]  # keccak256\n    if selector[0] == target_byte:\n        print(f\"Found: {name} -&gt; 0x{selector.hex()}\")\n        break\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>In practice, we use \u003Ccode>cast sig\u003C\u002Fcode> from Foundry or a Rust tool that brute-forces function names to find selectors with desired prefixes. For a contract with 8 functions, mining 8 compatible selectors takes milliseconds.\u003C\u002Fp>\n\u003Ch2 id=\"bytecode-size-impact\">Bytecode Size Impact\u003C\u002Fh2>\n\u003Cp>Bytecode size directly affects deployment cost (200 gas per byte via CREATE, 32,000 base). A comparison:\u003C\u002Fp>\n\u003Ctable>\u003Cthead>\u003Ctr>\u003Cth>Approach\u003C\u002Fth>\u003Cth>Runtime Bytecode\u003C\u002Fth>\u003Cth>Deploy Gas (runtime only)\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\n\u003Ctr>\u003Ctd>Solidity (8 funcs)\u003C\u002Ftd>\u003Ctd>~800 bytes\u003C\u002Ftd>\u003Ctd>160,000 gas\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Huff jump table (8 funcs)\u003C\u002Ftd>\u003Ctd>~200 bytes\u003C\u002Ftd>\u003Ctd>40,000 gas\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Huff minimal (2 funcs)\u003C\u002Ftd>\u003Ctd>~61 bytes\u003C\u002Ftd>\u003Ctd>12,200 gas\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Cp>For MEV bots that redeploy contracts frequently (to rotate addresses or update parameters via CREATE2), smaller bytecode directly translates to lower operational costs.\u003C\u002Fp>\n\u003Ch2 id=\"limitations\">Limitations\u003C\u002Fh2>\n\u003Col>\n\u003Cli>\u003Cstrong>Non-standard ABI\u003C\u002Fstrong> — External tools (Etherscan, wallets) cannot decode your calldata without custom ABI definitions.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Selector mining\u003C\u002Fstrong> — Requires upfront work and constrains function naming.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Maintenance cost\u003C\u002Fstrong> — Huff is harder to audit and modify than Solidity.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Packed tables\u003C\u002Fstrong> — The \u003Ccode>__tablestart\u003C\u002Fcode> and \u003Ccode>__tablesize\u003C\u002Fcode> builtins have edge cases with the Huff compiler; test thoroughly.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"summary\">Summary\u003C\u002Fh2>\n\u003Cp>Jump tables replace Solidity’s O(N) dispatch chain with O(1) computed jumps. The gas savings compound across millions of calls — a meaningful advantage for high-frequency contracts like MEV bots and DEX routers. Combined with vanity selector mining, you can build contracts where the dispatch overhead is under 20 gas regardless of interface size. In the next article, we will explore advanced Huff patterns: adaptive execution, multi-operator auth, and memory layout tricks.\u003C\u002Fp>\n","en","b0000000-0000-0000-0000-000000000001",true,"2026-03-28T10:44:22.894074Z","Deep EVM #11: Huff Jump Tables — O(1) Function Dispatch","Build an O(1) function dispatcher in Huff using packed jump tables. Compare gas costs with Solidity dispatch and learn vanity selector mining.","huff jump table function dispatch",null,"index, follow",[22,27,31,35],{"id":23,"name":24,"slug":25,"created_at":26},"c0000000-0000-0000-0000-000000000016","EVM","evm","2026-03-28T10:44:21.513630Z",{"id":28,"name":29,"slug":30,"created_at":26},"c0000000-0000-0000-0000-000000000020","Gas Optimization","gas-optimization",{"id":32,"name":33,"slug":34,"created_at":26},"c0000000-0000-0000-0000-000000000017","Huff","huff",{"id":36,"name":37,"slug":38,"created_at":26},"c0000000-0000-0000-0000-000000000018","Yul","yul","Blockchain",[41,47,53],{"id":42,"title":43,"slug":44,"excerpt":45,"locale":12,"category_name":39,"published_at":46},"de000000-0000-0000-0000-000000000003","The Ethereum Interoperability Layer: How 55+ L2s Become One Chain","ethereum-interoperability-layer-how-55-l2s-become-one-chain","Ethereum has 55+ Layer 2 rollups, fragmenting liquidity and user experience. The Ethereum Interoperability Layer — combining cross-rollup messaging, shared sequencers, and based rollups — aims to unify them into a single composable network.","2026-03-28T10:44:35.632478Z",{"id":48,"title":49,"slug":50,"excerpt":51,"locale":12,"category_name":39,"published_at":52},"de000000-0000-0000-0000-000000000002","ZK Proofs Beyond Rollups: Verifiable AI Inference on Ethereum","zk-proofs-beyond-rollups-verifiable-ai-inference-ethereum","Zero-knowledge proofs are no longer just a scaling tool. In 2026, zkML enables verifiable AI inference on-chain, ZK coprocessors move heavy computation off-chain with on-chain verification, and new proving systems like SP1 and Jolt make it practical.","2026-03-28T10:44:35.618408Z",{"id":54,"title":55,"slug":56,"excerpt":57,"locale":12,"category_name":39,"published_at":58},"dd000000-0000-0000-0000-000000000003","EIP-7702 in Practice: Building Smart Account Flows After Pectra","eip-7702-in-practice-building-smart-account-flows-after-pectra","EIP-7702 lets any Ethereum EOA temporarily act as a smart contract within a single transaction. Here is how to implement batch transactions, gas sponsorship, and social recovery using the new account abstraction primitive.","2026-03-28T10:44:35.031290Z",{"id":13,"name":60,"slug":61,"bio":62,"photo_url":19,"linkedin":19,"role":63,"created_at":64,"updated_at":64},"Open Soft Team","open-soft-team","The engineering team at Open Soft, building premium software solutions from Bali, Indonesia.","Engineering Team","2026-03-28T08:31:22.226811Z"]