Ir al contenido principal
BlockchainMar 28, 2026

Deep EVM #11: Jump Tables en Huff — Despacho de Funciones O(1) Sin Overhead de Solidity

OS
Open Soft Team

Engineering Team

El problema del despacho de funciones

Cuando llamas a un contrato, los primeros 4 bytes del calldata son el selector de función — un hash keccak256 truncado de la firma. El contrato necesita mapear este selector al código correspondiente.

Solidity genera un despacho lineal: compara el selector contra cada función hasta encontrar coincidencia. Con N funciones, esto es O(N) en el peor caso.

// Solidity genera (simplificado):
if selector == 0xa9059cbb jump transfer    // 3+3+10 = 16 gas
if selector == 0x70a08231 jump balanceOf   // 16 gas
if selector == 0x18160ddd jump totalSupply  // 16 gas
// ... más comparaciones ...
revert

Para un contrato con 50 funciones, la función más “lejana” paga 50 * 16 = 800 gas solo en despacho. En MEV, esto es inaceptable.

Jump tables: O(1) constante

Una jump table usa el selector directamente como índice para saltar a la posición correcta. Sin comparaciones, sin branches — un solo salto.

Concepto

  1. Tomar los primeros N bits del selector
  2. Usarlos como offset en una tabla almacenada en el bytecode
  3. Saltar directamente a la dirección indicada

Implementación en Huff

#define jumptable DISPATCH_TABLE {
    balanceOf   // selector 0x70a08231 -> posición 0
    transfer    // selector 0xa9059cbb -> posición 1
    totalSupply // selector 0x18160ddd -> posición 2
}

#define macro MAIN() = takes(0) returns(0) {
    // Leer selector
    0x00 calldataload 0xe0 shr
    
    // Buscar en la jump table
    __tablestart(DISPATCH_TABLE)    // [table_offset, selector]
    // ... lógica de lookup ...
}

Huff proporciona __tablestart y __tablesize para referenciar jump tables embebidas en el bytecode.

Hash perfecto para selectores

Para un conjunto conocido de selectores, podemos encontrar una función hash que mapee cada selector a un índice único sin colisiones:

#define macro PERFECT_DISPATCH() = takes(0) returns(0) {
    0x00 calldataload 0xe0 shr  // [selector]
    
    // Función hash perfecta (precalculada offline):
    // h(selector) = (selector * MAGIC_NUMBER) >> SHIFT
    // donde MAGIC_NUMBER y SHIFT se eligen para evitar colisiones
    
    dup1
    0x0f and                    // Usar los últimos 4 bits como índice
    0x02 shl                    // Multiplicar por 4 (cada entrada = 4 bytes)
    __tablestart(DISPATCH_TABLE)
    add                         // [table_addr]
    
    // Leer la dirección de salto desde la tabla
    0x00 codecopy              // Copiar 2 bytes del bytecode
    0x00 mload                 // Cargar la dirección
    0xf0 shr                   // Alinear
    jump                       // Saltar directamente
}

Benchmarks

MétodoGas (mejor caso)Gas (peor caso)Gas (promedio, 20 funciones)
Solidity lineal16320168
Solidity binary search488064
Huff jump table353535

La jump table de Huff tiene coste constante sin importar cuántas funciones tenga el contrato. Para contratos con muchas funciones (como routers de DEX), el ahorro es masivo.

Jump tables packed vs padded

Packed table

Cada entrada ocupa solo 2 bytes (dirección de salto). Más compacto, pero requiere calcular offsets.

Padded table

Cada entrada ocupa 32 bytes. Más desperdicio, pero el lookup es trivial con multiplicación.

// Packed: 2 bytes por entrada
// Offset = index * 2
// Más compacto, menos gas en codecopy

// Padded: 32 bytes por entrada
// Offset = index * 32
// Más simple, pero bytecode más grande

Consideraciones prácticas

  1. Colisiones de hash — La función hash perfecta debe ser verificada exhaustivamente offline
  2. Selectores desconocidos — Siempre incluir un fallback para selectores que no coincidan
  3. Tamaño del bytecode — Jump tables grandes pueden aumentar el coste de despliegue
  4. Mantenimiento — Añadir funciones requiere recalcular la función hash perfecta

Aplicaciones en MEV

Los bots de MEV son el caso de uso principal para jump tables:

  • Un router que ejecuta arbitraje a través de 50+ pools necesita despacho O(1)
  • La latencia de 12 segundos por bloque significa que cada gas ahorrado se convierte en ventaja competitiva
  • Los contratos de ejecución MEV típicamente tienen 3-5 funciones de punto de entrada, pero el patrón se escala a cualquier número

Conclusión

Las jump tables transforman el despacho de funciones de O(N) a O(1), eliminando una fuente significativa de overhead en contratos con muchas funciones. Para desarrolladores de MEV y protocolos de alto rendimiento, este patrón es esencial.