NUEVO: Helius adquiere Light Protocol
Comisiones de prioridad: comprende la mecánica de las comisiones de transacción de Solana
Blog/Fundamentos

Comisiones de prioridad: comprende la mecánica de las comisiones de transacción de Solana

Developer Experience Engineer0xIchigo en X0xIchigo en LinkedIn0xIchigo en GitHub
10 min de lectura

¿De qué trata este artículo?

Solana es rápida. Aun así, incluso en la blockchain más rápida disponible, los usuarios quieren optimizar el procesamiento de las transacciones importantes. Las comisiones de prioridad permiten colocar la transacción de un usuario al principio de la cola de ejecución. Son comisiones adicionales y opcionales que el usuario puede agregar a su transacción.

Este artículo explora brevemente los matices del procesamiento de transacciones en Solana. Explica las transacciones, su ciclo de vida y cómo funcionan sus comisiones. Después, analiza las comisiones de prioridad, cómo implementarlas mediante programación y las prácticas recomendadas.

Las transacciones y su ciclo de vida

Las transacciones se usan para invocar programas de Solana y efectuar cambios de estado. Son conjuntos de instrucciones (es decir, directivas para invocar un único programa) que indican al validador qué acciones debe realizar, en qué cuentas y si cuentan con los permisos necesarios.

El ciclo de vida general de una transacción en Solana es el siguiente:

  • El usuario tiene un objetivo claro para la acción que desea realizar. Por ejemplo, Alice quiere enviarle 10 SOL a Bob
  • El usuario genera una transacción para la acción deseada. Por ejemplo, Alice crea una transacción con una instrucción para transferir 10 SOL de su cuenta a la de Bob. Alice también incluye un hash de bloque reciente y firma la transacción con su clave privada
  • El usuario envía la transacción a la red. Después, recibe información que indica si la transacción se agregó correctamente. Por ejemplo, Alice envía la transacción con un estado de compromiso confirmado. Cuando se confirma la transacción, recibe una firma de transacción. Alice puede usar esta firma en un explorador de bloques, como Orb, para comprobar que envió correctamente 10 SOL a Bob. Se debitan 10 SOL de su cuenta y se acreditan 10 SOL en la de Bob

Cuando los usuarios envían una transacción firmada a la red, usan un proveedor de RPC como Helius. Los RPC de Helius reciben la transacción y consultan la programación de líderes actual. En Solana, solo determinados validadores se encargan de agregar entradas al libro mayor en momentos específicos. El líder se encarga de producir un bloque para su slot actual y tiene asignados cuatro slots consecutivos. La transacción firmada se envía al líder actual y a los dos siguientes.

El líder actual valida la transacción firmada y realiza otros pasos de preprocesamiento antes de programarla para su ejecución. A diferencia de otras L1, como Ethereum, Solana no tiene una cola de transacciones global. La mayoría de los validadores usan la implementación del programador proporcionada por Solana Labs. Sin embargo, los validadores que ejecutan el cliente validador de Jito usan una seudomempool (es decir, MempoolStream) para ordenar las transacciones. El programador predeterminado usa varios hilos, cada uno de los cuales mantiene una cola de transacciones pendientes de ejecución. Las transacciones se ordenan en bloques combinando el orden de llegada (FIFO) y las comisiones de prioridad. Es importante señalar que este orden es intrínsecamente no determinista, ya que las transacciones se asignan a los hilos de ejecución de forma parcialmente aleatoria.

Cuando se ejecuta una transacción, se propaga mediante Turbine y sus comisiones se pagan según corresponda.

Cómo funcionan las comisiones de transacción en Solana

Las comisiones de transacción son pequeños importes que se pagan para procesar transacciones en Solana. El líder actual procesa las transacciones enviadas a través de la red para generar entradas en el libro mayor. Cuando la transacción se confirma como parte del estado, se paga una comisión para respaldar el diseño económico de Solana. Las comisiones de transacción aportan beneficios fundamentales a Solana. Estas:

  • Remuneran a los validadores
  • Reducen el uso del espacio de la red al introducir un costo en tiempo real para las transacciones
  • Aportan estabilidad económica a largo plazo a la red mediante una comisión mínima capturada por el protocolo

Solana depende a corto plazo de recompensas inflacionarias basadas en el protocolo para proteger la red. La red tiene una tasa de inflación global programada para recompensar a los validadores y lograr este objetivo. A largo plazo, Solana depende de las comisiones de transacción para mantener la seguridad. Una parte fija (establecida inicialmente en el 50 %) de cada comisión de transacción se quema y el resto se envía al líder actual. Solana quema comisiones para reforzar el valor de SOL y, al mismo tiempo, disuadir a los validadores maliciosos de censurar transacciones.

Las comisiones de transacción se calculan a partir de una comisión base establecida de forma estática por firma y de los recursos computacionales usados durante la transacción, medidos en unidades de cómputo (CU). Esta comisión base puede variar entre el 50 % y el 1000 % del objetivo de lamports por firma. El objetivo predeterminado por firma está establecido actualmente en 10 000. A cada transacción se le asigna un presupuesto máximo de CU denominado presupuesto de cómputo. Si se supera este presupuesto, el entorno de ejecución detiene la transacción y devuelve un error. El presupuesto máximo por transacción es de 1,4 millones de CU, y el límite de espacio de bloque es de 48 millones de CU.

¿Qué son las comisiones de prioridad?

Debido a estas limitaciones, las transacciones con una gran carga computacional podrían ocupar el espacio de bloque y retrasar otras transacciones. Solana introdujo una comisión opcional que permite que una transacción obtenga prioridad frente a otras en la cola del líder. Se conoce como comisión de prioridad. Pagar esta comisión impulsa la transacción y reduce su tiempo de ejecución. Resulta útil para transacciones urgentes o de alto valor. La prioridad de comisión de una transacción se determina según la cantidad de unidades de cómputo que solicita. Cuantas más unidades de cómputo solicite, mayor será la comisión que deberá pagar para mantener su prioridad en la cola de transacciones. Cobrar más por una mayor cantidad de unidades de cómputo evita el spam de transacciones con una gran carga computacional.

Las comisiones de prioridad son el producto del presupuesto de cómputo de una transacción y el precio de sus unidades de cómputo, medido en microlamports: priorityFees = computeBudget * computeUnitPrice

Presupuesto de cómputo

El computeBudget especifica la cantidad máxima de unidades de cómputo que puede consumir una transacción, los costos asociados con las distintas operaciones que puede realizar y los límites operativos que debe respetar. Las siguientes operaciones tienen un costo de cómputo:

  • Ejecutar instrucciones SBF
  • Transferir datos entre programas
  • Realizar llamadas al sistema (por ejemplo, registrar eventos, crear una dirección de programa o efectuar CPIs)

En las invocaciones entre programas (CPIs), el programa invocado opera dentro del presupuesto computacional del programa que lo invoca (es decir, el programa principal). Si el programa invocado consume todo el presupuesto computacional restante o supera un límite establecido, toda la cadena de llamadas a programas falla. Esto incluye la ejecución de la transacción inicial que comenzó el proceso.

Puedes consultar el presupuesto de cómputo actual aquí.

Cómo implementar comisiones de prioridad mediante programación

La comisión de priorización de una transacción se establece mediante una instrucciónhttps://github.com/solana-labs/solana/blob/2971e84ec87815adb1e4def95cbcd8d0d96845f5/sdk/src/compute_budget.rs#L59SetComputeUnitPrice y una instrucción SetComputeUnitLimit opcional. Si no se proporciona una instrucción SetComputeUnitPrice, la transacción tendrá la prioridad más baja de forma predeterminada, ya que no se proporciona ninguna comisión adicional. Si no se proporciona una instrucción SetComputeUnitLimit, el límite se calcula como el producto de la cantidad de instrucciones de la transacción y el límite predeterminado de unidades de cómputo. El entorno de ejecución usa el precio y el límite de las unidades de cómputo para calcular la comisión de priorización, que determina la prioridad de la transacción correspondiente.

Debemos agregar estas instrucciones a la transacción deseada para incluir comisiones de prioridad mediante programación. En Javascript, se ve así:

Código
import {
  Keypair,
  Connection,
  PublicKey,
  Transaction,
  SystemProgram,
  LAMPORTS_PER_SOL,
  sendAndConfirmTransaction,
  ComputeBudgetProgram,
} from "@solana/web3.js";

async function main() {
  // Initialize an RPC client
  const clusterUrl = "http://127.0.0.1:8899";
  const connection = new Connection(clusterUrl, "confirmed");

  // Initialize new sender and receiver keypairs
  const fromKeypair = Keypair.generate();
  const toPubkey = new PublicKey(Keypair.generate().publicKey);

  // Airdrop SOL to the from_keypair
  const airdropAmount = 100 * LAMPORTS_PER_SOL;
  try {
    const signature = await connection.requestAirdrop(
      fromKeypair.publicKey,
      airdropAmount
    );
    console.log("Airdrop requested. Signature:", signature);
    await connection.confirmTransaction({
      signature,
      confirmation: "confirmed",
    });
  } catch (e) {
    console.error("Failed to request airdrop:", e);
    return;
  }

  // Check if airdrop was successful
  const balance = await connection.getBalance(fromKeypair.publicKey);
  if (balance < airdropAmount) {
    console.error(
      "Airdrop was not successful. The current balance is insufficient"
    );
    return;
  }

  // Airdrop SOL to the toPubkey
  const airdropAmountTo = 100 * LAMPORTS_PER_SOL; // 1 SOL in lamports
  try {
    const signature = await connection.requestAirdrop(toPubkey, airdropAmount);
    console.log("Airdrop requested. Signature:", signature);
    await connection.confirmTransaction({
      signature,
      confirmation: "confirmed",
    });
  } catch (e) {
    console.error("Failed to request airdrop:", e);
    return;
  }

  // Check if airdrop was successful
  const balanceTo = await connection.getBalance(toPubkey);
  if (balance < airdropAmount) {
    console.error(
      "Airdrop was not successful. The current balance is insufficient"
    );
    return;
  }

  console.log(`Account balance: ${balance / LAMPORTS_PER_SOL} SOL`);

  // Create the priority fee instructions
  const computePriceIx = ComputeBudgetProgram.setComputeUnitPrice({
    microLamports: 1,
  });

  const computeLimitIx = ComputeBudgetProgram.setComputeUnitLimit({
    units: 200_000,
  });

  // Create the transfer instruction
  const transferIx = SystemProgram.transfer({
    fromPubkey: fromKeypair.publicKey,
    toPubkey,
    lamports: 100_000,
  });

  // Create the transaction with priority fees
  const transaction = new Transaction().add(
    computePriceIx,
    computeLimitIx,
    transferIx
  );

  // Fetch the recent blockhash and sign the transaction
  transaction.recentBlockhash = (
    await connection.getLatestBlockhash()
  ).blockhash;
  transaction.sign(fromKeypair);

  // Send the transaction
  try {
    const txid = await sendAndConfirmTransaction(connection, transaction, [
      fromKeypair,
    ]);
    console.log("Transaction sent successfully with signature", txid);
  } catch (e) {
    console.error("Failed to send transaction:", e);
  }
}

main();

En este fragmento:

  • Configuramos un entorno de prueba mediante Localhost
  • Creamos dos billeteras nuevas (es decir, fromKeypair y toPubkey)
  • Enviamos por airdrop 100 SOL a cada billetera
  • Verificamos que los airdrops se hayan realizado correctamente
  • Creamos las instrucciones de la comisión de prioridad (es decir, computePriceIx y computeLimitIx)
  • Creamos una instrucción de transferencia para enviar 100 000 lamports de fromKeypair a toPubkey
  • Creamos una transacción nueva y le agregamos todas las instrucciones
  • Adjuntamos el hash de bloque más reciente a la transacción y la firmamos
  • Enviamos la transacción y confirmamos que se haya enviado correctamente

Eso es todo: las comisiones de prioridad son simplemente instrucciones que se agregan a una transacción para pagar por una ejecución más rápida. La mayor parte de este fragmento configura nuestro entorno de desarrollo y dos billeteras para transferir SOL entre ellas. Lo que nos interesa es crear las instrucciones para agregar comisiones de prioridad y adjuntarlas a una transacción:

Código
// Other code

const computePriceIx = ComputeBudgetProgram.setComputeUnitPrice({
	microLamports: 1,
});

const computeLimitIx = ComputeBudgetProgram.setComputeUnitLimit({
	units: 200_000,
});

// Other code

const transaction = new Transaction().add(computePriceIx, computeLimitIx, transferIx);

// Rest of the code

Prácticas recomendadas

El orden de estas instrucciones importa, ya que se ejecutan secuencialmente. Por ejemplo, si tienes una transacción que supera el límite de cómputo predeterminado (es decir, 200 000 CU) antes de ampliar ese límite, la transacción fallará. Supongamos que tenemos la siguiente transacción:

  • La instrucción 1 usa 100 000
  • La instrucción 2 usa 150 000
  • La instrucción 3 amplía el límite de cómputo

Esta transacción fallará a menos que se intercambien las instrucciones 2 y 3. Por lo tanto, recomendamos agregar la instrucción del límite de cómputo antes de incluir otras instrucciones en tu transacción. Recuerda: no necesitas usar la instrucción SetComputeLimit si quieres agregar comisiones de prioridad a tu transacción. Es totalmente opcional. La ubicación de la instrucción SetComputePrice no importa.

Recomendamos usar el método RPC getRecentPrioritizationFees para obtener una lista de las comisiones de prioridad pagadas recientemente. Estos datos permiten estimar una comisión de prioridad adecuada para que el clúster procese las transacciones y, al mismo tiempo, minimizar las comisiones pagadas. Como alternativa, Helius ofrece una nueva API de comisiones de prioridad, que explicaremos en la siguiente sección.

Las transacciones también deben solicitar la cantidad mínima de unidades de cómputo necesarias para su ejecución, a fin de minimizar estas comisiones. Ten en cuenta que los costos no se ajustan cuando la cantidad de unidades de cómputo solicitadas supera el total de unidades usadas por una transacción.

Algunos proveedores de billeteras, como Phantom, reconocen que las dApps pueden establecer comisiones de prioridad para las transacciones. Sin embargo, desaconsejan hacerlo porque suele generar una complejidad innecesaria para los usuarios finales. En su lugar, recomiendan a los desarrolladores de dApps permitir que Phantom aplique las comisiones de prioridad en nombre del usuario. Solfare, por ejemplo, aborda el problema detectando automáticamente si Solana tiene una carga elevada y aumenta ligeramente las comisiones para priorizar tu transacción frente a otras.

API de comisiones de prioridad de Helius

Calcular las comisiones de prioridad mediante el método RPC getRecentPrioritizationFees resulta intuitivo para calcularlas por slot. Sin embargo, puede ser difícil debido a las condiciones siempre cambiantes de la red y a la naturaleza de la respuesta de getRecentPrioritizationFees' (es decir, devuelve una lista de valores de los últimos 150 bloques, que solo sirve para estimar el valor mínimo de las comisiones).

La API de comisiones de prioridad de Helius incorpora un nuevo método, getPriorityFeeEstimate, que simplifica la respuesta a un único valor y toma en cuenta los mercados de comisiones globales y locales. Este método usa un conjunto predefinido de percentiles para determinar la estimación. Estos percentiles, o niveles, van desde NONE (percentil 0) hasta UNSAFE_MAX (percentil 100, etiquetado como no seguro para evitar que los usuarios agoten accidentalmente sus fondos). Los usuarios también pueden solicitar todos los niveles de prioridad y ajustar mediante lookbackSlots el rango usado en este cálculo. Así, de manera similar a getRecentPrioritizationFees, los usuarios pueden consultar una determinada cantidad de slots anteriores para calcular la estimación.

Por ejemplo, podemos calcular todos los niveles de comisiones de prioridad del programa Jupiter v6 con el siguiente script:

Código
const url = `https://mainnet.helius-rpc.com/?api-key=`;

const getRecentPrioritizationFees = async () => {
  const response = await fetch(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "getPriorityFeeEstimate",
      params: [{
        "accountKeys": ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"],
        "options": {
            "includeAllPriorityFeeLevels": true,
        }
      }]
    }),
  });
  const data = await response.json();
  console.log("Fee: ", data);
};

getRecentPrioritizationFees();

Este endpoint está en desarrollo activo. Para obtener más información, consulta la documentación de Helius.

Conclusión

Explorar el mundo de las transacciones de Solana revela un sistema sofisticado que equilibra la eficiencia de la red con los incentivos económicos. Comprender cómo funcionan las transacciones, sus comisiones y las comisiones de prioridad permite que desarrolladores y usuarios tomen decisiones mejor fundamentadas para optimizar sus interacciones en Solana. La capacidad de implementar comisiones de prioridad mediante programación abre nuevas posibilidades para las transacciones urgentes y de alto valor. Esto aporta mayor flexibilidad y eficiencia a las operaciones en Solana.

Si llegaste hasta aquí, ¡gracias, anon! Escribe tu dirección de correo electrónico a continuación para no perderte ninguna novedad sobre Solana. ¿Quieres profundizar más? Únete a nuestro Discord y comienza hoy mismo a construir el futuro en la blockchain de mayor rendimiento.

Recursos adicionales y lecturas complementarias

Suscríbete a Helius

Mantente al día con las novedades del desarrollo en Solana y recibe actualizaciones cuando publiquemos

Imagen ampliada