Краткое содержание
@solana/transaction-introspection преобразует ответ RPC getTransaction в
список инструкций с реальными адресами. Он разрешает индексы аккаунтов
(включая адреса таблиц подстановки), декодирует внутренние инструкции CPI и
обходит полное дерево инструкций в порядке, принятом в обозревателях блоков.
Ищете информацию о том, как программа инспектирует смежные инструкции on-chain во время выполнения? Это сисвар Instructions — см. Интроспекция инструкций. Данная страница посвящена декодированию транзакции off-chain из ответа RPC.
Ответ getTransaction не предоставляет готовый
список инструкций. Аккаунты хранятся как числовые индексы,
версионированные транзакции
разделяют эти аккаунты на статические ключи и адреса, загруженные из
таблиц подстановки адресов, а
внутренние инструкции из Cross Program Invocation (CPI) поступают в виде
закодированных блобов в метаданных транзакции.
@solana/transaction-introspection выполняет это разрешение за вас. Он
декодирует транзакцию в проводном формате, сопоставляет каждый индекс аккаунта с
его Address, нормализует внутренние инструкции и возвращает инструкции в том
же порядке «внешние, затем внутренние», который используется в обозревателях
блоков. Возвращаемые инструкции напрямую подключаются к хелперам
parseXInstruction клиентов, сгенерированных с помощью codama (например,
@solana-program/*).
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-ноде разбирать инструкции на стороне сервера,
поэтому на клиенте ничего не остаётся для декодирования.
Обход инструкций
walkInstructions возвращает каждую инструкцию — внешнюю и внутреннюю — в виде
массива в том порядке, в котором их отображает блок-эксплорер: каждая внешняя
инструкция, за которой сразу следуют внутренние инструкции, порождённые её CPI.
Каждая инструкция содержит поле 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 — размеченное объединение: { kind: "outer", index } для инструкции
верхнего уровня или { kind: "inner", outerIndex, innerIndex, stackHeight? }
для инструкции CPI, вложенной во внешнюю.
meta является необязательным — передавайте rpcTx.meta напрямую, даже если
оно может быть null. Без мета walkInstructions возвращает только внешние
инструкции.
Разбор с помощью клиента программы
Каждая инструкция из walkInstructions является ResolvedInstruction, поэтому
она работает напрямую с предикатами из @solana/instructions и вспомогательными
функциями identifyXInstruction / parseXInstruction, сгенерированными
клиентами @solana-program/* — без шага преобразования.
В этом примере выполняется аудит каждой инструкции Token Program SyncNative в
транзакции — независимо от того, выполнялась ли она на верхнем уровне или внутри
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-инструкции из метаданных транзакции.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, запросите UiTransactionEncoding::JsonParsed у
getTransaction и позвольте RPC-ноде
разрешить аккаунты и внутренние инструкции за вас.
Смотрите также
- Структура транзакции — анатомия ответа, который декодирует этот пакет
- Версионные транзакции — как таблицы поиска адресов разделяют аккаунты на статические и загружаемые группы
getTransaction— метод RPC, возвращающий транзакцию
Is this page helpful?