每笔普通的 Solana 交易都带着一个 recent blockhash,它在约 150 个区块(大约 60 秒)后过期。错过这个窗口,交易就永久失效。
durable nonce 用一个你自己控制的账户里存的值替换掉那个 blockhash,而这个值不会随时间过期。它在少数几种场景下很有用,在大多数场景下只是多余的开销。
nonce 账户到底是什么
一个链上账户,里面存着一个 nonce 值和一个 authority。这个值只有在有人对它执行 advanceNonce 指令时才会变。
机制就这么简单,而它带来一个关键结论:★一笔签在这个 nonce 上的交易会一直有效,直到 nonce 被推进为止 —— 不管中间隔了多久。★
代价是:每一笔这样的交易,第一条指令必须是 nonceAdvance。这条指令既消耗当前 nonce、又生成下一个,这就是防重放的机制。
import {
Connection, Keypair, SystemProgram, Transaction, NONCE_ACCOUNT_LENGTH,
} from "@solana/web3.js";
// 一次性准备:创建并初始化 nonce 账户。
const nonceAccount = Keypair.generate();
const rent = await connection.getMinimumBalanceForRentExemption(
NONCE_ACCOUNT_LENGTH,
);
const tx = new Transaction().add(
SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: nonceAccount.publicKey,
lamports: rent,
space: NONCE_ACCOUNT_LENGTH,
programId: SystemProgram.programId,
}),
SystemProgram.nonceInitialize({
noncePubkey: nonceAccount.publicKey,
authorizedPubkey: nonceAuthority.publicKey,
}),
);
★这个账户是租金豁免的,所以那些 lamport 是被锁住而不是花掉了。★ 用 nonceWithdraw 关闭账户就能拿回来。
怎么用
和普通交易有三处不同:
// 1. 从账户里读当前的 nonce 值。
const info = await connection.getNonce(nonceAccount.publicKey);
const tx = new Transaction();
// 2. advanceNonce 必须是第一条指令。
tx.add(
SystemProgram.nonceAdvance({
noncePubkey: nonceAccount.publicKey,
authorizedPubkey: nonceAuthority.publicKey,
}),
);
tx.add(yourActualInstruction);
// 3. recentBlockhash 填的是 nonce 值,不是真的 blockhash。
tx.recentBlockhash = info.nonce;
tx.feePayer = payer.publicKey;
★如果 nonceAdvance 不是第一条,交易会失败。★ 这是最常见的实现错误,而且从报错信息里看不出原因。
如果你的问题是"拥堵时交易过期"而不是"需要长期有效",免费领个 BoltTx key 是个更简单、值得先试的解法。
durable nonce 真正有用的场景
多签审批。 一笔需要在几小时甚至几天里收集多个签名的交易,用不了 recent blockhash。这是最经典的用例,durable nonce 很大程度上就是为它而存在的。
有人工延迟的硬件钱包流程。 签名要等人、等设备确认、或者等审批队列,很容易超过 blockhash 窗口。
预签交易。 现在构造好、之后靠触发条件提交的任何东西 —— 条件单、计划中的操作、待命的应急交易。
离线签名。 一台隔离的签名机产出交易,然后带到联网机器上提交。
什么时候它帮不上忙
★这一段值得说直白点,因为 durable nonce 经常被推荐去解决它解决不了的问题。★
拥堵时交易失败。 nonce 去掉的是过期,不是竞争。上链取决于 priority fee、重试行为、以及提交路由。nonce 只会让一笔注定失败的交易多有效一阵子,不会让它更容易被收录。
追求速度的交易机器人。 你现在多了一次账户读取、一条额外指令、以及对 nonce 状态的串行依赖。对一个决策周期在一秒以内的机器人来说,★一个后台刷新的 recent blockhash 既更简单也更快。★
重试循环。 持续重发到 blockhash 过期,本身已经覆盖了普通的重试场景。nonce 让你能重试更久 —— 而那很少是瓶颈:如果一分钟的重试没让它上链,再来十分钟也不会。
串行化问题
这是搭高吞吐系统的人最容易意外的限制。
★一个 nonce 账户同一时刻只能支撑一笔在途交易。★ 当一笔用它的交易落块时,nonce 就推进了,而所有签在旧值上的其它交易全部作废。
交易 A 和交易 B 都签在 nonce 值 N 上
→ A 落块,nonce 推进到 N+1
→ ★B 现在永久无效★
所以要并发,就需要一个 nonce 账户池,每笔在途交易一个,每个都要租金豁免。对一个每秒发很多笔的机器人来说,这是一笔可观的锁定资金和管理负担 —— 这也是 durable nonce 和高频交易配不到一起的另一个原因。
失败模式
nonceAdvance 不在第一条。 交易被拒。永远放第一条,没有例外。
nonce 值过期了。 你读了 nonce,别的东西把它推进了,你的交易就无效了。如果还有别的东西会碰这个账户,签名前要重新读。
authority 不对。 authorizedPubkey 必须签名。当 nonce authority 和 fee payer 不是同一个时容易出错 —— 而在多签场景里那是常态。
账户没做到租金豁免。 创建时资金不足,账户可能被回收。用 getMinimumBalanceForRentExemption,别写死一个数字。
忘了账户还在。 租金豁免的 lamport 会一直锁着,直到你 nonceWithdraw。被遗忘的 nonce 账户就是悄悄锁死的资金。
上链应该是什么水平
我们投递节点上的真实交易:中位确认 336 毫秒 —— 不到一个 slot。
★绝大多数交易落块的时候,离 blockhash 窗口还远得很。★ 如果你的交易在过期,原因通常不是窗口太短 —— 而是费用、路由,或者一个提前停止的重试循环。
怎么判断
一个简短的测试:
这笔交易需要保持有效超过一分钟吗?
├─ 需要,因为要等人或多方签名 → ★用 durable nonce★
├─ 需要,因为是预签、等触发条件 → ★用 durable nonce★
└─ 不需要,它只是在拥堵时一直失败 → ★费用、重试、路由★
第三种情况是大多数人实际所处的,而 nonce 对它是错的工具。
BoltTx 在这里的位置
我们做提交,也就是上面那第三种情况。
提交走我们自建的四区域投递节点,带 SWQoS 路由,不暴露公共 mempool —— 交易在落块之前,传输途中观察不到。用 durable nonce 的交易和普通交易走同一条路 —— nonce 影响的是有效性,不是路由。
你在本地签名。我们不托管资金、不代签、不修改交易内容。tip 带在交易里、从你自己的钱包链上支付,交易失败时跟着一起 revert —— 这是 Solana 原子交易的机制决定的。只有到达链上的交易才计费。
免费领 API key,没有月费:
const connection = new Connection("https://la.bolttx.io/?api-key=YOUR_KEY");
常见问题
Solana 的 durable nonce 是什么?
一个链上账户,里面存着一个替代交易中 recent blockhash 的 nonce 值。因为这个值只有在 advanceNonce 指令执行时才变,所以交易不会随时间过期。
什么时候该用 durable nonce? 当一笔交易必须比约 150 个区块的 blockhash 窗口活得更久时:跨时间收集的多签审批、有人工延迟的硬件钱包流程、等触发条件的预签交易、以及离线签名。
durable nonce 能帮交易在拥堵时上链吗? 不能。它去掉的是过期,不是竞争。上链取决于 priority fee、重试行为和提交路由。nonce 只是让交易有效更久,不会让它更容易被收录。
怎么创建 Solana 的 nonce 账户?
创建一个 NONCE_ACCOUNT_LENGTH 大小、资金达到租金豁免的账户,然后用将要签署推进操作的 authority 执行 SystemProgram.nonceInitialize。用 getMinimumBalanceForRentExemption,别写死金额。
我的 durable nonce 交易为什么失败?
最常见的是 nonceAdvance 不在第一条 —— 这是硬性要求。其它原因包括 nonce 值已过期、签名的 authority 不对、或者账户从来没达到租金豁免。
一个 nonce 账户能同时用于多笔交易吗? 不能。当一笔用它的交易落块时 nonce 就推进了,所有签在旧值上的其它交易全部无效。要并发就需要一个 nonce 账户池。
一个 nonce 账户要花多少钱?
NONCE_ACCOUNT_LENGTH 对应的租金豁免余额,它是被锁住而不是花掉。用 nonceWithdraw 关闭账户就能拿回来。
交易机器人该用 durable nonce 吗? 通常不该。你多了一次账户读取、一条额外指令、一个串行依赖,换来的是去掉一个快速机器人根本不会碰到的过期问题。后台刷新的 recent blockhash 更简单也更快。
durable nonce 交易能保持有效多久? 直到 nonce 被推进为止。没有时间限制 —— 只要中间没有别的东西推进过那个 nonce,一笔签好的交易几天后照样能提交。
nonce 和 recent blockhash 有什么区别? recent blockhash 来自链上,约 150 个区块后过期。nonce 来自你控制的账户,只有你推进它时才变。两者占据交易里的同一个字段。
每笔交易前都要重新读 nonce 吗? 如果还有别的东西可能推进那个账户,要。如果这个 nonce 只由一个进程独占、并且它自己记录每次推进,那可以在本地维护这个值,只在失败时重读。
能关闭 nonce 账户拿回 lamport 吗?
能,用 nonce authority 签署 SystemProgram.nonceWithdraw。否则被遗忘的 nonce 账户会无限期锁着那些 lamport。
用 durable nonce 会额外花钱吗?
advanceNonce 指令增加计算单元,因而让费用略微上升,再加上锁在账户里的租金豁免 lamport。两者都不大,但都是真实成本。
durable nonce 适合多签吗? 这是它的主要用例。从多方收集签名花的时间比一个 blockhash 的寿命长,而 nonce 让这件事变得可行,不用反复重建交易。
为什么我的交易看起来很快、却还是过期了? 把取 blockhash 到提交之间的间隔打进日志。启动时取一次、签名环节在等什么、或者一个不停重发已过期字节的循环,都会造成这个现象,而网络本身并不慢。