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

Seguimiento de eventos entre cadenas en Base: registros de depósito y pruebas de retiro

Aprende a analizar registros de depósito L1->L2 y pruebas de retiro L2->L1 en Base usando RPC, con ejemplos ejecutables y solución de problemas.

TL;DR

Para rastrear la actividad entre cadenas en Base, debes leer registros tanto en Ethereum (L1) como en Base (L2). Un depósito L1->L2 se inicia con una transacción al OptimismPortal que emite un evento TransactionDeposited; op-node deriva una transacción de depósito que aparece en un bloque de Base con su propio hash y recibo L2. Para retiros L2->L1, comienzas con un evento MessagePassed en L2, luego pruebas y finalizas en L1 usando raíces de salida y juegos de disputa. Esta guía explica las estructuras de eventos, proporciona ejemplos RPC ejecutables para analizarlos y verificarlos, e incluye una lista de verificación para solucionar problemas.

Respuesta directa: Cómo rastrear eventos entre cadenas en Base

Para rastrear la actividad entre cadenas en Base, debes leer registros tanto en Ethereum (L1) como en Base (L2). Un depósito L1->L2 se inicia con una transacción al OptimismPortal que emite un evento TransactionDeposited; op-node deriva una transacción de depósito que aparece en un bloque de Base con su propio hash y recibo L2. Para retiros L2->L1, comienzas con un evento MessagePassed en L2, luego pruebas y finalizas en L1 usando raíces de salida y juegos de disputa. Esta guía explica las estructuras de eventos, proporciona ejemplos RPC ejecutables para analizarlos y verificarlos, e incluye una lista de verificación para solucionar problemas.

La clave es nunca confundir el hash de la transacción de depósito L1 con el hash de la transacción L2 resultante. El hash L1 identifica la llamada al portal; el hash L2 se deriva del depósito y aparece en un bloque posterior de Base. Puedes confirmar la inclusión consultando el recibo L2 y verificando su estado. Para retiros, el proceso involucra tres fases: iniciar en L2, probar en L1 y finalizar en L1. Cada fase emite eventos distintos que puedes monitorear con registros RPC.

Esta guía es parte del centro de aprendizaje de OnFinality y complementa nuestros artículos sobre endpoints RPC de la red Base (Asistente RPC) y finalidad OP-Stack y bloques seguros/finalizados en Base.

Mecanismo: Depósitos L1 a L2 en OP-Stack

En cadenas OP-Stack como Base, un depósito L1->L2 es un proceso de dos pasos. Primero, un usuario llama a la función depositTransaction en el contrato OptimismPortal en Ethereum. Esto emite un evento TransactionDeposited con campos que codifican el depósito. Segundo, op-node (el cliente de consenso) deriva una transacción de depósito de ese evento y la incluye en un bloque de Base. La transacción L2 resultante tiene su propio hash y recibo, distintos de la transacción L1.

El evento TransactionDeposited se define en la interfaz OptimismPortal. Sus campos indexados incluyen from, to, version y opaqueData (este último no está indexado en versiones más recientes). El opaqueData contiene el mint, valor, límite de gas y calldata para la transacción L2. Para analizarlo, necesitas el ABI correcto y debes saber cuántos campos indexados esperar: las versiones anteriores tenían una indexación diferente.

Para una especificación detallada del protocolo, consulta las especificaciones de OP Stack sobre depósitos. La documentación de Base también describe parámetros específicos de la cadena. Al integrar, verifica siempre contra los ABI de contrato actuales, ya que el protocolo evoluciona (por ejemplo, la actualización Ecotone cambió algunas estructuras de eventos).

  • El hash de la transacción L1 no es el hash de la transacción L2.
  • El depósito aparece en un bloque de Base después de que op-node procesa el bloque L1.
  • Puedes rastrear depósitos escaneando registros TransactionDeposited en la dirección del portal.
  • El campo status del recibo L2 confirma la ejecución exitosa.

Mecanismo: Retiros L2 a L1 y pruebas

Los retiros de Base a Ethereum son más complejos. En L2, un usuario llama a withdrawTransaction (o initiateWithdrawal en interfaces más nuevas) en el L2CrossDomainMessenger, que emite un evento MessagePassed. Este evento contiene el hash de retiro, nonce, remitente, destino y datos. El retiro no es final hasta que se prueba y finaliza en L1.

Después de iniciar el retiro, la raíz de salida L2 que incluye el retiro debe proponerse en L1. En el modo Superchain actual, esto se hace a través de un contrato DisputeGame. Un relayer envía una prueba de que el retiro está incluido en una raíz de salida propuesta, y después de que pasa la ventana de disputa, el retiro puede finalizarse. En el sistema heredado, un proponente de salida enviaba raíces de salida directamente al OptimismPortal.

El paso de prueba llama a proveWithdrawalTransaction en el OptimismPortal, pasando el hash de retiro, la prueba y la prueba de raíz de salida. La finalización llama a finalizeWithdrawalTransaction. Ambos pasos emiten eventos: WithdrawalProven y WithdrawalFinalized en L1, y RelayedMessage en L2 cuando se ejecuta el mensaje.

Para detalles autorizados, consulta las especificaciones de OP Stack sobre retiros. Los nombres de las fases y las direcciones de los contratos cambian con las actualizaciones, así que siempre verifica los últimos artefactos de implementación.

Ejemplo ejecutable: Análisis de registros de depósito y verificación de recibos L2

El siguiente script de Node.js usa ethers.js para conectarse tanto a un endpoint L1 como a uno L2. Escanea eventos TransactionDeposited en el OptimismPortal para una dirección from específica, luego consulta el recibo L2 para la transacción derivada. Debes reemplazar las URL de los endpoints y las direcciones de los contratos con las tuyas (consulta endpoints RPC de la red Base (Asistente RPC) para endpoints de Base).

El script demuestra cómo filtrar registros por dirección y tema, decodificar los datos del evento y luego usar el depositCount o el número de bloque para encontrar la transacción L2 correspondiente. En la práctica, podrías usar un relayer o indexador que escuche registros en tiempo real a través de WebSocket, pero este ejemplo usa eth_getLogs por simplicidad.

const { ethers } = require('ethers');

// Reemplaza con tus endpoints y direcciones
const L1_RPC = 'https://eth-mainnet.example.com';
const L2_RPC = 'https://base-mainnet.example.com';
const PORTAL_ADDRESS = '0x...'; // OptimismPortal en L1

const portalAbi = [
  'event TransactionDeposited(address indexed from, address indexed to, uint256 indexed version, bytes opaqueData)'
];

async function main() {
  const l1Provider = new ethers.JsonRpcProvider(L1_RPC);
  const l2Provider = new ethers.JsonRpcProvider(L2_RPC);
  const portal = new ethers.Contract(PORTAL_ADDRESS, portalAbi, l1Provider);

  // Ejemplo: escanear los últimos 1000 bloques para depósitos desde una dirección específica
  const fromAddress = '0x...'; // dirección del depositante
  const latestBlock = await l1Provider.getBlockNumber();
  const fromBlock = latestBlock - 1000;

  const filter = portal.filters.TransactionDeposited(fromAddress);
  const logs = await l1Provider.getLogs({
    ...filter,
    fromBlock,
    toBlock: latestBlock
  });

  for (const log of logs) {
    const parsed = portal.interface.parseLog(log);
    console.log('Registro de depósito L1:', log.transactionHash);
    console.log('Depositante:', parsed.args.from);
    console.log('Destino:', parsed.args.to);
    console.log('Versión:', parsed.args.version);
    // opaqueData es bytes; necesitas decodificar más para obtener mint, valor, límite de gas, datos
    // Por simplicidad, solo registramos los datos crudos
    console.log('OpaqueData:', parsed.args.opaqueData);

    // Espera a que el depósito se incluya en un bloque L2
    // En la práctica, harías polling o usarías el depositCount de un relayer
    // Aquí solo esperamos unos segundos y luego consultamos el recibo L2
    // Necesitas derivar el hash de la transacción L2 del evento de depósito; esto no es trivial
    // Para demostración, asumimos que tienes un mapeo de depositCount a hash de transacción L2
    // En su lugar, mostramos cómo verificar el recibo L2 para un hash L2 conocido
    const l2TxHash = '0x...'; // derivado del depósito
    const receipt = await l2Provider.getTransactionReceipt(l2TxHash);
    if (receipt) {
      console.log('Estado del recibo L2:', receipt.status); // 1 = éxito
    } else {
      console.log('Transacción L2 aún no encontrada');
    }
  }
}

main().catch(console.error);

Ejemplo ejecutable: Lectura de pruebas de retiro y finalización

Para retiros, necesitas monitorear eventos MessagePassed en L2 y luego rastrear la prueba y finalización en L1. El siguiente script demuestra cómo consultar registros MessagePassed del L2CrossDomainMessenger y luego verificar eventos WithdrawalProven y WithdrawalFinalized en el OptimismPortal.

Este ejemplo está simplificado; en una integración real usarías el hash de retiro para correlacionar eventos entre cadenas. El evento MessagePassed incluye el hash de retiro como un campo indexado, que puedes usar para filtrar registros L1.

const { ethers } = require('ethers');

const L1_RPC = 'https://eth-mainnet.example.com';
const L2_RPC = 'https://base-mainnet.example.com';
const L2_MESSENGER = '0x...'; // L2CrossDomainMessenger en Base
const PORTAL = '0x...'; // OptimismPortal en L1

const l2MessengerAbi = [
  'event MessagePassed(uint256 indexed nonce, address indexed sender, address indexed target, uint256 value, uint256 gasLimit, bytes data, bytes32 withdrawalHash)'
];
const portalAbi = [
  'event WithdrawalProven(bytes32 indexed withdrawalHash, address indexed from, address indexed to, uint256 timestamp)',
  'event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success)'
];

async function main() {
  const l2Provider = new ethers.JsonRpcProvider(L2_RPC);
  const l1Provider = new ethers.JsonRpcProvider(L1_RPC);
  const l2Messenger = new ethers.Contract(L2_MESSENGER, l2MessengerAbi, l2Provider);
  const portal = new ethers.Contract(PORTAL, portalAbi, l1Provider);

  // Obtener eventos MessagePassed recientes
  const latestL2Block = await l2Provider.getBlockNumber();
  const filter = l2Messenger.filters.MessagePassed();
  const logs = await l2Provider.getLogs({
    ...filter,
    fromBlock: latestL2Block - 1000,
    toBlock: latestL2Block
  });

  for (const log of logs) {
    const parsed = l2Messenger.interface.parseLog(log);
    const withdrawalHash = parsed.args.withdrawalHash;
    console.log('MessagePassed en L2:', log.transactionHash);
    console.log('Hash de retiro:', withdrawalHash);

    // Verificar en L1 eventos de prueba y finalización
    const proofFilter = portal.filters.WithdrawalProven(withdrawalHash);
    const proofLogs = await l1Provider.getLogs({
      ...proofFilter,
      fromBlock: 0,
      toBlock: 'latest'
    });
    console.log('Eventos de prueba encontrados:', proofLogs.length);

    const finalFilter = portal.filters.WithdrawalFinalized(withdrawalHash);
    const finalLogs = await l1Provider.getLogs({
      ...finalFilter,
      fromBlock: 0,
      toBlock: 'latest'
    });
    console.log('Eventos de finalización encontrados:', finalLogs.length);
  }
}

main().catch(console.error);

Lista de verificación para solucionar problemas en el seguimiento de eventos entre cadenas

Al integrar el seguimiento de eventos entre cadenas, puedes encontrar varios problemas comunes. Usa esta lista de verificación para diagnosticar problemas.

Primero, verifica que estás usando la dirección del portal correcta para la versión de la cadena. Base ha pasado por actualizaciones, y la dirección del portal puede cambiar. Siempre obtén la última implementación de la documentación oficial de Base o del registro de superchain de OP Stack.

Segundo, asegúrate de que tu decodificación de eventos coincida con el ABI actual. El número de campos indexados en TransactionDeposited cambió con el tiempo. Si ves datos corruptos, verifica la versión del ABI.

Tercero, si ves un evento de depósito L1 pero no una transacción L2 correspondiente, espera al siguiente bloque derivado. Los depósitos se procesan asincrónicamente; el bloque L2 puede no producirse inmediatamente. Puedes hacer polling al endpoint L2 para el hash de transacción esperado.

Cuarto, al escanear registros históricos, ten en cuenta los límites de rango de bloques. Muchos proveedores restringen eth_getLogs a un cierto rango (por ejemplo, 10,000 bloques). Si necesitas eventos más antiguos, divide tu escaneo en rangos más pequeños. Este es un método documentado; consulta Monitoreo de endpoints RPC y salud del nodo para más información sobre límites de tasa.

Finalmente, para retiros, recuerda que 'probado' no es 'finalizado'. La ventana de disputa debe pasar antes de la finalización. Si ves un evento WithdrawalProven pero no WithdrawalFinalized, puede simplemente estar esperando a que transcurra la ventana.

  • Dirección del portal incorrecta para la versión de la cadena.
  • ABI incorrecto o número de campos indexados incorrecto.
  • Depósito aún no incluido en un bloque L2.
  • Rango de bloques demasiado grande para eth_getLogs.
  • Confundir 'probado' con 'finalizado'.

Limitaciones y actualizaciones del protocolo

El seguimiento de eventos entre cadenas está sujeto a limitaciones y actualizaciones del protocolo. La finalidad de los depósitos en L2 sigue el encabezado de capa segura, que puede retrasarse con respecto al último bloque. Las salidas de retiro maduran en L1 solo después de que pasa la ventana de disputa, lo que puede tomar días. Estas restricciones de tiempo están definidas por el protocolo y pueden cambiar con actualizaciones.

Por ejemplo, la transición del sistema de raíces de salida a juegos de disputa (pruebas de falla) cambió cómo se prueban los retiros. Siempre consulta las especificaciones actuales de OP Stack y los últimos ABI de contratos. No confíes en direcciones codificadas o firmas de eventos sin verificación.

Al usar proveedores RPC, ten en cuenta que los límites de tasa y los límites de rango de bloques varían según el proveedor. La página de precios RPC de OnFinality documenta nuestro servicio, pero para otros proveedores, consulta su documentación. Para sistemas de producción, considera usar un servicio de API dedicado para manejar alto rendimiento.

Próximos pasos y lecturas adicionales

Ahora que entiendes cómo rastrear eventos entre cadenas, puedes construir un relayer, indexador o integración de billetera. Comienza configurando endpoints RPC confiables tanto para Ethereum como para Base. OnFinality proporciona endpoints RPC de la red Base (Asistente RPC) y endpoints RPC de la red Base (Asistente RPC) para uso en producción.

Para profundizar tu comprensión del modelo de finalidad de Base, lee Finalidad OP-Stack y bloques seguros/finalizados en Base. Para manejar errores RPC, consulta Tiempos de espera y reintentos RPC de Base. Si necesitas datos históricos, consulta Nodos de archivo de Base y estado histórico.

Para mejores prácticas generales de RPC, explora el centro de aprendizaje de OnFinality y Monitoreo de endpoints RPC y salud del nodo.

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