
如何使用 Solana Web3.js 2.0 SDK 开始构建
在深入探讨之前,我们要感谢 Evan 和 Nick 审阅本文。非常感谢他们提供的宝贵反馈和见解。
简介
Solana Web3.js SDK 是一个强大的 TypeScript 和 JavaScript 库,可用于在 Node.js、Web 和 React Native 平台上构建 Solana 应用。2024 年 11 月 7 日,Anza 推出了备受期待的 2.0 SDK 更新,带来了一系列现代 JavaScript 特性和改进。主要亮点包括为 bigint 和加密功能采用标准 JS 类型,以及缩小 bundle 体积,对开发者而言是一次重大升级。
如果你一直在使用 @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. 性能改进
更快的加密操作:借助 Node.js 和现代浏览器等现代 JavaScript 环境中的原生加密 API,密钥对生成、交易签名和消息验证速度最高可提升 10 倍。
2. 更小、更高效的应用
Web3.js 2.0 完全支持 tree shaking,因此你可以只引入实际使用的库内容,尽可能缩小 bundle 体积。此外,新 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 驼峰命名法。
私钥现在称为 privateKey,可通过 keyPairSigner.privateKey 访问。在 web3.js v2 中,通常应在 web3.js v1 使用 secretKey 的位置改用 KeyPairSigner。
地址/公钥
web3.js v1 中使用 PublicKey 的地方,在 web3.js v2 中只需使用地址。例如,KeyPairSigner 具有 keypairSigner.address 属性,即其公钥。你可以使用 address 函数将字符串形式的公钥转换为地址。
SOL 和代币数量
数量使用 JS 原生的 BigInt 类型。因此,你需要在数字末尾添加 n,即将一个 1 写成 1n。
工厂
许多功能均可配置,因此新版不再提供预设实现(例如 doThing()),而是提供一个工厂(名为 doThingFactory()),让你可以创建自己的 doThing() 函数。例如:
- 要发送并确认交易,可以使用首选选项运行一次
sendAndConfirmTransactionFactory(),并获得一个自定义的sendAndConfirmTransaction()函数。之后每次需要发送和确认交易时,都可以使用该sendAndConfirmTransaction()。 - 要在 devnet 或 localnet 上获取空投,可以运行一次
airdropFactory(),并获得一个自定义的airdrop()函数,之后需要空投时即可调用。
如何使用 Web3.js 2.0 发送交易
Helius 最近发布了 Kite,一个面向 web3.js v2 的 TypeScript 框架,其中为大多数常见 Solana 任务提供了一次调用即可完成的函数。
我们将使用 Web3.js 2.0 构建一个客户端程序,把 lamport 转账到另一个钱包。该程序将演示如何提高交易成功率并缩短确认时间。
发送交易时,我们将遵循以下最佳实践:
- 使用 confirmed 承诺级别获取最新区块哈希
- 按照 Helius Priority Fee API 的建议设置优先费
- 优化计算单元
- 发送交易时将 maxRetries 设为 0,并将 skipPreflight 设为 true
即使出现网络拥堵,这种方法也能确保最佳性能和可靠性。
前提条件
- 安装 Node.js
- 使用兼容的 IDE(例如 VS Code 或 Cursor)
安装
首先创建一个基础 Node.js 项目,为应用搭建结构。
运行以下命令创建一个 package.json 文件,用于管理依赖项和项目元数据:
npm init -y创建 src 目录,并在其中添加用于存放主要代码的 index.ts 文件:
mkdir src
touch src/index.ts接下来,使用 npm 安装使用 Solana Web3.js 2.0 SDK 所需的依赖项:
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 中,定义 lamport 转账的源地址和目标地址。我们将使用 address() 函数,根据提供的字符串生成目标公钥。
对于源地址,我们将使用其 secretKey 派生 KeyPair。
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 控制面板中找到——只需注册或登录,然后前往“端点”部分。
sendAndConfirmTransactionFactory 函数会构建一个可复用的交易发送器。该发送器需要 RPC 连接来发送交易,并需要 RPC 订阅来监控交易状态。
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,
});创建转账指令
加入最近的区块哈希可以防止重复,并为交易设定有效期——每笔交易都必须包含有效的区块哈希,才能被接受并执行。对于这笔交易,我们将使用 confirmed 承诺级别获取最新区块哈希。
接下来,我们将使用 getTransferSolInstruction() 创建系统程序提供的预定义转账指令。这需要指定金额、源地址
和目标地址。源地址必须始终是 Signer,而目标地址应为公共地址。
import {
// ...
lamports,
} from "@solana/web3.js";
import { getTransferSolInstruction } from "@solana-program/system";
/**
* STEP 1: CREATE THE TRANSFER TRANSACTION
*/
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const instruction = getTransferSolInstruction({
amount: lamports(1n),
destination: destinationAddress,
source: sourceKeypair,
});
创建交易消息
接下来创建交易消息。现在所有交易消息都能感知版本,无需再处理不同类型(例如 Transaction 与 VersionedTransaction)。
我们会将源地址设为费用支付方,加入区块哈希,并添加 lamport 转账指令。
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("Transaction message created");
pipe 函数常用于函数式编程,它会创建一个函数序列,其中一个函数的输出会成为下一个函数的输入。这里,它通过设置费用支付方和有效期、添加指令等转换,逐步构建交易消息。
初始化交易消息:
createTransactionMessage({ version: 0 }) 从一条基础交易消息开始。
设置费用支付方:
message => setTransactionMessageFeePayer(fromKeypair.address, message) 添加费用支付方的地址。
使用区块哈希设置有效期
message => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, message) 使用最新区块哈希,确保交易在指定时间范围内有效。
添加转账指令
message => appendTransactionMessageInstruction(instruction, message) 将操作(例如转账 lamport)附加到消息中。
每个箭头函数 message => (...) 都会修改消息,并将更新后的消息传递到下一步,最终生成一条完整构建的新交易消息。
签署交易
我们将使用指定的签名器,即源 Keypair,签署交易。
import {
// ...
signTransactionMessageWithSigners,
} from "@solana/web3.js";
// ...
/**
* STEP 2: SIGN THE TRANSACTION
*/
const signedTransaction = await signTransactionMessageWithSigners(transactionMessage);
console.log("Transaction signed");
估算优先费
此时可以继续发送并确认交易。不过,我们还应通过设置优先费和调整计算单元来优化交易。这些优化有助于提高交易成功率并缩短确认时间,尤其是在网络拥堵期间。
为了设置优先费,我们将使用 Helius 的 Priority Fee API。它要求提供 Base64 格式的序列化交易。虽然该 API 也支持 Base58 编码,但当前 SDK 可以直接提供 Base64 格式的交易,从而简化流程。
import {
// ...
getBase64EncodedWireTransaction,
} from "@solana/web3.js";
/**
* STEP 3: GET PRIORITY FEE FROM SIGNED TRANSACTION
*/
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("Setting priority fee to ", priorityFee);
通常,将 priorityLevel 设为 High 就足够了。不过,采用高级优先费策略,例如使用序列化交易和账户密钥,可以在网络拥堵期间显著提高交易成功率。
优化计算单元
接下来,我们将估算交易消息实际消耗的计算单元。
然后将该值乘以 1.1,增加 10% 的缓冲。这个缓冲会涵盖优先费和其他计算单元指令所使用的计算单元,我们稍后会添加这些内容。
某些指令(例如 lamport 转账)的计算单元估算值可能较低。为了确保资源充足,我们添加了一项保护措施:如果估算值低于 1000,则将计算单元设为至少 1000。
import {
// ...
getComputeUnitEstimateForTransactionMessageFactory,
} from "@solana/web3.js";
/**
* STEP 4: OPTIMIZE COMPUTE UNITS
*/
const getComputeUnitEstimateForTransactionMessage = getComputeUnitEstimateForTransactionMessageFactory({
rpc,
});
// Request an estimate of the actual compute units this message will consume.
let computeUnitsEstimate = await getComputeUnitEstimateForTransactionMessage(transactionMessage);
computeUnitsEstimate = computeUnitsEstimate < 1000 ? 1000 : Math.ceil(computeUnitsEstimate * 1.1);
console.log("Setting compute units to ", computeUnitsEstimate);
重新构建并签署交易
现在,我们已经获得了这笔交易所需的优先费和计算单元。由于交易已经签署,无法直接添加新指令。因此,我们将使用新的区块哈希重新构建整条交易消息。
区块哈希的有效期只有约 1 至 2 分钟,而获取优先费和计算单元需要一些时间。为了避免发送交易时区块哈希过期,重新构建交易时获取一个新的区块哈希会更安全。
在重新构建的交易中,我们将添加两条额外指令:
- 一条用于设置优先费的指令;
- 另一条用于设置计算单元的指令
最后,我们将签署这笔更新后的交易,为提交做好准备:
import {
// ...
appendTransactionMessageInstructions,
} from "@solana/web3.js";
import { getSetComputeUnitLimitInstruction, getSetComputeUnitPriceInstruction } from "@solana-program/compute-budget";
/**
* STEP 5: REBUILD AND SIGN FINAL TRANSACTION
*/
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("Rebuilt the transaction and signed it");
发送并确认交易
接下来,使用 sendAndConfirmTransaction 函数发送并确认已签署的交易。
承诺级别设为 confirmed,与之前获取区块哈希时保持一致,同时将 maxRetries 设为 0。skipPreflight 选项设为 true,从而绕过预检以加快执行速度;不过,只有在你确定交易签名已经过验证且不存在其他错误时,才应使用此设置。
之前已通过提供 RPC 和 RPC 订阅 URL 创建 sendAndConfirmTransaction。使用 RPC 订阅 URL 可以检查交易状态,无需手动轮询。
在错误处理部分,代码会检查预检期间发生的错误。由于我们已将 skipPreflight 设为 true,因此这项检查是多余的。不过,如果你没有将其设为 true,这项检查会很有帮助。
import {
getSignatureFromTransaction,
isSolanaError,
SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE,
} from "@solana/web3.js";
import { getSystemErrorMessage, isSystemError } from "@solana-program/system";
/**
* STEP 6: SEND AND CONFIRM THE FINAL TRANSACTION
*/
console.log("Sending and confirming transaction");
await sendAndConfirmTransaction(finalSignedTransaction, {
commitment: "confirmed",
maxRetries: 0n,
skipPreflight: true,
});
console.log("Transfer confirmed: ", getSignatureFromTransaction(finalSignedTransaction));
运行代码
最后,可以运行代码:
npx esrun send-transaction.ts
总结
Solana Web3.js 2.0 SDK 的发布是一项变革性更新,让开发者能够在 Solana 上创建速度更快、效率更高且更具扩展性的应用。通过采用现代 JavaScript 标准,并引入原生加密 API、tree shaking 和自动生成的 TypeScript 客户端等功能,该 SDK 显著改善了开发者体验和应用性能。
编程示例的完整代码可在 GitHub 上查看。
如果你读到了这里,感谢你,anon!请务必在下方输入邮箱地址,以免错过 Solana 的最新动态。准备好深入探索了吗?立即阅读 Helius 博客上的最新文章,继续你的 Solana 之旅。
资源
相关文章
订阅 Helius
及时了解 Solana 开发的最新动态,并在我们发布新内容时收到更新


