Skip to content

Add a function createReactiveStoreFromRpcAndSubscription#1528

Merged
mcintyre94 merged 5 commits into
mainfrom
rpc-subscriptions-w-slot
Apr 8, 2026
Merged

Add a function createReactiveStoreFromRpcAndSubscription#1528
mcintyre94 merged 5 commits into
mainfrom
rpc-subscriptions-w-slot

Conversation

@mcintyre94
Copy link
Copy Markdown
Member

@mcintyre94 mcintyre94 commented Apr 8, 2026

Problem

In my earlier PR I added an API to get a reactive store (subscribe, getState, getError) for RPC subscriptions. But another case, which is more complex for apps, is where you want to get the current state and then keep it updated with a subscription. An example of this was the balance functionality in our react app, which makes a call to getBalanceand then subscribes to account updates, and keeps track of the slot to always accept only the most recent data. Just having a reactive subscription is insufficient for this use case.

Summary of Changes

This PR adds a new function createReactiveStoreFromRpcAndSubscription. It's added to pakcages/kit since it combines RPC and subscriptions.

It takes as input:

  • An RPC request that returns a SolanaRpcResponse, ie provides a slot in context
  • A map function from the response shape of that request, to TItem
  • An RPC subscription request that returns notifications with SolanaRpcResponse, providing a slot in context
  • A map function from the notification shape of that request, to TItem

It then returns a ReactiveStore as defined in the previous PR which provides the latest data (by slot number) from either data source, and notifies subscribers only when newer data is available (or an error occurs). It handles the unified abort signal and error handling.

This generalises the approach used in balance.ts, while providing an output shape compatible with any reactive framework.

The PR also updates the balance.ts in the react-app to demonstrate this, binding the store to an SWR subscription.

This would enable frameworks targeting react, SWR, or any other reactive library etc to provide bindings like useBalance() with minimal framework logic.

Outside of balance, it would be useful for any case where we have an RPC and a subscription providing the same data that can change. Anything on accounts (getAccountInfo + account notifications), and potentially also transaction signatures (getSignatureStatuses+ signature notifications).

I also realised afterward that we could provide an async generator API for this, which only yields values when the slot is newer. Same logic, but presented as an async generator API. I'll add that later as a follow up PR.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 8, 2026

🦋 Changeset detected

Latest commit: 7fc0245

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

This PR includes changesets to release 46 packages
Name Type
@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/codecs Minor
@solana/compat Minor
@solana/errors Minor
@solana/fast-stable-stringify Minor
@solana/functional Minor
@solana/instruction-plans Minor
@solana/instructions Minor
@solana/keys Minor
@solana/nominal-types Minor
@solana/offchain-messages Minor
@solana/options Minor
@solana/plugin-core Minor
@solana/plugin-interfaces Minor
@solana/program-client-core Minor
@solana/programs Minor
@solana/promises Minor
@solana/react Minor
@solana/rpc-api Minor
@solana/rpc-graphql Minor
@solana/rpc-parsed-types Minor
@solana/rpc-spec-types Minor
@solana/rpc-spec Minor
@solana/rpc-subscriptions-api 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/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

Copy link
Copy Markdown
Member Author

mcintyre94 commented Apr 8, 2026

@bundlemon
Copy link
Copy Markdown

bundlemon Bot commented Apr 8, 2026

BundleMon

Files updated (4)
Status Path Size Limits
kit/dist/index.browser.mjs
3.28KB (+509B +17.86%) -
kit/dist/index.native.mjs
3.28KB (+508B +17.83%) -
kit/dist/index.node.mjs
3.28KB (+508B +17.84%) -
@solana/kit production bundle
kit/dist/index.production.min.js
46.62KB (+233B +0.49%) -
Unchanged files (138)
Status Path Size Limits
errors/dist/index.node.mjs
19.38KB -
errors/dist/index.browser.mjs
19.36KB -
errors/dist/index.native.mjs
19.36KB -
rpc-graphql/dist/index.browser.mjs
18.82KB -
rpc-graphql/dist/index.native.mjs
18.81KB -
rpc-graphql/dist/index.node.mjs
18.81KB -
wallet-account-signer/dist/index.node.mjs
16.43KB -
wallet-account-signer/dist/index.native.mjs
16.42KB -
wallet-account-signer/dist/index.browser.mjs
16.42KB -
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 -
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 -
offchain-messages/dist/index.browser.mjs
4.89KB -
offchain-messages/dist/index.native.mjs
4.89KB -
offchain-messages/dist/index.node.mjs
4.89KB -
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 -
rpc-transformers/dist/index.browser.mjs
3.16KB -
rpc-transformers/dist/index.native.mjs
3.16KB -
rpc-transformers/dist/index.node.mjs
3.16KB -
signers/dist/index.browser.mjs
3.14KB -
signers/dist/index.native.mjs
3.14KB -
signers/dist/index.node.mjs
3.14KB -
react/dist/index.browser.mjs
3.09KB -
react/dist/index.native.mjs
3.09KB -
react/dist/index.node.mjs
3.09KB -
addresses/dist/index.browser.mjs
2.93KB -
addresses/dist/index.native.mjs
2.92KB -
addresses/dist/index.node.mjs
2.92KB -
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.41KB -
sysvars/dist/index.browser.mjs
2.37KB -
sysvars/dist/index.native.mjs
2.37KB -
sysvars/dist/index.node.mjs
2.37KB -
transaction-confirmation/dist/index.native.mj
s
2.36KB -
transaction-confirmation/dist/index.browser.m
js
2.35KB -
rpc-subscriptions-spec/dist/index.node.mjs
2.21KB -
rpc-subscriptions-spec/dist/index.native.mjs
2.17KB -
rpc-subscriptions-spec/dist/index.browser.mjs
2.16KB -
keys/dist/index.browser.mjs
2.1KB -
keys/dist/index.native.mjs
2.1KB -
keys/dist/index.node.mjs
2.1KB -
subscribable/dist/index.node.mjs
1.97KB -
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 -
subscribable/dist/index.native.mjs
1.92KB -
subscribable/dist/index.browser.mjs
1.91KB -
rpc-transport-http/dist/index.browser.mjs
1.91KB -
rpc-transport-http/dist/index.native.mjs
1.9KB -
rpc/dist/index.native.mjs
1.81KB -
rpc/dist/index.browser.mjs
1.8KB -
rpc-transport-http/dist/index.node.mjs
1.72KB -
rpc-types/dist/index.browser.mjs
1.53KB -
rpc-types/dist/index.native.mjs
1.53KB -
rpc-types/dist/index.node.mjs
1.53KB -
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-api/dist/index.browser.mjs
976B -
rpc-api/dist/index.native.mjs
975B -
rpc-api/dist/index.node.mjs
973B -
compat/dist/index.browser.mjs
969B -
compat/dist/index.native.mjs
968B -
compat/dist/index.node.mjs
966B -
rpc-spec-types/dist/index.browser.mjs
962B -
rpc-spec-types/dist/index.native.mjs
961B -
rpc-spec-types/dist/index.node.mjs
959B -
rpc-subscriptions-api/dist/index.native.mjs
870B -
rpc-subscriptions-api/dist/index.node.mjs
869B -
rpc-subscriptions-api/dist/index.browser.mjs
868B -
rpc-spec/dist/index.browser.mjs
852B -
rpc-spec/dist/index.native.mjs
851B -
rpc-spec/dist/index.node.mjs
850B -
promises/dist/index.browser.mjs
799B -
promises/dist/index.native.mjs
798B -
promises/dist/index.node.mjs
797B -
assertions/dist/index.browser.mjs
783B -
instructions/dist/index.browser.mjs
771B -
instructions/dist/index.native.mjs
770B -
instructions/dist/index.node.mjs
768B -
plugin-core/dist/index.browser.mjs
749B -
plugin-core/dist/index.native.mjs
749B -
plugin-core/dist/index.node.mjs
747B -
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 -
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
137B -
codecs/dist/index.native.mjs
136B -
codecs/dist/index.node.mjs
134B -
event-target-impl/dist/index.browser.mjs
133B -
ws-impl/dist/index.node.mjs
131B -
text-encoding-impl/dist/index.browser.mjs
122B -
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 +1.72KB +0.35%

Final result: ✅

View report in BundleMon website ➡️


Current branch size history | Target branch size history

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 8, 2026

Documentation Preview: https://kit-docs-nofv92u3r-anza-tech.vercel.app

@mcintyre94 mcintyre94 requested a review from lorisleiva April 8, 2026 09:14
@mcintyre94 mcintyre94 marked this pull request as ready for review April 8, 2026 09:14
Copy link
Copy Markdown
Member

@lorisleiva lorisleiva left a comment

Choose a reason for hiding this comment

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

Nice! I love that you can wrap a reactive store in another reactive store whilst providing an RPC pending request for the initial fetch. 👌

Comment thread packages/kit/src/create-reactive-store-from-rpc-and-subscription.ts Outdated
Comment thread packages/kit/src/create-reactive-store-from-rpc-and-subscription.ts Outdated
@mcintyre94 mcintyre94 force-pushed the rpc-subscriptions-w-slot branch from 2cfa212 to cbdcf38 Compare April 8, 2026 12:56
@mcintyre94 mcintyre94 force-pushed the subscriptions-reactive branch from 632c232 to 20263d3 Compare April 8, 2026 12:56
@mcintyre94 mcintyre94 changed the base branch from subscriptions-reactive to graphite-base/1528 April 8, 2026 12:57
@mcintyre94 mcintyre94 force-pushed the graphite-base/1528 branch from 20263d3 to f53ce07 Compare April 8, 2026 13:14
@mcintyre94 mcintyre94 force-pushed the rpc-subscriptions-w-slot branch from cbdcf38 to efcc9b8 Compare April 8, 2026 13:14
@mcintyre94 mcintyre94 changed the base branch from graphite-base/1528 to main April 8, 2026 13:14
This creates a reactive store that combines an RPC and RPC Subscription,
that can both map to the same data type. It exposes the latest value
from either source, by slot comparison.
@mcintyre94 mcintyre94 force-pushed the rpc-subscriptions-w-slot branch from efcc9b8 to 7fc0245 Compare April 8, 2026 13:25
@mcintyre94 mcintyre94 added this pull request to the merge queue Apr 8, 2026
Merged via the queue into main with commit 09a7509 Apr 8, 2026
14 checks passed
@mcintyre94 mcintyre94 deleted the rpc-subscriptions-w-slot branch April 8, 2026 13:41
@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 Apr 23, 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.

2 participants