Skip to content

Add helpers to create client interfaces from a raw Rpc - #1898

Merged
lorisleiva merged 1 commit into
mainfrom
08-07-add_helpers_to_create_client_interfaces_from_a_raw_rpc
Aug 9, 2026
Merged

Add helpers to create client interfaces from a raw Rpc#1898
lorisleiva merged 1 commit into
mainfrom
08-07-add_helpers_to_create_client_interfaces_from_a_raw_rpc

Conversation

@lorisleiva

Copy link
Copy Markdown
Member

Add createClientWithGetMinimumBalanceFromRpc, createClientWithFetchAccountsFromRpc and createClientWithInterfacesFromRpc to @solana/kit. These convenience helpers let consumers that only have a raw Rpc object construct the corresponding client interfaces (ClientWithGetMinimumBalance and ClientWithFetchAccounts) without assembling a full Kit client. createClientWithInterfacesFromRpc fills in whichever interfaces the RPC supports and narrows its return type accordingly.

@lorisleiva
lorisleiva marked this pull request as ready for review August 7, 2026 11:59
@changeset-bot

changeset-bot Bot commented Aug 7, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 44d8f96

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

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

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

lorisleiva commented Aug 7, 2026

Copy link
Copy Markdown
Member Author

@bundlemon

bundlemon Bot commented Aug 7, 2026

Copy link
Copy Markdown

BundleMon

Files updated (4)
Status Path Size Limits
kit/dist/index.browser.mjs
4.86KB (+256B +5.43%) -
kit/dist/index.native.mjs
4.85KB (+256B +5.43%) -
kit/dist/index.node.mjs
4.85KB (+256B +5.43%) -
@solana/kit production bundle
kit/dist/index.production.min.js
55.79KB (+157B +0.28%) -
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 -
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 -
rpc-api/dist/index.browser.mjs
1.04KB -
rpc-api/dist/index.native.mjs
1.04KB -
rpc-api/dist/index.node.mjs
1.04KB -
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 +925B +0.16%

Final result: ✅

View report in BundleMon website ➡️


Current branch size history | Target branch size history

@lorisleiva

Copy link
Copy Markdown
Member Author

@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 three convenience helpers to @solana/kitcreateClientWithGetMinimumBalanceFromRpc, createClientWithFetchAccountsFromRpc, and createClientWithInterfacesFromRpc — that wrap a raw Rpc object into the ClientWithGetMinimumBalance and/or ClientWithFetchAccounts plugin interfaces without assembling a full Kit client. It ships with unit tests, typetests, README docs, and a minor changeset (appropriate for new features).

The API shape, type-level narrowing in ClientInterfacesFromRpc<TRpc>, typetests, and docs are all well done. Unfortunately there's a fundamental runtime problem that the unit tests don't catch. (I would have marked this "request changes", but I don't have that permission on this repo — treat this as such.)

Critical: in rpc capability checks never pass for real Kit Rpc objects

Every Rpc produced by createRpc / createSolanaRpc / createSolanaRpcFromTransport is a Proxy (see makeProxy in packages/rpc-spec/src/rpc.ts). That proxy defines only get, defineProperty, and deleteProperty traps, and its target is the createJsonRpcApi proxy — which itself has no has trap and wraps a plain {} target. The in operator therefore falls through both proxies to Reflect.has({}, ...) and always returns false for every method name.

Consequences:

  • createClientWithInterfacesFromRpc(createSolanaRpc(url)) returns an empty object at runtime, while its type claims both interfaces — client.getMinimumBalance is not a function for the primary real-world use case.
  • createClientWithFetchAccountsFromRpc on a real RPC never takes the getMultipleAccounts path and silently degrades to N getAccountInfo calls, contradicting the documented behaviour.

The unit tests pass only because they use plain-object mocks, where in works. Note that typeof rpc.getAccountInfo === 'function' is no better as a detection mechanism — the get trap returns a function for any property name — so runtime feature detection on these proxies is fundamentally unreliable. Some options to consider:

  1. Since ClientInterfacesFromRpc<TRpc> already narrows the return type statically, always build both interfaces at runtime and let the cast hide the untyped ones (the current not.toHaveProperty test expectations would need to change).
  2. Accept an explicit config telling the helper which interfaces/methods to back.
  3. Add a has trap to the RPC proxies in @solana/rpc-spec — though that's a larger change and createJsonRpcApi is intentionally a catch-all, so there may be no method list to consult.

Other issues

  • The withoutHeader computation derives a per-byte rate via integer division, which can silently underestimate rent (see inline comment). An exact subtraction-based approach is available.
  • Tests: please add at least one test that exercises these helpers against a proxy-backed RPC (e.g. built with createRpc + createJsonRpcApi and a mock transport, or createSolanaRpcFromTransport). That test would have caught the in issue immediately, and it guards against future regressions in how the proxies interact with these helpers.

Notes for subsequent reviewers

  • Verify the intended semantics of withoutHeader against the runtime's rent formula ((BASE_ACCOUNT_SIZE + data_len) × rate) — the current implementation and the interface docs in @solana/plugin-interfaces agree on intent, but the arithmetic deserves a close look.
  • The changeset (minor) and README additions look consistent with repo conventions; no concerns there.
  • The typetests are thorough and the ClientInterfacesFromRpc<TRpc> conditional type handles the Rpc<SolanaRpcApi> superset case correctly.

Comment thread packages/kit/src/create-client-with-interfaces-from-rpc.ts Outdated
Comment thread packages/kit/src/create-client-with-interfaces-from-rpc.ts Outdated
Comment thread packages/kit/src/create-client-with-interfaces-from-rpc.ts
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

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

Base automatically changed from 08-07-add_a_clientwithfetchaccounts_interface to main August 7, 2026 16:17
Add `createClientWithGetMinimumBalanceFromRpc`, `createClientWithFetchAccountsFromRpc` and `createClientWithInterfacesFromRpc` to `@solana/kit`. These convenience helpers let consumers that only have a raw `Rpc` object construct the corresponding client interfaces (`ClientWithGetMinimumBalance` and `ClientWithFetchAccounts`) without assembling a full Kit client. `createClientWithInterfacesFromRpc` fills in whichever interfaces the RPC supports and narrows its return type accordingly.
@lorisleiva
lorisleiva force-pushed the 08-07-add_helpers_to_create_client_interfaces_from_a_raw_rpc branch from 5209565 to 44d8f96 Compare August 7, 2026 16:29

@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.

Re-review

Verdict: approve. (I don't have permission to submit a formal APPROVE review on this repo, so this is a comment review — please treat it as an approval.)

All points from my previous review have been addressed — nicely done:

  • in rpc capability checks (critical): Resolved by building both interfaces unconditionally and letting the ClientInterfacesFromRpc<TRpc> return type do the narrowing (option 1 from my review). The docs, README, and JSDoc now clearly state that a raw Rpc's capabilities cannot be detected at runtime and that calling an unsupported method fails at the underlying RPC call. That's an honest, well-documented contract.
  • Proxy-backed RPC test: The new with a proxy-backed RPC describe block exercises the helpers against a real createRpc + createJsonRpcApi proxy with a mock transport, covering getMinimumBalance, the single-account getAccountInfo path, and the multi-account getMultipleAccounts path. This is exactly the regression guard that would have caught the original issue.
  • withoutHeader arithmetic: The per-byte rate is now derived from getMinimumBalanceForRentExemption(0n) with a documented argument for why the division by BASE_ACCOUNT_SIZE is exact (rate * 128 / 128 === rate under the runtime's linear rent formula). Single round-trip, exact under the current formula — good resolution.
  • Empty address list: fetchAccounts([]) now short-circuits to [] without issuing an RPC call, with test coverage.

Notes for subsequent reviewers

  • The exactness of the withoutHeader derivation relies on the header-only balance being an exact integer multiple of BASE_ACCOUNT_SIZE, which holds under the current rent parameters (lamports_per_byte_year × exemption_threshold is integral). If a cluster ever ran with rent parameters where that product is non-integral, a small truncation error could reappear — theoretical today, and the code comment documents the assumption, so nothing to change here.
  • Typetests cover all the interesting ClientInterfacesFromRpc<TRpc> branches, including rejection of single-account-method RPCs and the Rpc<SolanaRpcApi> superset case.

Changeset (minor), README, and export wiring all look consistent with repo conventions. Ship it.

@lorisleiva
lorisleiva requested a review from mcintyre94 August 7, 2026 16:36

@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!

@lorisleiva
lorisleiva added this pull request to the merge queue Aug 9, 2026
Merged via the queue into main with commit 4a5f717 Aug 9, 2026
13 checks passed
@lorisleiva
lorisleiva deleted the 08-07-add_helpers_to_create_client_interfaces_from_a_raw_rpc branch August 9, 2026 08:37
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

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

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.

3 participants