Logo
Nuevos usuarios de RPC: 35% de descuento el primer mesVer oferta
OnFinality Learn
Guías de red y protocolo14 min de lectura

eth_getCode: Cómo leer el bytecode de un contrato correctamente

Aprende cómo eth_getCode devuelve el bytecode EVM desplegado, por qué '0x' es ambiguo y cómo distinguir el código de las lecturas de estado en proxies y delegaciones EIP-7702.

TL;DR

eth_getCode(address, blockParameter) devuelve el bytecode EVM codificado en hexadecimal desplegado en una dirección para un bloque dado, o '0x' si la dirección es una cuenta de propiedad externa (EOA), nunca fue desplegada o se autodestruyó. A diferencia de las lecturas de estado como eth_getStorageAt y eth_call, que devuelven valores calculados por el código, eth_getCode devuelve el código en sí. El parámetro de bloque es crítico porque el código cambia en el despliegue y a través de proxies de actualización; eth_getCode(addr, 'latest') ve el código de implementación detrás de un proxy solo después de resolver la dirección de implementación. Los designadores de delegación EIP-7702 (0xef0100 + dirección del delegado) hacen que una prueba ingenua de 'longitud > 0 significa contrato' clasifique erróneamente a los EOA delegados. Esta guía cubre la derivación de direcciones, la detección de prefijos de bytecode, la verificación de slots de proxy, la comparación con artefactos y las limitaciones honestas de eth_getCode.

Qué devuelve eth_getCode y por qué importa el parámetro de bloque

La especificación de Ethereum JSON-RPC define eth_getCode(address, blockParameter) como el retorno del bytecode EVM codificado en hexadecimal en una dirección dada para un bloque específico. El valor de retorno es una cadena DATA con prefijo 0x, o '0x' si la dirección es una cuenta de propiedad externa (EOA), una dirección inexistente o un contrato que se ha autodestruido. Esta ambigüedad es fundamental: '0x' no te dice cuál de esos tres casos aplica. La definición canónica del método está documentada en la referencia de eth_getCode de Ethereum JSON-RPC.

El parámetro de bloque no es opcional en la práctica. El código cambia en el despliegue y mediante proxies de actualización, por lo que eth_getCode(addr, 'latest') refleja el código actual, mientras que eth_getCode(addr, '0x...') refleja el código histórico. Si estás depurando una secuencia de despliegue y uso, 'latest' puede mostrar código que no existía en el bloque del recibo. Fija siempre el parámetro de bloque cuando la reproducibilidad importe.

Las lecturas de estado como eth_getStorageAt y eth_call devuelven valores calculados por ese código, no el código en sí. Usa eth_getCode cuando necesites verificar qué está desplegado; usa lecturas de estado cuando necesites inspeccionar slots de almacenamiento o simular llamadas. La guía de eth_getStorageAt y empaquetado de slots cubre el lado del almacenamiento en profundidad.

  • eth_getCode devuelve bytecode; eth_getStorageAt devuelve una palabra de almacenamiento de 32 bytes; eth_call devuelve el resultado de una llamada simulada.
  • '0x' es ambiguo: EOA, nunca desplegado o autodestruido.
  • Fija el parámetro de bloque para lecturas de bytecode reproducibles.
  • Referencia autorizada: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getcode

Distinguir EOAs de contratos por la longitud del bytecode

Un contrato tiene una longitud de bytecode mayor que cero tras un despliegue exitoso. Una EOA no tiene código, por lo que eth_getCode devuelve '0x' (longitud cero). Esta es la heurística estándar para distinguir EOAs de contratos en el código. Sin embargo, no es suficiente por sí sola debido a los contratos autodestruidos y a las delegaciones EIP-7702.

Durante una secuencia de despliegue y uso, el código en 'latest' no es el mismo que el código en el bloque del recibo. Si consultas eth_getCode inmediatamente después de enviar una transacción de despliegue, el código puede no ser visible aún en el bloque que esperas. Consulta siempre en el bloque del recibo o posterior, y verifica primero el estado de la transacción.

Para un ejemplo ejecutable, el fragmento de Node.js a continuación comprueba la longitud del código y detecta un prefijo de bytecode Solidity conocido (0x60806040). Usa un endpoint JSON-RPC genérico; reemplaza la URL por el endpoint de tu proveedor. La guía del nodo RPC de Ethereum (RPC Assistant) de OnFinality explica cómo obtener un endpoint.

const https = require('https');

const RPC_URL = 'https://your-ethereum-rpc-endpoint';
const ADDRESS = '0xYourContractAddress';

function rpcCall(method, params) {
  return new Promise((resolve, reject) => {
    const data = JSON.stringify({ jsonrpc: '2.0', id: 1, method, params });
    const req = https.request(RPC_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' } }, (res) => {
      let body = '';
      res.on('data', (chunk) => body += chunk);
      res.on('end', () => resolve(JSON.parse(body)));
    });
    req.on('error', reject);
    req.write(data);
    req.end();
  });
}

async function checkCode() {
  const result = await rpcCall('eth_getCode', [ADDRESS, 'latest']);
  const code = result.result;
  const length = (code.length - 2) / 2; // bytes
  console.log('Bytecode length:', length, 'bytes');
  if (length === 0) {
    console.log('No code: EOA, never-deployed, or self-destructed.');
    return;
  }
  if (code.startsWith('0x60806040')) {
    console.log('Detected Solidity 0.8.x bytecode prefix.');
  }
  if (code.startsWith('0xef0100')) {
    console.log('EIP-7702 delegation designator detected.');
  }
}

checkCode().catch(console.error);

Derivación de la dirección del contrato y su lectura posterior

Las direcciones de contrato se derivan de forma determinista: CREATE usa keccak256(rlp([sender, nonce]))[12:], mientras que CREATE2 usa keccak256(0xff ++ sender ++ salt ++ keccak256(init_code))[12:]. Puedes calcular la dirección esperada antes del despliegue y luego leerla con eth_getCode para confirmar el despliegue. Esto es útil para despliegues contrafactuales y para verificar que una fábrica produjo la dirección esperada.

Leer la dirección no te dice si el código coincide con tu fuente. Solo te dice que existe algún código. Para validar el despliegue, compara un hash de bytecode con el artefacto del compilador, teniendo en cuenta las sustituciones de argumentos del constructor y de variables inmutables. La guía de pruebas de cuenta y almacenamiento eth_getProof cubre cómo probar el estado de una cuenta en un bloque.

Si usas un patrón de proxy, la dirección que despliegas es el proxy, no la implementación. eth_getCode(proxy, 'latest') devuelve el bytecode del proxy, no el de la implementación. Debes resolver primero la dirección de implementación, normalmente leyendo un slot de almacenamiento específico.

  • Dirección CREATE = keccak256(rlp([sender, nonce]))[12:].
  • Dirección CREATE2 = keccak256(0xff ++ sender ++ salt ++ keccak256(init_code))[12:].
  • eth_getCode en un proxy devuelve el bytecode del proxy, no el de la implementación.
  • Usa eth_getStorageAt para leer el slot de implementación (comúnmente 0x360894... para EIP-1967).

Patrones de proxy y resolución del código de implementación

Los proxies actualizables almacenan la dirección de implementación en un slot de almacenamiento. El estándar EIP-1967 define un slot específico: 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc. Para leer el código de implementación, primero llama a eth_getStorageAt(proxy, slot, blockParameter) para obtener la dirección de implementación, luego llama a eth_getCode(implementation, blockParameter).

Este proceso de dos pasos es necesario porque eth_getCode no sigue la delegación del proxy. Devuelve el código en la dirección exacta que consultas. Si consultas el proxy, obtienes la lógica de fallback del proxy, no las funciones de la implementación. La guía de simulación con anulación de estado eth_call muestra cómo simular llamadas directamente contra la implementación.

El ejemplo de Node.js a continuación verifica el slot de implementación de un proxy y luego lee el código de implementación. Reemplaza la URL de RPC y la dirección del proxy por tus propios valores.

const https = require('https');

const RPC_URL = 'https://your-ethereum-rpc-endpoint';
const PROXY = '0xYourProxyAddress';
const IMPLEMENTATION_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';

function rpcCall(method, params) {
  return new Promise((resolve, reject) => {
    const data = JSON.stringify({ jsonrpc: '2.0', id: 1, method, params });
    const req = https.request(RPC_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' } }, (res) => {
      let body = '';
      res.on('data', (chunk) => body += chunk);
      res.on('end', () => resolve(JSON.parse(body)));
    });
    req.on('error', reject);
    req.write(data);
    req.end();
  });
}

async function resolveImplementation() {
  const slotResult = await rpcCall('eth_getStorageAt', [PROXY, IMPLEMENTATION_SLOT, 'latest']);
  const implAddress = '0x' + slotResult.result.slice(26); // last 20 bytes
  console.log('Implementation address:', implAddress);
  const codeResult = await rpcCall('eth_getCode', [implAddress, 'latest']);
  const code = codeResult.result;
  console.log('Implementation bytecode length:', (code.length - 2) / 2, 'bytes');
  console.log('First 10 bytes:', code.slice(0, 22));
}

resolveImplementation().catch(console.error);

Designadores de delegación EIP-7702 y riesgos de clasificación errónea

EIP-7702 introduce un nuevo tipo de cuenta: una EOA que delega la ejecución a un contrato. El designador de delegación es un prefijo de bytecode específico: 0xef0100 seguido de la dirección del delegado de 20 bytes. Esto significa que una EOA delegada ahora devuelve código que comienza con 0xef0100, por lo que una prueba ingenua de 'longitud > 0 significa contrato' la clasifica erróneamente como contrato. El EIP está documentado como pendiente de activación; el comportamiento puede variar según el proveedor y la red.

Para clasificar correctamente una cuenta, comprueba el prefijo 0xef0100. Si está presente, la cuenta es una EOA con delegación, no un contrato. La dirección del delegado son los siguientes 20 bytes. Luego puedes llamar a eth_getCode en la dirección del delegado para leer el código de implementación real.

Esta distinción importa para las herramientas que asumen que cualquier dirección con código es un contrato. Las carteras, indexadores y herramientas de seguridad deben actualizar sus heurísticas. La referencia autorizada es la especificación EIP-7702.

  • Designador de delegación: 0xef0100 ++ dirección del delegado (20 bytes).
  • Una EOA delegada devuelve código, pero no es un contrato.
  • Comprueba el prefijo 0xef0100 antes de aplicar la heurística 'longitud > 0'.
  • EIP-7702 está documentado como pendiente de activación; verifícalo en tu red objetivo.

Validar el bytecode desplegado contra artefactos del compilador

Para validar que un contrato desplegado coincide con la fuente esperada, compara un hash de bytecode con el artefacto del compilador. El artefacto contiene el bytecode de creación y el bytecode desplegado. El bytecode desplegado en cadena puede diferir del artefacto debido a sustituciones de argumentos del constructor (que se añaden al bytecode de creación, no al bytecode desplegado) y sustituciones de variables inmutables (que se incrustan en el bytecode desplegado en posiciones específicas).

El enfoque estándar es eliminar el trailer de metadatos CBOR del bytecode en cadena y luego comparar los bytes restantes con el bytecode desplegado del artefacto tras aplicar las sustituciones inmutables conocidas. El trailer de metadatos es un mapa codificado en CBOR al final del bytecode de solc; eth_getCode no lo recorta. Debes recortarlo tú mismo.

Para un método reproducible, registra el número de bloque, el resultado de eth_getCode, el hash del artefacto y el hash del bytecode recortado. Usa una tabla de resultados para comparar entre endpoints. La guía de Decodificar motivos de revert y errores personalizados cubre técnicas de depuración relacionadas.

  • Elimina el trailer de metadatos CBOR antes de comparar con el artefacto.
  • Los argumentos del constructor se añaden al bytecode de creación, no al bytecode desplegado.
  • Las variables inmutables se incrustan en el bytecode desplegado; aplica las sustituciones antes de hashear.
  • Registra el número de bloque y el endpoint para la reproducibilidad.

Tabla de resultados: medir el comportamiento de eth_getCode contra tu endpoint

Dado que el comportamiento del proveedor varía, mide eth_getCode contra tu propio endpoint. Crea una tabla de resultados con columnas: URL del endpoint, parámetro de bloque, dirección, longitud del bytecode devuelto, primeros 10 bytes y si el resultado coincide con un artefacto conocido. Ejecuta la misma consulta contra múltiples endpoints y parámetros de bloque para detectar inconsistencias.

Este método lo verifica el lector; no afirma cifras específicas de OnFinality. Úsalo para comparar proveedores, confirmar la semántica del parámetro de bloque y detectar diferencias de caché o balanceo de carga. La página de precios de RPC explica cómo elegir un plan según tu volumen de consultas.

Para una medición completa, incluye un bloque histórico y un bloque 'latest'. Si el bytecode difiere, has detectado una actualización o una reorganización. Vuelve a verificar tras cualquier actualización ejecutando de nuevo la tabla.

  • Columnas: endpoint, bloque, dirección, longitud, prefijo, coincidencia con artefacto.
  • Ejecuta contra al menos dos endpoints para detectar inconsistencias.
  • Incluye bloques históricos y latest para detectar actualizaciones.
  • Vuelve a verificar tras cada actualización o bifurcación.

Limitaciones y compensaciones de eth_getCode

eth_getCode no recorta los metadatos. El trailer CBOR al final del bytecode de solc se devuelve tal cual. Si comparas el bytecode sin procesar con un artefacto, obtendrás una discrepancia a menos que recortes el trailer. Esta es una fuente común de falsos negativos en herramientas de verificación.

eth_getCode no descompila. Devuelve bytes sin procesar. Para entender qué hace el código, necesitas un descompilador o el código fuente. También devuelve bytes diferentes entre bifurcaciones, así que fija el parámetro de bloque y vuelve a verificar tras una actualización. Una reorganización puede cambiar el código en un bloque dado; confirma siempre la finalidad.

Por último, eth_getCode no sigue proxies ni delegaciones EIP-7702. Debes resolver tú mismo la dirección de implementación o la dirección del delegado. Esta es una elección de diseño deliberada: el método RPC devuelve el código en la dirección exacta, no el código efectivo tras la delegación.

  • Sin recorte de metadatos: se incluye el trailer CBOR.
  • Sin descompilación: solo bytes sin procesar.
  • Dependiente de la bifurcación: fija el parámetro de bloque.
  • Sin resolución de proxy o delegación: resuelve las direcciones manualmente.

Solución de problemas comunes de eth_getCode

Si eth_getCode devuelve '0x' para un contrato desplegado, comprueba el parámetro de bloque. Puede que estés consultando un bloque anterior al despliegue. Verifica también la dirección: un error tipográfico o un error de checksum devolverán '0x'. Si el contrato se autodestruyó, '0x' es correcto. Usa eth_getTransactionReceipt para confirmar el estado del despliegue y el número de bloque.

Si la longitud del bytecode es inesperada, comprueba si hay una delegación EIP-7702 (prefijo 0xef0100) o un patrón de proxy. Si consultas un proxy, estás viendo el bytecode del proxy, no el de la implementación. Resuelve primero el slot de implementación. Si comparas con un artefacto, recorta el trailer de metadatos CBOR y aplica las sustituciones inmutables.

Si los resultados difieren entre endpoints, puede que estés accediendo a bifurcaciones o capas de caché diferentes. Fija el parámetro de bloque y compara el hash del bloque. Usa el centro de aprendizaje de OnFinality para guías relacionadas sobre lecturas de estado y pruebas.

  • '0x' para un contrato desplegado: comprueba el parámetro de bloque y la dirección.
  • Longitud inesperada: comprueba EIP-7702 o patrón de proxy.
  • Discrepancia con el artefacto: recorta el trailer CBOR y aplica las sustituciones inmutables.
  • Diferencias entre endpoints: fija el parámetro de bloque y compara el hash del bloque.

Próximos pasos: integrar eth_getCode en tu flujo de trabajo

Integra eth_getCode en tu pipeline de despliegue para verificar que el bytecode correcto está en cadena. Tras el despliegue, consulta eth_getCode en el bloque del recibo y compara el hash del bytecode recortado con tu artefacto. Guarda el número de bloque y el hash para auditabilidad. Para contratos actualizables, resuelve el slot de implementación y verifica el código de implementación por separado.

Para el monitoreo en producción, vuelve a consultar periódicamente eth_getCode en 'latest' y compara con el hash esperado. Alerta ante cambios. Esto detecta actualizaciones no autorizadas y cambios de administrador de proxy. Usa el servicio de API para automatizar estas comprobaciones en múltiples redes.

Para empezar, obtén un endpoint desde la página de la red Ethereum de OnFinality y ejecuta los ejemplos de esta guía. Para precios y detalles de planes, consulta precios de RPC. Para una visión más amplia de los métodos RPC de Ethereum, consulta la guía del nodo RPC de Ethereum (RPC Assistant).

  • Verifica el hash del bytecode tras el despliegue en el bloque del recibo.
  • Monitorea el código en 'latest' para detectar actualizaciones no autorizadas.
  • Resuelve los slots de implementación para contratos proxy.
  • Automatiza las comprobaciones con el servicio de API.

Nunca te preocupes por la infraestructura nuevamente

OnFinality elimina la carga pesada de DevOps para que puedas construir de forma más inteligente y rápida.

Comenzar