概要
@solana/transaction-introspectionは、getTransaction RPCレスポンスを
実際のアドレスを持つinstructionsのリストに変換します。アカウントインデックス
(ルックアップテーブルアドレスを含む)を解決し、内部CPI
instructionsをデコードし、
エクスプローラーの順序でinstruction全体のツリーをたどります。
プログラムが実行時に_オンチェーン_で兄弟instructionsを検査する方法をお探しですか? それはInstructions sysvarです。Instruction Introspectionをご覧ください。このページは RPCレスポンスから_オフチェーン_でトランザクションをデコードすることについて説明しています。
getTransactionレスポンスは、すぐに使えるinstructionsのリストを提供しません。アカウントは数値インデックスとして保存され、バージョン管理されたトランザクションはそれらのアカウントを静的キーとアドレスルックアップテーブルから読み込まれたアドレスに分割し、Cross
Program
Invocation(CPI)からの内部instructionsはトランザクションメタデータ内にエンコードされたブロブとして届きます。
@solana/transaction-introspectionはこの解決処理を代わりに行います。ワイヤートランザクションをデコードし、すべてのアカウントインデックスをそれぞれのAddressにマッピングし、内部instructionsを正規化して、ブロックエクスプローラーが表示するのと同じ外部から内部への順序でinstructionsを返します。返されたinstructionsは、codamaが生成したクライアント(例:@solana-program/*)のparseXInstructionヘルパーに直接接続できます。
npm install @solana/transaction-introspection @solana/kit @solana/instructions @solana-program/token
レスポンスのデコード
ワイヤーフォーマットエンコーディングでトランザクションをフェッチし、レスポンスをdecodeTransactionFromRpcResponseに渡します。
import { createSolanaRpc, signature } from "@solana/kit";import { decodeTransactionFromRpcResponse } from "@solana/transaction-introspection";const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");const txid ="3jUKrQp1UGq5ih6FTDUUt2kkqUfoG2o4kY5T1DoVHK2tXXDLdxJSXzuJGY4JPoRivgbi45U2bc7LZfMa6C4R3szX";const rpcTx = await rpc.getTransaction(signature(txid), {commitment: "confirmed",encoding: "base64",maxSupportedTransactionVersion: 0}).send();if (!rpcTx) throw new Error(`Transaction ${txid} not found`);const { compiledMessage, loadedAddresses } =decodeTransactionFromRpcResponse(rpcTx);
以降のスニペットでは、このステップのrpcTx、compiledMessage、およびloadedAddressesを再利用します。
base64、base58、またはjsonエンコーディングでフェッチしてください。
decodeTransactionFromRpcResponseはjsonParsedレスポンスを拒否します。そのエンコーディングは
RPCノードにサーバー側でinstructionsを解析させるため、クライアント側でデコードするものが
残りません。
instructionsを反復処理する
walkInstructions
は、ブロックエクスプローラーが表示する順序(各アウター命令の直後にそのCPIが生成したインナー命令が続く形)で、すべてのinstruction(アウターおよびインナー)を配列として返します。各instructionには、コール階層内での位置を示す
trace フィールドが含まれています。
import { walkInstructions } from "@solana/transaction-introspection";for (const ix of walkInstructions({compiledMessage,loadedAddresses,meta: rpcTx.meta})) {const location =ix.trace.kind === "outer"? `outer[${ix.trace.index}]`: `inner[${ix.trace.outerIndex}/${ix.trace.innerIndex}]`;console.log(location, ix.programAddress, ix.accounts?.length ?? 0);}
trace は直和型です:トップレベルのinstructionの場合は
{ kind: "outer", index }、アウター命令の下にネストされたCPI
instructionの場合は { kind: "inner", outerIndex, innerIndex, stackHeight? }
となります。
meta はオプションです — null の可能性がある場合でも、rpcTx.meta
を直接渡してください。メタデータがない場合、walkInstructions
はアウター命令のみを返します。
プログラムクライアントでパースする
walkInstructions から得られる各instructionは ResolvedInstruction
であるため、@solana/instructions の述語や、@solana-program/*
クライアントが生成する identifyXInstruction / parseXInstruction
ヘルパーと変換ステップなしに直接連携できます。
この例では、トランザクション内のすべての Token Program の SyncNative
instruction(トップレベルで実行されたものか、CPI内で実行されたものかを問わず)を監査します。
import {isInstructionForProgram,isInstructionWithAccounts,isInstructionWithData} from "@solana/instructions";import { walkInstructions } from "@solana/transaction-introspection";import {identifyTokenInstruction,parseSyncNativeInstruction,TOKEN_PROGRAM_ADDRESS,TokenInstruction} from "@solana-program/token";for (const ix of walkInstructions({compiledMessage,loadedAddresses,meta: rpcTx.meta})) {if (!isInstructionForProgram(ix, TOKEN_PROGRAM_ADDRESS)) continue;if (!isInstructionWithData(ix) || !isInstructionWithAccounts(ix)) continue;if (identifyTokenInstruction(ix) !== TokenInstruction.SyncNative) continue;const parsed = parseSyncNativeInstruction(ix);console.log(ix.trace, parsed);}
低レベルヘルパー
walkInstructions
はほとんどのユースケースをカバーしていますが、このパッケージはその内部で使用されている構成要素も公開しています:
getInstructionsFromCompiledTransactionMessage(compiledMessage, loadedAddresses?)アウター命令のみを、解決済みアカウントおよびデコード済みデータとともに返します。getInnerInstructionsFromMeta(meta, accountMetas)はトランザクションメタデータからインナーCPI instructionsをデコードします。getAccountMetasFromCompiledTransactionMessage(compiledMessage, loadedAddresses?)トランザクション順でアカウントの完全なリストを返します。これは、前のヘルパーがインナー命令のアカウントインデックスを解決するために必要な入力です。
import {getAccountMetasFromCompiledTransactionMessage,getInnerInstructionsFromMeta,getInstructionsFromCompiledTransactionMessage} from "@solana/transaction-introspection";const outer = getInstructionsFromCompiledTransactionMessage(compiledMessage,loadedAddresses);const accountMetas = getAccountMetasFromCompiledTransactionMessage(compiledMessage,loadedAddresses);if (!rpcTx.meta) throw new Error("Transaction metadata missing");const inner = getInnerInstructionsFromMeta(rpcTx.meta, accountMetas);
Rustをお使いですか?
このパッケージに相当するRust版は存在しません。RustでパースされたinstructionsをRustで取得するには、getTransaction
から UiTransactionEncoding::JsonParsed
をリクエストし、RPCノードにアカウントとインナー命令の解決を任せてください。
関連情報
- トランザクション構造 - このパッケージがデコードするレスポンスの構造
- バージョン管理されたトランザクション - アドレスルックアップテーブルがアカウントを静的グループと読み込みグループに分割する仕組み
getTransaction- トランザクションを返すRPCメソッド
Is this page helpful?