Solana 主网已经在纪元 1035 开始时激活 txv1 功能开关,正式上线 v1 交易格式。最直接的变化是:单笔交易上限从 1232 字节提升到 4096 字节,增幅约 3.3 倍。ZK 证明、大型多重签名、批量操作等此前必须拆成多笔链上交易的负载,现在可以放进一笔原子交易里完成。
不过,对读取、索引和赞助交易的团队来说,这不是一次无声升级,而是一处破坏性变更。下面拆开看迁移重点。
更大的交易尺寸
最大交易尺寸现为 4096 字节,高于此前的 1232 字节。它为 ZK 证明、多签和批量操作提供了更多空间。
txv1 功能开关已于 2026 年 9 月 15 日约 01:00 UTC、纪元 1035 开始时于主网激活,测试网与开发网也已同步激活。尺寸增加由 SIMD-0296 定义,并通过 SIMD-0385 引入的 v1 交易格式实现。
现有的 v0 和 legacy 交易格式继续有效。暂时没有更大交易需求的应用程序和钱包不会受影响;想使用新尺寸的应用则需要更新到 v1 交易。
重大变更:
| 如果你的应用 |
是否重大变更 |
你需要做什么 |
| 读取交易或区块 |
是 |
传递 maxSupportedTransactionVersion: 1。了解更多 |
| 索引交易 |
是 |
从 transactionConfig 而非 ComputeBudget 指令中读取 v1 交易的限制。了解更多 |
| 发送交易 |
否(可选加入) |
如果使用 v1,你必须显式设置计算单元和数据大小限制(两者默认均为零)。了解更多 |
| 赞助费用或共同签署他人的交易 |
是 |
扫描 ComputeBudget 指令的上限不再具有约束力。从 transactionConfig 读取你依赖的限制。了解更多 |
主网激活:纪元 1035
txv1 功能开关已在 2026 年 9 月 15 日约 01:00 UTC、主网纪元 1035 开始时激活。v1 交易现已在主网、测试网和开发网上线。
| 项目 |
状态 |
| 主网激活 |
已上线 |
| 开发网激活 |
已上线 |
| 重大变更? |
是,对于读取、索引和赞助交易 |
| 需要索引变更? |
是,从 transactionConfig 读取限制 |
| 功能开关 |
txv1aq4pp281K9um3tnPgkfX8UqtFT6wcVW3hNezGLL |
当前功能开关激活状态:
| 集群 |
激活状态 |
| 测试网 |
已激活 |
| 开发网 |
已激活 |
| 主网 |
已激活 |
除主网外,v1 交易已在以下环境启用:
对 V1 交易的支持
以下是首批处理 v1 交易的版本。升级你的依赖项以避免错误。
迁移清单
对于用户
更新你的钱包。
钱包和应用程序继续像今天一样工作。legacy 和 v0 交易格式不变,你批准、签名或发送交易的方式也没有任何改变。为了受益于 Dapp 使用 v1 交易,你需要确保自己的钱包是最新的。
这次升级的核心收益是空间。以前必须拆分为多个链式交易的工作,例如机密转账使用的 ZK 证明、大型多签和批量操作,现在可以作为单个原子交易完成。这意味着签名更少,也只需一次确认而不是多次。
对于开发者
读取交易是重大变更。发送 v1 是可选加入。
首先升级依赖项(参见对 V1 交易的支持),然后根据以下情况处理。
如果你读取或流式传输交易 — RPC 消费者、索引器、浏览器、Geyser 管道(详情):
- 通过 HTTP RPC 调用获取数据时,在
getTransaction 和 getBlock 上将 maxSupportedTransactionVersion 设置为整数 1。
- 通过 Websockets 流式传输数据时,在
blockSubscribe 上将 maxSupportedTransactionVersion 设置为整数 1。
- 通过 Websockets 的
blockSubscribe 流式传输区块数据时,将 block: null 与错误一起作为失败处理,而不是空区块。
- 更新原始交易解码器以识别 v1
129 (0x81) 版本前缀并解析其新布局。
- 重新生成 Geyser / gRPC protobuf 存根,然后在检查
versioned 标志之前,通过 Message.config 的存在来识别 v1。
如果你检查交易的计算预算或优先费用 — 索引器、链上程序、中继器、支付方以及任何赞助费用的人(索引详情,链上详情,赞助商详情):
- 解析 V1 交易负载时,读取
transactionConfig 而不是检查 ComputeBudget 指令。
- 在跨版本比较之前规范化优先费用:v0 以每计算单元微 lamports 表示价格,v1 以 lamports 表示总额。
如果你想发送 V1 交易(详情):
- 如果你显式添加了 ComputeBudget 指令,请将它们从交易中移除,改用
messageConfig。
- 如果你在 V0 中使用了地址查找表 (ALT),请将它们从交易中移除,直接将账户放入指令中(最多 64 个账户)。
- 显式设置计算单元限制和加载账户数据大小(默认值为零,没有它们你的交易将失败)。
- 通过一次将两个限制都设为最大的模拟来估算两者;将数据大小向上取整到下一个 32 KiB 页。
- 将优先费用从每计算单元微 lamports 转换为总 lamports。
- 模拟和发送时传递
encoding: 'base64'。
- 如果钱包签署交易,请在构建之前检查它是否声明支持 v1,如果不支持则回退到 v0(详情)。
- 使用
@solana/kit 或 @solana/web3.js 3.x 构建 v1。
如果你构建钱包 — 浏览器扩展、移动钱包或嵌入式签名器(详情):
- 只有在钱包实际解析和签署 v1 后,才在
supportedTransactionVersions 中声明 1。过早声明会使 dapp 发送钱包将拒绝的交易。
- 向用户显示来自消息配置的 v1 计算限制和优先费用,而不是来自 ComputeBudget 指令。
下面的技术细节完整解释了每一项。示例代码可在此处获取。
对于验证器和 RPC 运营商
鉴于 v1 已上线,Jito-Solana 和 RPC 运营商需要 v4.2.2。
所有 v4.2.x 验证器都在共识层支持更大的交易;但是,有两类节点需要特定版本。
Jito-Solana 验证器 — v4.2.2 或更高版本。 早期版本不支持 v1 交易。这意味着运行旧版 jito-solana 构建的领导者不会将 v1 交易构建到其区块中。所有 jito-solana 运营商都应升级到 v4.2.2。
RPC 节点 — Agave v4.2.2 或更高版本。 v4.2.2 之前的版本在进入存储时将 v1 消息降级为 v0,因为转换在 config 字段之前检查 versioned 标志(在 v4.2.2 中修复)。交易仍然会返回,但作为 v0,其计算预算被清零且版本报告错误。所有 RPC 运营商都应升级到 Agave v4.2.2。
GitHub:交易 V1 示例代码提供了针对本地验证器的每种语言的可运行示例——发送、解码、读取区块和通过 gRPC 索引——以及一个版本矩阵,记录了处理 v1 的每个依赖项的首个版本,包括那些静默丢弃配置而不是报错的依赖项。所有重大变更的详细信息如下:
对于交易读取者
v1 交易现已上链 — 未选择加入则读取失败。
重大变更:读取交易和区块
v1 交易现已上链,因此未选择加入的 RPC 消费者在请求的响应包含 v1 交易时会失败。修复只需一行:
- 在
getTransaction 和 getBlock 上传递 maxSupportedTransactionVersion: 1。它必须是 JSON 整数 1,而不是字符串 "1"。
- 传递
0 或 legacy 在遇到 v1 交易时会像省略参数一样失败。
- 该参数声明你的客户端可以解码的最高版本,不是上限,也不是对特定版本的请求。将其提高到
1 不会改变 legacy 和 v0 交易的返回方式。
每种读取表面在未选择加入时的行为:
| 方法 |
无 maxSupportedTransactionVersion: 1 时 |
getTransaction |
v1 交易失败,错误 -32015 |
getBlock |
一个 v1 交易导致整个区块失败 — 无部分结果 |
blockSubscribe |
发出 block: null 并停止推进 — 在第一个 v1 Slot卡住 |
getSignaturesForAddress |
不受影响 — v1 签名正常列出 |
选择加入的响应在 v1 交易的消息中携带一个新的 transactionConfig 对象(对于 legacy 和 v0 完全不存在):
{
"message": {
"instructions": ["… 此处无 ComputeBudget 指令 …"],
"recentBlockhash": "GsdgFbNBoZmAB5uPHfk2xUFYyM4Wg2hYZBfBrxrqjxfF",
"transactionConfig": {
"computeUnitLimit": 30000,
"heapSize": null,
"loadedAccountsDataSizeLimit": 200000,
"priorityFee": null
}
}
}
对于索引器
解码 v1 交易配置而不是 ComputeBudget 指令。
重大变更:索引交易
ComputeBudget 扫描返回空。 任何通过扫描 ComputeBudget 程序的指令来推导优先费用或计算单元限制的管道,对于每个 v1 交易都将报告零,且不会报错。在 v1 中,这些值位于 transactionConfig 中。这是最可能导致静默错误分析而不是明显失败的路径。
Geyser / gRPC 流完全没有版本门控。 protobuf 中没有版本字段。过时的消费者不会在 v1 上报错;它会静默地将其误读为具有空计算预算的 v0。重新生成你的 protobuf 存根,并在 Message.config(字段 7)上按结构检测版本,按此顺序检查:
| 按此顺序检查 |
版本 |
config 字段存在 |
v1 |
config 不存在,versioned 为 true |
v0 |
| 两者皆无 |
legacy |
顺序很重要,因为 versioned 布尔值对于 v0 和 v1 都为 true。首先测试它会把每个 v1 交易静默分类为 v0,并带有清零的计算预算。
在 TypeScript 中:
function messageVersion(message: Message): "legacy" | "v0" | "v1" {
if (message.config !== undefined) {
return "v1";
}
return message.versioned ? "v0" : "legacy";
}
在 Rust 中:
fn message_version(message: &Message) -> MessageVersion {
match (&message.config, message.versioned) {
(Some(_), _) => MessageVersion::V1,
(None, true) => MessageVersion::V0,
(None, false) => MessageVersion::Legacy,
}
}
config 是一个子消息,子消息字段在 proto3 中总是携带显式存在性——因此无论你的解码器如何处理默认值,以及你的模式副本是否将该字段标记为 optional,此检查都成立。
跨版本比较费用需要规范化。 v0 将优先费用表示为每计算单元微 lamports 的价格;v1 将其表示为 lamports 的总额。要将两者放在一个仪表板中,请将 v0 价格乘以交易实际请求的计算单元限制,然后除以 1,000,000:
20,000 CU × 250,000 微-lamports/CU = 5,000 lamports // v0
5,000 lamports // 等效的 v1
对于交易发送者
发送 v1 是可选加入,但其资源限制必须显式设置。
发送 v1 交易
在 legacy 和 v0 中,设置资源限制是可选的,因为运行时提供默认回退值。在 v1 中,省略它会将资源限制设置为零,并制造出一笔无法运行的交易。
| 未设置的字段 |
legacy / v0 |
v1 |
| 计算单元限制 |
每个 ix 200k,最大 1.4M |
0 CU |
| 加载账户数据大小 |
64 MiB |
0 字节 |
| 堆大小 |
32 KiB |
32 KiB |
配置为空的 v1 交易在账户加载时因 MaxLoadedAccountsDataSizeExceeded 而失败,这意味着你请求的预算字节数不足。始终显式设置计算单元限制和加载账户数据大小限制。 使用两个限制都设为最大进行一次模拟,从结果中读取 unitsConsumed 和 loadedAccountsDataSize,并将它们写入配置。数据大小要向上取整到下一个 32 KiB 页,因为区块成本模型按 32 KiB 页收费,低于下一页边界的空间是免费的。
为尚不存在的账户预留数据预算空间。加载一个账户收取 64 字节的基础元数据加上其数据长度,而加载一个不存在的账户则不收取任何费用,因此创建一个账户是从 0 到至少 64 字节的阶跃变化。如果在你的模拟和发送之间创建了一个账户,那么精确针对模拟测量的数据大小限制可能会在交易到达网络时超出 MaxLoadedAccountsDataSizeExceeded。
一个 SOL 转账为一个全新的钱包注资。加载发生在执行之前,因此接收者在估算时仍然不存在。检查是 > 而不是 >=,因此 149 恰好落在限制上并通过。
另外三个发送变更:
- 优先费用现在是总额,而不是价格。 v0 设置每计算单元微 lamports;v1 设置 lamports 的绝对总额。不要沿用每 CU 的乘法。
- ComputeBudget 指令变为空操作。 它们既不被解析也不被拒绝,它们成功执行但什么都不做,消耗 150 个计算单元和一个指令槽位。移除它们。
- 大型交易使用 base64 编码。 提交超过 1232 字节需要
encoding: "base64"。base58 无论交易版本如何都保持在 1232 字节的上限,并且出于弃用原因故意未提高,因此 4096 字节的上限只能通过 base64 达到。
如果你将交易发送到钱包,你必须首先确保钱包支持 v1 交易。版本支持因钱包而异,并可能因用户安装的钱包版本而异。
Wallet Standard 将钱包的 supportedTransactionVersions 信息作为适配器上的只读集合公开。你的 dapp 必须在尝试发送 v1 交易之前读取该字段:
import {
SolanaSignAndSendTransaction,
SolanaSignTransaction
} from "@solana/wallet-standard-features";
import { getWallets } from "@wallet-standard/app";
import type { Wallet } from "@wallet-standard/base";
const wallets = getWallets();
export function supportedTransactionVersions(wallet: Wallet): ReadonlySet<string> {
const versions = new Set<string>();
for (const name of [SolanaSignAndSendTransaction, SolanaSignTransaction] as const) {
const feature = wallet.features[name] as
| { supportedTransactionVersions?: readonly unknown[] }
| undefined;
for (const version of feature?.supportedTransactionVersions ?? [])
versions.add(String(version));
}
return versions;
}
export function supportsV1(wallet: Wallet): boolean {
return supportedTransactionVersions(wallet).has("1");
}
仅仅因为钱包添加了对 v1 的支持并不意味着用户拥有该钱包的最新版本。因此,如果 supportedTransactionVersions 检查产生意外响应,你可能会提示用户升级他们的钱包。
发送 v1 的 dapp 使用 @solana/kit 或 @solana/web3.js 3.x 构建交易,并使用 @solana/react 或 @solana/wallet-adapter 3.x(即将推出)。
对于钱包
只有在你能实际签署 v1 时才声明支持它。
钱包必须声明它们签署的版本
dapp 不能要求钱包签署钱包不理解的版本,因此钱包必须声明其支持的内容,dapp 必须检查。
根据 Wallet Standard,solana:signTransaction 和 solana:signAndSendTransaction 功能各携带一个 supportedTransactionVersions 数组。
钱包必须只声明其能实际签署的版本。
钱包还应在批准 UI 中正确显示 v1 资源限制。v1 交易的计算单元限制、加载账户数据大小和优先费用位于消息配置中,其优先费用是 lamports 的总额,而不是每计算单元的价格。读取 ComputeBudget 指令来构建批准屏幕的钱包将错误地表示 v1 交易,因为这些指令在那里是空操作。
对于程序开发者
程序无法读取 v1 交易的计算预算或优先费用。
重大变更:链上检查计算预算
程序今天通过扫描 Instructions sysvar 中的 ComputeBudget 指令来读取计算预算。在 v1 中,这些值位于消息上,并且没有任何东西向链上代码暴露消息配置:Instructions sysvar 只携带指令,没有 sysvar 持有配置,也没有 syscall 返回它。
v1 交易中的 ComputeBudget 指令不会被拒绝;它们作为空操作执行,这意味着它们内部设置的值不再可靠。目前没有 sysvar 来确定交易的版本或提取消息配置。这意味着一旦 v1 交易上线,程序应停止依赖检查 ComputeBudget 指令。
我们正在评估其他选项。请返回此处查看更新。
对于支付方和服务器签名者
扫描指令的费用上限不约束 v1 交易。
重大变更:赞助和共同签署交易
如果你共同签署、赞助费用或验证他人构建的交易(例如,中继器、支付方和无 Gas 后端):
- 在任何门控之前检测版本。 与程序不同,服务器总能分辨它持有的是什么。解码支付方交给你的 base64 直接产生版本——v1 的消息第一个字节是
0x81。
- 对于 v1 交易,从
transactionConfig 读取优先费用和计算限制,而不是从 ComputeBudget 指令。优先费用是 lamports 的总额,而不是每 CU 的微 lamports。给你版本的同一次解码也给你配置。注意:空操作的 ComputeBudget 指令可以存在于 V1 指令上。它们与实际请求的 v1 资源无关。应忽略它们。
- 在
getTransaction 和 getBlock 上传递 maxSupportedTransactionVersion: 1。 它是一个上限,而不是建议:将其保留为 0,读回 v1 交易会因 -32015 失败,而交易本身在链上。
SDK 速查表
使用这些 API 构建、发送和读取 v1 交易。
TypeScript — @solana/kit
@solana/kit 8.0.0 或更高版本
| 任务 |
API |
| 构建 v1 消息 |
createTransactionMessage({ version: 1 }) |
| 设置资源限制 |
setTransactionMessageComputeUnitLimit |
|
setTransactionMessageLoadedAccountsDataSizeLimit |
|
setTransactionMessageHeapSize |
|
setTransactionMessagePriorityFeeLamports |
| 在模拟前预留限制空间 |
fillTransactionMessageProvisoryResourceLimits |
| 通过模拟测量限制 |
estimateResourceLimitsFactory |
| 将测量到的限制写回 |
estimateAndSetResourceLimitsFactory |
| 解码线上的交易 |
getTransactionDecoder |
|
getCompiledTransactionMessageDecoder |
|
decompileTransactionMessage |
| 读取配置 |
TransactionMessage 的 v1 分支上的 message.config |
| 选择加入读取 v1 |
getTransaction / getBlock 上的 maxSupportedTransactionVersion: 1 |
计算单元限制、加载账户数据大小限制和堆大小设置器适用于任何版本的消息。优先费用不同:v1 存储 lamports 的总额,而 legacy 和 v0 使用每计算单元微 lamports。对 v1 使用 setTransactionMessagePriorityFeeLamports,对 legacy 和 v0 使用 setTransactionMessageComputeUnitPrice;Kit 通过其类型强制执行此区别。
Rust — solana-*
*solana- 4.2.x**
| 任务 |
API |
| 构建 v1 消息 |
v1::Message::try_compile_with_config |
| 设置资源限制 |
v1::TransactionConfig::empty() |
|
.with_compute_unit_limit(…) |
|
.with_loaded_accounts_data_size_limit(…) |
|
.with_heap_size(…) |
|
.with_priority_fee(…) |
| 在模拟前预留限制空间 |
— |
| 通过模拟测量限制 |
从 simulateTransaction 读取 unitsConsumed 和 loadedAccountsDataSize。 |
| 将测量到的限制写回 |
— |
| 解码线上的交易 |
VersionedTransaction 反序列化,或 RPC 响应上的 EncodedTransaction::decode |
| 读取配置 |
VersionedMessage::V1(m) => m.config |
| 选择加入读取 v1 |
RpcTransactionConfig / RpcBlockConfig 上的 max_supported_transaction_version: Some(1) |
TypeScript、Rust、Go 和 Python 的完整示例代码可在 GitHub 获取。
技术细节
大小限制
新的每笔交易大小限制为 4096 字节,高于此前的 1232。之前的限制是由 Solana 保守使用 1280 字节 MTU(最大传输单元)设定的,扣除开销后为交易负载留下 1232 字节。
2022 年,QUIC 成为默认的交易接收协议。它不指定最大流大小,因此可以提高最大交易大小。
上限保持在 4096 而不是更大,因为这匹配验证器硬件使用的标准 4 KiB 内存页。它保持了每笔交易内存处理的低成本,并避免强制单笔交易跨越多个页面。
一旦交易超过 MTU,它必须被分割到多个 QUIC 帧中,而单个丢失的帧会触发整个集合的重传。这使得更大的交易在可靠接收方面成本更高,并给验证器的在途缓冲区带来更大压力。将交易大小保持在 4096 以下避免了这个问题。
4096 字节的上限覆盖了依赖 bundle 的用例的很大一部分,让开发者可以将它们作为单个原子交易落地。由于更大的交易消耗更多验证器带宽,调度器预计会要求更高的优先费用才能让它们落地,而不是同等优先级的较小交易。
v1 格式
更大的尺寸仅在 v1 交易格式中可用,该格式在 SIMD-0385 中规定。
v1 重新排序了交易信封。签名移到尾部,交易判别符移到第一个槽位(偏移量零),让基础设施无需反序列化任何内容即可识别格式。对于 v1,该判别符是字节 129 (0x81)。
v1 将 0x81 放在字节 0,并将签名附加在最后,没有长度前缀——计数由头部隐含。配置掩码及其值是新的。
配置掩码和值是新引入的:计算单元限制、加载账户数据大小限制、堆大小和优先费用位于消息中的固定位置,而不是在 ComputeBudgetProgram 指令内部。这意味着网络可以通过一次固定偏移读取来按优先费用对交易进行排序,而不是扫描和反序列化其指令列表。
限制也发生了变化。v1 交易可以更大,但必须更扁平:
| 限制 |
legacy |
v0 |
v1 |
| 交易大小 |
1232 字节 |
1232 字节 |
4096 字节 |
| 账户地址 |
~32,受大小限制 |
64,通过查找表 |
64,内联 |
| 地址查找表 |
不支持 |
支持 |
不支持 |
| 重复地址 |
允许 |
允许 |
拒绝 |
上述 legacy 和 v0 数字是运行时限制,不是格式限制。两种格式可以编码比它们能执行的更多的账户——运行时拒绝任何锁定超过 64 个账户的交易。Legacy 交易甚至达不到这个数字:64 个内联地址每个 32 字节是 2048 字节,因此 legacy 交易在其 1232 字节预算中大约在 32 个地址左右耗尽,具体取决于它携带多少签名和多少指令数据。v0 通过引用每个 1 字节的查找表地址来达到完整的 64 个。v1 使用普通内联地址达到相同的 64 个,因为 2048 字节可以轻松容纳在 4096 内,并让 64 成为在净化时检查的显式格式限制。一份草案提案 SIMD-0596 将把账户限制提高到 96。
更大的尺寸支持了不适合 1232 字节的交易级工作负载,包括机密转账使用的 ZK 证明、Winternitz 一次性签名、机构经常使用的嵌套多签,以及 BLS 等签名方案。
关于此升级
更大的交易尺寸对 Solana 开发者来说是一项重大的可用性改进。通过将每笔交易的字节限制提高到 4096 字节,此升级让开发者可以将以前过大的工作负载作为单个原子交易落地,而不是使用地址查找表或 Jito bundles 将它们拼接在一起。在实践中,这也降低了成本和延迟——需要支付的签名更少,并且只需一次确认,而不是多个链式交易。
此升级解决了自 Solana 早期以来应用团队不得不围绕其设计的实际上限。功能激活为应用开发者带来了全新一类能力。它于 2026 年 9 月 15 日在纪元 1035 开始时在主网上线。
格式细节在 SIMD-0385 中规定,大小增加在 SIMD-0296 中规定。另请参阅关于版本化交易的开发者文档和 transaction-v1-examples 获取 Rust、TypeScript、Python 和 Go 的可运行代码。
了解更多:Solana 升级
原文链接:solana.com/upgrades/larger-transaction-sizes