Skip to content

Add ClientWithSubscribeToPayer/Identity interfaces#1551

Merged
mcintyre94 merged 1 commit into
mainfrom
client-subscribe-signer
Apr 21, 2026
Merged

Add ClientWithSubscribeToPayer/Identity interfaces#1551
mcintyre94 merged 1 commit into
mainfrom
client-subscribe-signer

Conversation

@mcintyre94
Copy link
Copy Markdown
Member

This PR adds new types ClientWithSubscribeToPayer and ClientWithSubscribeToIdentity in plugin-interfaces. These allow a plugin (such as the wallet plugin) to notify subscribers when they modify these fields, without those consumers needing to know which plugin has made them reactive.

It's already the case that client.payer can be made dynamic by the wallet plugin, by using a get() function. This retains compatibility with the payer: TransactionSigner type. But in order to make this useful for a reactive UI that may for example render based on the current value of client.payer, we also need to be able to subscribe to changes.

Reactive consumers can then use client.subscribeToPayer(() => {}) to receive these notifications.

Alternatives considered:

  • Add these types to the wallet plugin only. This would work, but this would need to be duplicated by any other plugin that wants to make these fields reactive. In practice we'd be binding consumers (like a react library) to the wallet plugin's types. I think it makes more sense to define the types as plugin-interfaces and allow consumers to be agnostic to how the fields are made dynamic
  • Make payer and identity actually act as a reactive store (with subscribe and getValue functions), this would be a breaking change and annoying for most uses. These types provides the same functionality only to consumers that need to react to changes to these values. This is likely only reactive UI - anything using client.payer already gets the correct updated value

Extracted from anza-xyz/kit-plugins#200, which originally added these types to kit-plugin-signer.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 21, 2026

🦋 Changeset detected

Latest commit: 66f08b9

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

This PR includes changesets to release 46 packages
Name Type
@solana/plugin-interfaces Minor
@solana/kit Minor
@solana/program-client-core 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/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

@mcintyre94
Copy link
Copy Markdown
Member Author

@trevor-cortex

@bundlemon
Copy link
Copy Markdown

bundlemon Bot commented Apr 21, 2026

BundleMon

Unchanged files (144)
Status Path Size Limits
@solana/kit production bundle
kit/dist/index.production.min.js
47.45KB -
errors/dist/index.node.mjs
19.66KB -
errors/dist/index.browser.mjs
19.64KB -
errors/dist/index.native.mjs
19.64KB -
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.67KB -
wallet-account-signer/dist/index.native.mjs
16.65KB -
wallet-account-signer/dist/index.browser.mjs
16.65KB -
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 -
kit/dist/index.browser.mjs
3.72KB -
kit/dist/index.native.mjs
3.72KB -
kit/dist/index.node.mjs
3.72KB -
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.16KB -
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 -
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 -
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.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 -
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 -
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
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 -
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 -

No change in files bundle size

Final result: ✅

View report in BundleMon website ➡️


Current branch size history | Target branch size history

Copy link
Copy Markdown

@trevor-cortex trevor-cortex left a comment

Choose a reason for hiding this comment

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

Summary

Adds two new type-only exports to @solana/plugin-interfaces: ClientWithSubscribeToPayer and ClientWithSubscribeToIdentity, each advertising a sibling subscribeTo<Capability>(listener): () => void function on the client. The goal is to let reactive consumers (UI hooks, stores) observe changes to client.payer / client.identity without naming the specific plugin that made them reactive.

The PR is type-only — no runtime code, no behavior change for existing consumers. Comes with a changeset (minor on both plugin-interfaces and kit), README entry with a clear table, and compile-time type tests covering both interfaces and their composability.

Design notes

The convention is sound and fits the package's existing style. A few things worth calling out for subsequent reviewers / implementers:

  • Listener takes no arguments and consumers must re-read client.payer / client.identity after a notification. This is deliberate and matches the snapshot-based model used by React's useSyncExternalStore — the docblock explains it and notes that over-notification is acceptable. Good choice; a value-passing listener would make it too easy to leak stale references.
  • Idempotent unsubscribe is a documented contract ("must be idempotent") but not enforced by the type system. Worth verifying this actually holds in the wallet-plugin implementation (kit-plugins#200) — calling unsubscribe twice should not throw or double-remove.
  • Duck-typing via intersection (ClientWithPayer & ClientWithSubscribeToPayer) is the intended usage pattern shown in the README and @example. Plugins that don't make the capability reactive simply don't install the hook, and downstream libraries that want to stay agnostic can fall back to a no-op subscribe as the docblock suggests.
  • readonly modifier: the new types use readonly subscribeToPayer: ... while existing sibling interfaces (ClientWithPayer, ClientWithIdentity) use plain { payer: TransactionSigner }. Not worth changing — readonly is semantically correct here (consumers shouldn't reassign the subscribe fn), just noting the minor stylistic divergence.

Things to watch out for

  • The type name SubscribeToCapability reads ambiguously — it's the signature of a subscribe function, not "subscribe to a capability." Minor bikeshed, happy to leave as-is given the docblock clarifies it, but a name like SubscribeToFn would be slightly more self-describing. Not blocking.
  • Since this lands in plugin-interfaces, any future plugin that wants to participate in the convention needs to use exactly these names (subscribeToPayer, subscribeToIdentity) — the whole point is duck-typing. Worth keeping that in mind if a third pattern emerges later (e.g. subscribeToRpc); the convention should probably be documented somewhere more permanent than just this module's top-of-file comment once there are 3+ instances.

Looks good to me — no blocking concerns.

Comment thread packages/plugin-interfaces/src/__typetests__/subscribe-to-typetest.ts Dismissed
Comment thread packages/plugin-interfaces/src/__typetests__/subscribe-to-typetest.ts Dismissed
Comment thread packages/plugin-interfaces/src/__typetests__/subscribe-to-typetest.ts Dismissed
Comment thread packages/plugin-interfaces/src/__typetests__/subscribe-to-typetest.ts Dismissed
Comment thread packages/plugin-interfaces/src/__typetests__/subscribe-to-typetest.ts Dismissed
Comment thread packages/plugin-interfaces/src/__typetests__/subscribe-to-typetest.ts Dismissed
Comment thread packages/plugin-interfaces/src/__typetests__/subscribe-to-typetest.ts Dismissed
@mcintyre94 mcintyre94 force-pushed the client-subscribe-signer branch from a7a7aa6 to 8b00943 Compare April 21, 2026 10:49
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 21, 2026

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

@mcintyre94
Copy link
Copy Markdown
Member Author

Renamed SubscribeToCapability type to SubscribeToFn.

@mcintyre94 mcintyre94 requested a review from lorisleiva April 21, 2026 10:50
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.

Thanks!

Comment thread packages/plugin-interfaces/README.md
@mcintyre94 mcintyre94 force-pushed the client-subscribe-signer branch from 8b00943 to 66f08b9 Compare April 21, 2026 15:32
@mcintyre94 mcintyre94 enabled auto-merge April 21, 2026 15:33
@mcintyre94 mcintyre94 added this pull request to the merge queue Apr 21, 2026
Merged via the queue into main with commit d24f908 Apr 21, 2026
13 checks passed
@mcintyre94 mcintyre94 deleted the client-subscribe-signer branch April 21, 2026 15:43
@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

github-actions Bot commented May 6, 2026

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 May 6, 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