Skip to content

Commit d3f9ff5

Browse files
committed
rework a few things and move around
1 parent d9a0c73 commit d3f9ff5

File tree

101 files changed

+2431
-308
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+2431
-308
lines changed

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ Solana SDK delivers React-focused tooling for building Solana applications. This
66

77
| Package | Description |
88
| --- | --- |
9-
| [`@solana/react-hooks`](packages/react-hooks) | React bindings over the headless [`@solana/client-core`](../client-core) SDK. Supplies context providers plus hooks for wallet management, balances, transfers, and SPL helpers. |
9+
| [`@solana/react-hooks`](packages/react-hooks) | React bindings over the headless [`@solana/client`](packages/client) SDK. Supplies context providers plus hooks for wallet management, balances, transfers, and SPL helpers. |
1010
| [`@solana/example-react-hooks`](examples/react-hooks) | Tailwind/Vite demo application showcasing the hooks with a polished UI. Handy as a reference or quick-start template. |
1111

1212
---
1313

1414
## Transaction-helper DX
1515

16-
`@solana/client-core` now folds automatic transaction preparation into the public surface. You can lean on `client.helpers.transaction.prepareAndSend` for the common “build → simulate → send” flow while still opting into the bare `prepareTransaction` utility when you need to log or inspect the wire payload. Feed it `addressLookupTables` and it automatically switches to v0 transactions—otherwise it stays legacy so you never have to name the version explicitly.
16+
`@solana/client` now folds automatic transaction preparation into the public surface. You can lean on `client.helpers.transaction.prepareAndSend` for the common “build → simulate → send” flow while still opting into the bare `prepareTransaction` utility when you need to log or inspect the wire payload. Feed it `addressLookupTables` and it automatically switches to v0 transactions—otherwise it stays legacy so you never have to name the version explicitly.
1717

1818
```ts
1919
const signature = await client.helpers.transaction.prepareAndSend(
@@ -46,22 +46,22 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on how to get involved.
4646

4747
## `@solana/react-hooks`
4848

49-
React glue around the client-core SDK. You either hand it a `SolanaClient` instance or a config object and the provider wires everything together. Hooks expose cluster and wallet state in a React-friendly way so you can focus on UI.
49+
React glue around the client SDK. You either hand it a `SolanaClient` instance or a config object and the provider wires everything together. Hooks expose cluster and wallet state in a React-friendly way so you can focus on UI.
5050

5151
### Install
5252

5353
```bash
5454
# using pnpm
55-
pnpm add @solana/react-hooks @solana/client-core react react-dom
55+
pnpm add @solana/react-hooks @solana/client react react-dom
5656

5757
# or npm
58-
npm install @solana/react-hooks @solana/client-core react react-dom
58+
npm install @solana/react-hooks @solana/client react react-dom
5959
```
6060

6161
### Quick start
6262

6363
```tsx
64-
import type { SolanaClientConfig } from '@solana/client-core';
64+
import type { SolanaClientConfig } from '@solana/client';
6565
import {
6666
SolanaClientProvider,
6767
useConnectWallet,
@@ -248,7 +248,7 @@ pnpm --filter @solana/example-react-hooks build
248248
npx --yes pnpm --filter @solana/example-react-hooks build
249249
```
250250

251-
> If you consume `@solana/react-hooks` from the workspace, ensure the `@solana/client-poc` import is aliased to `@solana/client-core` (the example’s Vite config handles this).
251+
> If you consume `@solana/react-hooks` from the workspace, ensure the `@solana/client-poc` import is aliased to `@solana/client` (the example’s Vite config handles this).
252252
253253
---
254254

examples/react-hooks/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ The example mirrors the vanilla proof-of-concept by wiring wallet discovery, SOL
66

77
## Compute-unit tuned transactions
88

9-
`useTransactionPool` now exposes the same `prepareAndSend` helper that lives in `@solana/client-core`. You can surface compute-unit simulation plus logging in a single call:
9+
`useTransactionPool` now exposes the same `prepareAndSend` helper that lives in `@solana/client`. You can surface compute-unit simulation plus logging in a single call:
1010

1111
```tsx
1212
import { useMemo } from 'react';

examples/react-hooks/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@
1515
"typecheck": "pnpm test:typecheck"
1616
},
1717
"dependencies": {
18-
"@solana/client-core": "workspace:*",
19-
"@solana/react-hooks": "workspace:*",
20-
"@solana/kit": "^5.0.0",
2118
"@radix-ui/react-slot": "^1.1.0",
19+
"@solana-program/system": "^0.9.0",
20+
"@solana/client": "workspace:*",
21+
"@solana/kit": "^5.0.0",
22+
"@solana/react-hooks": "workspace:*",
2223
"class-variance-authority": "^0.7.0",
2324
"clsx": "^2.1.1",
2425
"react": "^19.0.0",

examples/react-hooks/src/App.tsx

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,27 @@
1-
import { createSolanaRpcClient, type SolanaClientConfig, type WalletConnector } from '@solana/client-core';
2-
import { SolanaClientProvider, useConnectWallet, useWallet, useWalletStandardConnectors } from '@solana/react-hooks';
1+
import { createSolanaRpcClient, type SolanaClientConfig, type WalletConnector } from '@solana/client';
2+
import {
3+
SolanaClientProvider,
4+
SolanaQueryProvider,
5+
useConnectWallet,
6+
useWallet,
7+
useWalletStandardConnectors,
8+
} from '@solana/react-hooks';
39
import { useEffect, useMemo, useRef } from 'react';
410

11+
import { AccountInspectorCard } from './components/AccountInspectorCard.tsx';
12+
import { AirdropCard } from './components/AirdropCard.tsx';
513
import { BalanceCard } from './components/BalanceCard.tsx';
614
import { ClusterStatusCard } from './components/ClusterStatusCard.tsx';
15+
import { LatestBlockhashCard } from './components/LatestBlockhashCard.tsx';
16+
import { ProgramAccountsCard } from './components/ProgramAccountsCard.tsx';
17+
import { SendTransactionCard } from './components/SendTransactionCard.tsx';
18+
import { SignatureWatcherCard } from './components/SignatureWatcherCard.tsx';
19+
import { SimulateTransactionCard } from './components/SimulateTransactionCard.tsx';
720
import { SolTransferForm } from './components/SolTransferForm.tsx';
821
import { SplTokenPanel } from './components/SplTokenPanel.tsx';
22+
import { StoreInspectorCard } from './components/StoreInspectorCard.tsx';
23+
import { TransactionPoolPanel } from './components/TransactionPoolPanel.tsx';
24+
import { Tabs, TabsContent, TabsList, TabsTrigger } from './components/ui/tabs.tsx';
925
import { WalletControls } from './components/WalletControls.tsx';
1026

1127
const LAST_CONNECTOR_STORAGE_KEY = 'solana:last-connector';
@@ -39,7 +55,9 @@ export default function App() {
3955

4056
return (
4157
<SolanaClientProvider config={clientConfig}>
42-
<DemoApp connectors={walletConnectors} />
58+
<SolanaQueryProvider>
59+
<DemoApp connectors={walletConnectors} />
60+
</SolanaQueryProvider>
4361
</SolanaClientProvider>
4462
);
4563
}
@@ -107,17 +125,44 @@ function DemoApp({ connectors }: DemoAppProps) {
107125
</span>
108126
<h1>Solana Client Toolkit</h1>
109127
<p>
110-
This example wraps the headless <code>@solana/client-core</code> with a React context provider
111-
and showcases the hooks exposed by <code>@solana/react-hooks</code>.
128+
This example wraps the headless <code>@solana/client</code> with a React context provider and
129+
showcases the hooks exposed by <code>@solana/react-hooks</code>. Explore state, transactions,
130+
and query helpers via the tabs below.
112131
</p>
113132
</header>
114-
<div className="grid gap-6 md:grid-cols-2">
115-
<ClusterStatusCard />
116-
<WalletControls connectors={connectors} />
117-
<BalanceCard />
118-
<SolTransferForm />
119-
<SplTokenPanel />
120-
</div>
133+
<Tabs defaultValue="state">
134+
<TabsList>
135+
<TabsTrigger value="state">Wallet &amp; State</TabsTrigger>
136+
<TabsTrigger value="transactions">Transfers &amp; Transactions</TabsTrigger>
137+
<TabsTrigger value="queries">Queries &amp; Diagnostics</TabsTrigger>
138+
</TabsList>
139+
<TabsContent value="state">
140+
<div className="grid gap-6 lg:grid-cols-2">
141+
<ClusterStatusCard />
142+
<WalletControls connectors={connectors} />
143+
<BalanceCard />
144+
<AccountInspectorCard />
145+
<AirdropCard />
146+
<StoreInspectorCard />
147+
</div>
148+
</TabsContent>
149+
<TabsContent value="transactions">
150+
<div className="grid gap-6 lg:grid-cols-2">
151+
<SolTransferForm />
152+
<SendTransactionCard />
153+
<SplTokenPanel />
154+
<TransactionPoolPanel />
155+
</div>
156+
</TabsContent>
157+
<TabsContent value="queries">
158+
<div className="grid gap-6 lg:grid-cols-2">
159+
<LatestBlockhashCard />
160+
<ProgramAccountsCard />
161+
<SimulateTransactionCard />
162+
<SignatureWatcherCard />
163+
</div>
164+
</TabsContent>
165+
</Tabs>
121166
</div>
122167
</div>
123168
);
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { useAccount } from '@solana/react-hooks';
2+
import { type ChangeEvent, useMemo, useState } from 'react';
3+
4+
import { Button } from './ui/button';
5+
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from './ui/card';
6+
import { Input } from './ui/input';
7+
8+
export function AccountInspectorCard() {
9+
const [address, setAddress] = useState('');
10+
const [watch, setWatch] = useState(false);
11+
const target = address.trim();
12+
const account = useAccount(target === '' ? undefined : target, {
13+
fetch: target !== '',
14+
skip: target === '',
15+
watch,
16+
});
17+
18+
const formattedData = useMemo(() => {
19+
if (!account?.data) {
20+
return 'No account data fetched yet.';
21+
}
22+
try {
23+
return JSON.stringify(account.data, null, 2);
24+
} catch {
25+
return String(account.data);
26+
}
27+
}, [account]);
28+
29+
const statusLabel = (() => {
30+
if (target === '') {
31+
return 'Enter an address to fetch the cache entry.';
32+
}
33+
if (!account) {
34+
return 'Loading account data…';
35+
}
36+
if (account.error) {
37+
return `Fetch error: ${formatError(account.error)}`;
38+
}
39+
return `Lamports: ${account.lamports ?? 'unknown'} · Slot: ${account.slot ?? 'unknown'}`;
40+
})();
41+
42+
const handleAddressChange = (event: ChangeEvent<HTMLInputElement>) => {
43+
setAddress(event.target.value);
44+
};
45+
46+
return (
47+
<Card>
48+
<CardHeader>
49+
<div className="space-y-1.5">
50+
<CardTitle>Account Inspector</CardTitle>
51+
<CardDescription>
52+
Use <code>useAccount</code> to populate the client store cache and optionally subscribe to live
53+
account changes.
54+
</CardDescription>
55+
</div>
56+
</CardHeader>
57+
<CardContent className="space-y-4">
58+
<div className="space-y-2">
59+
<label htmlFor="account-address">Address</label>
60+
<Input
61+
autoComplete="off"
62+
id="account-address"
63+
onChange={handleAddressChange}
64+
placeholder="Base58 account address"
65+
value={address}
66+
/>
67+
</div>
68+
<label className="flex items-center gap-2 text-sm text-muted-foreground">
69+
<input
70+
checked={watch}
71+
className="size-4 rounded border-border text-primary focus-visible:ring-2 focus-visible:ring-ring/60"
72+
onChange={(event) => setWatch(event.target.checked)}
73+
type="checkbox"
74+
/>
75+
Watch account for changes
76+
</label>
77+
<div className="space-y-2 text-sm text-muted-foreground">
78+
<p aria-live="polite">{statusLabel}</p>
79+
{account?.fetching ? (
80+
<span className="status-badge" data-state="success">
81+
Refreshing…
82+
</span>
83+
) : null}
84+
</div>
85+
<div className="log-panel max-h-60 overflow-auto whitespace-pre-wrap">{formattedData}</div>
86+
</CardContent>
87+
<CardFooter className="text-xs text-muted-foreground">
88+
<Button disabled={target === ''} onClick={() => setAddress('')} type="button" variant="ghost">
89+
Clear
90+
</Button>
91+
</CardFooter>
92+
</Card>
93+
);
94+
}
95+
96+
function formatError(error: unknown): string {
97+
if (error instanceof Error) {
98+
return error.message;
99+
}
100+
if (typeof error === 'string') {
101+
return error;
102+
}
103+
return JSON.stringify(error);
104+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { lamportsMath } from '@solana/client';
2+
import { useWalletActions, useWalletSession } from '@solana/react-hooks';
3+
import { type FormEvent, useState } from 'react';
4+
5+
import { Button } from './ui/button';
6+
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from './ui/card';
7+
import { Input } from './ui/input';
8+
9+
export function AirdropCard() {
10+
const session = useWalletSession();
11+
const actions = useWalletActions();
12+
const [amount, setAmount] = useState('1');
13+
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
14+
const [result, setResult] = useState<string | null>(null);
15+
16+
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
17+
event.preventDefault();
18+
if (!session) {
19+
return;
20+
}
21+
setStatus('loading');
22+
setResult(null);
23+
try {
24+
const lamports = lamportsMath.fromSol(amount.trim(), { label: 'Airdrop amount' });
25+
const signature = await actions.requestAirdrop(session.account.address, lamports);
26+
setResult(signature.toString());
27+
setStatus('success');
28+
} catch (error) {
29+
setResult(formatError(error));
30+
setStatus('error');
31+
}
32+
};
33+
34+
return (
35+
<Card aria-disabled={!session}>
36+
<CardHeader>
37+
<div className="space-y-1.5">
38+
<CardTitle>Wallet Actions</CardTitle>
39+
<CardDescription>
40+
Call the headless client via <code>useWalletActions</code> to trigger RPC helpers, such as a
41+
devnet airdrop.
42+
</CardDescription>
43+
</div>
44+
</CardHeader>
45+
<form onSubmit={handleSubmit}>
46+
<CardContent className="space-y-4">
47+
<div className="space-y-2">
48+
<label htmlFor="airdrop-amount">Amount (SOL)</label>
49+
<Input
50+
autoComplete="off"
51+
disabled={!session}
52+
id="airdrop-amount"
53+
min="0"
54+
onChange={(event) => setAmount(event.target.value)}
55+
placeholder="1"
56+
step="0.1"
57+
type="number"
58+
value={amount}
59+
/>
60+
</div>
61+
<p className="text-sm text-muted-foreground">
62+
{session
63+
? `Requester: ${session.account.address.toString()}`
64+
: 'Connect a devnet wallet to request an airdrop.'}
65+
</p>
66+
</CardContent>
67+
<CardFooter className="flex flex-wrap gap-2">
68+
<Button disabled={!session || status === 'loading'} type="submit">
69+
{status === 'loading' ? 'Requesting…' : 'Request Airdrop'}
70+
</Button>
71+
{result ? (
72+
<span
73+
aria-live="polite"
74+
className="status-badge"
75+
data-state={status === 'error' ? 'error' : 'success'}
76+
>
77+
{result}
78+
</span>
79+
) : null}
80+
</CardFooter>
81+
</form>
82+
</Card>
83+
);
84+
}
85+
86+
function formatError(error: unknown): string {
87+
if (error instanceof Error) {
88+
return error.message;
89+
}
90+
if (typeof error === 'string') {
91+
return error;
92+
}
93+
return JSON.stringify(error);
94+
}

examples/react-hooks/src/components/BalanceCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { lamportsToSolString } from '@solana/client-core';
1+
import { lamportsToSolString } from '@solana/client';
22
import { useBalance, useWallet } from '@solana/react-hooks';
33
import { type ChangeEvent, useEffect, useState } from 'react';
44

0 commit comments

Comments
 (0)