Home

Awesome

Single-byte jumptable EVM function dispatcher in Huff •

Inspired by the latest article by Degatchi, this is a proof-of-concept implementation of a single-byte jumptable EVM function dispatcher written in Huff.

Introduction

Under canonical ABI standards, the first 4 bytes of transaction calldata is reserved for the function selector. These bytes are matched against the selectors of all the defined functions in the contract. If a match is found, the program jumps to the corresponding function and executes it.

The standard Solidity implementation looks something like this:

PUSH1 0x00        // [0x00]
CALLDATALOAD      // [calldata[0:32]]
PUSH1 0xE0        // [0xE0, calldata[0:32]]
SHR               // [selector]

/* dispatcher loop, run n times for n functions */
DUP1              // [selector, selector]
PUSH4 0x12345678  // [0x12345678, selector, selector]
EQ                // [0x12345678 == selector, selector]
PUSH1 0x40        // [0x40, 0x12345678 == selector, selector]
JUMPI             // [selector]
/* end dispatcher loop */

The JUMPI instruction jumps to the function code (at PC=0x40 in the example above) if its selector (0x12345678 in the above example) matches the one provided in the calldata. If no match is found, the program continues to the next function signature.

There is an alternative approach which can be used for optimization: the binary search dispatching method. This method is more gas-efficient in the case of a large number of functions, but the specific savings depend on the number of arbitrary pivot points you choose for the binary search.

This repo implements a third approach, which is outside of the standard ABI.

How it works

The idea is to use a single-byte selector, which is then used to shift into a jump table. The jump table is a 32-bytes word packed with program counter locations corresponding to the function jump destinations program counters. This way, we don't have to iterate over the function selectors and can jump directly to the right function, which saves some gas if the number of functions is large enough.

Please take a look at the source code at src/CustomDispatcher.huff for the full details.

Running the tests

You can get started and run the tests with the following commands (assuming you have Foundry and Huff installed):

git clone git@github.com:merklefruit/huff-single-byte-dispatcher.git
cd huff-custom-dispatcher
forge test

Limitations and caveats

Benchmarks

Standard ABI dispatcher

Single-byte dispatcher

Comparison

Here are the marginal gas savings expressed as a function of the function position n:

c(n) = ((34 + 22 * (n - 1)) / 32 - 1) * 100

Acknowledgements

Disclaimer

These smart contracts are being provided as is. No guarantee, representation or warranty is being made, express or implied, as to the safety or correctness of the user interface or the smart contracts. They have not been audited and as such there can be no assurance they will work as intended, and users may experience delays, failures, errors, omissions, loss of transmitted information or loss of funds. The creators are not liable for any of the foregoing. Users should proceed with caution and use at their own risk.