优先费用:了解 Solana 的交易费机制

这篇文章是关于什么的?

Solana速度很快。然而,即使在最快的区块链上,用户也希望优化重要交易的交易处理。优先费是确保用户的交易被放置在执行排序队列前面的一种方法。这些是用户可以添加到其交易中的额外可选费用。

本文简要探讨了 Solana 上事务处理的细微差别。它涵盖了交易、交易的生命周期以及交易费用的运作方式。然后,本文探讨了优先费、如何以编程方式实施它们以及最佳实践。

交易及其生命周期

事务用于调用 Solana 程序并执行状态更改。它们是指令集(即单个程序调用的指令),告诉验证器要执行哪些操作、对哪些帐户以及它们是否具有必要的权限。

Solana 上交易的一般生命周期如下:

  • 用户对于他们想要执行的操作有明确的目标。例如,Alice 想发送 10 SOL 给 Bob
  • 用户为其所需的操作生成交易。例如,Alice 创建了一个交易,其中包含将 10 SOL 从她的账户转移到 Bob 账户的指令。 Alice 还包含最近的区块哈希并使用她的私钥签署交易
  • 用户将交易发送到网络。然后,他们会收到有关交易是否已成功添加的信息。例如,Alice 发送具有已确认承诺状态的交易。交易确认后,她会收到交易签名。 Alice 可以在区块浏览器(例如X-ray)上使用此交易签名,以查看她已成功发送 Bob 10 SOL。她的账户已被扣除 10 SOL,而 Bob 的账户被记入 10 SOL

当用户向网络发送签名交易时,他们使用 RPC 提供程序,例如 Helius。 Helius 的 RPC 接收交易并检查当前的领导者时间表。在 Solana 上,只有特定的验证者负责在特定时间将条目附加到分类账中。领导者负责为其当前时隙生成一个块,并被分配四个连续的时隙。签名的交易被发送给当前领导者和接下来的两位领导者。

当前领导者在安排交易执行之前验证已签名的交易并执行其他预处理步骤。与以太坊等其他 L1 不同,Solana 没有全局交易队列。大多数验证器使用Solana Labs 提供的调度程序实现。然而,运行 Jito 验证器客户端的验证器使用伪内存池(即MempoolStream)来排序交易。默认调度程序是多线程的,每个线程维护一个等待执行的事务队列。通过结合先进先出(FIFO)和优先费将交易排序成块。值得注意的是,这种顺序本质上是不确定的,因为事务分配给执行线程有些随机。

当交易被执行时,它通过Turbine传播,并相应地支付其费用。

Solana 上的交易费用如何运作

交易费用是在 Solana 上处理交易时支付的小额费用。当前领导者处理通过网络发送的交易以生成分类帐条目。当交易被确认为国家的一部分时,需要支付一定的费用来支持 Solana 的经济设计。交易费用从根本上有利于 Solana。他们:

  • 向验证者提供补偿
  • 通过引入实时交易成本来减少网络空间
  • 通过协议规定的最低费用为网络提供长期的经济稳定性
显示基本费用和优先费用如何燃烧并发送给验证者的图表
来源:Solana 上的 MEV,作者:@0xShitTrader for Umbra Research

Solana 依靠基于通货膨胀协议的奖励来确保短期内网络的安全。该网络有一个预定的全球通货膨胀率来奖励验证者实现这一目标。从长远来看,Solana 依靠交易费来维持安全。每笔交易费用的固定部分(最初设置为 50%)被销毁,其余部分发送给当前的领导者。 Solana 通过燃烧费用来增强 SOL 的价值,同时阻止恶意验证者审查交易。

交易费用是根据每个签名静态设置的基本费用以及交易期间使用的计算资源(以计算单位 (CU) 衡量)计算的。此基本费用的范围可以是每个签名目标 lamports 的 50% 到 1000%。每个签名的默认目标当前设置为 10,000。每个事务都分配一个最大 CU 预算,称为计算预算。超过此预算会导致运行时停止事务并返回错误。每笔交易的最大预算为140万CU,块空间限制为4800万CU

什么是优先费?

由于这些限制,计算量大的交易可能会填满区块空间,从而延迟其他交易。 Solana 引入了一项可选费用,允许交易根据领导者队列中的其他交易确定自己的优先级,称为优先费。支付这笔费用可以有效地促进您的交易,从而加快执行时间。这对于时间敏感或高价值的交易非常有用。交易的费用优先级由其请求的计算单元的数量决定。交易请求的计算单元越多,维持其在交易队列中的优先级所需支付的费用就越高。对更多计算单元收取更多费用可以防止计算量大的交易垃圾邮件。

优先级费用是交易的计算预算与其在 micro-lamports 中测量的计算单位价格的乘积:priorityFees =computeBudget *computeUnitPrice

计算预算

computeBudget指定事务可以消耗的最大计算单元数、与事务可能执行的不同操作相关的成本以及事务必须遵守的操作界限以下操作会产生计算成本:

  • 执行SBF指令
  • 在程序之间传递数据
  • 调用系统调用(例如,记录、创建程序地址、CPI)

对于跨程序调用(CPI),被调用程序在调用(即父)程序的计算预算内运行。如果被调用的程序用完所有剩余的计算预算或超出设定的限制,则会导致整个程序调用链失败。这包括执行启动该流程的初始事务。

当前的计算预算可以在此处找到。

如何以编程方式实施优先收费

交易的优先级费用通过设置SetComputeUnitPrice 指令和可选的SetComputeUnitLimit 指令来设置。如果未提供SetComputeUnitPrice指令,则交易将默认为最低优先级,因为不提供额外费用。如果未提供SetComputeUnitLimit指令,则限制将计算为事务中的指令数与默认计算单元限制的乘积。运行时使用计算单元价格和计算单元限制来计算优先级费用,该费用用于对给定交易进行优先级排序。

我们必须将这些指令添加到我们所需的交易中,以编程方式添加优先费用。在 Javascript 中,它看起来像下面这样:

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();

在这个片段中,我们:

  • 使用Localhost搭建测试环境
  • 创建两个新钱包(即fromKeypairtoPubkey
  • 空投两个钱包 100 SOL
  • 确保空投成功
  • 创建优先费用指令(即,computePriceIxcomputeLimitIx
  • 创建一条传输指令,从fromKeypairtoPubkey发送 100, 000 个lamport
  • 创建一个新交易并将所有指令添加到其中
  • 将最新的区块哈希附加到交易中并对其进行签名
  • 发送并确认交易发送成功

就是这样- 优先费只是添加到交易中的指令,以支付更快的执行时间。此代码片段的大部分内容配置我们的开发环境和两个钱包以在它们之间传输 SOL。我们感兴趣的是创建添加优先费并将其附加到交易的说明:


// 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

最佳实践

这些指令的顺序很重要,因为它们是按顺序执行的。例如,如果您的交易在扩展计算限制之前超出了默认计算限制(即 200k CU) ,则该交易将失败。假设我们有以下交易:

  • 指令 1 使用 100k
  • 指令2使用150k
  • 指令 3 扩展了计算限制

除非指令 2 和指令 3 交换,否则该事务将会失败。因此,建议在向交易添加其他指令之前添加计算限制指令。请记住,如果您想向交易添加优先费用,则无需使用SetComputeLimit指令- 它完全是可选的。SetComputePrice指令的位置并不重要。

建议使用getRecentPrioritizationFees RPC 方法来获取最近支付的优先费用列表。该数据可用于估计交易的适当优先费用,以确保它们由集群处理并最大限度地减少支付的费用。或者,Helius 提供了新的Priority Fee API,我们将在下一节中介绍。

交易还应该请求执行所需的最小计算单元量,以最大限度地减少这些费用。请注意,当请求的计算单元数量超过事务使用的总单元数时,不会调整成本。

一些钱包提供商,例如Phantom,认识到 dApp 可以设置优先交易费用。然而,他们不鼓励这样做,因为这通常会给最终用户带来不必要的复杂性。相反,他们敦促 dApp 开发者让 Phantom 代表用户收取优先费用。例如,Solfare通过自动检测 Solana 是否处于负载状态来解决这个问题,并稍微增加费用以使您的交易优先于其他交易。

Helius 优先费 API

使用getRecentPrioritizationFees  RPC 方法计算优先级费用 对于按时段计算费用具有直观意义。然而,考虑到不断变化的网络条件和 getRecentPrioritizationFees 响应的性质(即返回过去 150 个区块的值列表,这仅有助于衡量设置费用的最小值),这可能很困难。

Helius 优先费用 API 引入了一种新方法 getPriorityFeeEstimate,考虑到全球和本地费用市场,该方法将响应简化为单个值。此方法使用一组预定义的百分位数来确定估计值。这些百分位数或级别的范围从 NONE  (第 0 个百分位数)到 UNSAFE_MAX  (第 100 个百分位数,并标记为不安全,以防止用户意外耗尽资金)。用户还可以选择指定接收所有优先级并通过 lookbackSlots调整此计算中使用的范围。因此,与getRecentPrioritizationFees 类似 ,用户可以回顾估算计算中的一定数量的槽位。

例如,我们可以使用以下脚本计算 Jupiter v6 程序的所有优先费用级别:


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();

该端点正在积极开发中。有关更多信息,请访问 Helius 文档

结论

探索 Solana 交易的世界揭示了一个平衡网络效率与经济激励的复杂系统。了解交易的运作方式以及交易费用和优先权费用,使开发人员和用户能够做出更明智的决策,以优化他们在 Solana 上的交互。以编程方式实施优先费用的能力为高价值和时间敏感的交易开辟了新的途径。这使得 Solana 的运营具有更大的灵活性和效率。

💡
原文链接:Solana Nodes — A Primer on Solana RPCs, Validators, and RPC providers
本文由SlerfTools翻译,转载请注明出处。

SlerfTools专为Solana设计的工具箱,致力于简化区块链操作,提供无编程全可视化界面,使发币管理流动性无代码创建Dapp等复杂过程变得安全简单。