交易自省

摘要

@solana/transaction-introspectiongetTransaction RPC 响应转换为 一个包含真实地址的指令列表。它解析账户索引 (包括查找表地址),解码内层 CPI 指令,并按 区块浏览器的顺序遍历完整的指令树。

想了解程序如何在运行时 链上 检查同级指令? 那是 Instructions sysvar——请参阅 指令 自省。本页 介绍的是如何从 RPC 响应中 离链 解码交易。

getTransaction 响应并不会直接提供可用的指令列表。账户以数字索引的形式存储, 版本化交易 将这些账户拆分为静态密钥和从 地址查找表 加载的地址,而来自 Cross Program Invocation(CPI)的内层指令则以编码数据块的形式存储在交易元数据中。

@solana/transaction-introspection 为您完成此解析工作。它解码链上交易,将每个账户索引映射回其对应的 Address,规范化内层指令,并按区块浏览器所展示的外层优先、内层次之的顺序返回指令。返回的指令可直接用于 codama 生成的客户端(例如 @solana-program/*)的 parseXInstruction 辅助函数中。

Terminal
npm install @solana/transaction-introspection @solana/kit @solana/instructions @solana-program/token

解码响应

以 wire 格式编码获取交易,并将响应传递给 decodeTransactionFromRpcResponse

Decode a transaction
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);

后续代码片段将复用此步骤中的 rpcTxcompiledMessageloadedAddresses

请使用 base64base58json 编码进行获取。 decodeTransactionFromRpcResponse 会拒绝 jsonParsed 响应——该 编码要求 RPC 节点在服务端解析指令,因此 客户端没有任何内容可供解码。

遍历指令

walkInstructions 以数组形式返回每条指令——包括外层指令和内层指令——其顺序与区块浏览器显示的一致:每条外层指令后紧跟其 CPI 所产生的内层指令。每条指令都带有 trace 字段,用于描述其在调用层次结构中的位置。

Walk every instruction
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 是一个可判别联合类型:顶层指令对应 { kind: "outer", index },嵌套在外层指令下的 CPI 指令对应 { kind: "inner", outerIndex, innerIndex, stackHeight? }

meta 是可选的——即使其值可能为 null,也可以直接传入 rpcTx.meta。若没有元数据,walkInstructions 将仅返回外层指令。

使用程序客户端进行解析

来自 walkInstructions 的每条指令都是 ResolvedInstruction 类型,因此可直接与 @solana/instructions 中的谓词函数以及由 @solana-program/* 客户端生成的 identifyXInstruction / parseXInstruction 辅助函数配合使用,无需额外的转换步骤。

以下示例审计一笔交易中所有 Token Program SyncNative 指令,无论其运行在顶层还是 CPI 内部。

Find and parse SyncNative instructions
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 指令。
  • getAccountMetasFromCompiledTransactionMessage(compiledMessage, loadedAddresses?) 按交易顺序返回完整的账户列表——即前一个辅助函数解析内层指令账户索引所需的输入。
Resolve outer and inner instructions separately
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 中获取已解析的指令,请从 getTransaction 请求 UiTransactionEncoding::JsonParsed,由 RPC 节点为您解析账户和内层指令。

另请参阅

Is this page helpful?

Table of Contents

Edit Page
©️ 2026 Solana 基金会版权所有