Skip to content

fix: stop upcasting transaction version to bigint - #1917

Merged
mcintyre94 merged 1 commit into
anza-xyz:mainfrom
amilz:fix/DEV-858-get-transaction-version-bigint
Aug 13, 2026
Merged

fix: stop upcasting transaction version to bigint#1917
mcintyre94 merged 1 commit into
anza-xyz:mainfrom
amilz:fix/DEV-858-get-transaction-version-bigint

Conversation

@amilz

@amilz amilz commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Kit's RPC response transformer upcasts every JSON integer to a bigint unless the field's keypath appears in a per-method allow-list (getAllowedNumericKeypaths() in packages/rpc-api/src/index.ts). version was missing from that list on getTransaction, getBlock transactions, and getTransactionsForAddress, so it arrives at runtime as 0n while still typechecking as TransactionVersion ('legacy' | 0 | 1).

The result is a silent mismatch between the declared type and the runtime value:

if (transaction.version === 0) {
    // never runs: the value is `0n`, and `0n === 0` is false
}

There is no compiler error and no runtime error; the branch simply never runs. This was found in Explorer, where it silently disabled an entire feature.

Changes

Allow-list version on all three methods. Three added keypaths.

Testing

New unit tests in packages/rpc-api/src/__tests__/allowed-numeric-keypaths-test.ts push canned responses through createSolanaRpcApi() and assert version arrives as a number. Three of the four fail against the unpatched allow-list; the fourth covers the 'legacy' string case, which was never affected and guards against over-correcting.

Follow-ups, deliberately not in this PR

Token balance numerics. The same audit found that uiTokenAmount.uiAmount is missing from the allow-list on these same three methods, and that simulateTransaction has no token balance keypaths allow-listed at all. uiAmount is an f64, and the transformer's guard is Number.isInteger, so a whole-number balance was upcast while a fractional one was not. That fix is ready and will follow immediately after this merges. It is kept separate because it adds a new public export to @solana/rpc-transformers and so carries a minor bump, whereas this change is a pure patch.

@solana/rpc-subscriptions-api. Same class of bug, different root cause. Its allow-list is keyed by notification method name (blockNotifications), but the response transformer is invoked with the subscribe request, whose methodName is blockSubscribe (rpc-subscriptions-pubsub-plan.ts:96). The lookup never matches, so the entire subscriptions allow-list is dead code and every field it names is upcast. There is a second defect underneath: the publisher is memoized per (channel, responseTransformer) and its closure captures the first subscribeRequest, so the transform is resolved per channel where it needs to be per subscription. Fixing it flips affected fields from bigint to number at runtime, which is breaking for anyone who adapted, so it needs its own change and its own bump. Adding correct keypaths to a list that is never consulted would ship an inert change that reads as fixed.

@changeset-bot

changeset-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: de5dea7

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

This PR includes changesets to release 48 packages
Name Type
@solana/rpc-api Patch
@solana/kit Patch
@solana/program-client-core Patch
@solana/rpc Patch
@solana/sysvars Patch
@solana/transaction-introspection Patch
@solana/react Patch
@solana/rpc-graphql Patch
@solana/transaction-confirmation 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/codecs Patch
@solana/compat Patch
@solana/errors Patch
@solana/fast-stable-stringify Patch
@solana/fixed-points Patch
@solana/functional Patch
@solana/instruction-plans Patch
@solana/instructions Patch
@solana/keys Patch
@solana/nominal-types Patch
@solana/offchain-messages Patch
@solana/options Patch
@solana/plugin-core Patch
@solana/plugin-interfaces Patch
@solana/programs Patch
@solana/promises Patch
@solana/rpc-parsed-types Patch
@solana/rpc-spec-types Patch
@solana/rpc-spec Patch
@solana/rpc-subscriptions-api 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/signers Patch
@solana/subscribable Patch
@solana/transaction-messages Patch
@solana/transactions Patch
@solana/wallet-account-signer 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

Kit's RPC response transformer upcasts every JSON integer to a `bigint` unless the field's keypath appears in a per-method allow-list. `version` was missing from that list on `getTransaction`, `getBlock` transactions, and `getTransactionsForAddress`, so it arrived at runtime as `0n` while still typechecking as `TransactionVersion` (`'legacy' | 0 | 1`).

The result is a silent mismatch between the declared type and the runtime value. A check like `if (transaction.version === 0)` compiles cleanly and is always false, because `0n === 0` is false. There is no compiler error and no runtime error; the branch simply never runs.

The keypath is now allow-listed on all three methods, so `version` arrives as a number and matches its declared type.
@amilz
amilz force-pushed the fix/DEV-858-get-transaction-version-bigint branch from e6e8eab to de5dea7 Compare August 12, 2026 23:46
@amilz amilz changed the title fix: stop upcasting transaction version and token balance numerics to bigint fix: stop upcasting transaction version to bigint Aug 12, 2026
@bundlemon

bundlemon Bot commented Aug 13, 2026

Copy link
Copy Markdown

BundleMon

Files updated (4)
Status Path Size Limits
@solana/kit production bundle
kit/dist/index.production.min.js
55.81KB (+18B +0.03%) -
rpc-api/dist/index.browser.mjs
1.05KB (+15B +1.41%) -
rpc-api/dist/index.native.mjs
1.05KB (+15B +1.41%) -
rpc-api/dist/index.node.mjs
1.05KB (+15B +1.42%) -
Unchanged files (146)
Status Path Size Limits
errors/dist/index.node.mjs
21.81KB -
errors/dist/index.browser.mjs
21.79KB -
errors/dist/index.native.mjs
21.79KB -
rpc-graphql/dist/index.browser.mjs
18.82KB -
rpc-graphql/dist/index.native.mjs
18.82KB -
rpc-graphql/dist/index.node.mjs
18.82KB -
wallet-account-signer/dist/index.node.mjs
18.5KB -
wallet-account-signer/dist/index.browser.mjs
18.48KB -
wallet-account-signer/dist/index.native.mjs
18.48KB -
transaction-messages/dist/index.browser.mjs
11.34KB -
transaction-messages/dist/index.native.mjs
11.34KB -
transaction-messages/dist/index.node.mjs
11.34KB -
instruction-plans/dist/index.browser.mjs
7.02KB -
instruction-plans/dist/index.native.mjs
7.02KB -
instruction-plans/dist/index.node.mjs
7.02KB -
react/dist/index.browser.mjs
5.32KB -
react/dist/index.native.mjs
5.32KB -
react/dist/index.node.mjs
5.32KB -
codecs-data-structures/dist/index.browser.mjs
5.29KB -
codecs-data-structures/dist/index.native.mjs
5.29KB -
codecs-data-structures/dist/index.node.mjs
5.29KB -
offchain-messages/dist/index.browser.mjs
5.25KB -
offchain-messages/dist/index.native.mjs
5.24KB -
offchain-messages/dist/index.node.mjs
5.24KB -
fixed-points/dist/index.browser.mjs
5.08KB -
fixed-points/dist/index.native.mjs
5.07KB -
fixed-points/dist/index.node.mjs
5.07KB -
kit/dist/index.browser.mjs
4.86KB -
kit/dist/index.native.mjs
4.85KB -
kit/dist/index.node.mjs
4.85KB -
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.16KB -
rpc-transformers/dist/index.native.mjs
3.16KB -
rpc-transformers/dist/index.node.mjs
3.15KB -
subscribable/dist/index.node.mjs
3.13KB -
keys/dist/index.node.mjs
3.06KB -
subscribable/dist/index.native.mjs
3.06KB -
subscribable/dist/index.browser.mjs
3.05KB -
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 -
transaction-introspection/dist/index.browser.
mjs
2.73KB -
transaction-introspection/dist/index.native.m
js
2.73KB -
transaction-introspection/dist/index.node.mjs
2.73KB -
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.23KB -
rpc-subscriptions-spec/dist/index.native.mjs
2.19KB -
rpc-subscriptions-spec/dist/index.browser.mjs
2.19KB -
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-types/dist/index.browser.mjs
1.9KB -
rpc-types/dist/index.native.mjs
1.9KB -
rpc-types/dist/index.node.mjs
1.9KB -
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/dist/index.browser.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 -
plugin-core/dist/index.browser.mjs
1.18KB -
plugin-core/dist/index.native.mjs
1.18KB -
plugin-core/dist/index.node.mjs
1.18KB -
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 -
compat/dist/index.browser.mjs
969B -
compat/dist/index.native.mjs
968B -
compat/dist/index.node.mjs
966B -
rpc-spec/dist/index.browser.mjs
898B -
rpc-spec/dist/index.native.mjs
897B -
rpc-spec/dist/index.node.mjs
896B -
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 -
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 +63B +0.01%

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.

Nice, thanks!

On your follow ups, uiTokenAmount.uiAmount is defined as Option<f64> on the RPC, so agreed that shouldn't be bigint either

I'm unsure about the notifications one, happy to take a look at a PR but feel free to just open an issue on that if it's easier/needs more investigation.

@mcintyre94
mcintyre94 added this pull request to the merge queue Aug 13, 2026
Merged via the queue into anza-xyz:main with commit 82c4ceb Aug 13, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants