{"content":{"title":"如何在Solana上使用版本化交易","body":"## 概述 \r\n\r\n在 2022 年 10 月 10 日（[Epoch 358](https://explorer.solana.com/epoch/358)），Solana 通过一个称为“ [版本化交易](https://edge.docs.solana.com/developing/versioned-transactions)”的概念，添加了对新交易版本类型的支持。在此更改之后，Solana 运行时现在支持两种类型的交易：“遗留”（旧交易）和“0”（包含 [地址查找表](https://edge.docs.solana.com/developing/lookup-tables) 的交易）。版本化交易将允许你今天使用地址查找表，并在未来可能具备其他功能。\r\n\r\n在本指南中，你将创建并执行版本 0 (V0) 交易。\r\n\r\n_如果你需要帮助确保你现有的客户端应用程序可以支持版本化交易，请查看我们的_ [_指南：如何更新你的 Solana 客户端以处理版本化交易_](https://www.quicknode.com/guides/solana-development/how-to-update-your-solana-client-to-handle-versioned-transactions)。\r\n\r\n#### 你将需要什么 \r\n\r\n- 安装 [Nodejs](https://nodejs.org/en/)（版本 16.15 或更高）\r\n- 具备 Typescript 经验，并安装 [ts-node](https://www.npmjs.com/package/ts-node)\r\n- 具备运行基本 Solana 交易的经验（[指南：如何使用 Javascript 在 Solana 上发送交易](https://learnblockchain.cn/article/11161)）\r\n\r\n## 设置你的项目 \r\n\r\n在终端中创建一个新的项目目录：\r\n\r\n```\r\nmkdir solana-versioned-tx\r\ncd solana-versioned-tx\r\n\r\n```\r\n\r\n为你的应用创建一个文件，**app.ts**：\r\n\r\n```\r\necho > app.ts\r\n\r\n```\r\n\r\n使用“yes”标志初始化你的项目，以使用新包的默认值：\r\n\r\n```\r\nyarn init --yes\r\n#或\r\nnpm init --yes\r\n\r\n```\r\n\r\n创建一个启用了 .json 导入的 **tsconfig.json**：\r\n\r\n```\r\ntsc -init --resolveJsonModule true\r\n\r\n```\r\n\r\n#### 安装 Solana Web3 依赖项 \r\n\r\n我们需要添加 Solana Web3 库以用于本练习。在终端中输入：\r\n\r\n```\r\nyarn add @solana/web3.js@1\r\n#或\r\nnpm install @solana/web3.js@1\r\n\r\n```\r\n\r\n让我们开始吧。\r\n\r\n## 设置你的应用 \r\n\r\n#### 导入必要的依赖项 \r\n\r\n打开 **app.ts**，在_第1行_ 粘贴以下导入，以获取 Solana Web3 库中的一些基本方法和类：\r\n\r\n```\r\nimport { Connection, Keypair, LAMPORTS_PER_SOL, SystemProgram, TransactionInstruction, TransactionMessage, VersionedTransaction } from '@solana/web3.js';\r\n\r\n```\r\n\r\n简化调试的日志\r\n\r\n你现在可以访问 RPC 端点的日志，帮助你更有效地排除问题。如果你在 RPC 调用中遇到问题，只需查看 QuickNode 仪表板上的日志以快速识别和解决问题。了解有关日志历史限制的更多信息，请访问 [我们的定价页面。](https://www.quicknode.com/pricing#features)\r\n\r\n#### 创建钱包并空投 SOL \r\n\r\n我们需要完成的第一项任务是创建一个带有钱包的账户并为其提供资金。我们将使用下面的实用工具自动生成一个新钱包并空投 1 SOL 到其上。（如果你更喜欢手动方式，你也可以使用 `Keypair.generate()` 和 `requestAirdrop()` 函数来实现）。\r\n\r\n🔑使用 Devnet SOL 生成新钱包\r\n\r\n创建新钱包\r\n\r\n一旦你成功生成了密钥对，你将注意到两个新常量：`secret` 和 `SIGNER_WALLET`，这是一个 Keypair。`secret` 是一个 32 字节数组，用于生成公钥和私钥。`SIGNER_WALLET` 是一个 Keypair 实例，用于签署交易（我们已空投一些 devnet SOL 以支付 gas 费用）。如果你还没有将其添加到代码中，请确保将其放在其他常量下方。\r\n\r\n在按导入内容粘贴新密钥后，添加：\r\n\r\n```\r\nconst secret = [0,...,0]; // 👈 用你的密钥替换\r\nconst SIGNER_WALLET = Keypair.fromSecretKey(new Uint8Array(secret));\r\nconst DESTINATION_WALLET = Keypair.generate();\r\n\r\n```\r\n\r\n我们定义了两个钱包：`SIGNER_WALLET` 将向我们的 `DESTINATION_WALLET` 发送 SOL。我们还添加了一个常量 `LOOKUP_TABLE_ADDRESS`，稍后我们将更新它以引用地址查找表的链上账户 ID。\r\n\r\n#### 设置你的 QuickNode 端点 \r\n\r\n要在 Solana 上进行构建，你需要一个 API 端点来连接网络。你可以使用公共节点或部署和管理自己的基础架构；但是，如果你想要 8 倍的响应速度，你可以将繁重的工作交给我们。\r\n\r\nQuickNode 现在接受 Solana 付款 🚀\r\n\r\n你现在可以 **使用 USDC 在 Solana 上支付 QuickNode 计划**。作为第一个接受 Solana 付款的多链供应商，我们为开发人员简化了过程——无论你是在创建新帐户还是管理现有帐户。[在这里了解有关使用 Solana 付款的更多信息](https://blog.quicknode.com/quicknode-now-accepts-solana-payments/)。\r\n\r\n了解为什么超过 50% 的 Solana 项目选择 QuickNode，并在 [这里](https://www.quicknode.com/signup?utm_source=internal&utm_campaign=guides&utm_content=how-to-use-versioned-transactions-on-solana) 注册一个免费账户。我们将使用 Solana Devnet 节点。\r\n\r\n复制 HTTP 供应商链接：\r\n\r\n![](https://img.learnblockchain.cn/2025/02/24/0-32e2e904682695d2fd322a17df638d05.png)\r\n\r\n在 **app.ts** 的导入语句下，声明你的 RPC 并建立与 Solana 的 _连接_：\r\n\r\n```\r\nconst QUICKNODE_RPC = 'https://example.solana-devnet.quiknode.pro/0123456/';\r\nconst SOLANA_CONNECTION = new Connection(QUICKNODE_RPC);\r\n\r\n```\r\n\r\n你的环境看起来应该是这样的：\r\n\r\n![](https://img.learnblockchain.cn/2025/02/24/1-e3d8f61d036c26938bbb8ec9bb5b4654.png)\r\n\r\n好了，让我们构建吧！\r\n\r\n## 创建版本 0 交易 \r\n\r\n版本 0 交易要求我们使用一个新类 _VersionedTransaction_，它需要我们传递一个 _VersionedMessage_ 作为参数。_VersionedMessage_ 接受一个 _Message_ 或 _MessageV0_。虽然非常相似，但 _MessageV0_ 允许使用地址查找表！\r\n\r\n让我们开始创建一个新的转账 _TransactionInstruction_，将 0.1 SOL 从我们的 **SIGNER_WALLET** 发送到我们的 **DESTINATION_WALLET**。由于我们的交易消息需要一个 _Transaction Instructions_ 的数组，请将指令包含在数组中：\r\n\r\n```\r\nconst instructions: TransactionInstruction[] = [\\\r\n    SystemProgram.transfer({\\\r\n        fromPubkey: SIGNER_WALLET.publicKey,\\\r\n        toPubkey: DESTINATION_WALLET.publicKey,\\\r\n        lamports: 0.01 * LAMPORTS_PER_SOL,\\\r\n    }),\\\r\n];\r\n\r\n```\r\n\r\n现在让我们构建一个新的函数 **createAndSendV0Tx**，接受我们的 _TransactionInstruction_ 数组：\r\n\r\n```\r\nasync function createAndSendV0Tx(txInstructions: TransactionInstruction[]) {\r\n\r\n}\r\n\r\n```\r\n\r\n接下来，我们定义一个 `confirmTransaction` 函数，我们将其在我们的 **createAndSendV0Tx** 函数中调用。将以下代码添加到 **app.ts** 文件中：\r\n\r\n```\r\nasync function confirmTransaction(\r\n    connection: Connection,\r\n    signature: TransactionSignature,\r\n    desiredConfirmationStatus: TransactionConfirmationStatus = 'confirmed',\r\n    timeout: number = 30000,\r\n    pollInterval: number = 1000,\r\n    searchTransactionHistory: boolean = false\r\n): Promise<SignatureStatus> {\r\n    const start = Date.now();\r\n\r\n    while (Date.now() - start < timeout) {\r\n        const { value: statuses } = await connection.getSignatureStatuses([signature], { searchTransactionHistory });\r\n\r\n        if (!statuses || statuses.length === 0) {\r\n            throw new Error('无法获得签名状态');\r\n        }\r\n\r\n        const status = statuses[0];\r\n\r\n        if (status === null) {\r\n            // 如果状态为 null，交易尚未被认可\r\n            await new Promise(resolve => setTimeout(resolve, pollInterval));\r\n            continue;\r\n        }\r\n\r\n        if (status.err) {\r\n            throw new Error(`交易失败: ${JSON.stringify(status.err)}`);\r\n        }\r\n\r\n        if (status.confirmationStatus && status.confirmationStatus === desiredConfirmationStatus) {\r\n            return status;\r\n        }\r\n\r\n        if (status.confirmationStatus === 'finalized') {\r\n            return status;\r\n        }\r\n\r\n        await new Promise(resolve => setTimeout(resolve, pollInterval));\r\n    }\r\n\r\n    throw new Error(`交易确认超时，超过 ${timeout}ms`);\r\n}\r\n\r\n```\r\n\r\n这将不断轮询 Solana 网络，直到交易状态被确认或达到超时。我们添加了一些超时和轮询间隔的默认值，但你可以根据需要进行调整。\r\n\r\n现在让我们组装我们的函数。将这 5 个步骤添加到你的 **createAndSendV0Tx** 函数的主体中。\r\n\r\n**步骤 1：** 使用 **getLatestBlockhash** 从网络中获取最新的区块哈希：\r\n\r\n```\r\n    // 步骤 1 - 获取最新区块哈希\r\n    let latestBlockhash = await SOLANA_CONNECTION.getLatestBlockhash('confirmed');\r\n    console.log(\"   ✅ - 获取最新区块哈希。最后有效高度:\", latestBlockhash.lastValidBlockHeight);\r\n\r\n```\r\n\r\n注意：我们传递了参数 'confirmed'，以使哈希不太可能属于已丢弃的分支。\r\n\r\n**步骤 2：** 使用我们的 **txInstructions** 参数和 **latestBlockhash**，我们可以通过构造一个新的 _Message_ 并执行 _.compileToV0Message()_ 方法来创建一个新的 _MessageV0_：\r\n\r\n```\r\n    // 步骤 2 - 生成交易消息\r\n    const messageV0 = new TransactionMessage({\r\n        payerKey: SIGNER_WALLET.publicKey,\r\n        recentBlockhash: latestBlockhash.blockhash,\r\n        instructions: txInstructions\r\n    }).compileToV0Message();\r\n    console.log(\"   ✅ - 编译交易消息\");\r\n    const transaction = new VersionedTransaction(messageV0);\r\n\r\n```\r\n\r\n接着将我们的消息传递给 _VersionedTransaction_ 的新实例。恭喜！你已创建了一个版本化交易！ \r\n\r\n**步骤 3：** 使用所需的 `Signers` 数组签署交易。在这种情况下，仅使用我们的 **SIGNER_WALLET**。\r\n\r\n```\r\n    // 步骤 3 - 用所需的 `Signers` 签署你的交易\r\n    transaction.sign([SIGNER_WALLET]);\r\n    console.log(\"   ✅ - 交易已签署\");\r\n\r\n```\r\n\r\n> _注意：在将版本化交易发送到集群之前，必须先签署它。来源：_ [_edge.docs.solana.com_](https://edge.docs.solana.com/developing/lookup-tables#more-resources) _。_\r\n\r\n**步骤 4：** 使用 **sendTransaction** 将交易发送到集群，这将返回一个交易签名/ID：\r\n\r\n```\r\n    // 步骤 4 - 将我们的 v0 交易发送到集群\r\n    const txid = await SOLANA_CONNECTION.sendTransaction(transaction, { maxRetries: 5 });\r\n    console.log(\"   ✅ - 交易已发送到网络\");\r\n\r\n```\r\n\r\n注意：我们传递了参数 'maxRetries'，以允许 RPC 在必要时重试将交易发送给领导者。\r\n\r\n**步骤 5：** 最后，等待集群确认交易已成功：\r\n\r\n```\r\n    // 步骤 5 - 确认交易\r\n    const confirmation = await confirmTransaction(SOLANA_CONNECTION, txid);\r\n    if (confirmation.err) { throw new Error(\"   ❌ - 交易未确认.\") }\r\n    console.log('🎉 交易成功确认！', '\\n', `https://explorer.solana.com/tx/${txid}?cluster=devnet`);\r\n\r\n```\r\n\r\n如果成功，我们记录成功交易的查询 URL。\r\n\r\n好的！我们准备好尝试了！\r\n\r\n## 运行你的代码 \r\n\r\n要执行我们的函数，在文件底部调用 **createAndSendV0Tx**，并将 **instructions** 作为参数传递：\r\n\r\n```\r\ncreateAndSendV0Tx(instructions);\r\n\r\n```\r\n\r\n我们的最终代码可在 GitHub 上找到，[在这里](https://github.com/quiknode-labs/qn-guide-examples/blob/main/solana/versioned-tx/versionedTest.ts)。\r\n\r\n然后，在终端中调用：\r\n\r\n```\r\nts-node app.ts\r\n\r\n```\r\n\r\n你是否看到类似的内容？\r\n\r\n![](https://img.learnblockchain.cn/2025/02/24/2-1fa2fb23c46d29fe1ac29e18dce09bbb.png)\r\n\r\n干得好！如果你想将你的代码与我们的代码进行对比，我们已在 [GitHub， 这里](https://github.com/quiknode-labs/qn-guide-examples/blob/main/solana/versioned-tx/versionedTest.ts) 提供。\r\n\r\n## 下一步 \r\n\r\n现在你可以构建并执行版本化交易，接下来是看看 V0 交易能做些什么。请查看我们的指南：如何在 Solana 上使用查找表（即将推出），以了解 Solana 新的查找表功能及其使用方法。\r\n\r\n如果你遇到问题，或者有疑问，或者只是想讨论一下，欢迎通过 [Discord](https://discord.gg/quicknode) 或 [Twitter](https://twitter.com/QuickNode) 联系我们！\r\n\r\n \r\n\r\n>- 原文链接： [quicknode.com/guides/sol...](https://www.quicknode.com/guides/solana-development/transactions/how-to-use-versioned-transactions-on-solana)\r\n>- 登链社区 AI 助手，为大家转译优秀英文文章，如有翻译不通的地方，还请包涵～"},"author":{"user":"https://learnblockchain.cn/people/25306","address":null},"history":null,"timestamp":1740450082,"version":1}