Solana 区块链有几种不同类型的费用和成本,这些费用和成本是使用无许可网络所需的。 它们可以分为几种特定类型:

  • 交易费用 - 验证者处理交易/指令的费用
  • 优先费用 - 提高交易处理顺序的可选费用
  • 租金 - 保持链上数据存储的保留余额

交易费用

在 Solana 区块链上的链上程序中处理逻辑(指令)所支付的小额费用称为“交易费用”。
当每个交易 (包含一个或多 个指令 )通过网络发送时,它会由当前的 验证者领导者处理。一旦确认为全局状态交易,这个 交易费用 将支付给网络,以帮助支 持 Solana 区块链的经济设计。

交易费用不同于账户数据存储押金费租金 。虽然交易费用是支付给 Solana 网 络处理指令的费用,但租金押金是保存在账户中以在区块链上存储其数据并可回收的费 用。 如上所述,每笔交易费用的固定比例被 燃烧(销毁)。这是为了巩固 SOL 的经 济价值,从而维持网络的安全性。与完全销毁交易费用的方案不同,领导者仍然有动力在 其时隙(创建区块的机会)中包含尽可能多的交易。

目前,Solana 的基本交易费用设定为每个签名 5k lamports 的固定值。 在这个基本费用 之上,可以添加任何额外的优先费用 。

为什么要支付交易费用?

交易费用在 Solana 的经济设计中提供了许多好处,主要包 括:

  • 为验证者网络提供补偿,以支付处理交易所需的 CPU/GPU 计算资源
  • 通过引入交易的实际成本来减少网络垃圾邮件
  • 通过每笔交易的协议捕获最低费用金额,为网络提供长期经济稳定性

基本经济设计

许多区块链网络(包括比特币和以太坊)依赖于通胀性 基于协议的奖励 来在短期内保护 网络。从长期来看,这些网络将越来越依赖 交易费用 来维持安全性。
在 Solana 上也是如此。具体来说: 具体而言:

  • 每笔交易费用的固定比例(最初为 50%)被 燃烧(销毁),其余部分归当前处理交易 的领导者所有。
  • 预定的全球通胀率 为奖励提 供了一个来源,分配给 Solana 验证者 。

费用收取

交易需要至少有一个账户签署交易并且是可写的。 这些 可写签名账户 首先在账户列表 中序列化,并且第一个总是用作“费用支付者”。

在处理任何交易指令之前,费用支付者账户 的余额将被扣除以 支付交易费用。 如果费用支付者余额不足以支付交易费用,交易处理将停止并导致交易失 败。

如果余额足够,费用将被扣除,交易的指令将开始执行。 如果任何指令导致错误,交易处 理将停止,并最终在 Solana 账本中记录为失败交易。 对于这些失败的交易,费用仍然由 运行时收取。

如果任何指令返回错误或违反运行时限制,所有账户更改除了交易费用扣除将被回 滚。 这是因为验证者网络已经花费了计算资源来收集交易并开始初步处理。

和以太坊基本一致

费用分配

交易费用 被部分销毁 ,剩余费用由生成包含相应交易的区块的验证者收取。 具体来 说,50%被销毁 ,50%分配给 生成区块的验证者。

为什么要销毁部分费用?

如上所述,每笔交易费用的固定比例被 燃烧(销毁)。 这是为了巩固 SOL 的经济价 值,从而维持网络的安全性。 与完全销毁交易费用的方案不同,领导者仍然有动力在其时 隙(创建区块的机会)中包含尽可能多的交易。
销毁的费用还可以帮助防止恶意验证者审查交易,通过 在分叉考虑实施。

攻击示例

在具有恶意或审查领导者 的历史证明(PoH) 分叉的情况下

  • 由于审查导致的费用损失,我们预计总销毁费用将 少于 可比的诚实分叉
  • 如果审查领导者要补偿这些丢失的协议费用,他们将不得不自己替换其分叉上的销毁费用
  • 从而可能减少最初的审查动机

计算交易费用

给定交易的完整费用基于两个主要部分计算

  • 每个签名的静态设定基本费用,以及
  • 交易期间使用的计算资源,以“计算单元”为 单位衡量

由于每个交易可能需要不同数量的计算资源,每个交易作为 计算预算 的一部分被分配了 每个交易的最大 计算单元 数量。

计算预算

在一个交易中链上执行的所有操作都需要消耗不同数量的计算资源(计算成本),这些资源 由验证者在处理时消耗。消耗这些资源的最小单位称为 "计算单元"。 为了防止滥用计算 资源,每个交易被分配一个“计算预算”。该预算指定了有 关计算单元的详细信息,包括:

  • 与交易可能执行的不同类型操作相关的计算成本(每个操作消耗的计算单元),
  • 交易可以消耗的最大计算单元数量(计算单元限制),
  • 以及交易必须遵守的操作界限(如账户数据大小限制)

当交易消耗其整个计算预算(计算预算耗尽)或超过某个界限(如尝试超 过最大调用堆栈深度或最大加载账户数 据大小限制)时,运行时将停止交易处理并返回错误。导致交易失败且没有状态更改(除了 交易费用被收取 )。 导致交易失败且没有状态更改(除了交易费用 被收取 )。

账户数据大小限制

一个交易可以通过包含一个 SetLoadedAccountsDataSizeLimit 指令来指定它允许加载的 账户数据的最大字节数(不超过运行时的绝对最大值)。 如果没有提供 SetLoadedAccountsDataSizeLimit,交易将默认使用运行时的 MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES值。

可以使用 ComputeBudgetInstruction::set_loaded_accounts_data_size_limit 函数来 创建这个指令:

let instruction = ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(100_000);

计算单元

在一个交易中链上执行的所有操作都需要消耗不同数量的计算资源(计算成本),这些资源 由验证者在处理时消耗。消耗这些资源的最小单位称为 "计算单元"。 消耗这些资源的最 小单位称为 "计算单元"。

当一个交易被处理时,每个在链上执行的指令都会逐步消耗计算单元(消耗预算)。 由于 每个指令执行的逻辑不同(写入账户、cpi、执行系统调用等),每个指令可能会消 耗不同数量的 计算单元。

每个交易都有一个 计算单元限制,可以是运行时设置的默认限 制,也可以是显式请求的更高限制。一旦交易超过其计算单元限制,其处理将停止,导致交 易失败。 一旦交易超过其计算单元限制,其处理将停止,导致交易失败。

以下是一些常见的会产生计算成本的操作:

  • 执行指令
  • 在程序之间传递数据
  • 执行系统调用
  • using sysvars
  • 使用 msg! 宏记录日志
  • 记录公钥
  • 创建程序地址(PDA)
  • 跨程序调用(CPI)
  • 加密操作

对于 跨程序调用,被调用的指令继承其父级的计算预算和限制。 如果一个被调用的指令消耗了交易的剩余预算,或超过了限制,整个调用链和顶层交易处 理将停止。

对于 跨程序调用,被调用的指令继承其父级的计算预算和限制。 如果一个被调用的指令消耗了交易的剩余预算,或超过了限制,整个调用链和顶层交易处 理将停止。

你可以在 Solana 运行时的 ComputeBudget 中找到更多关于所有消耗计算单元的操作的详细信息。

计算单元限制

每个交易都有一个最大计算单元数(CU)称为 "计算单元限制"。 每个交易,Solana 运 行时有一个绝对最大计算单元限制为 140 万 CU 并设置了一个默认请求的最大限制为 每个指令 20 万 CU。

一个交易可以通过包含一个 SetComputeUnitLimit 指令来请求一个更具体和优化的计算 单元限制。 可以是更高或更低的限制。 但它永远不能请求超过每个交易的绝对最大限制。

虽然交易的默认计算单元限制在大多数情况下适用于简单交易,但它们通常不够优化(对于 运行时和用户)。对于更复杂的交易,如调用执行多个 CPI 的程序,你可能需要为交易请 求更高的计算单元限制。 对于更复杂的交易,如调用执行多个 CPI 的程序,你可能需要为 交易请求更高的计算单元限制。

为你的交易请求最佳计算单元限制对于帮助你支付更少的交易费用和更好地在网络上调度你 的交易至关重要。 钱包、dApps 和其他服务应确保其计算单元请求是最佳的,以提供尽可 能好的用户体验。

计算单元价格

当一个交易希望支付更高的费用以提升其处理优先级时,它可以设置一个 "计算单元价 格"。 这个价格与 计算单元限制 结合使用,将用于确定交易的 优先级费用。

默认情况下,没 有设置计算单元价格因 此没有额外的优先级费用。

优先级费用

作为计算预算的一部分,运行时支持交易支付一个可选费用,称 为 "优先级费用"。支付这个额外费用有助于提升交易在处理时的优先级,从而实现更快 的执行时间。

如何计算优先级费用

一个交易的优先级费用通过将其计算单元限制乘以计算单元价格(以 微 lamports 为单位)来计算。这些值可以通过包含以下计算预算指令在每个交易中设置一 次:这些值可以通过包含以下计算预算指令在每个交易中设置一次:

  • SetComputeUnitLimit - 设置交易可以消耗的最大计算单元数
  • SetComputeUnitPrice - 设置交易愿意支付的额外费用以提升其优先级

如果没有提供 SetComputeUnitLimit 指令,将使用 默认计算单元限制。
如果没有提供 SetComputeUnitPrice 指令,交易将默认没有额外的提升费用和最低优先 级(即没有优先级费用)。

如何设置优先级费用

一个交易的优先级费用通过包含一个 SetComputeUnitPrice 指令来设置,并可选地包含 一个 SetComputeUnitLimit 指令。 运行时将使用这些值来计算优先级费用,这将用于在 区块内优先处理给定的交易。

你可以通过其 Rust 或 @solana/web3.js 函数来创建这些指令。 然后可以将每个指令包含 在交易中并像正常一样发送到集群。 另请参阅下面 的最佳实践。

与 Solana 交易中的其他指令不同,计算预算指令不需要任何账户。包含多个相同类型 指令的交易将失败。 包含多个相同类型指令的交易将失败。

Rust

Rust 的 solana-sdk crate 包含在 ComputeBudgetInstruction 中的函数,用于设置 compute unit limit 和 compute unit price 的指令

let instruction = ComputeBudgetInstruction::set_compute_unit_limit(300_000);
let instruction = ComputeBudgetInstruction::set_compute_unit_price(1);

Javascript

The @solana/web3.js library includes functions within the ComputeBudgetProgram class to craft instructions for setting the compute unit limit and compute unit price:

const instruction = ComputeBudgetProgram.setComputeUnitLimit({
  units: 300_000,
});
const instruction = ComputeBudgetProgram.setComputeUnitPrice({
  microLamports: 1,
});

优先费用最佳实践

以下是关于优先费用最佳实践的一般信息。 你还可以在本指南中找到更多关 于如何请求最佳计算的 详细信息,包括如何模拟交易以确定其大致计算使用量。

请求最少的计算单元

交易应请求执行所需的最少计算单元以最小化费用。 还要注意,当请求的计算单元数量超 过实际消耗的计算单元数量时,费用不会调整。

获取最近的优先费用

在将交易发送到集群之前,你可以使用 getRecentPrioritizationFees RPC 方法获取节点处理的最近区块中支付的优先费用列表。
然后你可以使用这些数据来估算适当的优先费用,以便 (a) 更好地确保交易被集群处理, 并且 (b) 最小化支付的费用。

租金

存入每个 Solana 账户 以保持其关联数据在链上可用的费用 称为“租金”。 此费用在每个账户的正常 lamport 余额中扣留,并在账户关闭时可回收。

租金不同于交易费用 。 租金是“支付”(在账户中扣留)以保持 数据存储在 Solana 区块链上,并且可以回收。 而交易费用是支付给网络上处 理指令 的费用。

所有账户都需要保持足够高的 lamport 余额(相对于其分配的空间)以成 为租金豁免并留在 Solana 区块链上。任何试图将账户余额减少到其相应 的租金豁免最低余额以下的交易将失败(除非余额减少到正好为零)。 Solana 上的新账户 (和程序)必须初始化足够的 lamports 以成为 租金豁免。情况并非总是如此。以 前,运行时会定期自动从低于其 租金豁免的最低余额 的每个账户中收取费用。最终将这 些账户减少到零余额并从全局状态中垃圾回收它们(除非手动补充)。

当账户的所有者不再希望将此数据保留在链上并在全局状态中可用时,所有者可以关闭账户 并回收租金存款。

这是通过将账户的全部 lamport 余额提取(转移)到另一个账户(即你的钱包)来完成 的。 通过将账户余额减少到正好为 0,运行时将在“ 垃圾回收 ”过程中从网络中删除账户及其关联数据。

租金费率

Solana 的租金费率在整个网络范围内设置,主要基于运行时设置的“ 每字节每年 lamports”。 目前,租金费率是一个静态金额,并存储 在Rent sysvar 中。

此租金费率用于计算在账户内扣留的租金确切金额,以分配给账户的空间(即可以存储在账 户中的数据量)。账户分配的空间越多,扣留的租金存款就越高。 账户分配的空间越多, 扣留的租金存款就越高。

租金豁免

账户必须维持高于链上存储其各自数据所需的最低金额的 Lamport 余额。这被称为“免租”,该余额被称为“免租最低余额”。
Solana 上的新账户(和程序)必须初始化足够的 lamports 以成为 租金豁免。情 况并非总是如此。以前,运行时会定期自动从低于其 租金豁免的最低余额 的每个账户 中收取费用。 最终将这些账户减少到零余额并从全局状态中垃圾回收它们(除非手动补 充)。

在创建新账户的过程中,你必须确保存入足够的 lamports 以超过此最低余额。 任何低于 此最低阈值的金额将导致交易失败。

每次账户余额减少时,运行时都会检查账户是否仍高于此租金豁免的最低余额。由于审查导 致的费用损失,我们预计总销毁费用将少于可比的诚实分叉

账户成为租金豁免的具体最低余额取决于区块链的当前租金费率和账户希望 分配的存储空间量(账户大小)。 因此,建议使 用getMinimumBalanceForRentExemption RPC 端点来计算给定账户大小的具体余额。

所需的租金存款金额也可以通过 solana rent CLI 子命令估 算:

solana rent 15000

# output
Rent per byte-year: 0.00000348 SOL
Rent per epoch: 0.000288276 SOL
Rent-exempt minimum: 0.10529088 SOL

垃圾回收

未保持大于零的 lamport 余额的账户将在称为垃圾回收的过程中从网络中删除。 此过程有 助于减少网络范围内不再使用/维护的数据存储。

在交易成功将账户余额减少到正好为 0 后,垃圾回收会由运行时自动进行。 任何试图将 账户余额减少到其租金豁免最低余额以下(不正好为零)的交易将失败。

重要的是要注意垃圾回收在交易执行完成之后发生。 如果有一个指令通过将账户余额 减少到零来“关闭”账户,则可以在同一交易中通过后续指令“重新打开”账户。 如果账户状 态在“关闭”指令中未被清除,则后续的“重新打开”指令将具有相同的账户状态。 这是一个 安全问题,因此了解垃圾回收生效的确切时间非常重要。

即使账户已从网络中删除(通过垃圾回收),它仍可能有与其地址相关的交易(无论是过去 的历史还是未来的交易)。 即使 Solana 区块浏览器可能显示“找不到账户”类型的消息, 你仍然可以查看与该账户相关的交易历史。

你可以阅读验证 器已实现的提案了 解更多关于垃圾回收的信息。