Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@joefitter
Copy link

Problem

Currently there are no type guards for transaction message version, so simply checking message.version in an implementation doesn't coerce type.

Summary of Changes

Added isLegacyTransactionMessage, assertIsLegacyTransactionMessage, isV0TransactionMessage and assertIsV0TransactionMessage along with associated errors.

@changeset-bot
Copy link

changeset-bot bot commented Dec 5, 2025

🦋 Changeset detected

Latest commit: c21d6b8

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

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

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

* Added isLegacyTransactionMessage and assertIsLegacyTransactionMessage helpers to packages/transaction-message
* Added isV0TransactionMessage and assertIsV0TransactoinMessage helpers to packages/transaction-message
@joefitter joefitter force-pushed the feature/type-guards-for-tx-message-version branch from a11414e to c21d6b8 Compare December 5, 2025 15:16
Copy link
Contributor

@steveluscher steveluscher left a comment

Choose a reason for hiding this comment

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

@joefitter
Copy link
Author

Hi @steveluscher, thanks for getting back to me.

So the implementation is the following:

export async function injectSolFeeIntoTx(
  env: Env,
  swapTransactionB64: string,
  feeInstruction: Instruction,
): Promise<string> {
  const client = getClient(env);

  const base64Encoder = getBase64Encoder();
  const transactionBytes = base64Encoder.encode(swapTransactionB64);

  const txDecoder = getTransactionDecoder();
  const tx = txDecoder.decode(transactionBytes);

  const compiledMsgDecoder = getCompiledTransactionMessageDecoder();
  const compiledMsg = compiledMsgDecoder.decode(tx.messageBytes);

  const lutAddresses =
    compiledMsg.version === 0
      ? (compiledMsg.addressTableLookups?.map((l) => l.lookupTableAddress) ??
        [])
      : [];

  const decompiledMsg = await decompileTransactionMessageFetchingLookupTables(
    compiledMsg,
    client.rpc,
  );

  const msgWithFee = appendTransactionMessageInstructions(
    [feeInstruction],
    decompiledMsg,
  );

  const addressesByLookupTableAddress = lutAddresses.length
    ? await fetchAddressesForLookupTables(lutAddresses, client.rpc)
    : {};

  const compressedMsg =
    Object.keys(addressesByLookupTableAddress).length > 0 &&
    msgWithFee.version === 0
      ? compressTransactionMessageUsingAddressLookupTables(
          // casting as we know this is a version 0 tx msg
          msgWithFee as typeof msgWithFee & { version: 0 },
          addressesByLookupTableAddress,
        )
      : msgWithFee;

  const txWithFee = compileTransaction(compressedMsg);
  return getBase64EncodedWireTransaction(txWithFee);
}

Here, checking msgWithFee.version isn't enough to cast the type as a V0TransactionMessage and the compressTransactionMessageUsingAddressLookupTables function complains so I thought adding a guard would be the preferred method for ensuring we have a V0 transaction. But please let me know if you have another suggestion?

@steveluscher
Copy link
Contributor

@mcintyre94, I think there's a problem with the return type of decompileTransactionMessage and by extension compressTransactionMessageUsingAddressLookupTables.

The return type there is BaseTransactionMessage which, by virtue of not being a tagged union, makes it impossible to refine just by asserting on message.version. The return type of those decompile methods should probably be TransactionMessage, and a bunch of typetests written for them. Thoughts?

The playground to make work is (link)

@mcintyre94
Copy link
Member

Sorry for the delay on this, taking a look!

@github-actions github-actions bot added the stale label Dec 31, 2025
mcintyre94 added a commit that referenced this pull request Jan 7, 2026
#### Problem

When we use `decompileTransactionMessage`, we get a `BaseTransactionMessage`, which is not a union type and therefore doesn't play nicely with Typescript type narrowing by version.

#### Summary of Changes

- Improve the typing of `decompileTransactionMessage`
- Add typetests

Part of a stack that eventually supersedes #1103
mcintyre94 added a commit that referenced this pull request Jan 7, 2026
…upTables (#1133)

#### Problem

When we use `decompileTransactionMessageFetchingLookupTables`, we get a `BaseTransactionMessage`, which is not a union type and therefore doesn't play nicely with Typescript type narrowing by version.

#### Summary of Changes

- Improve the typing of `decompileTransactionMessageFetchingLookupTables`
- Add typetests

Part of a stack that eventually supersedes #1103
mcintyre94 added a commit that referenced this pull request Jan 7, 2026
…tions (#1134)

#### Problem

Our `TransactionMessage` functions operate on `BaseTransactionMessage` instead of `TransactionMessage`, which limits the ability to type narrow the resulting transaction messages

#### Summary of Changes

- Change to use `TransactionMessage` in input/output types

Part of a stack that eventually supersedes #1103
mcintyre94 added a commit that referenced this pull request Jan 7, 2026
…functions (#1135)

#### Problem

Our `TransactionMessage` functions operate on `BaseTransactionMessage` instead of `TransactionMessage`, which limits the ability to type narrow the resulting transaction messages

#### Summary of Changes

- Change to use `TransactionMessage` in input/output types

Part of a stack that eventually supersedes #1103
mcintyre94 added a commit that referenced this pull request Jan 7, 2026
…ions (#1136)

#### Problem

Our `TransactionMessage` functions operate on `BaseTransactionMessage` instead of `TransactionMessage`, which limits the ability to type narrow the resulting transaction messages

#### Summary of Changes

- Change to use `TransactionMessage` in input/output types

Part of a stack that eventually supersedes #1103
mcintyre94 added a commit that referenced this pull request Jan 7, 2026
…nctions (#1137)

#### Problem

Our `TransactionMessage` functions operate on `BaseTransactionMessage` instead of `TransactionMessage`, which limits the ability to type narrow the resulting transaction messages

#### Summary of Changes

- Change to use `TransactionMessage` in input/output types

Part of a stack that eventually supersedes #1103
mcintyre94 added a commit that referenced this pull request Jan 7, 2026
#### Problem

Our `TransactionMessage` functions operate on `BaseTransactionMessage` instead of `TransactionMessage`, which limits the ability to type narrow the resulting transaction messages

#### Summary of Changes

- Change to use `TransactionMessage` in input/output types

Part of a stack that eventually supersedes #1103
mcintyre94 added a commit that referenced this pull request Jan 7, 2026
#1139)

This PR adds a new typetest that does the following:

- Decompile a transaction message using `decompileTransactionMessageFetchingLookupTables`
- Apply each function that modifies a transaction message
- Verify that it can still be type narrowed correctly

Note that we don't have any typetests like this that combine functionality from multiple packages. I've put this in `packages/kit/__typetests__/scenarios`, but don't feel strongly about where it should live. I do think it's important to have this as part of the repo though because it would be very easy to introduce a regression. 

Supersedes #1103, by avoiding the need to introduce assertion functions
@mcintyre94
Copy link
Member

@joefitter Thanks for this, and especially for the typetests and detailed explanation. I've merged a stack of PRs that should remove the need for these new functions, by making it so that you can branch on message.version after calling any combination of our TransactionMessage modifying functions.

See https://github.com/anza-xyz/kit/blob/6dbaf66015198bd912ec0800c1db1fd63b68e7a2/packages/kit/src/__typetests__/scenarios/transaction-message-decompile-modify-typetest.ts for a test of this

I'll close this PR because I don't think we need to add these functions now, but thanks again for all your help here!

@mcintyre94 mcintyre94 closed this Jan 7, 2026
@github-actions
Copy link
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 Jan 22, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants