全面解析 Solana Web3.js 2.0 SDK:新特性与实战迁移指南

·

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 没有任何外部依赖项,确保构建过程轻量且安全。

灵活性增强

开发者现在可以通过以下方式创建高度定制的解决方案:

链上程序的新 TypeScript 客户端现已托管在 @solana-program GitHub 组织中。这些客户端使用 Codama IDL 自动生成,使开发者能够快速为自定义程序生成客户端。

实战:使用 Web3.js 2.0 发送交易

下面我们将通过一个实际示例,演示如何使用 Web3.js 2.0 构建一个程序,将 lamport 转移到另一个钱包。此程序将展示提高交易成功率和缩短确认时间的技术。

我们将遵循以下发送交易的最佳实践:

  1. 获取具有确认承诺级别的最新区块哈希
  2. 按照优先费用 API 的建议设置优先费用
  3. 优化计算单元分配
  4. 发送交易时将 maxRetries 设置为 0,skipPreflight 设置为 true

这种方法即使在网络拥塞期间也能确保最佳性能和可靠性。

环境准备

项目初始化与安装

首先创建基本的 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

定义转账地址

在 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
  });
}

👉 获取专业 RPC 连接配置指南

创建转账指令

区块哈希防止交易重复并为交易提供生命周期保障:

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 生态系统中构建出更卓越的产品。