Skip to content

Update the example app to use transaction plan/send - #1872

Merged
mcintyre94 merged 1 commit into
mainfrom
example-app-instruction-plan
Aug 5, 2026
Merged

Update the example app to use transaction plan/send#1872
mcintyre94 merged 1 commit into
mainfrom
example-app-instruction-plan

Conversation

@mcintyre94

@mcintyre94 mcintyre94 commented Jul 30, 2026

Copy link
Copy Markdown
Member

This PR updates the react example app to use our transaction plan/send functions, instead of hand-crafting transactions. Note that in a previous PR I added solanaRpc to the client, so it already has transaction plan/send functions available.

  • The RPC plugin is updated to the latest version, with its improved CU calculation
  • The sign + send panel is refactored to use useSendTransaction
  • The other panels are refactored to use client.planTransaction to construct their transaction

@changeset-bot

changeset-bot Bot commented Jul 30, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: c490db6

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@bundlemon

bundlemon Bot commented Jul 30, 2026

Copy link
Copy Markdown

BundleMon

Unchanged files (150)
Status Path Size Limits
@solana/kit production bundle
kit/dist/index.production.min.js
55.54KB -
errors/dist/index.node.mjs
21.6KB -
errors/dist/index.browser.mjs
21.58KB -
errors/dist/index.native.mjs
21.58KB -
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.31KB -
wallet-account-signer/dist/index.browser.mjs
18.29KB -
wallet-account-signer/dist/index.native.mjs
18.29KB -
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 -
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 -
react/dist/index.browser.mjs
5.13KB -
react/dist/index.native.mjs
5.12KB -
react/dist/index.node.mjs
5.12KB -
fixed-points/dist/index.browser.mjs
5.08KB -
fixed-points/dist/index.native.mjs
5.07KB -
fixed-points/dist/index.node.mjs
5.07KB -
offchain-messages/dist/index.browser.mjs
5.06KB -
offchain-messages/dist/index.native.mjs
5.06KB -
offchain-messages/dist/index.node.mjs
5.06KB -
kit/dist/index.browser.mjs
4.61KB -
kit/dist/index.native.mjs
4.6KB -
kit/dist/index.node.mjs
4.6KB -
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 -

No change in files bundle size

Final result: ✅

View report in BundleMon website ➡️


Current branch size history | Target branch size history

@mcintyre94

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

Refactors the three transfer panels in the react-app example to use the new client-level planTransaction / useSendTransaction helpers instead of hand-rolling transaction messages, and bumps @solana/kit-plugin-rpc from 0.13.00.15.0 to pick up the improved CU calculation. Net result is a substantial simplification: SolanaSignAndSendTransactionFeaturePanel loses ~44 lines, and SolanaSignTransactionFeaturePanel / SolanaPartialSignTransactionFeaturePanel each drop the pipe(...) + blockhash-fetch boilerplate. assertCanSignAndSendTransactions and its now-unused imports are removed from walletCapability.ts, and the surrounding ErrorBoundary around the sign-and-send panel is dropped in root.tsx.

Example-app-only, private package — no changeset needed.

Things to watch out for

1. Behavioral change in sign-and-send: source is now client.payer instead of the connected wallet's signer. These should be equivalent here, because walletSigner({ chain }) is registered before solanaRpc(...) in ClientProvider.tsx and sets client.payer from the connected wallet — but it's worth double-checking that the panel behaves the same when there are multiple connected wallets or when the connected account is changed mid-session. If client.payer ever diverges from "the currently selected wallet signer" in this app, the transfer's source would silently follow client.payer rather than the account the user thinks they're operating from.

2. Removed capability guard for sign-and-send. The old code called assertCanSignAndSendTransactions(signer) at render time inside an ErrorBoundary with FeatureNotSupportedCallout, so an account that doesn't expose solana:signAndSendTransaction got a nice "not supported" UI. With this PR, the panel is unconditionally rendered and any incompatibility only surfaces at dispatch time via useSendTransaction's error field (which routes to ErrorDialog). That's a minor UX regression for accounts on the connected wallet that only support signTransaction — worth confirming whether useSendTransaction (or client.sendTransaction under the hood) picks a compatible signer automatically, or whether this now fails at click-time for those accounts. If it does fail at click time, consider keeping a lightweight capability check so the button is disabled rather than throwing after the user tries.

3. Partial-sign panel: fee-payer swap after planning. In SolanaPartialSignTransactionFeaturePanel, client.planTransaction(...) runs with the wallet as the implicit payer (via client.payer), then setTransactionMessageFeePayerSigner(feePayerSigner, planned) swaps the fee payer to the mock server signer. This works, and the comment on the line is helpful, but note that the plugin-rpc 0.15 CU calculation is done against the wallet-as-payer rather than the actual mock fee payer. In practice the CU estimate should be identical for a simple SOL transfer, but if this pattern is ever copied for a transaction where the fee payer materially changes account access (e.g. affects which accounts are writable), the estimate could drift. Not a blocker for the example — flagging for future readers.

Notes for subsequent reviewers

  • Verify with Callum whether useSendTransaction gracefully handles the case where the connected wallet account cannot sign-and-send (point 2 above). If it doesn't, either the ErrorBoundary/capability guard should stay, or the submit button should be disabled based on the connected account's capabilities.
  • The lockfile change is just the kit-plugin-rpc bump; @solana/kit-plugin-instruction-plan is still pinned to 0.13.0 as a transitive — nothing to do, but worth eyeballing that this is intentional and not a version-skew concern for the plugin-rpc author.
  • The formatting change in root.tsx around the ErrorBoundary for Balance is cosmetic (line wrap) and unrelated to the feature work — fine to keep.

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

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

@mcintyre94
mcintyre94 force-pushed the instruction-plan-hooks branch from 2066a9f to eecae8b Compare July 31, 2026 10:33
@mcintyre94
mcintyre94 force-pushed the example-app-instruction-plan branch 2 times, most recently from 921ceb8 to 220e70c Compare July 31, 2026 13:32
@mcintyre94

Copy link
Copy Markdown
Member Author

Added back the capability check and ErrorBoundary, these still make sense for sign + send. We check for assertCanSignTransactions because that's the capability the transaction executor uses.

Comment on lines +88 to 96
const message = await pipe(
planned,
// Set the lifetime to the latest blockhash
tx => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
// Estimate and set the resource limits for the transaction
async tx => await estimateAndSetResourceLimits(tx, { abortSignal: signal }),
);
const transaction = await signTransactionMessageWithSigners(message);
assertIsSendableTransaction(transaction);

@mcintyre94 mcintyre94 Jul 31, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Highlighting this, I think this may be an API gap

We refactor to use client.planTransaction which I think is much better, but our default transaction planner intentionally does not set the lifetime nor the CUs, both of which are left to the default transaction executor. See https://github.com/anza-xyz/kit-plugins/blob/497b55fcdd7c1df905083e7eda160e06b220d555/packages/kit-plugin-rpc/src/transaction-plan-executor.ts#L143

This means that the app needs to handle adding the blockhash, and setting CUs. The default executor now has a quite sophisticated approach to CUs, but I don't think an app can easily use it outside of the executor.

My instinct here is that it may make sense to split out a client.signTransaction from client.sendTransaction. This would return a fully signed transaction that is ready to send but has not been sent.

The default implementation of this in our solanaRpc plugin would be the current default executor, except without the call to sendAndConfirm, it'd just return the transaction after signing it.

This panel would then collapse to simply client.signTransaction(transferInstruction) here, and then whatever send logic the app chooses to use as the second part. This would also be a clean migration from the wallet-adapter signTransaction API.

This needs a bit more design thought, so leaving as-is in this PR - this demonstrates the small amount of transaction logic an app will currently still need for the sign-then-send flow.

@mcintyre94
mcintyre94 force-pushed the example-app-instruction-plan branch from 220e70c to 5135262 Compare July 31, 2026 15:32
@mcintyre94
mcintyre94 marked this pull request as ready for review July 31, 2026 15:35
@mcintyre94
mcintyre94 requested a review from lorisleiva July 31, 2026 15:35
@mcintyre94
mcintyre94 force-pushed the example-app-instruction-plan branch from 5135262 to d61fd65 Compare July 31, 2026 17:13
@mcintyre94
mcintyre94 force-pushed the instruction-plan-hooks branch from eecae8b to 8da5a8a Compare July 31, 2026 17:13

mcintyre94 commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

Merge activity

@mcintyre94
mcintyre94 force-pushed the instruction-plan-hooks branch from 8da5a8a to eb09af5 Compare August 4, 2026 16:21
@mcintyre94
mcintyre94 force-pushed the example-app-instruction-plan branch from d61fd65 to 585e985 Compare August 4, 2026 16:21
@mcintyre94
mcintyre94 changed the base branch from instruction-plan-hooks to graphite-base/1872 August 5, 2026 08:04
@mcintyre94
mcintyre94 changed the base branch from graphite-base/1872 to main August 5, 2026 16:54
@mcintyre94
mcintyre94 force-pushed the example-app-instruction-plan branch from 585e985 to c490db6 Compare August 5, 2026 16:55
@mcintyre94
mcintyre94 merged commit 9e96dc7 into main Aug 5, 2026
12 checks passed
@mcintyre94
mcintyre94 deleted the example-app-instruction-plan branch August 5, 2026 17:08
@github-actions

github-actions Bot commented Aug 5, 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