요약
@solana/transaction-introspection는 getTransaction RPC 응답을 실제 주소가
포함된 명령어 목록으로 변환합니다. 계정 인덱스(조회 테이블 주소 포함)를
확인하고, 내부 Cross Program Invocation 명령어를 디코딩하며, 탐색기 순서에
따라 전체 명령어 트리를 순회합니다.
런타임에 _온체인_에서 프로그램이 형제 명령어를 검사하는 방법을 찾고 계신가요? 그것은 Instructions sysvar입니다 - 명령어 분석을 참조하세요. 이 페이지는 RPC 응답으로부터 _오프체인_에서 트랜잭션을 디코딩하는 것에 관한 내용입니다.
getTransaction 응답은 바로 사용할 수 있는
명령어 목록을 제공하지 않습니다. 계정은 숫자 인덱스로 저장되며,
버전 트랜잭션은 해당 계정을
정적 키와 주소 조회 테이블에서
로드된 주소로 분리하고, cross-program invocations(CPIs)에서 발생한 내부 명령어는
트랜잭션 메타데이터 내에 인코딩된 블롭 형태로 전달됩니다.
@solana/transaction-introspection가 이 확인 작업을 대신 수행합니다. 와이어
트랜잭션을 디코딩하고, 모든 계정 인덱스를 해당 Address에 매핑하며, 내부
명령어를 정규화하고, 블록 탐색기와 동일한 외부-내부 순서로 명령어를 반환합니다.
반환된 명령어는 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 노드에서 서버 측으로 명령어를 파싱하도록 요청하므로,
클라이언트에서 디코딩할 내용이 남아 있지 않습니다.
명령어 순회
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 },
외부 명령어 아래에 중첩된 CPI 명령어의 경우
{ kind: "inner", outerIndex, innerIndex, stackHeight? }입니다.
meta는 선택 사항입니다. null일 수 있는 경우에도 rpcTx.meta를 직접
전달하세요. 메타 정보가 없으면 walkInstructions는 외부 명령어만 반환합니다.
프로그램 클라이언트로 파싱하기
walkInstructions에서 반환된 각 명령어는 ResolvedInstruction이므로,
@solana/instructions의 조건자(predicates)와 @solana-program/* 클라이언트가
생성한 identifyXInstruction / parseXInstruction 헬퍼에서 별도의 변환 과정
없이 직접 사용할 수 있습니다.
이 예제는 트랜잭션 내의 모든 Token Program SyncNative 명령어를
감사(audit)합니다. 최상위 레벨에서 실행되었는지 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에서 파싱된 명령어를 가져오려면
getTransaction에서
UiTransactionEncoding::JsonParsed를 요청하여 RPC 노드가 계정과 내부 명령어를
대신 확인하도록 하세요.
참고 항목
- 트랜잭션 구조 - 이 패키지가 디코딩하는 응답의 구조
- 버전된 트랜잭션 - 주소 조회 테이블이 계정을 정적 그룹과 로드된 그룹으로 분리하는 방법
getTransaction- 트랜잭션을 반환하는 RPC 메서드
Is this page helpful?