Zum Hauptinhalt springen
BlockchainMar 28, 2026

Deep EVM #11: Huff-Sprungtabellen — O(1)-Funktionsdispatch ohne Solidity-Overhead

OS
Open Soft Team

Engineering Team

Soliditys linearer Dispatch ist O(N)

Wenn Sie einen Solidity-Contract aufrufen, fuehrt die EVM als Erstes den Funktionsdispatcher aus — eine lineare if-else-Kette, die den Calldata-Selektor mit jeder bekannten Funktion vergleicht. Fuer einen Contract mit N Funktionen ist das O(N). Eine Sprungtabelle ersetzt das durch O(1) berechnete Spruenge und spart Tausende Gas ueber Millionen von Aufrufen.

Wie funktioniert eine Sprungtabelle?

Eine Sprungtabelle ist ein Array von Sprungadressen. Statt jeden Selektor einzeln zu vergleichen, berechnen Sie einen Index aus dem Selektor und springen direkt zur entsprechenden Adresse:

// Linearer Dispatch (O(N)):
// if selector == 0xa9059cbb: jump to transfer
// if selector == 0x70a08231: jump to balanceOf
// if selector == 0x095ea7b3: jump to approve
// ... N Vergleiche

// Sprungtabelle (O(1)):
// index = hash(selector) % table_size
// jump to table[index]

Sprungtabelle in Huff implementieren

// Sprungtabelle mit 4 Eintraegen
#define jumptable DISPATCH_TABLE {
    transfer_entry    // Index 0
    balanceOf_entry   // Index 1
    approve_entry     // Index 2
    totalSupply_entry // Index 3
}

#define macro MAIN() = takes(0) returns(0) {
    // Selektor aus Calldata laden
    0x00 calldataload 0xe0 shr  // [selector]
    
    // Index berechnen (Selektor mod Tabellengroesse)
    0x04 mod                     // [index]
    
    // Sprungadresse aus Tabelle laden
    0x02 mul                     // [byte_offset] (2 Bytes pro Eintrag)
    __tablestart(DISPATCH_TABLE) add  // [table_addr + offset]
    
    // Adresse laden und springen
    0x00 0x00 codecopy           // Adresse in Memory kopieren
    0x00 mload shr(240)          // Obere 2 Bytes extrahieren
    jump                         // Springen!
}

Gasvergleich

FunktionenSolidity (if-else)Huff (Sprungtabelle)Ersparnis
5~200 Gas~80 Gas60%
10~400 Gas~80 Gas80%
20~800 Gas~80 Gas90%
50~2.000 Gas~80 Gas96%

Die Sprungtabelle hat konstante Kosten unabhaengig von der Anzahl der Funktionen. Bei MEV-Bots mit 50+ Funktionen spart das erheblich.

Kollisionsbehandlung

Wenn zwei Selektoren denselben Index berechnen (Kollision), benoetigen Sie eine Fallback-Strategie:

  1. Perfekte Hash-Funktion — Waehlen Sie eine Tabellengroesse und Modulo-Operation, die keine Kollisionen erzeugt
  2. Vanity-Selektoren — Waehlen Sie Funktionsnamen, deren Selektoren keine Kollisionen erzeugen
  3. Sekundaerer Vergleich — Bei Kollision den vollen Selektor vergleichen

Vanity-Selektor-Mining

Sie koennen Funktionsnamen so waehlen, dass ihre Selektoren perfekt in Ihre Sprungtabelle passen:

import hashlib

def selector(name):
    return hashlib.sha3_256(name.encode()).hexdigest()[:8]

# Verschiedene Suffixe durchprobieren:
# transfer_v1(address,uint256) -> 0x...
# transfer_v2(address,uint256) -> 0x...
# Bis der Selektor mod table_size den gewuenschten Index ergibt

Gepackte vs. Nicht-gepackte Tabellen

Nicht-gepackt (32 Bytes pro Eintrag)

Einfacher zu implementieren, aber verschwendet Memory:

#define jumptable__packed PACKED_TABLE {
    entry1 entry2 entry3 entry4
}

Gepackt (2 Bytes pro Eintrag)

Kompakter, da Sprungadressen in Smart Contracts selten 65.535 ueberschreiten:

#define jumptable__packed PACKED_TABLE {
    entry1 entry2 entry3 entry4
}

Die gepackte Variante spart ~30 Bytes pro Eintrag im Bytecode — bei 50 Funktionen sind das 1.500 Bytes weniger Bereitstellungskosten.

Produktionsbeispiel

Ein typischer MEV-Bot-Dispatcher mit Sprungtabelle:

#define macro MAIN() = takes(0) returns(0) {
    // Keine Calldata = receive()
    calldatasize iszero receive_eth jumpi
    
    // Selektor laden
    0x00 calldataload 0xe0 shr
    
    // 8-Eintrag-Tabelle (8 = 2^3, effizientes Modulo mit AND)
    0x07 and  // index = selector & 7
    
    // Jeder Eintrag ist 2 Bytes
    0x02 mul
    __tablestart(DISPATCH) add
    
    codecopy_and_jump:
        // Sprungadresse laden und springen
        0x1e byte
        jump
    
    receive_eth:
        stop
}

Fazit

Sprungtabellen sind die effizienteste Methode fuer Funktionsdispatch in der EVM. Fuer Contracts mit vielen Funktionen — insbesondere MEV-Bots — ist die O(1)-Lookup-Zeit ein erheblicher Vorteil gegenueber Soliditys linearem Dispatch. Die Implementierung erfordert sorgfaeltige Planung der Tabellengroesse und Kollisionsvermeidung, aber der Gasgewinn ist es wert.