在深入之前,我们要感谢 Evan 和 Nick 对这篇文章的审阅。他们的宝贵反馈和见解非常感谢。
引言
Solana Web3.js SDK 是一个强大的 TypeScript 和 JavaScript 库,用于在 Node.js、Web 和 React Native 平台上构建 Solana 应用程序。在2024年11月7日,Anza推出了备受期待的2.0 SDK更新,引入了一系列现代 JavaScript 特性和改进。关键亮点包括大整数和加密的标准 JS 类型以及减少的包大小,使其成为开发人员的一次重要升级。
如果你一直在使用 @solana/web3.js,则需要将你的软件移植到新版本 v2.0 包,或明确指定版本以将其锁定为 v1.x。
在本文中,我们将探索 Web3.js 2.0 SDK 的最新更新,指导你完成迁移过程,并提供示例以帮助你入门。
本文假设读者对基础的 Solana 概念有一定的理解,比如发送交易、Solana 账户模型、区块哈希和 优先费用,同时具备 TypeScript 或 JavaScript 的经验。虽然建议熟悉 Web3.js SDK 的先前版本,但并不是强制要求。让我们开始吧!
Web3.js 2.0 中的新内容有什么?
让我们快速了解一下新的 Web3.js 2.0 SDK 提供了什么:
1. 性能改进
更快的加密操作:密钥对生成、交易签名和消息验证的速度提高了 10倍,利用了现代 JavaScript 环境(如 Node.js 和当前浏览器)中的本地加密 API。
2. 更小且高效的应用
Web3.js 2.0 完全支持 tree-shaking,允许你仅包含你使用的库的部分,从而将包大小降到最小。此外,新 SDK 没有外部依赖项,确保构建的轻量和安全。
3. 增强灵活性
开发者现在可以通过以下方式创建自定义解决方案:
- 使用自定义方法定义 RPC 实例
- 使用专门的网络传输或交易签名器
- 为网络、交易确认和编解码器组合自定义原语
新的 TypeScript 客户端现在托管在 @solana-program GitHub 组织下。这些客户端是通过 Codama 自动生成的,使开发者能够快速为自定义程序生成客户端。
你应该立即迁移到 Web3.js v2 吗?
截至 2025 年 2 月:
- 如果你正在用 JS/TS 制作新的 Solana 应用,并使用像系统程序、代币程序、关联代币程序等现有程序,你现在可以使用 web3.js v2。
- 如果你使用 Anchor 创建自定义链上应用,你可能希望等待——Anchor 并不支持 web3.js v2。你可能希望等待未来的 Anchor 更新。或者,使用 Codama 为你的链上应用创建 TypeScript 客户端,尽管这工作量稍大。
从 web3.js 版本 1 迁移
如果你使用过 web3.js v1,这里是重要差异的快速总结:
密钥对
在任何需要使用 Keypair 的地方,现在使用 KeyPairSigner。Keypair.generate() 现在是 generateKeyPairSigner()。此外,密钥对在任何地方都拼写为 keyPair,就像正常的 JS/TS camelCase。
私钥现在称为 privateKey,可通过 keyPairSigner.privateKey 访问 . 一般来说,在 web3.js v2 中,你在任何使用 secretKey 的地方使用 KeyPairSigner。
地址 / 公钥
在 web3.js v1 中使用 PublicKey 的地方,在 web3.js v2 中只需使用 address。例如,KeyPairSigner 有一个 keypairSigner.address 属性,这是它们的公钥。你可以使用 address 函数将字符串公钥转换为地址。
SOL 和代币数量
数量使用原生的 JS BigInt 类型。因此,你需要在数字的末尾添加 n,使 1 变为 1n。
工厂方法
许多特性是可配置的,因此不会有预设的实现(例如 doThing()),而是有一个工厂(称为 doThingFactory()),你可以用它创建自己的 doThing() 函数。例如:
- 要发送和确认交易,你运行一次带有你首选选项的
sendAndConfirmTransactionFactory(),并返回一个自定义的sendAndConfirmTransaction()函数。然后,你可以在需要发送和确认交易时使用你的sendAndConfirmTransaction()。 - 若要在 devnet 或 localnet 上获得空投,你运行一次
airdropFactory(),然后得到一个自定义的airdrop()函数,以便在你想要空投时使用。
如何使用 Web3.js 2.0 发送交易
Kite 框架
Helius 最近发布了 Kite,这是一个用于 web3.js v2 的 TypeScript 框架,其中包含大多数常见 Solana 任务的一次性函数。
我们将使用 Web3.js 2.0 构建一个客户端程序,以便将 lamports 转移到另一个钱包。该程序将展示提高交易成功率和更快确认时间的技术。
我们将遵循以下最佳实践来发送交易:
- 使用确认的承诺级别获取最新的区块哈希
- 设置 Helius 的 优先费用 API 推荐的优先费用
- 优化计算单位
- 将交易发送设置为 maxRetries 为 0,并将 skipPreflight 设置为 true
这种方法确保了即使在 网络拥塞 的情况下,仍然可以获得最佳的性能和可靠性。
先决条件
- 安装 Node.js
- 一个兼容的 IDE(例如 VS Code 或 Cursor)
安装
首先创建一个基本的 Node.js 项目以结构化你的应用。
运行以下命令以创建一个管理你的依赖项和项目元数据的 package.json 文件:
Shell 命令
npm init -y创建一个 src 目录,并在其中添加一个 index.ts 文件,其中将放置主要代码:
Shell 命令
mkdir src touch src/index.ts接下来,使用 npm 安装用于操作 Solana 的 Web3.js 2.0 SDK 所需的依赖项:
Shell 命令
npm install @solana/web3.js@2 @solana-program/system @solana-program/compute-budget esrun以下是每个包的描述:
@solana/web3.js: Solana Web3.js 2.0 SDK,对构建和管理 Solana 交易至关重要@solana-program/system: 提供对 Solana 系统程序的访问,允许进行如 lamport 转移等操作@solana-program/compute-budget: 用于设置优先费用和优化交易的计算单位esrun是一种简单的方法,可以直接从命令行运行 TypeScript 应用,而无需配置或包装函数。
定义转账地址
在 index.ts 中,定义用于转移 lamports 的源地址和目标地址。我们将使用 address() 函数从提供的字符串生成目标公钥。
对于源地址,我们将根据其 secretKey 推导出 KeyPair。
send-transaction.ts
import { address, createKeyPairSignerFromBytes, getBase58Encoder } from "@solana/web3.js"; const destinationAddress = address("public-key-to-send-lamports-to"); const secretKey = "add-your-private-key"; const sourceKeypair = await createKeyPairSignerFromBytes(getBase58Encoder().encode(secretKey)); 配置 RPC 连接
接下来,我们可以设置相关的 RPC 连接。createSolanaRpc 函数使用默认的 HTTP 传输与 RPC 服务器建立通信,适用于大多数用例。
同样,我们使用 createSolanaRpcSubscriptions 来建立 WebSocket 连接。rpc_url 和 wss_url 在 Helius Dashboard 中——只需注册或登录并导航到“端点”部分。
sendAndConfirmTransactionFactory 函数构建了一个可重用的交易发送器。该发送器需要 RPC 连接来发送交易和一个 RPC 订阅来监控交易状态。
send-transaction.ts
import { // ... createSolanaRpcSubscriptions, createSolanaRpc, sendAndConfirmTransactionFactory, } from "@solana/web3.js"; const rpc_url = "https://mainnet.helius-rpc.com/?api-key=<your-key>"; const wss_url = "wss://mainnet.helius-rpc.com/?api-key=<your-key>"; const rpc = createSolanaRpc(rpc_url); const rpcSubscriptions = createSolanaRpcSubscriptions(wss_url); const sendAndConfirmTransaction = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions, });创建转账指令
包括最近的区块哈希可以防止交易重复并为交易提供有效期——每个交易都必须包括一个有效的区块哈希才能被接受执行。对于此交易,我们将使用确认的承诺级别来获取最新的区块哈希。
然后,我们将使用 getTransferSolInstruction() 来创建由系统程序提供的预定义转账指令。这需要指定金额、源地址和目标地址。源地址必须始终是 Signer,而目标应为公钥。
send-transaction.ts
import { // ... lamports, } from "@solana/web3.js"; import { getTransferSolInstruction } from "@solana-program/system"; /** * 步骤 1:创建转账交易 */ const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); const instruction = getTransferSolInstruction({ amount: lamports(1n), destination: destinationAddress, source: sourceKeypair, }); 创建交易消息
然后我们将创建交易消息。所有交易消息现在都能够感知版本,消除了处理不同类型(如 Transaction 与 VersionedTransaction)的需要。
我们将设置源地址作为手续费支付者,包含区块哈希,并添加转移 lamports 的指令。
send-transaction.ts
import { // ... pipe, createTransactionMessage, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstruction, } from "@solana/web3.js"; // ... const transactionMessage = pipe( createTransactionMessage({ version: 0 }), (message) => setTransactionMessageFeePayer(sourceKeypair.address, message), (message) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, message), (message) => appendTransactionMessageInstruction(instruction, message), ); console.log("交易消息已创建"); pipe 函数,常用于函数式编程,创建一个函数序列,其中一个的输出成为下一个的输入。这里,它逐步构建交易消息,应用设置费用支付者、有效期和添加指令等变换。
初始化交易消息:
createTransactionMessage({ version: 0 }) 从基本的交易消息开始。
设置手续费支付者:
message=>setTransactionMessageFeePayer(fromKeypair.address, message) 添加费用支付者地址。
使用区块哈希设置有效期
message => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, message) 确保交易在一段时间内是有效的,使用最新的区块哈希。
添加转账指令
message => appendTransactionMessageInstruction(instruction, message) 将动作(如转移 lamports)附加到消息。
每个箭头函数 message => (...) 修改并将更新后的消息传递给下一步,从而生成一个完全构造的新交易消息。
签署交易
我们将使用指定的签名者签署交易,即源 Keypair。
send-transaction.ts
import { // ... signTransactionMessageWithSigners, } from "@solana/web3.js"; // ... /** * 步骤 2:签署交易 */ const signedTransaction = await signTransactionMessageWithSigners(transactionMessage); console.log("交易已签署"); 评估优先费用
在这一步,我们可以继续发送和确认交易。然而,我们应该通过设置优先费用和调整计算单位来优化交易。这些优化有助于提高交易成功率并减少确认时间,尤其是在网络拥塞期间。
为了设置优先费用,我们将使用 Helius的优先费用 API。这要求将交易序列化为 Base64 格式。虽然该 API 也支持 Base58 编码,但当前 SDK 直接提供 Base64 格式的交易,简化了这一过程。
send-transaction.ts
import { // ... getBase64EncodedWireTransaction, } from "@solana/web3.js"; /** * 步骤 3:从签署的交易中获取优先费用 */ const base64EncodedWireTransaction = getBase64EncodedWireTransaction(signedTransaction); const response = await fetch(rpc_url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", id: "helius-example", method: "getPriorityFeeEstimate", params: [\ {\ transaction: base64EncodedWireTransaction,\ options: {\ transactionEncoding: "base64",\ priorityLevel: "High",\ },\ },\ ], }), }); const { result } = await response.json(); const priorityFee = result.priorityFeeEstimate; console.log("将优先费用设置为 ", priorityFee); 将 priorityLevel 设置为 High 通常是足够的。然而,实现 高级优先费用策略 可以显著提高在网络拥塞期间的交易成功率。
优化计算单位
接下来,我们将评估该交易消息实际消耗的计算单位。
然后,我们通过将此值乘以 1.1 来添加10%的缓冲。此缓冲意味着用于优先费用和其他计算单位指令的计算单位,这些指令将在以后的操作中加入。
某些指令,如转移 lamports,可能具有较低的计算单位估算。为了确保资源充足,我们添加了一项保护措施,如果估算低于此阈值,则将计算单位设置为最低 1000。
send-transaction.ts
import { // ... getComputeUnitEstimateForTransactionMessageFactory, } from "@solana/web3.js"; /** * 步骤 4:优化计算单位 */ const getComputeUnitEstimateForTransactionMessage = getComputeUnitEstimateForTransactionMessageFactory({ rpc, }); // 请求该消息实际消耗的计算单位的估计。 let computeUnitsEstimate = await getComputeUnitEstimateForTransactionMessage(transactionMessage); computeUnitsEstimate = computeUnitsEstimate < 1000 ? 1000 : Math.ceil(computeUnitsEstimate * 1.1); console.log("将计算单位设置为 ", computeUnitsEstimate); 重建并签署交易
我们现在获得了此次交易所需的优先费用和计算单位。由于交易已经签署,我们不能直接添加新指令。因此,我们将使用新的区块哈希重建整个交易消息。
区块哈希的有效期大约为 1-2 分钟,获取优先费用和计算单位需要一些时间。为了避免在发送交易时丢失区块哈希,重建交易时获取一个新的区块哈希会更安全。
在此重建的交易中,我们将包含两项附加指令:
- 一项设置优先费用的指令;
- 另一项设置计算单位的指令。
最后,我们将签署这一更新的交易以准备提交:
send-transaction.ts
import { // ... appendTransactionMessageInstructions, } from "@solana/web3.js"; import { getSetComputeUnitLimitInstruction, getSetComputeUnitPriceInstruction } from "@solana-program/compute-budget"; /** * 步骤 5:重建并签署最终交易 */ const { value: finalLatestBlockhash } = await rpc.getLatestBlockhash().send(); const finalTransactionMessage = appendTransactionMessageInstructions( [\ getSetComputeUnitPriceInstruction({ microLamports: priorityFee }),\ getSetComputeUnitLimitInstruction({ units: computeUnitsEstimate }),\ ], transactionMessage, ); setTransactionMessageLifetimeUsingBlockhash(finalLatestBlockhash, finalTransactionMessage); const finalSignedTransaction = await signTransactionMessageWithSigners(finalTransactionMessage); console.log("重建交易并签署完成"); 发送并确认交易
接下来,使用 sendAndConfirmTransaction 函数发送并确认签署的交易。
提交的承诺级别设置为已确认,这与之前获取的区块哈希一致,而 maxRetries 设置为 0。skipPreflight 选项设置为 true,跳过 预检 检查以加快执行速度;然而,这只能在你确信交易签名已验证且没有其他错误时使用。
在创建之前的 sendAndConfirmTransaction 时,提供了 RPC 和 RPC 订阅 URL。使用 RPC 订阅 URL 检查交易状态,消除了手动轮询的需要。
在错误处理部分,代码检查预检过程中发生的错误。由于我们将 skipPreflight 设置为 true,因此该检查是多余的。然而,如果你没有将其设置为 true,这将很有用。
send-transaction.ts
import { getSignatureFromTransaction, isSolanaError, SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE, } from "@solana/web3.js"; import { getSystemErrorMessage, isSystemError } from "@solana-program/system"; /** * 步骤 6:发送并确认最终交易 */ console.log("发送并确认交易"); await sendAndConfirmTransaction(finalSignedTransaction, { commitment: "confirmed", maxRetries: 0n, skipPreflight: true, }); console.log("转账确认: ", getSignatureFromTransaction(finalSignedTransaction)); 运行代码
最后,我们可以运行代码:
npx esrun send-transaction.ts

结论
Solana 的 Web3.js 2.0 SDK 的发布是一次变革性的更新,使开发者能够在 Solana 上创造出更快、更高效和可扩展的应用程序。通过采用现代 JavaScript 标准和引入本地加密 API、tree-shakin 和自动生成的 TypeScript 客户端等特性,SDK 显著提升了开发者体验和应用程序性能。
编程示例的完整代码可在 GitHub 上找到。
如果你读到这里,感谢你,匿名者!确保在下面输入你的电子邮件地址,你将永远不会错过有关 Solana 的新动态的更新。准备深入了解吗?探索 Helius 博客 上的最新文章,继续你的 Solana 之旅。
资源
- 现在可用:Solana JavaScript SDK 2.0:Anza Labs
- Anza Web3.js 2.0 文档
- Breakpoint 2023:介绍新 Web3.js
- 介绍 Kite:现代 Solana TypeScript 框架,用于 web3.js 版本 2
- 原文链接: helius.dev/blog/how-to-s...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~