Solana Compute Unit 优化:CU 预算到底怎么设才对

Solana compute unit 是什么、怎么把 CU 预算设对、为什么大多数机器人要么付多了要么 CU 给少了。带例子的实操指南。

BoltTx Team··10 min read
solanacompute-unittransaction优化rpc

如果你在 Solana 上跑生产交易,compute unit (CU) 预算是那种"很容易设错、又很难注意到设错了"的参数。设太低,交易会静默失败;设太高,你白白多付 priority fee。我们聊过的大多数机器人操作者都中过这两个里的一个,而且自己没意识到。

这篇我们讲 compute unit 是什么、怎么把预算设对,以及把"调好了的机器人"和"在这一个参数上漏钱的机器人"分开的生产做法。

Compute Unit 是什么

CU 是 Solana 衡量计算工作量的单位。不同指令消耗不同的 CU:

两个值控制你交易的 CU 行为:

CU limit。 你交易能消耗的最大 CU。默认 200,000。超了交易就失败。

CU price。 每 CU 的 priority fee(单位 microlamports)。默认 0(无优先)。设得越高,这笔交易在打包队列里的优先级越高,也就更可能快速上链。

每笔交易的总 priority fee = cu_limit * cu_price / 1_000_000 / 1_000_000_000 SOL。

怎么把 CU Limit 设对

默认的 200,000 对大多数非平凡交易都是错的。显式设它:

import { ComputeBudgetProgram, Transaction } from "@solana/web3.js";

const tx = new Transaction()
  .add(ComputeBudgetProgram.setComputeUnitLimit({ units: 250_000 }))
  .add(yourActualInstruction);

合适的值要看交易在做什么。两种确定方法:

方法 1:通过模拟做 profile。

// 不显式设 limit,直接模拟一下
const sim = await connection.simulateTransaction(tx);
console.log("用了 CU:", sim.value.unitsConsumed);
// 把 limit 设为测出来值的 1.2-1.5 倍

对不太依赖链状态的交易很可靠。状态依赖的交易(价格影响路径选择那种),要在有代表性的条件下模拟。

方法 2:用动态 limit。

有些库(Jupiter swap API、特定配置的 Anchor)能帮你算预算。Jupiter 是这样:

const swapResp = await fetch(SWAP_URL, {
  method: "POST",
  body: JSON.stringify({
    quoteResponse: quote,
    userPublicKey: wallet.publicKey.toString(),
    dynamicComputeUnitLimit: true,  // <-- 这个
  }),
});

dynamicComputeUnitLimit: true 让 Jupiter 帮你估算这条路由合适的 limit。省得手动 profile。

常见的 CU Limit 错误

留默认 200,000。 很多真实交易需要更多。多跳 swap、复杂 DeFi 组合、CPI 多的程序——这些用默认值都会崩。

为了"以防万一"设太高。 用 150,000 CU 的交易给 1,000,000 limit,意味着你在为白白浪费的 850,000 付 priority fee。

不区分交易类型。 不同路由要不同预算。固定"永远 250,000"对一些交易行,对复杂的就崩。

比需要的 buffer 还大。 按测量值的 1.2-1.5 倍 profile,不是 3 倍 5 倍。

忘了 budget 指令要排在最前。 CU budget 指令必须是交易里最早的几条之一。放在其他指令后面不一定生效。

怎么把 CU Price 设对

这块在Solana Priority Fee 指南讲过了。简单回顾:

const tx = new Transaction()
  .add(ComputeBudgetProgram.setComputeUnitLimit({ units: 250_000 }))
  .add(ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 100_000 }))
  .add(yourActualInstruction);

不同用例的 CU 优化

不同负载有不同的 CU 画像。要知道的:

简单 swap(AMM v4)。 ~30,000-50,000 CU。默认预算够用,但你浪费了一些。

集中流动性 swap(CLMM、Whirlpool)。 每过一个 tick 的 CU 不固定。过 2-3 个 tick 的 swap 要 ~80,000-150,000 CU。过更多 tick(波动池子、大单),还要更多。Profile 你自己的实际用量。

Jupiter 多跳路由。 变化很大。用 dynamicComputeUnitLimit: true,别预测。

借贷操作。 借/还/清算因为多个 CPI,常需要 200,000-400,000 CU。

NFT mint。 压缩 NFT mint 便宜(~30,000 CU)。带元数据的标准 NFT mint 重些(~100,000-200,000 CU)。

套利交易。 多指令交易(关 ATA、swap、swap、关 ATA)累加得快。端到端 profile。

CU 不够时失败是什么样

CU 耗尽的失败长这样:

{
  err: { InstructionError: [N, "ComputationalBudgetExceeded"] }
}

交易被打包到块里,但跑到一半 CU 用光、被回滚。你为已经消耗的 CU 付了费,但没拿到结果。

生产里的症状:

看到这些模式,就要在真实条件下模拟一下、审一下 CU 预算。

CU 给太多是什么样

不那么明显,因为没失败:

CU limit 1,000,000、实际用 150,000——你为浪费掉的 850,000 在每笔交易上付 priority fee。在 100 microlamports/CU 时,每笔白付 0.000085 SOL。每天 1000 笔 = ~0.085 SOL/天 = ~31 SOL/年。

这周可以做什么

有生产交易的话:

  1. 审 CU limit。 你显式设了吗?数值合理吗?
  2. 每种交易类型都 profile。simulateTransaction 测真实消耗。
  3. limit 设为测量值的 1.2-1.5 倍。 紧到不浪费、松到能扛住最坏情况。
  4. Jupiter swap 用 dynamicComputeUnitLimit: true 别猜。
  5. 算每笔交易的"有效 CU 价格"。 总 priority fee ÷ 实际消耗 CU。这个值你希望它高(说明你没多付)。
  6. 协议升级后重新 profile。 程序升级时 CU 成本可能变。

常见的 CU Budget 模式

我们见过有效的生产模式:

// 简单 AMM swap
const cuLimit = 100_000;

// CLMM swap(尽量用 dynamic)
const cuLimit = await estimateClmmCu(pool, amount);

// Jupiter swap
// 在 swap API 调用里加 dynamicComputeUnitLimit: true

// 复杂多指令交易
// 通过模拟 profile,设测量值的 1.3 倍

机器人要处理多种交易类型的话,按交易类型参数化 CU limit,不要只用一个固定值。

在生产 CU 负载上试一下 BoltTx

CU 预算调优在你发交易量大的时候更重要——而这恰好是 BoltTx 这些属性最值钱的场景:

import { Connection } from "@solana/web3.js";

const connection = new Connection(
  "https://bolttx.io/?api-key=YOUR_API_KEY",
  "processed"
);

免费档注册。跑真实的 CU 重型负载,对比每笔上链交易的有效成本。

常见问题

每笔交易最多多少 CU? 1,400,000 CU。CU limit 不能设高于这个值。

能只设 CU price 不设 limit 吗? 能,但通常你两个都想设。不显式设 limit,你用的就是默认 200,000,可能不对。

CU limit 影响交易优先级吗? 不直接。总 priority fee = limit × price。Validator 按总 priority fee 排序,不光看 limit。

设太低会怎样? 交易跑到一半,因为 ComputationalBudgetExceeded 失败。你为已消耗的 CU 付了费。

budget 指令要不要包在签名里? budget 指令是交易的一部分;签名自动覆盖它,不用单独处理。

延伸阅读

返回博客列表