@solana/react-hooks 1.0.0-rc.1 → 1.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,432 +1,316 @@
1
1
  # @solana/react-hooks
2
2
 
3
- React hooks for `@solana/client`. Drop in the provider and call hooks instead of juggling RPC
4
- clients, wallets, and stores yourself.
5
-
6
- > **Status:** Experimental – breaking changes may land often.
3
+ React hooks for `@solana/client`. Wrap your app once and reach for hooks instead of wiring RPC, wallets, and stores by hand.
7
4
 
8
5
  ## Install
9
6
 
10
7
  ```bash
11
- pnpm add @solana/react-hooks
8
+ npm install @solana/client @solana/react-hooks
12
9
  ```
13
10
 
14
- ## Minimal example
11
+ ## Quickstart
15
12
 
16
- Build connectors first, create a client, and hand it to the combined provider.
13
+ 1. Choose wallet connectors (auto-discovery is the fastest way to start).
14
+ 2. Create a Solana client.
15
+ 3. Wrap your tree with `SolanaProvider` and use the hooks.
17
16
 
18
17
  ```tsx
19
- import { autoDiscover, backpack, createClient, phantom, solflare } from '@solana/client';
20
- import { SolanaProvider, useBalance, useConnectWallet, useWallet } from '@solana/react-hooks';
18
+ import { autoDiscover, createClient } from "@solana/client";
19
+ import {
20
+ SolanaProvider,
21
+ useBalance,
22
+ useWalletConnection,
23
+ } from "@solana/react-hooks";
21
24
 
22
- const walletConnectors = [...phantom(), ...solflare(), ...backpack(), ...autoDiscover()];
23
25
  const client = createClient({
24
- endpoint: 'https://api.devnet.solana.com',
25
- websocketEndpoint: 'wss://api.devnet.solana.com',
26
- walletConnectors,
26
+ endpoint: "https://api.devnet.solana.com",
27
+ walletConnectors: autoDiscover(),
27
28
  });
28
29
 
29
- function WalletButton() {
30
- const connectWallet = useConnectWallet();
31
- return <button onClick={() => connectWallet('phantom')}>Connect Phantom</button>;
32
- }
33
-
34
- function WalletBalance() {
35
- const wallet = useWallet();
36
- const balance = useBalance(wallet.status === 'connected' ? wallet.session.account.address : undefined);
37
-
38
- if (wallet.status !== 'connected') return <p>Connect a wallet</p>;
39
- if (balance.fetching) return <p>Loading…</p>;
40
-
41
- return <p>Lamports: {balance.lamports?.toString() ?? '0'}</p>;
42
- }
43
-
44
30
  export function App() {
45
- return (
46
- <SolanaProvider client={client} query={{ suspense: true }}>
47
- <WalletButton />
48
- <WalletBalance />
49
- </SolanaProvider>
50
- );
31
+ return (
32
+ <SolanaProvider client={client}>
33
+ {/* your components that call hooks go here */}
34
+ </SolanaProvider>
35
+ );
51
36
  }
52
37
  ```
53
38
 
54
- `SolanaProvider` composes `SolanaClientProvider` and `SolanaQueryProvider` with SWR v2-aligned defaults
55
- (`revalidateOnFocus`/`revalidateOnReconnect`/`revalidateIfStale` are `true`, `dedupingInterval` is `2000`,
56
- `focusThrottleInterval` is `5000`). Override them via the `query.config` prop or per-hook `swr` options.
57
- Prefer passing a `client`; `config`-based setup on `SolanaClientProvider` is still available for bespoke
58
- composition.
59
-
60
- Every hook exposes `UseHookNameParameters` / `UseHookNameReturnType` aliases so wrapper components stay in
61
- sync with the public API.
62
-
63
- ## Hooks at a glance
39
+ > **Next.js / RSC:** Components that call these hooks must be marked with `'use client'`.
64
40
 
65
- - `useWallet`, `useConnectWallet`, `useDisconnectWallet` – read or update the current wallet session.
66
- - `useBalance` / `useAccount` – fetch lamports once or keep account data in sync.
67
- - `useSolTransfer`, `useSplToken`, `useTransactionPool` – helper-driven flows for SOL, SPL, and
68
- general transactions.
69
- - `useSendTransaction` – prepare and submit arbitrary instructions with shared mutation state.
70
- - `useSignatureStatus`, `useWaitForSignature` – declarative helpers for tracking confirmations.
71
- - `useClientStore` – access the underlying Zustand store if you need low-level state.
41
+ ## Common Solana flows (copy/paste)
72
42
 
73
- ### Wallet helpers
43
+ These snippets assume a parent already handled wallet connection and can pass an address where needed.
74
44
 
75
- Read the current wallet session and expose connect/disconnect buttons.
45
+ ### Connect, disconnect, and show balance
76
46
 
77
47
  ```tsx
78
- const WalletActions = () => {
79
- const wallet = useWallet();
80
- const connect = useConnectWallet();
81
- const disconnect = useDisconnectWallet();
82
-
83
- if (wallet.status === 'connected') {
84
- return (
85
- <div>
86
- <p>{wallet.session.account.address.toString()}</p>
87
- <button onClick={() => disconnect()}>Disconnect</button>
88
- </div>
89
- );
90
- }
91
-
92
- return <button onClick={() => connect('phantom')}>Connect Phantom</button>;
93
- };
94
- ```
95
-
96
- ### Balance watcher
97
-
98
- Read lamports (cached plus live updates) for any address.
99
-
100
- ```tsx
101
- import { useBalance } from '@solana/react-hooks';
48
+ function WalletPanel() {
49
+ const { connectors, connect, disconnect, wallet, status } =
50
+ useWalletConnection();
51
+ const address = wallet?.account.address;
52
+ const balance = useBalance(address);
102
53
 
103
- function BalanceCard({ address }) {
104
- const { lamports, fetching, slot } = useBalance(address);
105
- if (fetching) return <p>Loading…</p>;
54
+ if (status === "connected") {
106
55
  return (
107
- <div>
108
- <p>Lamports: {lamports?.toString() ?? '0'}</p>
109
- <small>Last slot: {slot?.toString() ?? 'unknown'}</small>
110
- </div>
56
+ <div>
57
+ <p>{address?.toString()}</p>
58
+ <p>Lamports: {balance.lamports?.toString() ?? "loading…"}</p>
59
+ <button onClick={disconnect}>Disconnect</button>
60
+ </div>
111
61
  );
62
+ }
63
+
64
+ return connectors.map((c) => (
65
+ <button key={c.id} onClick={() => connect(c.id)}>
66
+ Connect {c.name}
67
+ </button>
68
+ ));
112
69
  }
113
70
  ```
114
71
 
115
- ### Account cache
116
-
117
- Fetch account data and optionally keep it in sync via subscriptions.
72
+ ### Read lamport balance (auto fetch + watch)
118
73
 
119
74
  ```tsx
120
- import { useAccount } from '@solana/react-hooks';
121
-
122
- function AccountInspector({ address }) {
123
- const account = useAccount(address, { watch: true });
124
-
125
- if (!account) return <p>Loading…</p>;
126
- if (account.error) return <p>Error loading account</p>;
127
-
128
- return <pre>{JSON.stringify(account.data, null, 2)}</pre>;
75
+ import { useBalance } from "@solana/react-hooks";
76
+
77
+ function BalanceCard({ address }: { address: string }) {
78
+ const { lamports, fetching, slot } = useBalance(address);
79
+ if (fetching) return <p>Loading…</p>;
80
+ return (
81
+ <p>
82
+ Lamports: {lamports?.toString() ?? "0"} (slot {slot?.toString() ?? "—"})
83
+ </p>
84
+ );
129
85
  }
130
86
  ```
131
87
 
132
- ### SOL transfers
133
-
134
- Trigger SOL transfers with built-in status tracking.
88
+ ### Send SOL
135
89
 
136
90
  ```tsx
137
- import { useSolTransfer } from '@solana/react-hooks';
138
-
139
- const SendSolButton = ({ destination, amount }) => {
140
- const { send, isSending } = useSolTransfer();
141
-
142
- return (
143
- <button
144
- disabled={isSending}
145
- onClick={() =>
146
- send({
147
- destination,
148
- lamports: amount,
149
- })
150
- }
151
- >
152
- {isSending ? 'Sending…' : 'Send SOL'}
153
- </button>
154
- );
155
- };
91
+ import { useSolTransfer } from "@solana/react-hooks";
92
+
93
+ function SendSol({ destination }: { destination: string }) {
94
+ const { send, isSending, status, signature, error } = useSolTransfer(); // expects a connected wallet
95
+ return (
96
+ <div>
97
+ <button
98
+ disabled={isSending}
99
+ onClick={() =>
100
+ send({ destination, lamports: 100_000_000n /* 0.1 SOL */ })
101
+ }
102
+ >
103
+ {isSending ? "Sending…" : "Send 0.1 SOL"}
104
+ </button>
105
+ <p>Status: {status}</p>
106
+ {signature ? <p>Signature: {signature}</p> : null}
107
+ {error ? <p role="alert">Error: {String(error)}</p> : null}
108
+ </div>
109
+ );
110
+ }
156
111
  ```
157
112
 
158
- ### SPL tokens
159
-
160
- Scope SPL helpers by mint and reuse the same API for balances and transfers.
113
+ ### SPL token balance + transfer
161
114
 
162
115
  ```tsx
163
- const SplBalance = ({ mint }) => {
164
- const { balance, send, isSending } = useSplToken(mint);
165
-
166
- return (
167
- <div>
168
- <p>Amount: {balance?.uiAmount ?? '0'}</p>
169
- <button
170
- disabled={isSending}
171
- onClick={() =>
172
- send({
173
- amount: 1n,
174
- destinationOwner: 'Destination111111111111111111111111',
175
- })
176
- }
177
- >
178
- Send 1 token
179
- </button>
180
- </div>
181
- );
182
- };
116
+ import { useSplToken } from "@solana/react-hooks";
117
+
118
+ function TokenPanel({
119
+ mint,
120
+ destinationOwner,
121
+ }: {
122
+ mint: string;
123
+ destinationOwner: string;
124
+ }) {
125
+ const { balance, send, isSending, owner } = useSplToken(mint);
126
+ return (
127
+ <div>
128
+ <p>Owner: {owner ?? "Connect wallet"}</p>
129
+ <p>Balance: {balance?.uiAmount ?? "0"}</p>
130
+ <button
131
+ disabled={isSending || !owner}
132
+ onClick={() => send({ amount: 1n, destinationOwner })}
133
+ >
134
+ {isSending ? "Sending…" : "Send 1 token"}
135
+ </button>
136
+ </div>
137
+ );
138
+ }
183
139
  ```
184
140
 
185
- ### Transaction pool
186
-
187
- Compose instructions, refresh blockhashes automatically, and send transactions from one hook.
141
+ ### Build and send arbitrary transactions
188
142
 
189
143
  ```tsx
190
- import type { TransactionInstructionInput } from '@solana/client';
191
-
192
- const useMemoizedInstruction = (): TransactionInstructionInput => ({
193
- accounts: [],
194
- data: new Uint8Array(),
195
- programAddress: 'Example1111111111111111111111111111111111',
196
- });
197
-
198
- const TransactionFlow = () => {
199
- const instruction = useMemoizedInstruction();
200
- const {
201
- addInstruction,
202
- prepareAndSend,
203
- sendStatus,
204
- latestBlockhash,
205
- } = useTransactionPool();
206
-
207
- return (
208
- <div>
209
- <button onClick={() => addInstruction(instruction)}>Add instruction</button>
210
- <button disabled={sendStatus === 'loading'} onClick={() => prepareAndSend()}>
211
- {sendStatus === 'loading' ? 'Sending…' : 'Prepare & Send'}
212
- </button>
213
- <p>Recent blockhash: {latestBlockhash.blockhash ?? 'loading…'}</p>
214
- </div>
215
- );
216
- };
144
+ import type { TransactionInstructionInput } from "@solana/client";
145
+ import { useTransactionPool } from "@solana/react-hooks";
146
+
147
+ function TransactionFlow({ ix }: { ix: TransactionInstructionInput }) {
148
+ const pool = useTransactionPool();
149
+ return (
150
+ <div>
151
+ <button onClick={() => pool.addInstruction(ix)}>Add instruction</button>
152
+ <button disabled={pool.isSending} onClick={() => pool.prepareAndSend()}>
153
+ {pool.isSending ? "Sending…" : "Prepare & Send"}
154
+ </button>
155
+ <p>Blockhash: {pool.latestBlockhash.blockhash ?? "loading…"}</p>
156
+ </div>
157
+ );
158
+ }
217
159
  ```
218
160
 
219
- ### Client store access
220
-
221
- Drop down to the underlying Zustand store when you need bespoke selectors.
161
+ ### Simple mutation helper (when you already have instructions)
222
162
 
223
163
  ```tsx
224
- import { useClientStore } from '@solana/react-hooks';
225
-
226
- function ClusterStatus() {
227
- const cluster = useClientStore((state) => state.cluster);
228
- return <p>Cluster: {cluster.status.status}</p>;
164
+ import { useSendTransaction } from "@solana/react-hooks";
165
+
166
+ function SendPrepared({ instructions }) {
167
+ const { send, isSending, signature, error } = useSendTransaction();
168
+ return (
169
+ <div>
170
+ <button disabled={isSending} onClick={() => send({ instructions })}>
171
+ {isSending ? "Submitting…" : "Send transaction"}
172
+ </button>
173
+ {signature ? <p>Signature: {signature}</p> : null}
174
+ {error ? <p role="alert">{String(error)}</p> : null}
175
+ </div>
176
+ );
229
177
  }
230
178
  ```
231
179
 
232
- ### General transaction sender
233
-
234
- Use `useSendTransaction` when you already have instructions/messages and just need a mutation helper
235
- that exposes `{ send, sendPrepared, status, error, signature }`. When no authority is supplied, it
236
- will use the currently connected wallet session by default.
180
+ ### Track confirmations for a signature
237
181
 
238
182
  ```tsx
239
- import { useSendTransaction } from '@solana/react-hooks';
240
-
241
- function SendAnythingButton({ instructions }) {
242
- const { send, isSending, signature, error } = useSendTransaction();
243
-
244
- return (
245
- <div>
246
- <button disabled={isSending} onClick={() => send({ instructions })}>
247
- {isSending ? 'Submitting…' : 'Send transaction'}
248
- </button>
249
- {signature ? <p>Signature: {signature}</p> : null}
250
- {error ? <p role="alert">Failed to send: {String(error)}</p> : null}
251
- </div>
252
- );
183
+ import { useWaitForSignature } from "@solana/react-hooks";
184
+
185
+ function SignatureWatcher({ signature }: { signature: string }) {
186
+ const wait = useWaitForSignature(signature, { commitment: "finalized" });
187
+ if (wait.waitStatus === "error") return <p role="alert">Failed</p>;
188
+ if (wait.waitStatus === "success") return <p>Finalized ✅</p>;
189
+ if (wait.waitStatus === "waiting") return <p>Waiting…</p>;
190
+ return <p>Provide a signature</p>;
253
191
  }
254
192
  ```
255
193
 
256
- ### Signature helpers
257
-
258
- Poll RPC for signature metadata or wait for a confirmation level without writing loops.
194
+ ### Query program accounts
259
195
 
260
196
  ```tsx
261
- import { useSignatureStatus, useWaitForSignature } from '@solana/react-hooks';
262
-
263
- function SignatureStatusCard({ signature }) {
264
- const status = useSignatureStatus(signature);
265
-
266
- if (status.isLoading) return <p>Loading…</p>;
267
- if (status.isError) return <p>RPC error.</p>;
197
+ import { SolanaQueryProvider, useProgramAccounts } from "@solana/react-hooks";
198
+
199
+ function ProgramAccounts({ program }: { program: string }) {
200
+ const query = useProgramAccounts(program);
201
+ if (query.isLoading) return <p>Loading…</p>;
202
+ if (query.isError) return <p role="alert">RPC error</p>;
203
+ return (
204
+ <div>
205
+ <button onClick={() => query.refresh()}>Refresh</button>
206
+ <ul>
207
+ {query.accounts.map(({ pubkey }) => (
208
+ <li key={pubkey.toString()}>{pubkey.toString()}</li>
209
+ ))}
210
+ </ul>
211
+ </div>
212
+ );
213
+ }
268
214
 
269
- return (
270
- <div>
271
- <p>Confirmation: {status.confirmationStatus ?? 'pending'}</p>
272
- <button onClick={() => status.refresh()}>Refresh</button>
273
- </div>
274
- );
215
+ function ProgramAccountsSection({ program }: { program: string }) {
216
+ return (
217
+ <SolanaQueryProvider>
218
+ <ProgramAccounts program={program} />
219
+ </SolanaQueryProvider>
220
+ );
275
221
  }
222
+ ```
276
223
 
277
- function WaitForSignature({ signature }) {
278
- const wait = useWaitForSignature(signature, { commitment: 'finalized' });
224
+ ### Simulate a transaction
279
225
 
280
- if (wait.waitStatus === 'error') return <p role="alert">Failed: {JSON.stringify(wait.waitError)}</p>;
281
- if (wait.waitStatus === 'success') return <p>Finalized!</p>;
282
- if (wait.waitStatus === 'waiting') return <p>Waiting for confirmation…</p>;
283
- return <p>Provide a signature</p>;
226
+ ```tsx
227
+ import { useSimulateTransaction } from "@solana/react-hooks";
228
+
229
+ function Simulation({ wire }: { wire: string }) {
230
+ const sim = useSimulateTransaction(wire);
231
+ if (sim.isLoading) return <p>Simulating…</p>;
232
+ if (sim.isError) return <p role="alert">Simulation failed</p>;
233
+ return (
234
+ <div>
235
+ <button onClick={() => sim.refresh()}>Re-run</button>
236
+ <pre>{JSON.stringify(sim.logs, null, 2)}</pre>
237
+ </div>
238
+ );
284
239
  }
285
240
  ```
286
241
 
287
- ## Query hooks
288
-
289
- Wrap a subtree with `<SolanaQueryProvider>` and call hooks like `useLatestBlockhash`,
290
- `useProgramAccounts`, `useSignatureStatus`, or `useSimulateTransaction`. Every hook returns
291
- `{ data, status, refresh }` so you can read the current value and trigger a refetch. Want to lean on React
292
- Suspense later? Pass `suspense` to `SolanaQueryProvider` and wrap just the section that should pause in a
293
- local `<Suspense>` boundary—no hook changes required:
242
+ ## Using Suspense (opt-in)
294
243
 
295
- The query provider ships SWR v2-aligned defaults: `revalidateOnFocus`/`revalidateOnReconnect`/`revalidateIfStale`
296
- are `true`, `dedupingInterval` is `2000`, and `focusThrottleInterval` is `5000`. Override them per-provider via the
297
- `query.config` prop or per-hook via the `swr` option.
244
+ Enable Suspense per subtree by setting `suspense` on `SolanaQueryProvider` and wrapping content in a React `<Suspense>` boundary. This keeps the rest of the UI non-blocking.
298
245
 
299
246
  ```tsx
300
- import { SolanaQueryProvider, useBalance } from '@solana/react-hooks';
301
- import { Suspense } from 'react';
247
+ import { SolanaQueryProvider, useBalance } from "@solana/react-hooks";
248
+ import { Suspense } from "react";
302
249
 
303
250
  function BalanceDetails({ address }: { address: string }) {
304
- const balance = useBalance(address);
305
- return <p>Lamports: {balance.lamports?.toString() ?? '0'}</p>;
251
+ const balance = useBalance(address);
252
+ return <p>Lamports: {balance.lamports?.toString() ?? "0"}</p>;
306
253
  }
307
254
 
308
255
  export function WalletPanel({ address }: { address: string }) {
309
- return (
310
- <SolanaQueryProvider suspense>
311
- {/* Only this block suspends while balance loads */}
312
- <Suspense fallback={<p>Loading balance…</p>}>
313
- <BalanceDetails address={address} />
314
- </Suspense>
315
- </SolanaQueryProvider>
316
- );
256
+ return (
257
+ <SolanaQueryProvider suspense>
258
+ <Suspense fallback={<p>Loading balance…</p>}>
259
+ <BalanceDetails address={address} />
260
+ </Suspense>
261
+ </SolanaQueryProvider>
262
+ );
317
263
  }
318
264
  ```
319
265
 
320
- ### Latest blockhash
321
-
322
- Poll or refetch the cluster's latest blockhash.
266
+ ## Provider SWR config (optional)
323
267
 
324
268
  ```tsx
325
- import { SolanaQueryProvider, useLatestBlockhash } from '@solana/react-hooks';
326
-
327
- function BlockhashTicker() {
328
- const latest = useLatestBlockhash({ swr: { refreshInterval: 20_000 } });
329
- if (latest.status === 'loading') return <p>Fetching blockhash…</p>;
330
- if (latest.status === 'error') return <p role="alert">Failed to fetch blockhash.</p>;
331
-
332
- return (
333
- <div>
334
- <button onClick={() => latest.refresh()}>Refresh</button>
335
- <p>Status: {latest.status}</p>
336
- <p>Blockhash: {latest.blockhash ?? 'unknown'}</p>
337
- </div>
338
- );
339
- }
340
-
341
- export function BlockhashCard() {
342
- return (
343
- <SolanaQueryProvider>
344
- <BlockhashTicker />
345
- </SolanaQueryProvider>
346
- );
269
+ export function App() {
270
+ return (
271
+ <SolanaProvider
272
+ client={client}
273
+ query={{
274
+ config: {
275
+ revalidateOnFocus: false,
276
+ revalidateOnReconnect: false,
277
+ refreshInterval: 30_000,
278
+ },
279
+ }}
280
+ >
281
+ <WalletPanel />
282
+ </SolanaProvider>
283
+ );
347
284
  }
348
285
  ```
349
286
 
350
- ### Program accounts
287
+ Defaults when you omit `query.config`:
288
+ - `revalidateOnFocus` / `revalidateOnReconnect` / `revalidateIfStale`: `true`
289
+ - `dedupingInterval`: `2000ms`
290
+ - `focusThrottleInterval`: `5000ms`
351
291
 
352
- ```tsx
353
- import { autoDiscover, backpack, createClient, phantom, solflare } from '@solana/client';
354
- import { SolanaProvider, SolanaQueryProvider, useProgramAccounts } from '@solana/react-hooks';
292
+ SWR background: stale-while-revalidate (RFC 5861): https://datatracker.ietf.org/doc/html/rfc5861
355
293
 
356
- const walletConnectors = [...phantom(), ...solflare(), ...backpack(), ...autoDiscover()];
357
- const client = createClient({
358
- endpoint: 'https://api.devnet.solana.com',
359
- websocketEndpoint: 'wss://api.devnet.solana.com',
360
- walletConnectors,
361
- });
362
-
363
- function ProgramAccountsList({ programAddress }) {
364
- const query = useProgramAccounts(programAddress);
365
- if (query.status === 'loading') return <p>Loading accounts…</p>;
366
- if (query.status === 'error') return <p role="alert">Retry later.</p>;
294
+ ### Work with the client store directly
367
295
 
368
- return (
369
- <div>
370
- <button onClick={() => query.refresh()}>Refresh</button>
371
- <p>Status: {query.status}</p>
372
- <ul>
373
- {query.accounts?.map(({ pubkey }) => (
374
- <li key={pubkey.toString()}>{pubkey.toString()}</li>
375
- ))}
376
- </ul>
377
- </div>
378
- );
379
- }
296
+ ```tsx
297
+ import { useClientStore } from "@solana/react-hooks";
380
298
 
381
- export function QueryDemo({ programAddress }) {
382
- return (
383
- <SolanaProvider client={client}>
384
- <SolanaQueryProvider>
385
- <ProgramAccountsList programAddress={programAddress} />
386
- </SolanaQueryProvider>
387
- </SolanaProvider>
388
- );
299
+ function ClusterBadge() {
300
+ const cluster = useClientStore((s) => s.cluster);
301
+ return <p>Endpoint: {cluster.endpoint}</p>;
389
302
  }
390
303
  ```
391
304
 
392
- ### Transaction simulation
393
-
394
- Simulate any transaction payload (wire string or object) and read RPC logs.
305
+ ## Notes and defaults
395
306
 
396
- ```tsx
397
- import { useSimulateTransaction } from '@solana/react-hooks';
307
+ - Wallet connectors: use `autoDiscover()` to pick up Wallet Standard injectables; or explicitly compose `phantom()`, `solflare()`, `backpack()`, etc.
308
+ - Queries: all RPC query hooks accept `swr` options under `swr` and `disabled` flags. Suspense is opt-in via `SolanaQueryProvider`’s `suspense` prop.
309
+ - Authorities: transaction helpers default to the connected wallet session when `authority` is omitted.
310
+ - Types: every hook exports `UseHookNameParameters` / `UseHookNameReturnType` aliases.
398
311
 
399
- function SimulationLogs({ transaction }) {
400
- const query = useSimulateTransaction(transaction);
401
- if (query.status === 'loading') return <p>Simulating…</p>;
402
- if (query.status === 'error') return <p role="alert">Simulation failed.</p>;
403
- return (
404
- <div>
405
- <button onClick={() => query.refresh()}>Re-run</button>
406
- <p>Status: {query.status}</p>
407
- <pre>{JSON.stringify(query.logs, null, 2)}</pre>
408
- </div>
409
- );
410
- }
411
- ```
312
+ ## More resources
412
313
 
413
- ## Going further
414
-
415
- - Wallet connection UI: `useWalletConnection` gives you the current wallet, connect/disconnect
416
- helpers, and the connector list from `client.connectors` (or an explicit override). Pair it with
417
- your preferred UI, or `WalletConnectionManager` for a simple modal state helper.
418
- - Signing helpers: the wallet session returned by `useWallet` exposes `signMessage`,
419
- `signTransaction`, and `sendTransaction` when supported by the connector. These connector methods
420
- replace the deprecated Wallet Standard shims.
421
- - Query hooks keep SWR options under `swr` for consistency (for example,
422
- `useProgramAccounts(address, { swr: { revalidateOnFocus: false } })`) and expose typed parameter
423
- and return aliases across all hooks.
424
- - Type helpers: use `UseHookNameParameters` / `UseHookNameReturnType` for public hooks.
425
- - Looking for examples? See `examples/vite-react` for a ready-to-run, tabbed playground that wires
426
- the provider, hooks, and mock UIs together across wallet/state, transaction, and query demos.
427
-
428
- ## Scripts
429
-
430
- - `pnpm build` – run both JS compilation and type definition emit
431
- - `pnpm test:typecheck` – strict type-checking without emit
432
- - `pnpm lint` / `pnpm format` – Biome-powered linting and formatting
314
+ - Playground: `examples/vite-react` (run with `pnpm install && pnpm dev`).
315
+ - Next.js reference app: `examples/nextjs`.
316
+ - Hook JSDoc lives in `src/hooks.ts`, `src/queryHooks.ts`, `src/ui.tsx`.