Skip to content

refactor!: put the wallet signer on the kit client (payer/identity) instead of grill's WalletProvider - #255

Draft
macalinao wants to merge 10 commits into
masterfrom
macalinao/wallet-signer-on-client
Draft

macalinao wants to merge 10 commits into
masterfrom
macalinao/wallet-signer-on-client

Conversation

@macalinao

@macalinao macalinao commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

What / why

Grill kept its own wallet plumbing (WalletProvider + WalletContext + useKitWallet) next to the kit client. With @solana/kit 8 the client can carry the signer itself through the payer / identity capabilities from @solana/plugin-interfaces, and @solana/react already has usePayer(client) / useIdentity(client) to read them. This PR moves the wallet signer onto the client and deletes grill's wallet context.

Follows #250, which moved grill onto @solana/react's ClientProvider. As agreed, @solana/wallet-adapter and @macalinao/wallet-adapter-compat stay. This does not migrate to wallet-standard / SelectedWalletAccountContextProvider.

Changes

  • grill
    • New reactiveSigner(initial?) kit plugin. It installs payer and identity (the same TransactionSendingSigner<Address>), subscribeToPayer / subscribeToIdentity, and a setSigner(signer | null) method. It also exports the ClientWithReactiveSigner type.
    • New useWalletSigner(): TransactionSendingSigner<Address> | null. It reads client.payer through usePayer from @solana/react.
    • useConnectedWallet() now uses useWalletSigner(). It still throws "Wallet is not connected".
    • GrillHeadlessProvider (the useSendTX path) and useSignTX read the signer from useWalletSigner().
    • Removed WalletProvider, WalletProviderProps, WalletContext, WalletContextState and useKitWallet.
  • wallet-adapter-compat: WalletAdapterCompatProvider still builds the TransactionSendingSigner from wallet-adapter. It now calls client.setSigner(...) in a layout effect instead of rendering grill's WalletProvider. It gets the client through useClientCapability({ capability: "setSigner" }), so a missing plugin fails with a clear error. @solana/react is now a peer dependency.
  • react-quarry: useKitWallet().signer becomes useWalletSigner().
  • example-dapp:
    • The client installs reactiveSigner(), and WalletAdapterCompatProvider moves inside ClientProvider.
    • The examples use useWalletSigner.
    • The wallet page now shows the signer held on the client: usePayer / useIdentity agree with useWalletSigner, and a nested ClientProvider with an empty reactiveSigner() stands in for the old nested WalletProvider signer={null}.
  • Docs: CLAUDE.md (Provider Hierarchy, Kit Wallet Integration), the root, grill and compat READMEs, and docs/grill/*.
  • Tests:
    • Unit tests for the plugin: throwing getters, non-enumerable getters, notify and unsubscribe, no-op when set to the same signer, keeping earlier plugins' capabilities.
    • SSR render tests (react-dom/server) for useWalletSigner / useConnectedWallet: connected, empty, no payer capability, a payer that is not a sending signer, and the throw.

Design decisions

  • Is there a kit plugin for this already? @solana/kit-plugin-signer only installs a static payer / identity. @solana/kit-plugin-wallet installs a reactive one, but it is built on wallet-standard, which is out of scope. So grill adds a small plugin, reactiveSigner, about 50 lines of logic. It follows kit-plugin-wallet's conventions:

    • non-enumerable getters that throw SOLANA_ERROR__WALLET__NO_SIGNER_CONNECTED while no signer is set, so Object.entries and spreading the client don't throw;
    • subscribeTo* functions from @solana/plugin-interfaces for reactivity.

    usePayer catches the throw and returns undefined, and it re-renders through subscribeToPayer (useSyncExternalStore).

  • payer vs identity: the plugin sets both to the same signer, like kit-plugin-wallet's walletSigner(), so kit-native code that reads either one works. Grill itself reads payer only. Grill's send and sign paths use the signer as the transaction fee payer, so payer is the capability that matches. Reading one capability everywhere also means grill can't end up with two different signers. A side effect: walletSigner() from @solana/kit-plugin-wallet can replace reactiveSigner() + WalletAdapterCompatProvider with no grill changes.

  • Narrowing: usePayer returns a TransactionSigner, but grill's send path needs a TransactionSendingSigner. useWalletSigner returns the payer only when isTransactionSendingSigner(payer), and null otherwise. A client with no payer capability at all also gives null, so read-only apps don't need the plugin.

  • No deprecated aliases: the useKitWallet / WalletProvider semantics (context-scoped, needed a provider) no longer apply. Keeping them would leave behind the code this PR is meant to delete. This is a breaking minor, since all packages are pre-1.0.

Migration

const client = createClient()
  .use(solanaRpcConnection({ rpcUrl }))
  .use(reactiveSigner()); // new

<ClientProvider client={client}>
  <WalletAdapterCompatProvider>   {/* now inside ClientProvider */}
    <GrillProvider>...</GrillProvider>
  </WalletAdapterCompatProvider>
</ClientProvider>

// const { signer } = useKitWallet();
const signer = useWalletSigner();

Caveats / follow-ups

  • A payer that can't send transactions, such as a keypair from @solana/kit-plugin-signer, counts as "no wallet" for grill. Follow-up: send through rpc when the payer is only a partial signer.
  • The signer is now global to the client and no longer scoped by a React subtree. To scope a different signer, nest a second ClientProvider (the example page shows this).
  • WalletAdapterCompatProvider sets the signer in a layout effect, so on the very first render the children see null until the effect runs. With autoConnect this matches the old behaviour, because the wallet connects asynchronously anyway.
  • The pre-existing signer.signMessage snippet in docs/grill/04-transactions.md was not changed. signMessage is not on a transaction signer.
  • Changeset: .changeset/wallet-signer-on-client.md (grill, react-quarry and wallet-adapter-compat, all minor).

bun run build, bun run lint and bun run test pass locally.

Summary by Sourcery

Move wallet signer state from Grill React context onto the Solana Kit client and update integrations to consume the reactive client capabilities.

New Features:

  • Add the reactiveSigner kit plugin and useWalletSigner hook for reactive client-based wallet signer access.
  • Expose the connected signer through the kit client's payer and identity capabilities for use by Grill transaction flows and Solana React hooks.

Enhancements:

  • Replace Grill's wallet context and provider plumbing with client-scoped signer capabilities.
  • Update wallet-adapter compatibility and React Quarry integrations to use the client-held signer.
  • Remove the deprecated wallet APIs and GrillSigner type in favor of standard Solana Kit signer types.

Documentation:

  • Update project, package, compatibility, example application, and Grill documentation for the new client-based wallet integration and migration path.

Tests:

  • Add coverage for reactive signer capabilities, subscriptions, signer replacement, missing capabilities, signer type narrowing, and disconnected-wallet behavior.

Chores:

  • Update the example dapp and package metadata for the new provider hierarchy and peer dependencies.

  • Removed the GrillSigner type. Use TransactionSendingSigner<Address> from @solana/kit instead.

Add a reactiveSigner() kit plugin that installs a mutable signer as the
client's payer and identity (with subscribeToPayer/subscribeToIdentity),
and read it with @solana/react's usePayer via a new useWalletSigner hook.

Remove WalletProvider, WalletContext and useKitWallet.
…client

WalletAdapterCompatProvider now calls client.setSigner() from grill's
reactiveSigner() plugin instead of rendering grill's WalletProvider, so it
must be rendered inside @solana/react's ClientProvider.
Install reactiveSigner() on the app's client, move
WalletAdapterCompatProvider inside ClientProvider, and switch the examples
to useWalletSigner. The wallet page now demonstrates the client-held signer
(usePayer/useIdentity, nested ClientProvider) instead of WalletProvider.
@coderabbitai

coderabbitai Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai

sourcery-ai Bot commented Sep 28, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR replaces grill's wallet-specific React context with a reactive signer held on the @solana/kit client as both payer and identity, bridges wallet-adapter into that capability through WalletAdapterCompatProvider, updates all consumers and examples to use useWalletSigner(), and removes the deprecated wallet APIs with accompanying tests, documentation, and changesets.

Sequence diagram for wallet signer synchronization

sequenceDiagram
  participant Wallet as WalletAdapter
  participant Compat as WalletAdapterCompatProvider
  participant Client as KitClient
  participant React as useWalletSigner
  participant Grill as GrillTransactionPath

  Wallet->>Compat: wallet state changes
  Compat->>Compat: createWalletTransactionSendingSigner
  Compat->>Client: setSigner(signer)
  Client-->>React: subscribeToPayer notification
  React->>Client: usePayer(client)
  Client-->>React: payer or no connected signer
  React-->>Grill: GrillSigner or null
  Grill->>Grill: useSendTX or useSignTX
Loading

File-Level Changes

Change Details Files
Moved wallet signer state from grill-owned React context into reactive kit client capabilities.
  • Added the reactiveSigner() plugin with mutable payer/identity getters, subscriptions, setSigner, and client typing.
  • Added useWalletSigner() via @solana/react's usePayer, filtering for transaction-sending signers.
  • Updated connected-wallet, send, sign, and headless paths to consume the client signer.
  • Removed WalletProvider, wallet context types, and useKitWallet.
  • Added plugin behavior tests and SSR hook tests covering missing, disconnected, partial, and connected signers.
packages/grill/src/plugins/reactive-signer.ts
packages/grill/src/plugins/reactive-signer.test.ts
packages/grill/src/hooks/use-wallet-signer.ts
packages/grill/src/hooks/use-wallet-signer.test.tsx
packages/grill/src/hooks/use-connected-wallet.ts
packages/grill/src/hooks/use-sign-tx.ts
packages/grill/src/providers/grill-headless-provider.tsx
packages/grill/src/contexts/wallet-context.ts
packages/grill/src/providers/wallet-provider.tsx
packages/grill/src/hooks/use-kit-wallet.ts
Changed wallet-adapter compatibility to synchronize its signer with the kit client.
  • Uses useClientCapability({ capability: "setSigner" }) to require the reactive signer plugin with a clear configuration error.
  • Builds the adapter signer as before and writes or clears it in a layout effect.
  • Made @solana/react a peer dependency and updated provider documentation and examples.
packages/wallet-adapter-compat/src/wallet-adapter-compat-provider.tsx
packages/wallet-adapter-compat/package.json
packages/wallet-adapter-compat/README.md
Migrated dependent packages and the example application to the client-scoped signer model.
  • Replaced useKitWallet().signer calls with useWalletSigner().
  • Installed reactiveSigner() and moved WalletAdapterCompatProvider inside ClientProvider.
  • Demonstrated shared payer/identity reads and signer scoping through nested client providers.
  • Updated type-coverage exports and example wallet flows.
packages/react-quarry/src/hooks/use-quarry-claim-mm.ts
packages/react-quarry/src/hooks/use-quarry-deposit-mm.ts
packages/react-quarry/src/hooks/use-quarry-withdraw-mm.ts
packages/react-quarry/src/providers/merge-miner-provider.tsx
apps/example-dapp/src/App.tsx
apps/example-dapp/src/components/SimpleDashboard.tsx
apps/example-dapp/src/components/examples/connect-wallet-gate.tsx
apps/example-dapp/src/lib/grill-type-coverage.ts
apps/example-dapp/src/routes/examples/index.tsx
apps/example-dapp/src/routes/examples/pdas.tsx
apps/example-dapp/src/routes/examples/transfer-sol.tsx
apps/example-dapp/src/routes/examples/wallet.tsx
apps/example-dapp/src/routes/examples/wrapped-sol.tsx
Updated public migration guidance and release metadata for the breaking pre-1.0 API change.
  • Documented the new provider hierarchy, plugin setup, client capabilities, and nullable/throwing hook behavior.
  • Added minor changesets for grill, react-quarry, and wallet-adapter-compat.
  • Refreshed repository, package, and example documentation.
.changeset/wallet-signer-on-client.md
CLAUDE.md
README.md
docs/grill/01-intro.md
docs/grill/02-setup.md
docs/grill/03-accounts.md
docs/grill/04-transactions.md
packages/grill/README.md
apps/example-dapp/README.md
bun.lock

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
grill-example-dapp aa9a403 Commit Preview URL

Branch Preview URL
Oct 30 2026, 06:59 AM

…ddress>

The Partial<signTransactions> half of GrillSigner was never read in grill;
useSignTX detects partial/modifying signers at runtime with kit's guards.
Use kit's TransactionSendingSigner<Address> directly instead.
…gner-on-client

# Conflicts:
#	bun.lock
#	packages/grill/src/hooks/use-connected-wallet.ts
#	packages/grill/src/providers/grill-headless-provider.tsx
#	packages/grill/src/types.ts
#	packages/grill/src/utils/internal/create-sign-tx.test.ts
#	packages/grill/src/utils/internal/create-sign-tx.ts
#	packages/react-quarry/src/hooks/use-quarry-claim-mm.ts
#	packages/react-quarry/src/hooks/use-quarry-deposit-mm.ts
#	packages/react-quarry/src/hooks/use-quarry-withdraw-mm.ts
#	packages/wallet-adapter-compat/package.json
- use-send-tx (new on master) reads the signer with useWalletSigner instead of the removed useKitWallet
- use-send-tx.test drops the removed WalletProvider wrapper
- convert this branch's new grill files (reactive-signer, use-wallet-signer and tests) to granular @solana/* imports
- wallet-adapter-compat keeps @solana/kit as a peer since it now depends on @solana/react
- changeset: point at @solana/signers and note the kit peer on wallet-adapter-compat
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.

1 participant