{"author":{"address":null,"user":"https://learnblockchain.cn/people/23919"},"content":{"body":"\u003c!--StartFragment--\u003e\r\n\r\n## **介绍**\r\n\r\nSolana [Web3.js SDK]是一个功能强大的 JavaScript 库，可用于在 Node.js、Web 和 React Native 平台上构建 Solana 应用程序。2024 年 11 月 7 日，Anza Labs 推出了备受期待的 Web3.js 2.0 SDK，引入了一系列现代 JavaScript 功能和改进。主要亮点包括可摇树和减小的包大小，这对开发人员来说是一项重大升级。 \r\n\r\n如果您编写了自动安装**@solana/web3.js**的开发人员内容或 shell 脚本，则需要在两个选项之间做出选择：将您的软件移植到新的 v2.0 API 或通过[明确指定版本]将其锁定到v1.x。 \r\n\r\n在本文中，我们将探讨 Web3.js 2.0 SDK 中的最新更新，指导您完成迁移过程，并提供示例来帮助您入门。 \r\n\r\n在深入研究之前，我们假设您对 Solana 的基本概念（例如发送交易、[账户模型]、区块哈希和[优先级费用]）有扎实的理解，并且具有 JavaScript 经验。虽然建议熟悉 Web3.js SDK 的先前版本，但这不是强制性的。让我们开始吧\r\n\u003c!--EndFragment--\u003e\r\n\u003c!--StartFragment--\u003e\r\n\r\n## **Web3.js 2.0 有什么新功能？**\r\n\r\n我们将快速探索新的 Web3.js 2.0 SDK 提供的功能： \r\n\r\n### **1.性能改进**\r\n\r\n**更快的加密操作**：利用 Node.js 和 Safari 17 等现代 JavaScript 环境中的原生 Ed25519 加密 API，  密钥对生成、交易签名和消息验证速度提高*10 倍*\r\n\r\n### **2.更小、更高效的应用程序**\r\n\r\nWeb3.js 2.0 完全可进行 tree-shak 优化，允许您仅包含您使用的库部分，从而最大限度地减少包大小。此外，新 SDK 没有任何外部依赖项，可确保轻量级且安全的构建。\r\n\r\n### **3. 增强灵活性**\r\n\r\n开发人员现在可以通过以下方式创建自定义解决方案：\r\n\r\n* 使用自定义方法定义 RPC 实例  \r\n* 使用专门的网络传输或交易签名者  \r\n* 为网络、交易确认和编解码器编写自定义原语  \r\n\r\n\r\n\r\n链上程序的新 TypeScript 客户端现在托管在[**@solana-program** GitHub 组织]中。这些客户端使用 Codama IDL 自动生成，使开发人员能够快速生成自定义程序的客户端。  \r\n\r\n## **如何使用 Web3.js 2.0 发送交易**\r\n\r\n我们将使用 Web3.js 2.0 构建一个程序，将 lamport 转移到另一个钱包。该程序将展示提高交易成功率和缩短确认时间的技术。 \r\n\r\n我们将遵循以下发送交易的最佳做法： \r\n\r\n1. **获取具有确认**承诺级别的  最新区块哈希\r\n2. 按照 Helius[优先费用 API的建议设置优先费用]\r\n3. 优化计算单元  \r\n4. 发送交易，将**maxRetries**设置为**0**，将**skipPreflight**设置为**true**  \r\n\r\n即使在网络拥塞期间，这种方法也能确保最佳性能和可靠性。  \r\n\r\n### **先决条件**\r\n\r\n* 安装[Node.js] \r\n* 兼容的 IDE（例如 VS Code）  \r\n\r\n### **安装**\r\n\r\n首先创建一个基本的 Node.js 项目来构建您的应用程序。运行以下命令创建一个*package.json*文件来管理您的依赖项和项目元数据：\r\n\u003c!--EndFragment--\u003e\r\n\r\n```\r\nnpm init -y\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n创建一个*src*目录，并在其中添加一个*index.js*文件，其中包含主代码：\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n```\r\nmkdir src  \r\ntouch src/index.js\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n接下来，使用*npm*安装使用 Solana 的 Web3.js 2.0 SDK 所需的依赖项：\r\n\r\n```\r\n@solana/web3.js@2  \u0026\u0026 npm install @solana-program/system  \u0026\u0026 npm install @solana-program/compute-budget\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n* **@solana/web3.js**：Solana Web3.js 2.0 SDK 对于构建和管理 Solana 交易至关重要\r\n* **@solana-program/system**：提供对 Solana 系统程序的访问，从而实现诸如 lamport 传输之类的操作\r\n* **@solana-program/compute-budget**：用于设置优先费用和优化交易的计算单元\r\n\r\n\u003c!--EndFragment--\u003e\r\n\u003c!--StartFragment--\u003e\r\n\r\n### **定义转移地址**\r\n\r\n*在index.js*中，我们定义传输 lampor 的源地址和目标地址。我们将使用**address()**函数从提供的字符串生成目标公钥。对于源，我们将使用它的**secretKey派生****KeyPair**。\r\n\u003c!--EndFragment--\u003e\r\n\r\n```\r\nimport { address, createKeyPairSignerFromBytes, getBase58Encoder } from '@solana/web3.js';\r\n\r\nasync function main() {\r\n    const destinationAddress = address('public-key-to-send-lamports-to');\r\n    const secretKey = \"add-your-private-key\";\r\n    const sourceKeypair = await createKeyPairSignerFromBytes(\r\n        getBase58Encoder().encode(secretKey)\r\n    );\r\n}\r\nmain();\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n### **配置 RPC 连接**\r\n\r\n接下来，我们可以设置相关的 RPC 连接。createSolanaRpc函数使用默认 HTTP 传输与 RPC 服务器建立通信，这对于大多数用例来说已经足够了。同样，我们使用createSolanaRpcSubscriptions建立 WebSocket 连接。您可以在[Helius Dashboard中找到]rpc_url和wss_url —只需注册或登录并导航到 Endpoints 部分即可。\r\n\r\n**sendAndConfirmTransactionFactory**函数构建可重复使用的交易发送方。此发送方需要 RPC 连接来发送交易，还需要 RPC 订阅来监控交易状态。\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n```\r\nimport { \r\n\t// ...\r\ncreateSolanaRpcSubscriptions, \r\ncreateSolanaRpc,\r\n\tsendAndConfirmTransactionFactory\r\n } from  '@solana/web3.js';\r\n\r\nasync function main() {\r\n   // ...\r\n\r\n    const rpc_url = \"https://mainnet.helius-rpc.com/?api-key=\u003cyour-key\u003e\";\r\n    const wss_url = \"wss://mainnet.helius-rpc.com/?api-key=\u003cyour-key\u003e\";\r\n\r\n    const rpc = createSolanaRpc(rpc_url);\r\n    const rpcSubscriptions = createSolanaRpcSubscriptions(wss_url);\r\n\r\n    const sendAndConfirmTransaction = sendAndConfirmTransactionFactory({\r\n        rpc,\r\n        rpcSubscriptions\r\n    });\r\n}\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n### **创建转账指令**\r\n\r\n区块哈希可防止重复，并为交易提供生命周期——每笔交易都必须包含有效的区块哈希才能被接受执行。对于此交易，我们将使用**已确认的**承诺级别获取最新的区块哈希。\r\n\r\n接下来，我们将使用**getTransferSolInstruction**函数创建系统程序提供的预定义转账指令。这需要指定金额、来源和目的地。来源必须始终是 Signer **，**而目的地应该是公共地址。\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n```\r\nimport {\r\n    // ...\r\n    lamports,\r\n} from '@solana/web3.js';\r\nimport { getTransferSolInstruction } from '@solana-program/system';\r\n\r\nasync function main() {\r\n    // ...\r\n\r\n    /**\r\n     * STEP 1: CREATE THE TRANSFER TRANSACTION\r\n     */\r\n    const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();\r\n    \r\n    const instruction = getTransferSolInstruction({\r\n        amount: lamports(1),\r\n        destination: toPubkey,\r\n        source: fromKeypair,\r\n    });\r\n}\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n### **创建交易信息**\r\n\r\n然后我们将创建交易消息。现在所有交易消息都具有版本感知能力，无需处理不同类型（例如**Transaction**与**VersionedTransaction**）。 \r\n\r\n我们将把来源设置为费用支付者，包括区块哈希，并添加转移 lamport 的指令。\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n```\r\nimport {\r\n    // ...\r\n    pipe,\r\n    createTransactionMessage,\r\n    setTransactionMessageFeePayer,\r\n    setTransactionMessageLifetimeUsingBlockhash,\r\n    appendTransactionMessageInstruction,\r\n} from '@solana/web3.js';\r\n\r\nasync function main() {\r\n    // ...\r\n\r\n    const transactionMessage = pipe(\r\n        createTransactionMessage({ version: 0 }),\r\n        tx =\u003e (\r\n            setTransactionMessageFeePayer(fromKeypair.address, tx)\r\n        ),\r\n        tx =\u003e (\r\n            setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx)\r\n        ),\r\n        tx =\u003e\r\n        appendTransactionMessageInstruction(\r\n            instruction,\r\n            tx,\r\n        ),\r\n    );\r\n    console.log(\"Transaction message created\");\r\n}\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n*pipe*函数常用于函数式编程，它创建一系列函数，其中一个函数的输出将成为下一个函数的输入。在这里，它逐步构建交易消息，应用设置费用支付者和有效期等转换并添加指令。\r\n\r\n1. **初始化交易消息：\\\r\n   **`createTransactionMessage({ version: 0 })`从基本交易消息开始。\\\r\n   \\\r\n\r\n2. **设置费用支付人：\\\r\n   **`tx =\u003e setTransactionMessageFeePayer(fromKeypair.address, tx)`添加费用支付人的地址。\\\r\n   \\\r\n\r\n3. **使用区块哈希设置生命周期：\\\r\n   **`tx =\u003e setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx)`使用最新的区块哈希确保交易在一定时间范围内有效。\\\r\n   \\\r\n\r\n4. **添加转移指令：\\\r\n   **`tx =\u003e appendTransactionMessageInstruction(instruction, tx)`将操作（例如，转移灯塔）附加到消息中。\r\n\r\n每个箭头函数*tx =\u003e (...)*修改并将更新的消息传递到下一步，生成一个完全构建的新交易消息。\r\n\r\n\u003c!--EndFragment--\u003e\r\n\u003c!--StartFragment--\u003e\r\n\r\n### **签署交易**\r\n\r\n**我们将使用指定的签名者，即源密钥对**，对交易进行签名。\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n```\r\nimport {\r\n  // ...\r\n  signTransactionMessageWithSigners\r\n} from '@solana/web3.js';\r\n\r\nasync function sendTransaction() {\r\n    // ...\r\n\r\n    /**\r\n     * STEP 2: SIGN THE TRANSACTION\r\n     */\r\n    const signedTransaction = await signTransactionMessageWithSigners(transactionMessage);\r\n    console.log(\"Transaction signed\");\r\n}\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n### **估算优先费用**\r\n\r\n在此步骤中，我们可以继续发送和确认交易。但是，我们应该通过设置优先级费用和调整计算单位来优化交易。这些优化有助于提高交易成功率并减少确认时间，尤其是在网络拥堵期间。\r\n\r\n要设置优先费用，我们将使用[Helius 的优先费用 API 。这需要]**Base64**格式的序列化交易。虽然 API 也支持**Base58编码，但当前 SDK 直接提供****Base64**格式的交易，从而简化了流程。\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n```\r\nimport {\r\n    // ...\r\n    getBase64EncodedWireTransaction\r\n} from '@solana/web3.js';\r\n\r\nasync function sendTransaction() {\r\n    // ...\r\n\r\n    /**\r\n     * STEP 3: GET PRIORITY FEE FROM SIGNED TRANSACTION\r\n     */\r\n\r\n    const base64EncodedWireTransaction = getBase64EncodedWireTransaction(signedTransaction);\r\n\r\n    const response = await fetch(rpc_url, {\r\n        method: 'POST',\r\n        headers: { 'Content-Type': 'application/json' },\r\n        body: JSON.stringify({\r\n            jsonrpc: '2.0',\r\n            id: 'helius-example',\r\n            method: 'getPriorityFeeEstimate',\r\n            params: [{\r\n                transaction: base64EncodedWireTransaction,\r\n                options: { \r\n                    transactionEncoding: \"base64\",\r\n                    recommended: true,\r\n                 }\r\n            }]\r\n        }),\r\n    });\r\n    const { result } = await response.json();\r\n    const priorityFee = result.priorityFeeEstimate;\r\n    console.log(\"Setting priority fee to \", priorityFee);\r\n}\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n通常情况下，设置建议的优先费用就足够了。但是，实施[高级优先费用策略]可以显著提高网络拥堵期间的交易成功率。\r\n\r\n### **优化计算单元**\r\n\r\n接下来，我们将估算交易消息实际消耗的计算单元。然后，我们将该值乘以 1.1，添加 10% 的缓冲区。此缓冲区占优先费用和额外计算单元指令使用的计算单元，我们将在稍后纳入这些计算单元。 \r\n\r\n某些指令（例如传输 Lamport）的计算单元估计值可能较低。为了确保有足够的资源，我们添加了一项保护措施，如果估计值低于此阈值，则将计算单元设置为最低 1000 个。\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n```\r\nimport {\r\n    // ...\r\n    getComputeUnitEstimateForTransactionMessageFactory\r\n} from '@solana/web3.js';\r\n\r\nasync function sendTransaction() {\r\n    // ...\r\n\r\n    /** \r\n     * STEP 4: OPTIMIZE COMPUTE UNITS\r\n     */\r\n     const getComputeUnitEstimateForTransactionMessage = getComputeUnitEstimateForTransactionMessageFactory({\r\n        rpc\r\n    });\r\n    // Request an estimate of the actual compute units this message will consume.\r\n    let computeUnitsEstimate = await getComputeUnitEstimateForTransactionMessage(transactionMessage);\r\n    computeUnitsEstimate = (computeUnitsEstimate \u003c 1000) ? 1000 : Math.ceil(computeUnitsEstimate * 1.1);\r\n    console.log(\"Setting compute units to \", computeUnitsEstimate);\r\n}\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n### **重建并签署交易**\r\n\r\n我们现在有了这笔交易所需的优先费用和计算单元。由于交易已经签名，我们无法直接添加新指令。相反，我们将使用新的区块哈希重建整个交易消息。 \r\n\r\n区块哈希的有效期只有 1-2 分钟左右，获取优先费用和计算单位需要一些时间。为了避免在发送交易时区块哈希过期的风险，在重建交易时获取新的区块哈希更为安全。\r\n\r\n在此重建的交易中，我们将包括两个附加指令： \r\n\r\n1. 一项设定优先费用的指令；\r\n2. 设置计算单元的另一条指令 \r\n\r\n最后，我们将签署此更新的交易以准备提交：\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n```\r\nimport {\r\n    // ...\r\n    appendTransactionMessageInstructions,\r\n} from '@solana/web3.js';\r\nimport { getSetComputeUnitLimitInstruction, getSetComputeUnitPriceInstruction } from '@solana-program/compute-budget';\r\n\r\nasync function sendTransaction() {\r\n    // ...\r\n\r\n    /**\r\n     * STEP 5: REBUILD AND SIGN FINAL TRANSACTION\r\n     */\r\n        const { value: finalLatestBlockhash } = await rpc.getLatestBlockhash().send();\r\n\r\n    const finalTransactionMessage = appendTransactionMessageInstructions(\r\n        [  \r\n            getSetComputeUnitPriceInstruction({ microLamports: priorityFee }), \r\n            getSetComputeUnitLimitInstruction({ units: computeUnitsEstimate }) \r\n        ],\r\n        transactionMessage,\r\n    );\r\n\r\n    setTransactionMessageLifetimeUsingBlockhash(finalLatestBlockhash, finalTransactionMessage);\r\n\r\n    const finalSignedTransaction = await signTransactionMessageWithSigners(finalTransactionMessage);\r\n    console.log(\"Rebuilt the transaction and signed it\");\r\n}\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n### **发送并确认交易**\r\n\r\n**接下来，使用sendAndConfirmTransaction**函数发送并确认已签名的交易。**承诺**级别设置为**confirmed**，与之前获取的区块哈希一致，而**maxRetries**设置为**0**。**skipPreflight**选项设置为**true**，绕过[预检检查]以加快执行速度；但是，仅当您确信您的交易签名已得到验证并且没有其他错误时才应使用此选项。\r\n\r\nsendAndConfirmTransaction之前是通过提供 RPC 和**RPC**订阅 URL 创建的。使用 RPC 订阅 URL 检查交易状态，无需手动轮询。\r\n\r\n在错误处理部分，代码会检查预检期间发生的错误。由于我们将**skipPreflight**设置为 true，因此此检查是多余的。但是，如果您不将其设置为 true，它将很有帮助。\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n```\r\nimport {\r\n    getSignatureFromTransaction,\r\n    isSolanaError,\r\n    SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE\r\n} from '@solana/web3.js';\r\nimport { getSystemErrorMessage, isSystemError } from '@solana-program/system';\r\n\r\nasync function sendTransaction() {\r\n    // ...\r\n\r\n    /**\r\n     * STEP 6: SEND AND CONFIRM THE FINAL TRANSACTION\r\n     */\r\n    try {\r\n        console.log(\"Sending and confirming transaction\");\r\n        await sendAndConfirmTransaction(finalSignedTransaction, { commitment: 'confirmed', maxRetries: 0, skipPreflight: true});\r\n        console.log('Transfer confirmed: ', getSignatureFromTransaction(finalSignedTransaction));\r\n    } catch (e) {\r\n        if (isSolanaError(e, SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE)) {\r\n            const preflightErrorContext = e.context;\r\n            const preflightErrorMessage = e.message;\r\n            const errorDetailMessage = isSystemError(e.cause, finalTransactionMessage) ?\r\n                getSystemErrorMessage(e.cause.context.code) : e.cause ? e.cause.message : '';\r\n            console.error(preflightErrorContext, '%s: %s', preflightErrorMessage, errorDetailMessage);\r\n        } else {\r\n            throw e;\r\n        }\r\n    }\r\n}\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n### **运行代码**\r\n\r\n最后我们可以运行代码：\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n![6749cebedd494bec4c5a1954_AD_4nXf7Rkp2bj2ol5EzOAbW_t-jFj8_I_fUEK4EuIsylRfQPb3AJ3OEeKd3gY1UKbdb4xIcyMGwjtkl8lPc4t65_iGwDeCytdE7nY5dujmTtFsXXVfkESwTVg29A7ExFzEQTd8XJAA2pw.png](https://img.learnblockchain.cn/attachments/2024/12/MLkk8afZ6757b9c9d3f63.png)\r\n\u003c!--StartFragment--\u003e\r\n\r\n## **结论**\r\n\r\nSolana 的 Web3.js 2.0 SDK 的发布是一项变革性更新，它使开发人员能够在 Solana 上创建更快、更高效、可扩展的应用程序。通过采用现代 JavaScript 标准并引入本机加密 API、tree-shakability 和自动生成的 TypeScript 客户端等功能，SDK 显著增强了开发人员体验和应用程序性能。\r\n\r\n\u003c!--EndFragment--\u003e\r\n作者：https://t.me/+P3Z7P_xQxbNlZWZl\r\n来源：https://www.fabipingtai.com","title":"如何开始使用 Solana Web3.js 2.0 SDK 进行构建"},"history":null,"timestamp":1733802538,"version":1}