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

eth_createAccessList: crea listas de acceso EIP-2930 para reducir el gas

Aprende cómo funciona la contabilidad de acceso frío/caliente de EIP-2929, cómo construir una lista de acceso EIP-2930 con eth_createAccessList y cómo verificar el delta neto de gas.

TL;DR

eth_createAccessList simula una llamada contra el estado actual y devuelve las direcciones y claves de almacenamiento que esa llamada tocaría, además de una cifra de gasUsed. Puedes adjuntar esa lista a una transacción tipo 1 (EIP-2930) o tipo 2 (EIP-1559) para que el primer acceso a cada slot se pague al precio declarado en la lista de acceso en lugar del precio más alto de acceso frío definido por EIP-2929. El compromiso es real: las listas de acceso añaden gas intrínseco por dirección y por clave de almacenamiento, más los bytes de calldata, por lo que solo compensan cuando los slots precalentados se reutilizan suficientes veces. La única forma fiable de saberlo es comparar eth_estimateGas con y sin la lista en el mismo bloque. Esta guía cubre el envelope de petición/respuesta, un ejemplo ejecutable en Node.js, una tabla de resultados que puedes completar contra tu propio endpoint y las limitaciones que convierten las listas de acceso en una optimización de nicho en lugar de una opción por defecto.

El modelo de acceso frío y caliente de EIP-2929

Antes de que las listas de acceso de EIP-2930 tengan sentido, necesitas entender el cambio contable que creó la oportunidad. EIP-2929 redefinió los costes de acceso al estado dividiendo cada dirección y cada slot de almacenamiento en dos estados: frío (aún no tocado en esta transacción) y caliente (ya tocado). El primer acceso a una dirección o slot dentro de una transacción es frío y cuesta bastante más gas que los accesos calientes posteriores.

Por eso declarar de antemano los slots a los que se va a acceder puede reducir el coste. Si una transacción va a tocar el mismo slot de almacenamiento varias veces, o tocar un conjunto de slots por los que la EVM cobraría precio frío, precalentarlos mediante una lista de acceso traslada esos primeros accesos al precio más barato de la lista de acceso. El mecanismo es puramente de ordenación y declaración: la EVM sigue realizando las mismas lecturas y escrituras, pero el recargo por frío se paga una vez a una tarifa conocida y más baja.

La consecuencia práctica es que las listas de acceso son una optimización específica para contratos con patrones de acceso a almacenamiento predecibles y repetidos. No sirven para una simple transferencia de valor, porque una transferencia normal no toca ningún slot de almacenamiento de contrato que pudiera beneficiarse.

  • Acceso frío: primer toque de una dirección o slot de almacenamiento en una transacción, cobrado a la tarifa más alta de EIP-2929.
  • Acceso caliente: cualquier toque posterior en la misma transacción, cobrado a la tarifa más baja.
  • Las listas de acceso precalientan direcciones y slots declarados para que su primer toque se facture al precio de la lista de acceso.
  • El beneficio escala con cuántas veces se reutilizan realmente los slots precalentados.

Qué es realmente una lista de acceso EIP-2930

EIP-2930 introdujo la transacción tipo 1 y el campo accessList: una lista de objetos, cada uno con una dirección y un array de storageKeys. Declarar un slot en esa lista le indica a la EVM que lo trate como caliente desde el inicio de la ejecución. Las transacciones tipo 2 (EIP-1559) también llevan un campo accessList, así que no estás obligado al modelo de tarifas legacy de tipo 1 solo por usar listas de acceso.

El campo es opcional en ambos tipos de transacción. Cuando se omite o está vacío, la transacción se comporta exactamente como antes de EIP-2930. Cuando está presente, el cliente paga gas intrínseco por cada dirección declarada y por cada clave de almacenamiento declarada, más el coste de calldata de codificar la lista. Ese coste intrínseco es la razón por la que una lista puede ser netamente negativa: pagas por adelantado un precalentamiento que puede que no aproveches del todo.

Si quieres inspeccionar cómo se serializan estos campos en el wire, la codificación de transacciones en bruto es de la misma familia que se cubre en eth_getRawTransactionByHash y codificaciones tipo 0/1/2. El accessList forma parte del payload RLP para transacciones tipo 1 y tipo 2.

  • Forma: accessList es un array de objetos { address, storageKeys[] }.
  • Las transacciones tipo 1 son el portador original de EIP-2930; las transacciones tipo 2 también pueden incluir el campo.
  • Coste intrínseco: por dirección declarada, por clave de almacenamiento declarada, más los bytes de calldata.
  • Una lista vacía u omitida es válida y equivale al comportamiento anterior a EIP-2930.

El envelope de petición y respuesta de eth_createAccessList

eth_createAccessList es un método de simulación. Le envías un objeto de llamada y un tag de bloque, y devuelve la lista de acceso que esa llamada necesitaría más una estimación de gasUsed. La especificación JSON-RPC de Ethereum documenta la forma de la petición y la respuesta. El objeto de llamada acepta from, to, data y value, y el segundo parámetro es el tag de bloque (por ejemplo "latest" o un número de bloque en hexadecimal).

La respuesta es un objeto con dos campos: accessList, un array de entradas { address, storageKeys[] }, y gasUsed, una cantidad en hexadecimal. El accessList es exactamente lo que pegarías en el campo accessList de una transacción. El gasUsed refleja la ejecución simulada, no una garantía del coste final minado, porque el estado puede cambiar entre la simulación y la inclusión.

Como se trata de una simulación contra un bloque concreto, el resultado solo es válido en ese bloque. Si el layout de almacenamiento del contrato o los slots tocados cambian, la lista puede quedar obsoleta. Trata la lista devuelta como un punto de partida para medir, no como un artefacto permanente.

  • Parámetros: [callObject, blockTag] donde callObject admite from, to, data, value.
  • Resultado: { accessList: [{ address, storageKeys[] }], gasUsed: hex }.
  • El accessList devuelto es directamente utilizable como campo de transacción.
  • La validez está ligada al bloque contra el que se ejecutó la simulación.

Ejemplo ejecutable en Node.js: crear, estimar, comparar

El ejemplo siguiente usa JSON-RPC en bruto sobre fetch para que puedas ver los envelopes exactos. Llama a eth_createAccessList para una llamada a contrato, imprime el accessList y el gasUsed, y luego llama a eth_estimateGas dos veces en el mismo bloque: una sin la lista y otra con ella. El delta es la cifra que importa.

Sustituye RPC_URL por tu endpoint. OnFinality expone Ethereum JSON-RPC a través de la página de la red Ethereum; el mismo método está disponible en cualquier nodo que lo implemente, aunque el soporte varía según el proveedor. El ejemplo deliberadamente no afirma una cifra de ahorro, porque el resultado depende del contrato y del bloque.

const RPC_URL = "https://your-endpoint.example";

async function rpc(method, params) {
  const res = await fetch(RPC_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params })
  });
  const json = await res.json();
  if (json.error) throw new Error(JSON.stringify(json.error));
  return json.result;
}

async function main() {
  const call = {
    from: "0xYourFromAddress",
    to: "0xContractAddress",
    data: "0xYourCalldata"
  };
  const blockTag = "latest";

  const created = await rpc("eth_createAccessList", [call, blockTag]);
  console.log("accessList:", JSON.stringify(created.accessList, null, 2));
  console.log("gasUsed (createAccessList):", created.gasUsed);

  const withoutList = await rpc("eth_estimateGas", [call, blockTag]);
  const withList = await rpc("eth_estimateGas", [
    { ...call, accessList: created.accessList },
    blockTag
  ]);

  const delta = BigInt(withList) - BigInt(withoutList);
  console.log("estimateGas without list:", withoutList);
  console.log("estimateGas with list:   ", withList);
  console.log("net delta (negative = saving):", delta.toString());
}

main().catch((e) => { console.error(e); process.exit(1); });

Incorporar el resultado en una transacción tipo 1 o tipo 2

Una vez que tienes el accessList, adjuntarlo es mecánico. Para una transacción tipo 1, establece type en 0x1 e incluye el campo accessList junto con gasPrice, gasLimit, nonce, to, value y data. Para una transacción tipo 2, establece type en 0x2 e incluye accessList junto con maxFeePerGas y maxPriorityFeePerGas. El campo accessList es idéntico en ambos casos.

Si usas ethers, el accessList se pasa a través del objeto de solicitud de transacción y la librería se encarga de la serialización. Si construyes transacciones en bruto, el accessList se codifica en RLP como parte del payload tipo 1 o tipo 2. Los detalles de codificación son de la misma familia descrita en eth_getRawTransactionByHash y codificaciones tipo 0/1/2.

La disciplina importante es verificar el efecto neto antes de difundir. Adjuntar una lista que no se paga a sí misma hace que la transacción sea estrictamente más cara. Compara siempre las estimaciones en el mismo bloque, como hace el ejemplo.

  • Tipo 1: type 0x1, gasPrice, accessList.
  • Tipo 2: type 0x2, maxFeePerGas, maxPriorityFeePerGas, accessList.
  • La forma del campo accessList es la misma para ambos tipos.
  • Verifica con eth_estimateGas antes de difundir.

El compromiso de coste: gas intrínseco frente a precalentamiento

Una lista de acceso no es gratis. Pagas gas intrínseco por cada dirección declarada y por cada clave de almacenamiento declarada, más el coste de calldata de codificar la lista. Ese coste se incurre tanto si la ejecución reutiliza realmente los slots precalentados como si no. El ahorro solo se materializa cuando los slots precalentados se tocan suficientes veces como para que los recargos por frío evitados superen el coste intrínseco.

Por eso el ahorro neto puede ser negativo. Una lista que declara muchos slots pero solo toca unos pocos una vez perderá gas. Una lista que declara un conjunto pequeño de slots calientes tocados repetidamente puede ganar. El punto de cruce depende del contrato, del calldata y del bloque, así que debe medirse en lugar de asumirse.

Para el contexto del mercado de tarifas en torno a la estimación, la mecánica de base fee y priority fee se cubre en Estimar el precio del gas con eth_feeHistory. Las listas de acceso cambian el componente de gas de ejecución, no el componente del mercado de tarifas, así que ambos análisis son complementarios.

  • Lado del coste: gas intrínseco por dirección, gas intrínseco por clave de almacenamiento, bytes de calldata.
  • Lado del beneficio: recargos por frío evitados en slots que realmente se reutilizan.
  • El ahorro neto puede ser positivo, cero o negativo según la reutilización.
  • Mide en el mismo bloque con y sin la lista.

Medición reproducible: una tabla de resultados que tú completas

Como los ahorros de las listas de acceso dependen del contrato y del bloque, el enfoque honesto es una tabla de medición que rellenas contra tu propio endpoint. Ejecuta el ejemplo de Node.js anterior para cada llamada que te interese, registra los valores y calcula el delta neto. No copies cifras de un artículo de blog, incluido este; las cifras dependen de un estado que cambia.

Usa el mismo tag de bloque para ambas estimaciones de una fila para que la comparación sea justa. Si quieres comprobar la estabilidad, repite la fila en un bloque posterior y anota si el delta se movió. Un delta que cambia de signo entre bloques es señal de que la optimización no es robusta para esa llamada.

  • Llamada: una etiqueta corta para la llamada a contrato que probaste.
  • Chain id: la red contra la que ejecutaste.
  • gasUsed de eth_createAccessList: el gas de ejecución simulado.
  • eth_estimateGas sin lista: la estimación de referencia.
  • eth_estimateGas con lista: la estimación con el accessList devuelto adjunto.
  • Delta neto: con lista menos sin lista; negativo significa ahorro.
  • Decisión: adjuntar la lista, omitirla o volver a medir en un bloque posterior.

Limitaciones y cuándo no ayudan las listas de acceso

La primera limitación es el soporte del método. eth_createAccessList requiere que el nodo lo implemente, y el soporte está documentado / varía según el proveedor. Si tu endpoint devuelve un error de método no encontrado, no puedes usar este flujo de trabajo sin cambiar de endpoint. La guía de nodos RPC de Ethereum (RPC Assistant) es una referencia útil para comprobar qué expone un nodo.

La segunda limitación es la obsolescencia. El resultado solo es válido en el bloque contra el que se simuló, porque el estado y la condición de frío/caliente cambian a medida que llegan nuevos bloques y otras transacciones modifican el almacenamiento. Una lista generada en el bloque N puede ser subóptima o incorrecta en el bloque N+1.

La tercera limitación es el alcance. Las listas de acceso rara vez ayudan en transferencias simples, porque una transferencia de valor normal no toca ningún slot de almacenamiento de contrato que se beneficie del precalentamiento. Tampoco son una victoria universal en L2s, donde el modelo de tarifas difiere y el ahorro puede no trasladarse. Por último, la estimación en sí es una simulación; el coste minado puede diferir si las rutas de ejecución divergen.

  • Soporte del método: documentado / varía según el proveedor.
  • Obsolescencia: válido solo en el bloque simulado.
  • Alcance: rara vez ayuda en transferencias simples.
  • Los modelos de tarifas de L2 difieren; los ahorros pueden no trasladarse.
  • La simulación no garantiza el coste minado.

Solución de problemas: método no encontrado, listas vacías, ahorros negativos

Método no encontrado es el fallo más común. Significa que el nodo no implementa eth_createAccessList. Consulta la lista de métodos del proveedor; si no aparece, no puedes generar listas de esta forma. Algunos proveedores lo exponen solo en endpoints archive o con debug habilitado, así que el mismo proveedor puede comportarse de forma distinta según el plan.

Un accessList siempre vacío normalmente significa que la llamada no toca ningún slot de almacenamiento de contrato que el método considere digno de declarar, o que la llamada revierte o es trivial. Verifica que el objeto de llamada sea correcto (to, data, from) y que el tag de bloque sea válido. Si la llamada revierte, la simulación puede devolver una lista vacía en lugar de un error.

Un ahorro negativo significa que el coste intrínseco de la lista superó los recargos por frío evitados. Este es un resultado legítimo, no un bug. Reduce la lista a solo los slots que realmente se reutilizan, o prescinde de la lista por completo. Vuelve a medir en el mismo bloque para confirmar que el delta es estable antes de decidir.

  • Método no encontrado: el nodo no implementa el método; consulta la documentación del proveedor.
  • Lista vacía: la llamada puede ser trivial, revertir o no tocar slots relevantes.
  • Ahorro negativo: la lista cuesta más de lo que ahorra; recórtala u omítela.
  • Vuelve a medir siempre en el mismo bloque antes de decidir.

Próximos pasos: estimar, trazar y elegir un endpoint

Las listas de acceso son una palanca entre varias para controlar el gas. La estimación en sí se cubre en eth_estimateGas: límites de gas y slippage, que explica cómo fijar un límite de gas con margen. El lado del mercado de tarifas se cubre en Estimar el precio del gas con eth_feeHistory. Juntos te dan la imagen completa del coste de una transacción.

Si necesitas entender por qué una llamada toca los slots que toca, el trazado es la siguiente herramienta. Trazado de transacciones de Ethereum con trace y debug muestra cómo inspeccionar la ejecución a nivel de opcode, lo cual es útil cuando una lista de acceso se comporta de forma inesperada.

Para la selección de endpoint, la página de la red Ethereum lista la red, y Precios de RPC y el Servicio de API describen cómo se aprovisiona el acceso. El hub de aprendizaje de OnFinality reúne el resto de las guías de esta serie. Empieza ejecutando la tabla de medición contra tu propio endpoint antes de adoptar listas de acceso en producción.

  • Estima límites de gas con margen: guía de eth_estimateGas.
  • Entiende los percentiles del mercado de tarifas: guía de eth_feeHistory.
  • Inspecciona la ejecución: guía de trace y debug.
  • Elige un endpoint: página de red, precios y 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