Logo
Nuevos usuarios de RPC: 35% de descuento el primer mesVer oferta
OnFinality Learn
Rendimiento y optimización14 min de lectura

Bundles de Jito en Solana vía RPC: propinas, atomicidad y diagnóstico de fallos

Un análisis técnico profundo de la mecánica de los bundles de Jito, las instrucciones de propina, la selección de slots y un método reproducible para diagnosticar por qué un bundle se incluyó o no.

TL;DR

Un bundle de Jito es un array ordenado de transacciones de Solana completamente firmadas que se envían a un block engine en lugar de difundirse a la red pública, con una garantía de atomicidad de que todos los miembros se incluyen consecutivamente en el slot objetivo o ninguno lo hace. La propina es una instrucción de transferencia simple de SOL dentro de un miembro del bundle, no un campo de comisión a nivel de protocolo, y su tamaño determina el atractivo económico del bundle en la subasta del block engine. Dado que los bundles se dirigen a un slot específico, el presupuesto de tiempo entre observar una oportunidad y enviarlo está acotado por el tiempo de slot, lo que hace que la latencia sea medible. El diagnóstico de fallos requiere separar los envíos estructuralmente inválidos (firma incorrecta, blockhash obsoleto, conflicto de bloqueo de cuenta) de los bundles válidos pero no incluidos (la propina perdió la subasta, se perdió el slot) y de los miembros ya incluidos o en conflicto. Este artículo proporciona un script de Node.js ejecutable que ensambla un bundle de dos transacciones con una propina, lo envía a través de la ruta del block engine y verifica el estado y el slot de cada miembro usando getSignatureStatuses.

Mecánica de los bundles y garantías de atomicidad

Un bundle de Jito es un array ordenado de transacciones de Solana completamente firmadas que se envían a un block engine en lugar de difundirse a la red pública. El block engine solo incluirá el bundle si cada transacción miembro se incluye consecutivamente en el slot objetivo, lo que significa que un solo miembro fallido descarta todo el envío. Esta garantía de atomicidad es la propiedad definitoria que separa los bundles de los envíos de transacciones estándar, donde cada transacción es independiente y puede incluirse en slots diferentes o no incluirse en absoluto.

El block engine recibe bundles de los searchers y los evalúa para su inclusión en el siguiente slot disponible. A diferencia de la propagación de transacciones basada en gossip de la red pública, el block engine opera como una ruta privilegiada con acceso directo a los productores de bloques. Esta arquitectura permite una inclusión de menor latencia para los bundles que ganan la subasta, pero también significa que la inclusión del bundle depende de una subasta de terceros en lugar del endpoint RPC del lector. Para envíos de transacciones estándar, consulta Tiempos de espera y estrategia de reintentos en RPC de Solana.

Cada transacción miembro de un bundle debe estar completamente firmada antes del envío. El block engine no firma transacciones en nombre del searcher. Esto significa que el searcher debe construir, firmar y serializar todas las transacciones localmente antes de enviar el bundle al endpoint del block engine. Luego, el bundle se acepta para el slot objetivo o se rechaza; no hay inclusión parcial.

  • Bundle = array ordenado de transacciones completamente firmadas con atomicidad en todo el array.
  • La inclusión en el block engine requiere que todos los miembros se incluyan consecutivamente en el slot objetivo.
  • Un solo miembro fallido descarta todo el envío del bundle.
  • El envío va a un block engine, no a la red de gossip de la red pública.

Instrucciones de propina y atractivo económico

La propina es una instrucción de transferencia dentro de un miembro del bundle, no un campo de comisión. Solana no tiene una subasta de prioridad a nivel de protocolo en la que participe un bundle; la propina es una transferencia simple de SOL a una de las cuentas de propina, y el atractivo económico del bundle es el tamaño de esa transferencia. Esta distinción es crítica para el diagnóstico: 'la propina era demasiado baja' y 'el bundle se descartó' son diagnósticos diferentes. Una propina baja significa que el bundle era estructuralmente válido y se envió correctamente, pero perdió la subasta frente a un bundle con una propina mayor. Un bundle descartado significa que el envío en sí falló o nunca fue elegible para el slot objetivo.

El conjunto de cuentas de propina está documentado por Jito y varía con el tiempo. Los lectores deben leer la lista actual de una fuente autorizada en lugar de codificar direcciones de forma fija, porque una dirección obsoleta produce un bundle estructuralmente válido y económicamente invisible. El block engine no reconocerá una transferencia a una cuenta de propina obsoleta como una propina válida, por lo que el bundle no se considerará en la subasta. Obtén siempre las cuentas de propina actuales de la documentación o API de Jito antes de construir un bundle.

La instrucción de propina suele colocarse como la última instrucción en la última transacción del bundle, aunque la ubicación exacta es una convención más que un requisito del protocolo. El block engine escanea el bundle en busca de una transferencia a una cuenta de propina reconocida y usa ese monto para clasificar el bundle frente a otros que apuntan al mismo slot. Para más información sobre cómo interactúan las comisiones de transacción y los presupuestos de cómputo con los miembros del bundle, consulta Niveles de compromiso y confirmación de transacciones en Solana.

  • Propina = instrucción de transferencia simple de SOL dentro de un miembro del bundle, no un campo de comisión.
  • Atractivo económico del bundle = tamaño de la transferencia de propina.
  • Las cuentas de propina están documentadas y cambian; lee la lista actual de una fuente autorizada.
  • Una dirección de propina obsoleta produce un bundle estructuralmente válido pero económicamente invisible.

Selección de slot y medición de latencia

Un bundle se dirige a un slot específico, lo que hace que la latencia sea medible. El presupuesto de tiempo entre observar una oportunidad y enviar el bundle está acotado por el tiempo de slot. Si el bundle llega después de que se cierra la ventana de producción de bloques del slot objetivo, no se incluirá independientemente del tamaño de la propina. Esto significa que el lector debe medir su propio margen de envío a slot en lugar de asumirlo. El margen es la diferencia entre el momento en que se envía el bundle y el momento en que se produce el bloque del slot objetivo.

Para medir este margen, registra la marca de tiempo inmediatamente antes de enviar el bundle y el número de slot objetivo. Una vez que pase el slot, consulta el block engine o la red para conocer el estado del bundle. Si el bundle no se incluyó, compara la marca de tiempo de envío con el tiempo estimado de producción del slot. Esta medición es específica de la ruta de red, la ubicación geográfica y la configuración del endpoint del lector. Para un tratamiento más amplio de la medición de latencia, consulta Latencia de RPC en Solana: medición y optimización.

La regla de selección de slot también significa que el envío de bundles es una carrera contra el tiempo. Los searchers que incluyen bundles de forma consistente suelen optimizar su ruta de red hacia el block engine y su pipeline local de construcción de transacciones. El lector debe tratar el margen de envío a slot como un parámetro ajustable y medirlo en condiciones realistas en lugar de confiar en mínimos teóricos.

  • Los bundles se dirigen a un slot específico; una llegada tardía significa que no hay inclusión.
  • El margen de envío a slot es el tiempo entre el envío y la producción del slot objetivo.
  • Mide tu propio margen; depende de la ruta de red, la ubicación y el endpoint.
  • Optimiza la construcción de transacciones y la ruta de red para reducir el margen.

Clases de fallo: inválido, no incluido y en conflicto

El diagnóstico de fallos requiere mantener tres clases rigurosamente separadas. Los envíos estructuralmente inválidos se manifiestan como una respuesta de error de la llamada de envío. Esta clase incluye una firma incorrecta, un blockhash obsoleto, un conflicto de bloqueo de cuenta o una instrucción de propina mal formada. El block engine rechaza el bundle de inmediato y no se envía ninguna transacción a la red. La respuesta de error suele incluir un código o mensaje de motivo que identifica el problema estructural específico.

Los bundles válidos pero no incluidos son la segunda clase. Aquí la llamada de envío tiene éxito, pero el bundle no aparece en la cadena. Esto ocurre porque la propina perdió la subasta o se perdió el slot. El bundle era estructuralmente válido y económicamente visible, pero otro bundle ofreció una propina mayor o llegó antes. Esta clase requiere verificar si el bundle se incluyó en el slot objetivo o en algún slot posterior. Si no se incluyó, es probable que la propina fuera demasiado baja o que el envío llegara demasiado tarde.

Los bundles ya incluidos o en conflicto son la tercera clase. Una transacción miembro puede haberse ejecutado a través de otra ruta, como un envío estándar o un bundle diferente. Esto cambia qué firmas aún se pueden encontrar en la cadena. Si un miembro ya se incluyó, la garantía de atomicidad del bundle se rompe y los miembros restantes pueden rechazarse o incluirse por separado. El lector debe verificar el estado de la firma de cada miembro individualmente para determinar qué ocurrió realmente. Para las reglas de expiración de blockhash que se aplican a cada miembro, consulta Nonce duradero y expiración de blockhash en Solana.

  • Estructuralmente inválido: firma incorrecta, blockhash obsoleto, conflicto de bloqueo de cuenta, propina mal formada.
  • Válido pero no incluido: el envío tiene éxito pero el bundle no aparece; la propina perdió la subasta o se perdió el slot.
  • Ya incluido o en conflicto: un miembro se ejecutó a través de otra ruta.
  • Verifica el estado de la firma de cada miembro individualmente para determinar el resultado real.

Script de Node.js ejecutable para ensamblar y verificar bundles

El siguiente script de Node.js ensambla un bundle de dos transacciones con una instrucción de propina, lo envía a través de la ruta del block engine y luego verifica qué ocurrió realmente obteniendo el estado de cada miembro con getSignatureStatuses y comprobando el slot confirmado. El script imprime una tabla con la firma enviada, el estado, el slot y si todos los miembros se incluyeron en el mismo slot. Este script es una plantilla; el lector debe reemplazar la cuenta de propina de marcador de posición con una dirección actual de la documentación de Jito y configurar su propio keypair y endpoint RPC.

El script usa @solana/web3.js para la construcción y firma de transacciones, y axios para las solicitudes HTTP al block engine. El bundle se envía como una carga útil JSON que contiene transacciones codificadas en base64. El endpoint del block engine y el método de autenticación varían según el proveedor; el lector debe consultar la documentación actual de bundles de Jito para conocer el formato exacto de la solicitud. El paso de verificación usa el método estándar getSignatureStatuses de JSON-RPC de Solana, que está documentado en la referencia de JSON-RPC de Solana.

const { Connection, Keypair, Transaction, SystemProgram, sendAndConfirmTransaction, PublicKey } = require('@solana/web3.js');
const axios = require('axios');

// Configuration — replace with your own values
const RPC_ENDPOINT = 'https://your-solana-rpc-endpoint';
const BLOCK_ENGINE_URL = 'https://your-block-engine-endpoint/api/v1/bundles';
const TIP_ACCOUNT = new PublicKey('REPLACE_WITH_CURRENT_TIP_ACCOUNT');
const TIP_LAMPORTS = 10000; // 0.00001 SOL — adjust based on current auction

async function main() {
  const connection = new Connection(RPC_ENDPOINT, 'confirmed');
  const payer = Keypair.generate(); // Replace with your funded keypair

  // Fetch a recent blockhash
  const { blockhash } = await connection.getLatestBlockhash('confirmed');

  // Transaction 1: a simple transfer (replace with your actual transaction)
  const tx1 = new Transaction({ recentBlockhash: blockhash, feePayer: payer.publicKey });
  tx1.add(SystemProgram.transfer({
    fromPubkey: payer.publicKey,
    toPubkey: Keypair.generate().publicKey,
    lamports: 1000,
  }));
  tx1.sign(payer);

  // Transaction 2: tip transfer to Jito tip account
  const tx2 = new Transaction({ recentBlockhash: blockhash, feePayer: payer.publicKey });
  tx2.add(SystemProgram.transfer({
    fromPubkey: payer.publicKey,
    toPubkey: TIP_ACCOUNT,
    lamports: TIP_LAMPORTS,
  }));
  tx2.sign(payer);

  // Serialize transactions to base64
  const serializedTx1 = tx1.serialize().toString('base64');
  const serializedTx2 = tx2.serialize().toString('base64');

  // Submit bundle to block engine
  const bundlePayload = {
    jsonrpc: '2.0',
    id: 1,
    method: 'sendBundle',
    params: [[serializedTx1, serializedTx2]],
  };

  let bundleResult;
  try {
    const response = await axios.post(BLOCK_ENGINE_URL, bundlePayload, {
      headers: { 'Content-Type': 'application/json' },
    });
    bundleResult = response.data;
    console.log('Bundle submission response:', JSON.stringify(bundleResult, null, 2));
  } catch (err) {
    console.error('Bundle submission failed:', err.response ? err.response.data : err.message);
    return;
  }

  // Wait for the target slot to pass
  await new Promise((resolve) => setTimeout(resolve, 2000));

  // Verify each member's status using getSignatureStatuses
  const signatures = [tx1.signature.toString(), tx2.signature.toString()];
  const statusResponse = await connection.getSignatureStatuses(signatures, {
    searchTransactionHistory: true,
  });

  // Print results table
  console.log('\n--- Bundle Verification Results ---');
  console.log('Signature | Status | Slot | Same Slot?');
  const slots = [];
  statusResponse.value.forEach((status, index) => {
    const sig = signatures[index];
    const confirmationStatus = status ? status.confirmationStatus : 'not found';
    const slot = status ? status.slot : 'N/A';
    slots.push(slot);
    console.log(`${sig.slice(0, 8)}... | ${confirmationStatus} | ${slot} |`);
  });
  const allSameSlot = slots.every((s) => s === slots[0] && s !== 'N/A');
  console.log(`All members landed in same slot: ${allSameSlot}`);
}

main().catch(console.error);

Tabla de resultados para medición específica del endpoint

Dado que la inclusión y la latencia de los bundles dependen de la ruta de red, el endpoint y la ubicación geográfica del lector, este artículo no proporciona cifras de referencia. En su lugar, el lector debe medir su propio margen de envío a slot y su tasa de inclusión de bundles usando una tabla de resultados. Ejecuta el script anterior varias veces en condiciones realistas, registrando la marca de tiempo de envío, el slot objetivo, si el bundle se incluyó, el slot en el que se incluyó y el monto de la propina. Esta tabla se convierte en la base para ajustar el tamaño de la propina y el momento de envío.

A continuación se muestra una estructura de tabla de resultados de ejemplo. Rellénala con tus propias mediciones. El objetivo es identificar patrones: ¿una propina más alta se correlaciona con la inclusión? ¿Un margen de envío a slot más corto mejora la inclusión? ¿Hay slots o momentos del día específicos en los que la inclusión es más probable? Estos patrones son específicos de tu configuración y no pueden generalizarse a partir de referencias de terceros.

Al medir, asegúrate de usar un endpoint RPC y un endpoint de block engine consistentes. Los cambios en cualquiera de los dos pueden afectar los resultados. Para la selección y configuración de endpoints, consulta Endpoints RPC de Solana (RPC Assistant).

  • Marca de tiempo de envío | Slot objetivo | ¿Incluido? | Slot de inclusión | Propina (lamports) | Margen de envío a slot (ms)
  • Ejecuta al menos 20 pruebas para establecer una línea base.
  • Varía el tamaño de la propina y mide la tasa de inclusión.
  • Varía el momento de envío y mide el margen de envío a slot.
  • Registra el endpoint y la URL del block engine para cada prueba.

Interacción con las restricciones estándar de Solana

Cada transacción miembro de un bundle sigue necesitando un blockhash reciente y expirará según el mismo calendario que cualquier transacción estándar de Solana. La capa de bundles no extiende la vida útil del blockhash. Si el blockhash de un miembro expira antes de que se incluya el bundle, el bundle se vuelve estructuralmente inválido y se rechazará. Esto significa que el lector aún debe gestionar la frescura del blockhash, ya sea enviando con prontitud o usando nonces duraderos para extender la vida útil de un miembro.

Cada miembro sigue consumiendo una comisión y un presupuesto de cómputo. El bundle no evita el mercado de comisiones ni los límites de cómputo de Solana. Cada transacción debe tener suficientes unidades de cómputo y pagar la comisión base. La propina es una transferencia adicional sobre estos costos. Los searchers deben tener en cuenta el costo total por bundle, incluidas las comisiones base, los costos de unidades de cómputo y la propina, al calcular la rentabilidad.

Los nonces duraderos aún se pueden usar para extender la vida útil de un miembro, lo que permite preparar un bundle con antelación y enviarlo más tarde sin que expire el blockhash. Esto se compone con la capa de bundles en lugar de reemplazarla. El lector debe tratar los bundles como una ruta de envío adicional que se sitúa sobre la maquinaria de confiabilidad estándar, no como un reemplazo de ella. Para un tratamiento detallado de los nonces duraderos, consulta Nonce duradero y expiración de blockhash en Solana.

  • Cada miembro del bundle necesita un blockhash reciente y expira según el calendario estándar.
  • Cada miembro consume una comisión y un presupuesto de cómputo; la propina es adicional.
  • Los nonces duraderos pueden extender la vida útil de un miembro y se componen con los bundles.
  • Los bundles añaden una ruta de envío; no reemplazan la maquinaria de confiabilidad estándar.

Limitaciones y compensaciones

La inclusión de bundles depende de la subasta de un block engine de terceros en lugar del endpoint del lector. Esto significa que incluso un bundle perfectamente construido con una propina competitiva puede no incluirse si un bundle con una propina mayor apunta al mismo slot. El lector no puede garantizar la inclusión solo mediante la selección o configuración del endpoint. La dinámica de la subasta del block engine es opaca y puede cambiar con el tiempo.

La rentabilidad de la propina no es algo que este artículo pueda prometer. La propina es un costo, y el retorno depende del valor de la oportunidad que captura el bundle. Los searchers deben calcular su propia rentabilidad en función de la oportunidad específica y las condiciones actuales de la subasta. Una propina que era suficiente ayer puede ser insuficiente hoy. No existe un monto de propina fijo que garantice la inclusión.

La lista de cuentas de propina y la superficie de la API cambian sin previo aviso. Jito puede añadir o eliminar cuentas de propina, cambiar el formato del endpoint del block engine o modificar la API de envío de bundles. El lector debe verificar cada comportamiento con la documentación actual antes de confiar en él en producción. Este artículo describe el comportamiento documentado a la fecha de redacción, pero el comportamiento específico del proveedor varía y puede cambiar. Para infraestructura que admita los métodos RPC estándar de Solana usados en la verificación, consulta Endpoints RPC de Solana (RPC Assistant) y Servicio de API.

  • La inclusión de bundles depende de una subasta de terceros, no de tu endpoint.
  • La rentabilidad de la propina no se puede prometer; calcula la tuya según el valor de la oportunidad.
  • La lista de cuentas de propina y la superficie de la API cambian sin previo aviso.
  • Verifica cada comportamiento con la documentación actual antes de usarlo en producción.

Solución de fallos comunes de bundles

Cuando falla un bundle, el primer paso es determinar qué clase de fallo aplica. Si la llamada de envío devolvió un error, el bundle era estructuralmente inválido. Revisa el mensaje de error para conocer las causas específicas: una firma incorrecta significa que la transacción no se firmó correctamente; un blockhash obsoleto significa que el blockhash expiró antes del envío; un conflicto de bloqueo de cuenta significa que otra transacción bloqueó una cuenta en el bundle; una instrucción de propina mal formada significa que la transferencia a la cuenta de propina no se construyó correctamente. Corrige el problema estructural y vuelve a enviar.

Si la llamada de envío tuvo éxito pero el bundle no apareció en la cadena, el bundle era válido pero no se incluyó. Esta es la clase de fallo más común. Verifica el monto de la propina frente a las condiciones actuales de la subasta. Si la propina era baja, auméntala y vuelve a enviar. Verifica el margen de envío a slot; si el bundle llegó tarde, optimiza tu ruta de red o envía antes. Usa la tabla de resultados para identificar si el tamaño de la propina o el momento de envío es el factor limitante.

Si una transacción miembro ya se incluyó a través de otra ruta, la garantía de atomicidad del bundle se rompe. Verifica el estado de la firma de cada miembro individualmente usando getSignatureStatuses. Si un miembro se incluyó y otros no, el bundle se ejecutó parcialmente, lo que significa que se violó la garantía de atomicidad. Esto puede ocurrir si un miembro se envió por separado o si el block engine incluyó solo parte del bundle. En este caso, es posible que los miembros restantes deban reenviarse como un nuevo bundle o como transacciones estándar. Para estrategias de reintento de envíos estándar, consulta Tiempos de espera y estrategia de reintentos en RPC de Solana.

  • Error en la llamada de envío → estructuralmente inválido: revisa firma, blockhash, bloqueos de cuenta, instrucción de propina.
  • Éxito en la llamada de envío pero sin inclusión → válido pero no incluido: revisa el tamaño de la propina y el margen de envío a slot.
  • Miembro ya incluido → en conflicto: revisa cada firma individualmente con getSignatureStatuses.
  • Usa la tabla de resultados para distinguir problemas de tamaño de propina de problemas de momento de envío.

Próximos pasos para el envío de bundles en producción

Para pasar de la experimentación a la producción, comienza estableciendo un pipeline de medición confiable. Ejecuta el script anterior con una configuración consistente y registra los resultados en una tabla. Usa la tabla para ajustar el tamaño de la propina y el momento de envío. Una vez que tengas una línea base, introduce variabilidad para probar la robustez: diferentes slots, diferentes momentos del día, diferentes condiciones de red. El objetivo es comprender tu tasa de inclusión y los factores que la afectan.

A continuación, integra nonces duraderos si tu caso de uso requiere preparar bundles con antelación. Esto desacopla la construcción del bundle del momento de envío y reduce el riesgo de expiración del blockhash. Para una guía detallada, consulta Nonce duradero y expiración de blockhash en Solana. Revisa también Niveles de compromiso y confirmación de transacciones en Solana para asegurarte de que tu lógica de verificación use el nivel de compromiso adecuado.

Por último, considera tu infraestructura. El envío de bundles se beneficia de rutas de red de baja latencia hacia el block engine. Si usas un endpoint RPC estándar para la verificación, asegúrate de que admita getSignatureStatuses con searchTransactionHistory. Para opciones de endpoints y precios, consulta Endpoints RPC de Solana (RPC Assistant), Precios de RPC y el centro de aprendizaje de OnFinality. Para detalles específicos de la red, consulta la página de la red Solana.

  • Establece un pipeline de medición y registra los resultados en una tabla.
  • Ajusta el tamaño de la propina y el momento de envío según tu propia tasa de inclusión.
  • Integra nonces duraderos para la preparación anticipada de bundles.
  • Verifica con getSignatureStatuses usando el nivel de compromiso adecuado.
  • Optimiza la ruta de red hacia el block engine para reducir el margen de envío a slot.

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