Skip to content

feat(transaction-introspection): add txn introspection package - #1611

Merged
mcintyre94 merged 20 commits into
anza-xyz:mainfrom
amilz:feat/add-transaction-introspection
Jun 25, 2026
Merged

feat(transaction-introspection): add txn introspection package#1611
mcintyre94 merged 20 commits into
anza-xyz:mainfrom
amilz:feat/add-transaction-introspection

Conversation

@amilz

@amilz amilz commented May 8, 2026

Copy link
Copy Markdown
Contributor

For consideration/discussion.

Adds @solana/transaction-introspection, a new package that bridges a getTransaction response and the auto-generated @solana-program/* parseXInstruction clients. Decodes responses encoded as base64, base58, or json; resolves account indices against static and ALT-loaded addresses; normalizes inner instructions from meta.innerInstructions; and exposes walkInstructions for streaming traversal of every instruction (outer and inner). Re-exported from @solana/kit.

Also hoists the inline GetTransactionApi response shapes in @solana/rpc-api into named exported types (Base64/Base58/Json/JsonParsed) so the new package can consume them directly.

@changeset-bot

changeset-bot Bot commented May 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 92c66d6

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 48 packages
Name Type
@solana/transaction-introspection Minor
@solana/errors Minor
@solana/rpc-api Minor
@solana/kit Minor
@solana/accounts Minor
@solana/addresses Minor
@solana/assertions Minor
@solana/codecs-core Minor
@solana/codecs-data-structures Minor
@solana/codecs-numbers Minor
@solana/codecs-strings Minor
@solana/compat Minor
@solana/fixed-points Minor
@solana/instruction-plans Minor
@solana/instructions Minor
@solana/keys Minor
@solana/offchain-messages Minor
@solana/options Minor
@solana/program-client-core Minor
@solana/programs Minor
@solana/react Minor
@solana/rpc-spec Minor
@solana/rpc-subscriptions-channel-websocket Minor
@solana/rpc-subscriptions-spec Minor
@solana/rpc-subscriptions Minor
@solana/rpc-transformers Minor
@solana/rpc-transport-http Minor
@solana/rpc-types Minor
@solana/rpc Minor
@solana/signers Minor
@solana/subscribable Minor
@solana/sysvars Minor
@solana/transaction-confirmation Minor
@solana/transaction-messages Minor
@solana/transactions Minor
@solana/wallet-account-signer Minor
@solana/plugin-interfaces Minor
@solana/rpc-graphql Minor
@solana/rpc-parsed-types Minor
@solana/rpc-subscriptions-api Minor
@solana/codecs Minor
@solana/fast-stable-stringify Minor
@solana/functional Minor
@solana/nominal-types Minor
@solana/plugin-core Minor
@solana/promises Minor
@solana/rpc-spec-types Minor
@solana/webcrypto-ed25519-polyfill Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@amilz
amilz force-pushed the feat/add-transaction-introspection branch from 2fd5630 to c635106 Compare May 8, 2026 14:53
@amilz
amilz marked this pull request as ready for review May 14, 2026 00:58
Comment thread packages/transaction-introspection/README.md
Comment thread packages/transaction-introspection/README.md Outdated
@github-actions github-actions Bot added the stale label Jun 4, 2026
@mcintyre94 mcintyre94 removed the stale label Jun 10, 2026
@bundlemon

bundlemon Bot commented Jun 11, 2026

Copy link
Copy Markdown

BundleMon

Files added (3)
Status Path Size Limits
transaction-introspection/dist/index.browser.
mjs
+2.75KB -
transaction-introspection/dist/index.native.m
js
+2.75KB -
transaction-introspection/dist/index.node.mjs
+2.75KB -
Files updated (10)
Status Path Size Limits
@solana/kit production bundle
kit/dist/index.production.min.js
54.14KB (+1.4KB +2.65%) -
errors/dist/index.browser.mjs
21.02KB (+264B +1.24%) -
errors/dist/index.node.mjs
21.04KB (+264B +1.24%) -
errors/dist/index.native.mjs
21.02KB (+263B +1.24%) -
wallet-account-signer/dist/index.browser.mjs
17.78KB (+222B +1.23%) -
wallet-account-signer/dist/index.native.mjs
17.78KB (+222B +1.23%) -
wallet-account-signer/dist/index.node.mjs
17.8KB (+221B +1.23%) -
kit/dist/index.browser.mjs
4.48KB (+13B +0.28%) -
kit/dist/index.native.mjs
4.48KB (+13B +0.28%) -
kit/dist/index.node.mjs
4.48KB (+13B +0.28%) -
Unchanged files (137)
Status Path Size Limits
rpc-graphql/dist/index.browser.mjs
18.82KB -
rpc-graphql/dist/index.native.mjs
18.81KB -
rpc-graphql/dist/index.node.mjs
18.81KB -
transaction-messages/dist/index.browser.mjs
11.32KB -
transaction-messages/dist/index.native.mjs
11.32KB -
transaction-messages/dist/index.node.mjs
11.32KB -
instruction-plans/dist/index.browser.mjs
6.58KB -
instruction-plans/dist/index.native.mjs
6.58KB -
instruction-plans/dist/index.node.mjs
6.58KB -
fixed-points/dist/index.browser.mjs
5.08KB -
fixed-points/dist/index.native.mjs
5.07KB -
fixed-points/dist/index.node.mjs
5.07KB -
offchain-messages/dist/index.browser.mjs
5.06KB -
offchain-messages/dist/index.native.mjs
5.06KB -
offchain-messages/dist/index.node.mjs
5.06KB -
codecs-data-structures/dist/index.browser.mjs
5.04KB -
codecs-data-structures/dist/index.native.mjs
5.03KB -
codecs-data-structures/dist/index.node.mjs
5.03KB -
transactions/dist/index.browser.mjs
4.07KB -
transactions/dist/index.native.mjs
4.07KB -
transactions/dist/index.node.mjs
4.07KB -
codecs-core/dist/index.browser.mjs
3.62KB -
codecs-core/dist/index.native.mjs
3.62KB -
codecs-core/dist/index.node.mjs
3.62KB -
webcrypto-ed25519-polyfill/dist/index.node.mj
s
3.61KB -
webcrypto-ed25519-polyfill/dist/index.browser
.mjs
3.59KB -
webcrypto-ed25519-polyfill/dist/index.native.
mjs
3.57KB -
rpc-subscriptions/dist/index.browser.mjs
3.37KB -
rpc-subscriptions/dist/index.node.mjs
3.34KB -
rpc-subscriptions/dist/index.native.mjs
3.31KB -
signers/dist/index.browser.mjs
3.26KB -
signers/dist/index.native.mjs
3.26KB -
signers/dist/index.node.mjs
3.26KB -
rpc-transformers/dist/index.browser.mjs
3.13KB -
rpc-transformers/dist/index.native.mjs
3.13KB -
rpc-transformers/dist/index.node.mjs
3.13KB -
react/dist/index.browser.mjs
3.09KB -
react/dist/index.native.mjs
3.09KB -
react/dist/index.node.mjs
3.09KB -
keys/dist/index.node.mjs
3.06KB -
addresses/dist/index.browser.mjs
2.93KB -
addresses/dist/index.native.mjs
2.92KB -
addresses/dist/index.node.mjs
2.92KB -
keys/dist/index.browser.mjs
2.85KB -
keys/dist/index.native.mjs
2.85KB -
subscribable/dist/index.node.mjs
2.68KB -
subscribable/dist/index.native.mjs
2.61KB -
subscribable/dist/index.browser.mjs
2.6KB -
codecs-strings/dist/index.browser.mjs
2.55KB -
codecs-strings/dist/index.node.mjs
2.51KB -
codecs-strings/dist/index.native.mjs
2.47KB -
transaction-confirmation/dist/index.node.mjs
2.42KB -
transaction-confirmation/dist/index.native.mj
s
2.37KB -
sysvars/dist/index.browser.mjs
2.37KB -
sysvars/dist/index.native.mjs
2.37KB -
transaction-confirmation/dist/index.browser.m
js
2.37KB -
sysvars/dist/index.node.mjs
2.37KB -
rpc-subscriptions-spec/dist/index.node.mjs
2.25KB -
rpc-subscriptions-spec/dist/index.native.mjs
2.2KB -
rpc-subscriptions-spec/dist/index.browser.mjs
2.2KB -
rpc/dist/index.node.mjs
1.95KB -
codecs-numbers/dist/index.browser.mjs
1.95KB -
codecs-numbers/dist/index.native.mjs
1.95KB -
codecs-numbers/dist/index.node.mjs
1.94KB -
rpc-transport-http/dist/index.browser.mjs
1.89KB -
rpc-transport-http/dist/index.native.mjs
1.89KB -
rpc/dist/index.native.mjs
1.81KB -
rpc-types/dist/index.browser.mjs
1.8KB -
rpc/dist/index.browser.mjs
1.8KB -
rpc-types/dist/index.native.mjs
1.8KB -
rpc-types/dist/index.node.mjs
1.8KB -
rpc-transport-http/dist/index.node.mjs
1.71KB -
rpc-subscriptions-channel-websocket/dist/inde
x.node.mjs
1.33KB -
rpc-subscriptions-channel-websocket/dist/inde
x.native.mjs
1.27KB -
rpc-subscriptions-channel-websocket/dist/inde
x.browser.mjs
1.26KB -
program-client-core/dist/index.browser.mjs
1.21KB -
program-client-core/dist/index.native.mjs
1.21KB -
program-client-core/dist/index.node.mjs
1.21KB -
options/dist/index.browser.mjs
1.18KB -
options/dist/index.native.mjs
1.18KB -
options/dist/index.node.mjs
1.17KB -
accounts/dist/index.browser.mjs
1.17KB -
accounts/dist/index.native.mjs
1.17KB -
accounts/dist/index.node.mjs
1.16KB -
rpc-spec-types/dist/index.browser.mjs
1.15KB -
rpc-spec-types/dist/index.native.mjs
1.15KB -
rpc-spec-types/dist/index.node.mjs
1.15KB -
rpc-api/dist/index.browser.mjs
998B -
rpc-api/dist/index.native.mjs
997B -
rpc-api/dist/index.node.mjs
995B -
compat/dist/index.browser.mjs
969B -
compat/dist/index.native.mjs
968B -
compat/dist/index.node.mjs
966B -
rpc-spec/dist/index.browser.mjs
918B -
rpc-spec/dist/index.native.mjs
918B -
rpc-spec/dist/index.node.mjs
917B -
rpc-subscriptions-api/dist/index.native.mjs
871B -
rpc-subscriptions-api/dist/index.browser.mjs
870B -
rpc-subscriptions-api/dist/index.node.mjs
870B -
promises/dist/index.native.mjs
841B -
promises/dist/index.node.mjs
840B -
promises/dist/index.browser.mjs
839B -
plugin-core/dist/index.browser.mjs
820B -
plugin-core/dist/index.native.mjs
819B -
plugin-core/dist/index.node.mjs
817B -
assertions/dist/index.browser.mjs
783B -
instructions/dist/index.browser.mjs
771B -
instructions/dist/index.native.mjs
770B -
instructions/dist/index.node.mjs
768B -
fast-stable-stringify/dist/index.browser.mjs
726B -
fast-stable-stringify/dist/index.native.mjs
725B -
assertions/dist/index.native.mjs
724B -
fast-stable-stringify/dist/index.node.mjs
724B -
assertions/dist/index.node.mjs
723B -
programs/dist/index.browser.mjs
329B -
programs/dist/index.native.mjs
327B -
programs/dist/index.node.mjs
325B -
fs-impl/dist/index.browser.mjs
245B -
event-target-impl/dist/index.node.mjs
230B -
functional/dist/index.browser.mjs
154B -
functional/dist/index.native.mjs
152B -
text-encoding-impl/dist/index.native.mjs
152B -
functional/dist/index.node.mjs
151B -
codecs/dist/index.browser.mjs
145B -
codecs/dist/index.native.mjs
144B -
codecs/dist/index.node.mjs
142B -
event-target-impl/dist/index.browser.mjs
133B -
ws-impl/dist/index.node.mjs
131B -
text-encoding-impl/dist/index.browser.mjs
122B -
fs-impl/dist/index.node.mjs
120B -
text-encoding-impl/dist/index.node.mjs
119B -
ws-impl/dist/index.browser.mjs
113B -
crypto-impl/dist/index.node.mjs
111B -
crypto-impl/dist/index.browser.mjs
109B -
rpc-parsed-types/dist/index.browser.mjs
66B -
rpc-parsed-types/dist/index.native.mjs
65B -
rpc-parsed-types/dist/index.node.mjs
63B -

Total files change +11.11KB +2.1%

Final result: ✅

View report in BundleMon website ➡️


Current branch size history | Target branch size history

@mcintyre94 mcintyre94 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating this! I made a few comments, the most important is that I think there is still a v1 gap in decodeFromJson.

I'll also kick off an AI review to see what I missed

Comment thread .changeset/crisp-squids-flow.md Outdated
Comment thread packages/transaction-introspection/src/__tests__/decode-rpc-transaction-test.ts Outdated
Comment thread packages/transaction-introspection/src/__tests__/walk-instructions-test.ts Outdated
Comment thread packages/transaction-introspection/src/decode-rpc-transaction.ts Outdated
Comment thread packages/transaction-introspection/src/decode-rpc-transaction.ts Outdated
Comment thread packages/transaction-introspection/src/decode-rpc-transaction.ts Outdated
Comment thread packages/transaction-introspection/src/decode-rpc-transaction.ts Outdated
Comment thread packages/transaction-introspection/src/get-all-addresses.ts Outdated
Comment thread packages/transaction-introspection/src/get-instructions.ts Outdated
@mcintyre94

Copy link
Copy Markdown
Member

@trevor-cortex

@trevor-cortex trevor-cortex left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

This PR adds a new @solana/transaction-introspection package that closes the gap between a getTransaction response and the auto-generated @solana-program/* parseXInstruction / identifyXInstruction clients. It decodes base64/base58/json responses into a CompiledTransactionMessage (+ wire Transaction for the binary encodings), resolves account indices against static + ALT-loaded addresses with proper signer/writable roles, normalises inner instructions from meta.innerInstructions, and exposes walkInstructions to enumerate every outer + inner instruction with a trace recording its location. Each returned item is itself a ResolvedInstruction, so it flows straight into isInstructionForProgram and the auto-generated identify/parse helpers.

It also tidies packages/rpc-api/src/getTransaction.ts by hoisting the four inline non-null response shapes (Base64, Base58, Json, JsonParsed) into named exported types, so the new package can consume them directly without re-deriving them. The underlying overload shapes look unchanged — purely an extraction.

Nice quality bar overall: thorough JSDoc, a real README with a quickstart and per-symbol docs, type tests for both the decodeTransactionFromRpcResponse overload narrowing and TracedInstruction interop with isInstructionForProgram, and unit tests covering legacy/v0/v1 + the JSON path + the ALT-loaded v0 walk. The _exhaustiveCheck: never in normalizeCompiledInstructions is a nice touch — a future CompiledTransactionMessage variant will fail to typecheck rather than silently misbehaving.

Key things to address

Echoing Callum's point — the v1 gap in decodeFromJson is the headline issue and I think it needs to be addressed before this lands. Details inline. A few smaller things alongside it (error-code semantics, .gitignore additions, PR-description / changeset drift). Everything else is healthy.

Notes for subsequent reviewers

  • The biggest semantic question is how strict the JSON path should be about transaction versions it does not understand. Today, anything other than 'legacy' is forced to 0 (rpcTx.version as 0), which silently mis-shapes v1+ responses. Worth deciding before this is published.
  • Worth a sanity-check on whether @solana/rpc-api should appear in the changeset. It now exports four new named types (GetTransactionApiResponseBase58/Base64/Json/JsonParsed). All publishable packages are version-locked via fixed, so the bump will propagate regardless, but the changeset entry as-is doesn't list rpc-api as user-facing; if you'd rather the release notes call out the new exports, the changeset should be expanded.
  • The rpc-api extraction is a structural refactor of a public type. I scanned the diff and the four hoisted aliases look line-for-line equivalent to the previous inline shapes, but worth a second pair of eyes to confirm nothing widens or narrows (especially the TMaxSupportedTransactionVersion extends void branches).
  • Inner-instructions normalisation in get-inner-instructions.ts reuses SOLANA_ERROR__TRANSACTION__FAILED_TO_DECOMPILE_INSTRUCTION_PROGRAM_ADDRESS_NOT_FOUND for both program-index and account-index out-of-range cases. Same code is reused in get-instructions.ts for account indices. See inline.
  • The PR description mentions filterInstructionsForProgram, but it isn't in src/index.ts or the README — the description is stale and the README correctly says you can use isInstructionForProgram directly. Worth tidying the description before merge.

Comment thread packages/transaction-introspection/src/decode-rpc-transaction.ts Outdated
Comment thread packages/transaction-introspection/src/get-instructions.ts Outdated
Comment thread .gitignore
Comment thread packages/transaction-introspection/src/decode-rpc-transaction.ts Outdated
Comment thread packages/transaction-introspection/src/decode-rpc-transaction.ts Outdated
Comment thread packages/transaction-introspection/src/decode-rpc-transaction.ts Outdated
@amilz
amilz force-pushed the feat/add-transaction-introspection branch from 4362a6a to adfa25e Compare June 16, 2026 17:12
amilz added 18 commits June 16, 2026 10:14
… transactions

Adds @solana/transaction-introspection, a new package that bridges a
getTransaction response and the auto-generated @solana-program/* parseXInstruction
clients. Decodes responses encoded as base64, base58, or json; resolves account
indices against static and ALT-loaded addresses; normalizes inner instructions
from meta.innerInstructions; and exposes walkInstructions and
filterInstructionsForProgram for streaming traversal of every instruction
(outer and inner). Re-exported from @solana/kit.

Also hoists the inline GetTransactionApi response shapes in @solana/rpc-api
into named exported types (Base64/Base58/Json/JsonParsed) so the new package
can consume them directly.
…rim public surface

Drops the synthesized Transaction for json responses — the empty-messageBytes
fake was a lie and required reconstructing signatures from a parallel array.
Now DecodedRpcTransaction.transaction is optional, and the base64/base58
overloads narrow it to a guaranteed Transaction so callers using those
encodings get static guarantees. Adds a typetest to lock the narrowing in.

Restores lifetimeToken parity across all three encodings (the prior refactor
silently dropped it from the json path), and asserts it on both the wire-decoder
and json paths so the asymmetry can't regress.

Trims the public API: switches src/index.ts from export * to explicit named
exports, marks getInstructionsFromCompiledTransactionMessageWithMetas @internal,
and updates the README for the walkInnerInstructionsFromMeta rename.

docs(transaction-introspection): align decoder docs with optional `transaction` and widen `compiledMessage` to guarantee `lifetimeToken`

Updates the docblocks on decodeTransactionFromRpcResponse and
DecodedRpcTransaction (and the matching README sections) so they describe
the post-refactor shape: `transaction` is omitted for json responses, not
empty-bytes; base64/base58 overloads statically guarantee a re-encodable
Transaction.

Widens DecodedRpcTransaction.compiledMessage to
`CompiledTransactionMessage & CompiledTransactionMessageWithLifetime` —
every path now sets `lifetimeToken`, so callers can read `.lifetimeToken`
without their own narrowing. Drops the corresponding cast in the tests.
…filter

Address PR feedback by making `TracedInstruction` a `ResolvedInstruction<T> & { trace }` rather than a `{ instruction, trace }` wrapper. Each walked item is now itself an `IInstruction` and can be passed directly to `isInstructionForProgram` from `@solana/instructions` and to the auto-generated `identifyXInstruction` / `parseXInstruction` helpers, removing the need for our own `filterInstructionsForProgram` (deleted).

`walkInstructions` and the renamed `getInnerInstructionsFromMeta` (was `walkInnerInstructionsFromMeta`) now return arrays rather than generators. A transaction is capped at 64 total instructions, so laziness wasn't buying anything, and arrays compose with native `.filter` / `.find` / `.map` for the common cases.

The typetest is renamed to `traced-instruction-typetest.ts` and rewritten to exercise narrowing via `isInstructionForProgram`.
V1 is rolling out on testnet and the rest of Kit already supports it, so the introspection helpers shouldn't be the odd ones out.

A new `normalizeCompiledInstructions` reduces `legacy`, `v0`, and `v1` to a single internal shape — for `v1` it zips `instructionHeaders` and `instructionPayloads` into the `{ programAddressIndex, accountIndices, data }` form the resolver already consumed. Inner instructions and ALT-loaded addresses are version-agnostic on the RPC side, so the rest of the package needed no change.

The response-type generics widen from `0 | void` to `TransactionVersion | void` to match upstream `@solana/rpc-api`, and the README notes that callers still need to pass `maxSupportedTransactionVersion` on `getTransaction` to actually receive `v0` or `v1`. A `_exhaustiveCheck: never` assignment in `normalizeCompiledInstructions` ensures a future variant of `CompiledTransactionMessage` won't silently slip through the version check.
…nsupported getTransaction shapes

Adds SOLANA_ERROR__TRANSACTION__FAILED_TO_DECOMPILE_INSTRUCTION_ACCOUNT_INDEX_OUT_OF_RANGE so account-index lookups no longer reuse the program-address error code, and a new TRANSACTION_INTROSPECTION domain (5664xxx) with CANNOT_DECODE_JSON_PARSED_TRANSACTION and UNRECOGNIZED_GET_TRANSACTION_RESPONSE so decodeTransactionFromRpcResponse no longer throws MALFORMED_MESSAGE_BYTES with empty bytes for inputs that have no message bytes at all. jsonParsed responses are now detected explicitly and rejected with their own error.
…nknown versions

decodeFromJson previously collapsed every versioned response to v0 (rpcTx.version as 0), so a v1 'json' response silently produced a mis-shaped compiled message. The version dispatch is now an exhaustive switch: v1 responses synthesize a V1CompiledTransactionMessage (the 'json' encoding carries no v1 transaction config, so the config is reported empty), and any version outside TransactionVersion throws SOLANA_ERROR__TRANSACTION__VERSION_NUMBER_NOT_SUPPORTED — the same error surface as getInstructionsFromCompiledTransactionMessage.

Also documents that message.recentBlockhash carries the nonce value for durable-nonce transactions, and aligns the JSON path with the wire decoder by omitting accountIndices/data when empty.
…nse types directly

Drops the Base64/Base58/JsonGetTransactionResponse aliases in favor of GetTransactionApiResponseBase64/Base58/Json from @solana/rpc-api. The aliases were pure renames, and their widened default type parameter (TransactionVersion | void vs rpc-api's void) meant an unparameterised alias was not the same type as its rpc-api counterpart.
…TransactionMessage

The flat-address list is derivable from getAccountMetasFromCompiledTransactionMessage by mapping each meta to its address, so the standalone helper added no capability worth a public API surface. The LoadedAddresses type moves to its own module.
…m resolved instructions

ResolvedInstruction no longer forces InstructionWithAccounts/InstructionWithData. Resolved outer and inner instructions now attach accounts and data only when non-empty, matching the kit Instruction conventions and the wire decoder's behavior, so isInstructionWithAccounts and isInstructionWithData from @solana/instructions behave as expected on the results.
…kInstructions output

walkInstructions now returns instructions in display order — each outer instruction followed immediately by its inner instructions — instead of all outer instructions followed by all inner ones. This matches how explorers present a transaction and makes positional iteration line up with execution structure.
The base64 test that claimed v0 coverage only differed from the legacy one by a type cast. It now encodes an actual v0 message with addressTableLookups and asserts the decoded version and lookups.
The changeset now includes a usage example and lists @solana/rpc-api (new named getTransaction response types) and @solana/errors (new error codes) as affected packages. The .gitignore entries for personal agent configs were moved to anza-xyz#1737.
…ing message header

The previous sniff inspected the first instruction, so a jsonParsed response for a transaction with no instructions (e.g. fee-only) passed as 'json' and crashed on the missing header with a raw TypeError. The 'json' encoding always carries the compiled-message header while jsonParsed never does, so the header is a reliable discriminator independent of instruction count.
…SON-derived v0 messages

The wire decoder drops the field when the message has no lookups, so the JSON path now does too — a lookup-free v0 transaction decodes to the same shape on both encodings. Also defers compiled-instruction construction into the legacy/v0 branches so the v1 path no longer base58-decodes every instruction's data twice.
… before identify/parse

The auto-generated identifyXInstruction / parseXInstruction helpers require data (and parse also accounts), which are optional on ResolvedInstruction, so every README and docblock example now narrows with isInstructionWithData / isInstructionWithAccounts first. The typetest also exercises those narrows over TracedInstruction so the examples' pattern is checked at compile time.
…peScript peer with the workspace

Bumps the stale 6.8.0 to 6.9.0 to match the fixed changesets group, moves @solana/rpc-types to devDependencies since only tests import it, and raises the TypeScript peer to >=5.4.0 like every other package.
…cases

normalizeCompiledInstructions now throws SOLANA_ERROR__TRANSACTION__INSTRUCTION_HEADERS_PAYLOADS_MISMATCH for v1 messages whose headers and payloads disagree in length, matching the transaction-messages decompile path, instead of failing with a raw TypeError. walkInstructions appends inner groups whose index matches no outer instruction instead of silently dropping them. Also adds wire-path (base64) coverage for v1 transaction decoding.
@amilz
amilz force-pushed the feat/add-transaction-introspection branch from adfa25e to 96dc50c Compare June 16, 2026 17:15
@amilz

amilz commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

@mcintyre94 @trevor-cortex thx for the feedback
Gone through each comment. Main changes

  • added handling for v1
  • reordered output to match explorer style (outer-1, inner-1, inner-n, outer-2, inner-1, etc.)
  • leveraged the suggested kit types where suggested
  • added new errors
  • added/updated some tests per feedback
  • updated docs per feedback
  • rebased b/c some conflicts w/ error commits

mcintyre94
mcintyre94 previously approved these changes Jun 24, 2026

@mcintyre94 mcintyre94 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks really good to me now, thanks for making all those changes!

To flag a relevant PR, #1776 adds getTransactionForAddress to the Kit RPC. If my PR to add version to that API is merged (solana-rpc/superbank#20) then AFAICT this would also be compatible with transactions from getTransactionsForAddress which would be super nice.

I'd suggest that we handle that as a follow up though, rather than blocking this until that's added.

Will do another AI review in case I missed anything!

Comment thread packages/transaction-introspection/src/__tests__/get-instructions-test.ts Outdated
@mcintyre94
mcintyre94 requested a review from trevor-cortex June 24, 2026 13:51

@trevor-cortex trevor-cortex left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took a fresh pass over the diff against my previous notes. All the substantive points from the last round are addressed and the package looks ready to ship.

What's been resolved since the last review

  • v1 gap in decodeFromJson (the headline) is closed. version is now read from the envelope and dispatched through a switch with a _exhaustiveCheck: never default that throws SOLANA_ERROR__TRANSACTION__VERSION_NUMBER_NOT_SUPPORTED. The legacy / v0 / v1 branches each build the correct compiled-message shape, and there's a dedicated test covering an unknown version: 99.
  • Error-code overloading for account-index lookups is fixed with the new SOLANA_ERROR__TRANSACTION__FAILED_TO_DECOMPILE_INSTRUCTION_ACCOUNT_INDEX_OUT_OF_RANGE (5663038), plus a new TRANSACTION_INTROSPECTION domain at 5664xxx with CANNOT_DECODE_JSON_PARSED_TRANSACTION and UNRECOGNIZED_GET_TRANSACTION_RESPONSE. jsonParsed now has its own dedicated, message-clear error rather than a generic decode failure.
  • .gitignore is cleaned up — only .docs/ and dist/ remain in the package-local file, no personal tooling state.
  • JSON instruction shape consistency is now explicit: the JSON path's getCompiledInstructions omits accountIndices / data when empty, with a comment noting it matches the wire decoder.
  • Default type-parameter widening is consistent — every overload defaults to TransactionVersion | void.
  • Changeset now correctly lists @solana/errors, @solana/rpc-api, @solana/kit, and @solana/transaction-introspection, and the description matches the README (no stale filterInstructionsForProgram).
  • loaded-addresses.ts extracted cleanly as a tiny module — nice.

The new tests are a notable jump in coverage: v0 + v1 JSON paths, unknown version, unrecognized shape, ALT-loaded v0 walking with mixed static/ALT-writable/ALT-readonly indices, and the v1 INSTRUCTION_HEADERS_PAYLOADS_MISMATCH case. The two type tests (decode-rpc-transaction-typetest.ts and traced-instruction-typetest.ts) lock down both the overload narrowing of transaction and the isInstructionForProgram / isInstructionWithData ergonomics for TracedInstruction.

Remaining observations (all minor)

None block merge — these are polish-level. See inline.

Notes for subsequent reviewers

  • The isJsonResponse / isJsonParsedResponse discrimination relies on the presence of message.header. That's a reasonable heuristic given the RPC contract, but it's worth being aware of: any future change to the jsonParsed shape that re-introduced a header field on message would silently route into the JSON path. The conditional is currently the only thing distinguishing the two encodings structurally — there's no encoding discriminator on the response itself.
  • Worth double-checking that getAccountMetasFromCompiledTransactionMessage produces the same role assignments as the kit decompileTransactionMessage helper for v0 messages with ALT-loaded addresses — the order (static → ALT writable → ALT readonly) matches the runtime, and the walk-instructions-test.ts ALT case exercises it, but if there's a canonical decoder elsewhere in the kit it would be reassuring to confirm shape parity once.
  • Callum already mentioned this in their approval, but flagging here for posterity: the related PR #1611 discussion of follow-up compatibility with getTransactionsForAddress (once version lands there) is a reasonable follow-up rather than a blocker.

Comment thread packages/transaction-introspection/src/get-inner-instructions.ts
Comment thread packages/transaction-introspection/src/decode-rpc-transaction.ts
Comment thread packages/transaction-introspection/src/decode-rpc-transaction.ts
Comment thread packages/transaction-introspection/src/get-instructions.ts
Replace the custom `getThrownError` helper in `get-instructions-test.ts` with `expect(() => ...).toThrow(new SolanaError(...))`, matching the pattern in `get-inner-instructions-test.ts` and asserting error context alongside the code. Rename the shadowed `meta` locals to `accountMeta` in the account-index lookups, and document that `decodeTransactionFromRpcResponse` can throw `SOLANA_ERROR__TRANSACTION__VERSION_NUMBER_NOT_SUPPORTED`.
@github-actions
github-actions Bot dismissed mcintyre94’s stale review June 24, 2026 22:37

Your organization requires reapproval when changes are made, so Graphite has dismissed approvals. No previous SHAs found; treated as if diff has changed at https://github.com/anza-xyz/kit/actions/runs/28134143740

@amilz

amilz commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

@mcintyre94 updated that test and a couple of nits from trevor

@mcintyre94 mcintyre94 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're good to ship this! Thankyou! 🚀

@mcintyre94
mcintyre94 enabled auto-merge June 25, 2026 11:06
@mcintyre94
mcintyre94 added this pull request to the merge queue Jun 25, 2026
Merged via the queue into anza-xyz:main with commit 772b82c Jun 25, 2026
11 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

🔎💬 Inkeep AI search and chat service is syncing content for source 'Solana Kit Docs'

@github-actions

Copy link
Copy Markdown
Contributor

Because there has been no activity on this PR for 14 days since it was merged, it has been automatically locked. Please open a new issue if it requires a follow up.

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Jul 10, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants