閱讀時間 7 分鐘
概覽
2022 年 10 月 10 日(Epoch 358),Solana 透過一項稱為「版本化交易」(Versioned Transactions)的概念,新增了對新交易版本類型的支援。 此變更後,Solana 執行環境現已支援兩種交易類型:「傳統型」(舊版交易)與「0 型」(包含位址查詢表的交易)。版本化交易讓您即日起即可使用位址查詢表,未來亦可能新增更多功能。
在本指南中,您將建立並執行一個版本 0(V0)交易。
如果您需要協助確保現有的客戶端應用程式能夠支援「版本化交易」,請參閱我們的指南:《如何更新您的 Solana 客戶端以處理版本化交易》。
您需要準備的物品
- 已安裝Node.js(版本 16.15 或更高)
- 已具備 TypeScript 經驗,並已安裝ts-node
- 具備執行基本 Solana 交易的經驗(指南:如何使用 JavaScript 在 Solana 上發送交易)
設定您的專案
請在終端機中使用以下指令建立一個新的專案目錄:
mkdir solana-versioned-tx
cd solana-versioned-tx
為您的應用程式建立一個名為app.ts 的檔案:
echo > app.ts
請使用「yes」參數初始化您的專案,以便為新套件採用預設值:
毛線 初始化 --是
#或
npm 初始化 --yes
建立一個已啟用 .json 匯入功能的tsconfig.json檔案:
tsc -init --resolveJsonModule true
安裝 Solana Web3 依賴項
在這個練習中,我們需要加入 Solana Web3 函式庫。請在終端機中輸入:
紗線 新增 @solana/web3.js@1
#或
npm 安裝 @solana/web3.js@1
我們開始吧。
設定您的應用程式
匯入必要的依賴項
開啟app.ts 檔案,並將以下導入語句貼到第 1 行,以從 Solana Web3 函式庫中取得幾個必要的方法和類別:
import { Connection, Keypair, LAMPORTS_PER_SOL, SystemProgram, TransactionInstruction, TransactionMessage, VersionedTransaction } from '@solana/web3.js';
建立錢包並領取 SOL 空投
我們首先需要完成的任務,是建立一個附帶錢包的帳戶並為其充值。我們將使用下方的便捷工具,自動生成一個新錢包,並向其中空投 1 SOL。(您也可以透過 Keypair.generate() 以及 requestAirdrop() (若您偏好更為手動的方式,也可使用這些函式)。
注意:若過於頻繁地發送空投請求,可能會觸發 429(請求過多)錯誤。
成功產生金鑰對後,您會發現有兩個新的常數: 秘密 以及 SIGNER_WALLET, 一組金鑰對。該 秘密 是一個 32 位元組的陣列,用於產生公鑰和私鑰。該 SIGNER_WALLET 這是一個用於簽署交易的 Keypair 實例(我們已空投了一些開發網 SOL 來支付 gas 費用)。若您尚未將其加入程式碼中,請務必在其他常數下方將其加入。
在您的 `import` 語句下方,貼上新的密鑰,並加入:
const secret = [0,...,0]; // 👈 請替換為您的密鑰
const SIGNER_WALLET = Keypair.fromSecretKey(new Uint8Array(secret));
const 目標錢包 = Keypair.產生();
我們已定義了兩個錢包: SIGNER_WALLET 將把 SOL 發送至我們的 DESTINATION_WALLET. 我們還新增了一個常數, LOOKUP_TABLE_ADDRESS,我們稍後會更新此處,使其引用我們的查詢表中的鏈上帳號 ID。
設定您的 Quicknode 端點
若要在 Solana 上進行開發,您需要一個 API 端點來連線至該網路。您可以使用公開節點,或自行部署並管理基礎架構;不過,若您希望將回應時間縮短 8 倍,不妨將繁重的工作交給我們處理。
了解為何超過 50% 的 Solana 專案都選擇 Quicknode,並在此註冊帳號。我們將使用一個 Solana Devnet 節點。
複製 HTTP 提供者連結:

在app.ts檔案中,於 import 語句下方,宣告您的 RPC 並建立與 Solana 的連線:
const QUICKNODE_RPC = 'https://example.solana-devnet.quiknode.pro/0123456/';
const SOLANA_CONNECTION = new Connection(QUICKNODE_RPC);
您的環境應如下所示:

好啦,讓我們開始打造吧!
建立版本 0 的交易
版本 0 交易要求我們使用一個新類別「VersionedTransaction」,該類別需要我們將「VersionedMessage」作為參數傳入。「VersionedMessage」可接受「Message」或「MessageV0」作為參數。雖然兩者非常相似,但「MessageV0」允許使用位址查詢表!
首先,讓我們建立一個新的轉帳TransactionInstruction,將 0.1 SOL 從我們的SIGNER_WALLET轉帳至DESTINATION_WALLET。由於我們的交易訊息需要一個Transaction Instructions 陣列,請將該指令封裝在陣列中:
const instructions: TransactionInstruction[] = [
SystemProgram.transfer({
fromPubkey: SIGNER_WALLET.publicKey,
toPubkey: DESTINATION_WALLET.publicKey,
lamports: 0.01 * LAMPORTS_PER_SOL,
}),
];
現在,讓我們建立一個新函式 `createAndSendV0Tx`,該函式將接受我們的 `TransactionInstruction` 陣列:
async function createAndSendV0Tx(txInstructions: TransactionInstruction[]) {
}
讓我們定義一個 confirmTransaction 我們將在我們的程式中呼叫的函式 createAndSendV0Tx 函式。請將以下程式碼加入您的 app.ts 檔案:
async function confirmTransaction(
connection: Connection,
signature: TransactionSignature,
desiredConfirmationStatus: TransactionConfirmationStatus = 'confirmed',
timeout: number = 30000,
pollInterval: number = 1000,
searchTransactionHistory: boolean = false
): Promise<SignatureStatus> {
const start = Date.now();
while (Date.now() - start < timeout) {
const { value: statuses } = await connection.getSignatureStatuses([signature], { searchTransactionHistory });
if (!statuses || statuses.length === 0) {
throw new Error('Failed to get signature status');
}
const status = statuses[0];
if (status === null) {
// If status is null, the transaction is not yet known
await new Promise(resolve => setTimeout(resolve, pollInterval));
continue;
}
if (status.err) {
throw new Error(`Transaction failed: ${JSON.stringify(status.err)}`);
}
if (status.confirmationStatus && status.confirmationStatus === desiredConfirmationStatus) {
return status;
}
if (status.confirmationStatus === 'finalized') {
return status;
}
await new Promise(resolve => setTimeout(resolve, pollInterval));
}
throw new Error(`Transaction confirmation timeout after ${timeout}ms`);
}
這僅會持續查詢 Solana 網路以獲取交易狀態,直到交易獲得確認或達到超時為止。我們已為超時時間和查詢間隔設定了一些預設值,但您可以根據需要進行調整。
現在讓我們來組建這個函式。請將這 5 個步驟加入你的createAndSendV0Tx函式的本體中。
步驟 1:使用getLatestBlockhash 從網路取得最新的區塊雜湊值:
// 步驟 1 - 取得最新區塊哈希
let 最新區塊哈希 = await SOLANA_CONNECTION.取得最新區塊哈希('已確認');
console.log(" ✅ - 已取得最新區塊哈希。最後有效高度:", 最新區塊哈希.最後有效區塊高度);
註:我們傳入參數「confirmed」,以降低該哈希值屬於已被捨棄的分叉的可能性。
步驟 2:利用我們的txInstructions參數和latestBlockhash,我們可以透過建立一個新的Message並執行.compileToV0Message()方法,來建立一個新的MessageV0:
// Step 2 - Generate Transaction Message
const messageV0 = new TransactionMessage({
payerKey: SIGNER_WALLET.publicKey,
recentBlockhash: latestBlockhash.blockhash,
instructions: txInstructions
}).compileToV0Message();
console.log(" ✅ - Compiled Transaction Message");
const transaction = new VersionedTransaction(messageV0);
接著,我們將訊息傳入一個新的VersionedTransaction 實例中。恭喜!您已建立了一個版本化交易!
步驟 3:使用一組簽署者陣列對交易進行簽署。在此情況下,僅有我們的SIGNER_WALLET。
// 步驟 3 - 使用所需的 `Signers` 對交易進行簽名
交易。簽署([簽署者錢包]);
控制台.log(" ✅ - 交易已簽署");
注意:將 VersionedTransaction 傳送至叢集時,必須在呼叫 sendAndConfirmTransaction 方法之前完成簽名。來源: edge.docs.solana.com。
步驟 4:使用sendTransaction 將交易傳送至叢集,此操作將回傳一筆交易的簽名/ID:
// Step 4 - Send our v0 transaction to the cluster
const txid = await SOLANA_CONNECTION.sendTransaction(transaction, { maxRetries: 5 });
console.log(" ✅ - Transaction sent to network");
注意:我們傳遞參數「maxRetries」,以便 RPC 在必要時能重新嘗試將交易傳送給領導者。
步驟 5:最後,請等待叢集確認交易已成功:
// Step 5 - Confirm Transaction
const confirmation = await confirmTransaction(SOLANA_CONNECTION, txid);
if (confirmation.err) { throw new Error(" ❌ - Transaction not confirmed.") }
console.log('🎉 Transaction Succesfully Confirmed!', '\n', `https://explorer.solana.com/tx/${txid}?cluster=devnet`);
若成功,我們會記錄該成功交易的區塊鏈瀏覽器網址。
好啦!我們準備好要試試看啦!
執行您的程式碼
要執行此函式,請呼叫檔案底部的createAndSendV0Tx,並將指令作為參數傳入:
createAndSendV0Tx(操作說明);
我們的最終程式碼已上傳至 GitHub,請點此處查看。
接著,在您的終端機中執行以下指令:
ts-node app.ts
你看到類似這樣的東西嗎?

做得很好!如果您想將您的程式碼與我們的進行比對,我們已將程式碼上傳至GitHub,請點此處。
下一步
既然您已經能夠建立並執行版本化交易,現在是時候來了解 V0 交易能帶來什麼功能了。請參閱我們的指南:《如何在 Solana 上使用查詢表》(即將推出),以進一步了解 Solana 的新查詢表功能及其使用方法。
如果你遇到困難、有疑問,或是單純想聊聊工作上的事,歡迎在Discord或Twitter 上聯絡我們!
我們 ❤️ 您的回饋!
如果您對本指南有任何意見或疑問,請告訴我們。我們非常樂意聽取您的意見。
