Solana Web3.js SDK 是一个功能强大的 JavaScript 库,可用于在 Node.js、Web 和 React Native 平台上构建 Solana 应用程序。近期推出的 Web3.js 2.0 SDK 引入了一系列现代 JavaScript 功能和改进,包括可摇树优化和减小的包大小,为开发者带来了显著的升级体验。
如果您此前已在使用 @solana/web3.js 的旧版本,现在面临两个选择:将项目迁移到新的 v2.0 API,或通过指定版本号将其锁定到 v1.x。本文将带您探索 Web3.js 2.0 SDK 的最新更新,指导您完成迁移过程,并提供实用示例帮助您快速上手。
在开始之前,建议您对 Solana 的基本概念(如发送交易、账户模型、区块哈希和优先级费用)有扎实的理解,并具备一定的 JavaScript 经验。虽然熟悉 Web3.js SDK 的先前版本会有帮助,但这并非强制要求。
Web3.js 2.0 的新特性
性能大幅提升
新版本利用现代 JavaScript 环境(如 Node.js 和 Safari 17)中的原生 Ed25519 加密 API,使密钥对生成、交易签名和消息验证速度提高了近 10 倍。
应用更小更高效
Web3.js 2.0 完全支持 tree-shaking 优化,允许您仅包含使用的库部分,从而显著减少包大小。此外,新 SDK 没有任何外部依赖项,确保构建过程轻量且安全。
灵活性增强
开发者现在可以通过以下方式创建高度定制的解决方案:
- 使用自定义方法定义 RPC 实例
- 采用专门的网络传输或交易签名者
- 为网络、交易确认和编解码器编写自定义原语
链上程序的新 TypeScript 客户端现已托管在 @solana-program GitHub 组织中。这些客户端使用 Codama IDL 自动生成,使开发者能够快速为自定义程序生成客户端。
实战:使用 Web3.js 2.0 发送交易
下面我们将通过一个实际示例,演示如何使用 Web3.js 2.0 构建一个程序,将 lamport 转移到另一个钱包。此程序将展示提高交易成功率和缩短确认时间的技术。
我们将遵循以下发送交易的最佳实践:
- 获取具有确认承诺级别的最新区块哈希
- 按照优先费用 API 的建议设置优先费用
- 优化计算单元分配
- 发送交易时将 maxRetries 设置为 0,skipPreflight 设置为 true
这种方法即使在网络拥塞期间也能确保最佳性能和可靠性。
环境准备
- 安装 Node.js 运行环境
- 准备兼容的 IDE(如 VS Code)
项目初始化与安装
首先创建基本的 Node.js 项目来构建应用程序。运行以下命令创建 package.json 文件:
npm init -y
创建 src 目录并在其中添加 index.js 文件作为主代码文件:
mkdir src
touch src/index.js
接下来安装必要的依赖项:
npm install @solana/web3.js@2
npm install @solana-program/system
npm install @solana-program/compute-budget
- @solana/web3.js:Solana Web3.js 2.0 SDK,用于构建和管理 Solana 交易
- @solana-program/system:提供对 Solana 系统程序的访问,实现 lamport 传输等操作
- @solana-program/compute-budget:用于设置优先费用和优化交易计算单元
定义转账地址
在 index.js 中,我们定义传输 lamport 的源地址和目标地址:
import { address, createKeyPairSignerFromBytes, getBase58Encoder } from '@solana/web3.js';
async function main() {
const destinationAddress = address('接收方公钥');
const secretKey = "您的私钥";
const sourceKeypair = await createKeyPairSignerFromBytes(
getBase58Encoder().encode(secretKey)
);
}
main();
配置 RPC 连接
设置 RPC 连接是连接 Solana 网络的关键步骤:
import {
createSolanaRpcSubscriptions,
createSolanaRpc,
sendAndConfirmTransactionFactory
} from '@solana/web3.js';
async function main() {
// ...
const rpc_url = "您的 RPC 端点 URL";
const wss_url = "您的 WebSocket 端点 URL";
const rpc = createSolanaRpc(rpc_url);
const rpcSubscriptions = createSolanaRpcSubscriptions(wss_url);
const sendAndConfirmTransaction = sendAndConfirmTransactionFactory({
rpc,
rpcSubscriptions
});
}
创建转账指令
区块哈希防止交易重复并为交易提供生命周期保障:
import { lamports } from '@solana/web3.js';
import { getTransferSolInstruction } from '@solana-program/system';
async function main() {
// ...
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const instruction = getTransferSolInstruction({
amount: lamports(1),
destination: destinationAddress,
source: sourceKeypair,
});
}
构建交易消息
使用函数式编程方式构建交易消息:
import {
pipe,
createTransactionMessage,
setTransactionMessageFeePayer,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstruction,
} from '@solana/web3.js';
async function main() {
// ...
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
tx => setTransactionMessageFeePayer(sourceKeypair.address, tx),
tx => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
tx => appendTransactionMessageInstruction(instruction, tx)
);
console.log("交易消息创建成功");
}
签署交易
使用指定签名者对交易进行签名:
import { signTransactionMessageWithSigners } from '@solana/web3.js';
async function sendTransaction() {
// ...
const signedTransaction = await signTransactionMessageWithSigners(transactionMessage);
console.log("交易签名完成");
}
估算优先费用
通过 API 获取推荐的优先费用:
import { getBase64EncodedWireTransaction } from '@solana/web3.js';
async function sendTransaction() {
// ...
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",
recommended: true,
}
}]
}),
});
const { result } = await response.json();
const priorityFee = result.priorityFeeEstimate;
console.log("设置优先费用为: ", priorityFee);
}
优化计算单元
估算并优化交易所需的计算单元:
import { getComputeUnitEstimateForTransactionMessageFactory } from '@solana/web3.js';
async function sendTransaction() {
// ...
const getComputeUnitEstimateForTransactionMessage = getComputeUnitEstimateForTransactionMessageFactory({ rpc });
let computeUnitsEstimate = await getComputeUnitEstimateForTransactionMessage(transactionMessage);
computeUnitsEstimate = (computeUnitsEstimate < 1000) ? 1000 : Math.ceil(computeUnitsEstimate * 1.1);
console.log("设置计算单元为: ", computeUnitsEstimate);
}
重建并签署最终交易
使用新的区块哈希和优化参数重建交易:
import { appendTransactionMessageInstructions } from '@solana/web3.js';
import { getSetComputeUnitLimitInstruction, getSetComputeUnitPriceInstruction } from '@solana-program/compute-budget';
async function sendTransaction() {
// ...
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("交易重建并签名完成");
}
发送并确认交易
最后发送并确认交易:
import {
getSignatureFromTransaction,
isSolanaError,
SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE
} from '@solana/web3.js';
import { getSystemErrorMessage, isSystemError } from '@solana-program/system';
async function sendTransaction() {
// ...
try {
console.log("发送并确认交易");
await sendAndConfirmTransaction(finalSignedTransaction, {
commitment: 'confirmed',
maxRetries: 0,
skipPreflight: true
});
console.log('转账确认: ', getSignatureFromTransaction(finalSignedTransaction));
} catch (e) {
if (isSolanaError(e, SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE)) {
const preflightErrorContext = e.context;
const preflightErrorMessage = e.message;
const errorDetailMessage = isSystemError(e.cause, finalTransactionMessage) ?
getSystemErrorMessage(e.cause.context.code) : e.cause ? e.cause.message : '';
console.error(preflightErrorContext, '%s: %s', preflightErrorMessage, errorDetailMessage);
} else {
throw e;
}
}
}
运行完整代码即可完成整个交易流程。
常见问题
Web3.js 2.0 与旧版本的主要区别是什么?
Web3.js 2.0 引入了多项重大改进,包括完全支持 tree-shaking 优化、移除外部依赖、性能提升近 10 倍的原生加密操作,以及更灵活的自定义解决方案构建能力。这些改进使得应用程序更小、更快、更安全。
迁移到 Web3.js 2.0 需要注意哪些关键点?
迁移时需要特别注意 API 的变化,特别是交易构建和签名方式的改变。建议逐步迁移,先在小规模项目中测试,确保所有功能正常工作后再全面部署。同时,注意新的错误处理机制和类型定义变化。
如何优化交易成功率?
提高交易成功率的关键措施包括:使用最新的区块哈希、设置适当的优先费用、优化计算单元分配,以及在网络拥堵时采用合理的重试策略。通过这些方法,即使在高峰时段也能保持较高的交易确认率。
Web3.js 2.0 支持哪些开发环境?
Web3.js 2.0 支持 Node.js、Web 浏览器和 React Native 平台,提供了统一的开发体验。无论是在服务器端还是客户端,都能使用相同的 API 和开发模式进行构建。
如何处理交易失败的情况?
交易失败时,首先检查错误信息中的具体原因。常见的失败原因包括余额不足、区块哈希过期、计算单元不足或网络拥堵。根据错误类型采取相应措施,如增加优先费用、更新区块哈希或调整计算单元分配。
总结
Solana 的 Web3.js 2.0 SDK 是一次重大升级,为开发者提供了更高效、灵活的开发工具。通过采用现代 JavaScript 标准并引入原生加密 API、tree-shaking 支持和自动生成的 TypeScript 客户端等功能,SDK 显著提升了开发体验和应用程序性能。
无论是构建新的去中心化应用还是迁移现有项目,Web3.js 2.0 都提供了强大的功能支持和性能优化。掌握其核心特性和最佳实践,将帮助您在 Solana 生态系统中构建出更卓越的产品。