Ir al contenido principal
BlockchainMar 28, 2026

Deep EVM #18: Depuración de Bytecode EVM — Trazas, Volcados de Stack y cast run

OS
Open Soft Team

Engineering Team

El reto de depurar bytecode

Cuando un contrato Huff o Yul falla, no hay mensajes de error legibles — solo un REVERT silencioso. La depuración de bytecode requiere herramientas especializadas que muestren el estado del stack, memoria y storage en cada paso.

Trazas de ejecución con Foundry

Foundry proporciona trazas detalladas con el flag -vvvv:

forge test --match-test testTransfer -vvvv

La salida muestra:

[PASS] testTransfer()
  Traces:
    [65432] HuffToken::transfer(alice, 100)
      PUSH1 0x00
      CALLDATALOAD
      PUSH1 0xe0
      SHR
      ...
      SSTORE [slot: 0x..., value: 900]
      SSTORE [slot: 0x..., value: 100]
      LOG3 (Transfer event)
      RETURN

cast run: análisis de transacciones on-chain

cast run de Foundry reproduce una transacción con traza completa:

# Reproducir una transacción fallida
cast run 0xABC123...DEF --rpc-url https://eth.llamarpc.com -vvvv

# Con traza de opcodes
cast run 0xABC123...DEF --debug

Esto es invaluable para entender por qué una transacción de un bot MEV falló en producción.

Depuración paso a paso

Foundry incluye un debugger interactivo:

forge debug --debug src/test/MiTest.t.sol --sig "testTransfer()"

El debugger muestra:

  • Estado del stack en cada opcode
  • Contenido de la memoria
  • Slots de storage modificados
  • Gas restante
  • Programa counter y opcode actual

Análisis de reverts

Cuando un contrato revierte, el proceso de diagnóstico es:

  1. Identificar el opcode REVERT — ¿En qué posición del bytecode ocurre?
  2. Trazar hacia atrás — ¿Qué condición falló justo antes del REVERT?
  3. Inspeccionar el stack — ¿Los valores son los esperados?
  4. Verificar calldata — ¿Se decodificaron correctamente los parámetros?
// En tu test, captura el error:
function testShouldRevert() public {
    (bool ok, bytes memory returnData) = token.call(
        abi.encodeWithSignature("transfer(address,uint256)", address(0), 100)
    );
    
    assertFalse(ok, "Debe revertir para address(0)");
    
    // Decodificar datos de revert si los hay
    if (returnData.length >= 4) {
        bytes4 selector = bytes4(returnData);
        emit log_named_bytes4("Error selector", selector);
    }
}

Herramientas complementarias

Tenderly

Plataforma web para simular y depurar transacciones con visualización del stack:

https://dashboard.tenderly.co/tx/mainnet/0xABC...

evm.codes

Referencia interactiva de opcodes con playground para probar bytecode:

https://www.evm.codes/playground

Bytecode descompiladores

Herramientas como Heimdall pueden descompilar bytecode a pseudo-Solidity, ayudando a entender contratos de terceros.

Técnicas de depuración para Huff

Logging temporal con eventos

Durante el desarrollo, inserta eventos para trazar valores:

#define macro DEBUG_LOG_UINT() = takes(1) returns(1) {
    // Stack: [value]
    dup1                    // [value, value]
    0x00 mstore             // [value]
    0x00                    // topic0 = 0 (debug event)
    0x20 0x00 log1          // [value]
}

Verificación de invariantes en runtime

#define macro ASSERT_EQ() = takes(2) returns(0) {
    // Stack: [a, b]
    eq assert jumpi
    0x00 0x00 revert
    assert:
}

Patrones comunes de bugs en Huff

  1. Stack underflow — POP de stack vacío. Causa: macro con takes() incorrecto
  2. Off-by-one en DUP/SWAP — DUP2 en vez de DUP3. Causa: contar mal la profundidad del stack
  3. Calldata offset erróneo — Leer del offset equivocado. Causa: no contar el selector de 4 bytes
  4. Memory overlap — Escribir sobre datos necesarios. Causa: no gestionar el free memory pointer
  5. Storage slot collision — Dos variables mapeadas al mismo slot. Causa: hash incorrecto del mapping

Conclusión

La depuración de bytecode EVM es un arte que requiere paciencia y herramientas adecuadas. Foundry, cast run, y el debugger interactivo son el toolkit esencial. Para contratos Huff, los comentarios de stack diligentes y el testing diferencial contra implementaciones de referencia son la primera línea de defensa contra bugs silenciosos.

Etiquetas