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

Recompensas de getBlock de Solana vs comisiones de transacción: flujo de lamports

Aprende qué contiene realmente el array de recompensas de getBlock y cómo reconciliar el flujo de lamports a nivel de bloque sin confundir las comisiones de transacción con las recompensas del líder.

TL;DR

El array de recompensas en una respuesta getBlock de Solana describe los lamports acreditados durante el procesamiento de ese bloque, no el total de comisiones pagadas por sus transacciones. El rewardType Fee es la porción que corresponde a las comisiones de transacción recaudadas, pero se divide entre el líder, la quema y otros receptores, por lo que no será igual a la suma de los campos de comisión por transacción. Para reconciliar correctamente, suma los campos de comisión de las transacciones, aísla las entradas con rewardType Fee, excluye las recompensas de Rent, Staking y Voting, y trata cualquier diferencia como una división estructural en lugar de un error. Esta guía proporciona un script ejecutable de Node.js y una tabla de resultados para que puedas medir la relación con tu propio endpoint.

Qué representa realmente el array de recompensas de getBlock

Cuando llamas al método RPC getBlock de Solana con las recompensas habilitadas, la respuesta incluye un array de recompensas. Cada entrada está indexada por pubkey y rewardType, y el campo lamports tiene signo porque algunas entradas representan débitos en lugar de créditos. El array describe los lamports acreditados o debitados durante el procesamiento de ese bloque, lo que incluye la recompensa por comisión del líder, pero también otros eventos de recompensa que no son comisiones de transacción en absoluto.

La referencia del RPC getBlock de Solana y el objeto de recompensa documenta los valores de rewardType, incluidos Fee, Rent, Staking y Voting. Esto significa que el array de recompensas es una vista contable a nivel de bloque, no un libro de comisiones de transacción. Si tratas la suma de recompensas como el total de comisiones pagadas por las transacciones del bloque, contarás de más o de menos según qué tipos de recompensa estén presentes.

Esta distinción es importante para exploradores de bloques, paneles de comisiones y trabajos de contabilidad. La página Proveedores y endpoints RPC de Solana (RPC Assistant) puede ayudarte a elegir un endpoint que devuelva datos completos de recompensas, pero la lógica de reconciliación debe ser correcta independientemente del proveedor.

  • las entradas de recompensas están indexadas por pubkey y rewardType
  • lamports tiene signo: los valores negativos son débitos
  • rewardType Fee es la porción que corresponde a las comisiones de transacción recaudadas
  • las recompensas de Rent, Staking y Voting no son comisiones de transacción

Cómo entran las comisiones de transacción y las comisiones de priorización en un bloque

Cada transacción de Solana declara una comisión y puede añadir una comisión de priorización. La documentación de comisiones de transacción y comisiones de priorización de Solana describe cómo se calculan y recaudan. Por lo tanto, las transacciones de un bloque implican un flujo total de comisiones: suma el campo fee de cada transacción y, si tienes datos de comisión de priorización, añádelos también.

Sin embargo, el array de recompensas no refleja simplemente esa suma. El runtime aplica una división: parte de la comisión va al líder, parte se quema y otros receptores pueden recibir porciones. Las entradas con rewardType Fee reflejan la porción del líder y cualquier otro crédito relacionado con comisiones, no el flujo bruto de comisiones. Por eso existe la pregunta en Stack Exchange sobre 'rewardType: Fee lamports': el número no coincide con la suma ingenua de las comisiones de transacción.

Para un análisis más profundo de cómo se codifican y analizan las transacciones en las respuestas de getBlock, consulta Transacciones versionadas de Solana y análisis de getBlock. Esa página cubre los detalles de codificación; esta página se centra en la identidad contable.

Las identidades contables que puedes afirmar

Puedes afirmar que la suma de los campos de comisión por transacción de todo el bloque debe relacionarse con las entradas con rewardType Fee, y que cualquier diferencia se explica por la división entre el líder, la quema y otros receptores que aplica el runtime. Esta es una relación estructural, no una igualdad exacta. La diferencia es esperada y debería ser estable entre bloques si la división es consistente.

Debes excluir por completo las entradas de recompensa Rent, Staking y Voting de la reconciliación de comisiones. Representan eventos económicos diferentes: el alquiler está relacionado con el almacenamiento de cuentas, las recompensas de staking están relacionadas con la activación y desactivación de stake, y las recompensas de votación están relacionadas con la votación de validadores. Incluirlas hará que tu reconciliación no tenga sentido.

Una tabla de reconciliación correcta debería mostrar: comisiones totales de transacción, lamports totales con rewardType Fee, la diferencia y una fila de 'no explicado' para cualquier residuo después de contabilizar las divisiones conocidas. Si la fila de no explicado es consistentemente cero, tu modelo está completo. Si no lo es, puede que te falte un tipo de recompensa o un componente de comisión.

  • Suma los campos de comisión de todas las transacciones del bloque
  • Aísla las entradas con rewardType Fee y suma sus lamports
  • Excluye las entradas de recompensa Rent, Staking y Voting
  • Calcula la diferencia y etiqueta cualquier residuo como no explicado

Trampas de calidad de datos que rompen la reconciliación

El array de recompensas puede estar ausente o vacío en lugar de ser un array de ceros. Si asumes que siempre está presente, tu script fallará en bloques donde no se devuelven recompensas. Comprueba siempre la presencia de la clave rewards y maneja con elegancia los arrays nulos o vacíos.

El campo lamports de una entrada de recompensa tiene signo porque algunas entradas representan débitos. Si descartas las entradas con lamports negativos, sobreestimarás el total. La cantidad está en lamports, así que divide de forma consistente al convertir a SOL. Además, la respuesta varía según la codificación de detalles de transacción solicitada y según el nivel de compromiso, por lo que una comparación entre niveles de compromiso no es una comparación equivalente.

Para obtener orientación sobre cómo manejar tiempos de espera y reintentos al obtener bloques, consulta Tiempos de espera y reintentos del RPC de Solana. Para consideraciones sobre datos históricos, consulta Consulta de datos históricos de Solana por RPC.

  • rewards puede estar ausente o vacío, no ser un array de ceros
  • lamports tiene signo; los valores negativos son débitos
  • divide entre 1e9 de forma consistente para obtener SOL
  • la codificación de detalles de transacción cambia la forma de lo que sumas
  • el nivel de compromiso cambia la respuesta; no mezcles niveles

Script ejecutable: obtén un bloque y reconcilia el flujo de lamports

El siguiente script de Node.js obtiene un bloque con todos los detalles de transacción, extrae cada comisión por transacción, extrae el array de recompensas dividido por rewardType e imprime una tabla de reconciliación con las diferencias calculadas y una fila etiquetada como 'no explicado'. Luego repite para un rango pequeño de bloques para que puedas ver si la diferencia es estable, estructural o puntual.

Reemplaza el endpoint RPC por el tuyo. El script usa la interfaz JSON-RPC estándar y no depende de ningún SDK específico de proveedor. Ejecútalo con algunos bloques para construir tu propia tabla de resultados.

const https = require('https');

const RPC_URL = 'https://api.mainnet-beta.solana.com';
const START_SLOT = 250000000;
const END_SLOT = START_SLOT + 4;

function rpcCall(method, params) {
  return new Promise((resolve, reject) => {
    const data = JSON.stringify({ jsonrpc: '2.0', id: 1, method, params });
    const url = new URL(RPC_URL);
    const options = {
      hostname: url.hostname,
      port: url.port || 443,
      path: url.pathname,
      method: 'POST',
      headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) }
    };
    const req = https.request(options, (res) => {
      let body = '';
      res.on('data', (chunk) => body += chunk);
      res.on('end', () => {
        try { resolve(JSON.parse(body)); } catch (e) { reject(e); }
      });
    });
    req.on('error', reject);
    req.write(data);
    req.end();
  });
}

async function reconcileBlock(slot) {
  const resp = await rpcCall('getBlock', [slot, {
    encoding: 'json',
    transactionDetails: 'full',
    rewards: true,
    maxSupportedTransactionVersion: 0
  }]);
  if (resp.error) {
    console.log(`Slot ${slot}: RPC error`, resp.error.message);
    return null;
  }
  const block = resp.result;
  if (!block) {
    console.log(`Slot ${slot}: no block`);
    return null;
  }

  let totalTxFees = 0;
  for (const tx of block.transactions || []) {
    const meta = tx.meta;
    if (meta && typeof meta.fee === 'number') {
      totalTxFees += meta.fee;
    }
  }

  const rewardsByType = {};
  let totalRewards = 0;
  for (const r of block.rewards || []) {
    const type = r.rewardType || 'Unknown';
    rewardsByType[type] = (rewardsByType[type] || 0) + r.lamports;
    totalRewards += r.lamports;
  }

  const feeReward = rewardsByType['Fee'] || 0;
  const rentReward = rewardsByType['Rent'] || 0;
  const stakingReward = rewardsByType['Staking'] || 0;
  const votingReward = rewardsByType['Voting'] || 0;
  const otherReward = totalRewards - feeReward - rentReward - stakingReward - votingReward;
  const difference = totalTxFees - feeReward;

  return {
    slot,
    totalTxFees,
    feeReward,
    rentReward,
    stakingReward,
    votingReward,
    otherReward,
    difference,
    unexplained: difference
  };
}

(async () => {
  const rows = [];
  for (let slot = START_SLOT; slot <= END_SLOT; slot++) {
    const row = await reconcileBlock(slot);
    if (row) rows.push(row);
  }
  console.log('slot | totalTxFees | FeeReward | Rent | Staking | Voting | Other | Difference | Unexplained');
  for (const r of rows) {
    console.log(`${r.slot} | ${r.totalTxFees} | ${r.feeReward} | ${r.rentReward} | ${r.stakingReward} | ${r.votingReward} | ${r.otherReward} | ${r.difference} | ${r.unexplained}`);
  }
})();

Tabla de resultados: mide con tu propio endpoint

Usa el script anterior para completar una tabla de resultados con tu propio endpoint y rango de bloques. La tabla debe tener una fila por bloque y columnas para las comisiones totales de transacción, los lamports con rewardType Fee, los lamports con rewardType Rent, los lamports con rewardType Staking, los lamports con rewardType Voting, los lamports de otras recompensas, la diferencia entre las comisiones totales y las recompensas Fee, y un residuo no explicado.

Ejecútalo en al menos 10 bloques para ver si la diferencia es estable. Si la diferencia es consistentemente una proporción fija de las comisiones totales, esa es la división documentada. Si varía, puede que te falte un tipo de recompensa o un componente de comisión. No afirmes cifras de recompensas medidas a partir de este artículo; los valores dependen de tu endpoint, nivel de compromiso y rango de bloques.

Para características de rendimiento específicas del proveedor, consulta la documentación de tu proveedor. La página de la red Solana y los precios de RPC de OnFinality describen los endpoints y planes disponibles, pero el método de reconciliación es independiente del proveedor.

  • Una fila por bloque; columnas para cada rewardType y la diferencia
  • Ejecútalo en más de 10 bloques para evaluar la estabilidad
  • No compares entre niveles de compromiso
  • Documenta tu endpoint y rango de bloques para la reproducibilidad

Fallos comunes y cómo evitarlos

Tratar la suma de recompensas como el total de comisiones es el fallo más común. El array de recompensas incluye entradas de Rent, Staking y Voting que no son comisiones de transacción. Aísla siempre las entradas con rewardType Fee antes de compararlas con las comisiones de transacción.

Descartar entradas de recompensa con lamports negativos es otra trampa. Las entradas negativas son débitos y deben incluirse en la suma. Sumar los campos de comisión de transacciones que fallaron o se omitieron también distorsionará tu total; incluye solo las transacciones que realmente se procesaron y se les cobró una comisión.

Comparar un bloque con compromiso confirmed contra uno con compromiso finalised no es una comparación equivalente porque la respuesta puede diferir. Olvidar que la codificación de la lista de transacciones del bloque cambia la forma de lo que sumas romperá tu analizador. Volver a derivar las comisiones a partir de los saldos en lugar de las transacciones es propenso a errores e innecesario; el campo fee es la fuente autorizada.

  • No trates la suma de recompensas como el total de comisiones
  • Incluye las entradas con lamports negativos
  • Excluye las transacciones fallidas u omitidas de la suma de comisiones
  • No mezcles niveles de compromiso
  • Usa el campo de comisión de la transacción, no los deltas de saldo

Lista de verificación para la resolución de problemas

Cuando tu reconciliación no coincida con lo esperado, repasa esta lista de verificación. Primero, verifica que las recompensas estén presentes en la respuesta. Si la clave rewards falta o está vacía, puede que tu endpoint no devuelva recompensas para ese bloque, o que necesites solicitarlas explícitamente.

Segundo, confirma que solo estás sumando las entradas con rewardType Fee para la comparación de comisiones. Tercero, comprueba que estás incluyendo los lamports negativos. Cuarto, asegúrate de usar el mismo nivel de compromiso para todos los bloques de tu comparación. Quinto, verifica que la codificación de detalles de transacción sea consistente y que estés leyendo el campo fee desde la ubicación correcta en la respuesta.

Si el residuo no explicado es grande, considera si tu endpoint devuelve los datos de comisión de priorización por separado. La documentación de comisiones de transacción y comisiones de priorización de Solana describe cómo se estructuran. Para conocer los mecanismos de cuentas y alquiler que afectan a los tipos de recompensa, consulta Información de cuentas, alquiler y cuentas de tokens de Solana por RPC.

  • Comprueba la presencia de recompensas y el soporte del endpoint
  • Suma solo rewardType Fee para la comparación de comisiones
  • Incluye los lamports negativos
  • Usa un nivel de compromiso consistente
  • Verifica la codificación de detalles de transacción y la ubicación del campo fee

Limitaciones y supuestos

Este método de reconciliación supone que las entradas con rewardType Fee capturan todos los créditos relacionados con comisiones para el líder y otros receptores. Si el runtime cambia cómo se dividen las comisiones o introduce nuevos tipos de recompensa, el método puede necesitar ajustes. También supone que el campo de comisión de la transacción está presente y es preciso para todas las transacciones del bloque.

El método no tiene en cuenta las comisiones de priorización a menos que tu endpoint las devuelva por separado. Tampoco intenta reconciliar las recompensas de alquiler o staking, que están fuera del alcance de la contabilidad de comisiones. Los resultados variarán según el endpoint, el nivel de compromiso y el rango de bloques, así que documenta siempre tus parámetros.

Para obtener una visión más amplia de las capacidades del RPC de Solana, consulta el centro de aprendizaje de OnFinality y la página de servicio de API. Estos recursos pueden ayudarte a elegir el endpoint adecuado para tu trabajo de contabilidad.

Próximos pasos: mantén separados el análisis y las preguntas sobre el runtime

Ahora que entiendes la identidad contable, mantén el detalle del análisis en la página hermana y las preguntas sobre el runtime en la página de cuentas y alquiler. Para el análisis de transacciones versionadas y la codificación de getBlock, continúa con Transacciones versionadas de Solana y análisis de getBlock. Para los mecanismos de alquiler, almacenamiento de cuentas y cuentas de tokens, consulta Información de cuentas, alquiler y cuentas de tokens de Solana por RPC.

Si necesitas datos históricos de bloques para hacer backtesting de tu reconciliación, consulta Consulta de datos históricos de Solana por RPC. Para la fiabilidad en producción, revisa Tiempos de espera y reintentos del RPC de Solana.

Para elegir un endpoint que devuelva datos completos de recompensas, empieza por Proveedores y endpoints RPC de Solana (RPC Assistant). Para detalles de precios y planes, consulta Precios de RPC.

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