Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/client-readme-alignment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solana/client": patch
---

Refresh README to mirror react-hooks structure with quickstart, copy/paste flows, and updated notes/scripts.
163 changes: 125 additions & 38 deletions packages/client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,79 +8,166 @@ in any runtime (React, Svelte, API routes, workers, etc.).
## Install

```bash
pnpm add @solana/client
npm install @solana/client
```

## Quick start
## Quickstart

1. Choose Wallet Standard connectors (auto-discovery is the fastest way to start).
2. Create a Solana client.
3. Call actions, watchers, and helpers anywhere in your app (React, APIs, workers, etc.).

```ts
import { autoDiscover, backpack, createClient, phantom, solflare } from "@solana/client";
import { autoDiscover, createClient } from "@solana/client";

const walletConnectors = [...phantom(), ...solflare(), ...backpack(), ...autoDiscover()];
const client = createClient({
endpoint: "https://api.devnet.solana.com",
websocketEndpoint: "wss://api.devnet.solana.com",
walletConnectors,
walletConnectors: autoDiscover(),
});

// Connect Wallet Standard apps via their connector ids.
await client.actions.connectWallet("phantom");

// Fetch an account once.
const account = await client.actions.fetchAccount(address);
console.log(account.lamports?.toString());
const wallet = client.store.getState().wallet;
if (wallet.status === "connected") {
const account = await client.actions.fetchAccount(wallet.session.account.address);
console.log(account.lamports?.toString());
}
```

## Common Solana flows (copy/paste)

### Connect, disconnect, and inspect wallet state

```ts
const connectors = client.connectors.all; // Wallet Standard-aware connectors

await client.actions.connectWallet(connectors[0].id);

const wallet = client.store.getState().wallet;
if (wallet.status === "connected") {
console.log(wallet.session.account.address.toString());
}

await client.actions.disconnectWallet();
```

### Fetch and watch lamports

// Watch lamports in real time.
const watcher = client.watchers.watchBalance({ address }, (lamports) => {
console.log("balance:", lamports.toString());
```ts
import { toAddress } from "@solana/client";

const address = toAddress("Fg6PaFpoGXkYsidMpWFKfwtz6DhFVyG4dL1x8kj7ZJup");

const lamports = await client.actions.fetchBalance(address);
console.log(`Lamports: ${lamports.toString()}`);

const watcher = client.watchers.watchBalance({ address }, (nextLamports) => {
console.log("Updated balance:", nextLamports.toString());
});

// Later…
watcher.abort();
```

## Core pieces
### Request an airdrop (devnet/testnet)

- **Client store** – Zustand store that tracks cluster status, accounts, subscriptions, transactions,
and wallet state. Provide your own store if you need custom persistence.
- **Actions** – Promise-based helpers (`fetchAccount`, `fetchBalance`, `sendTransaction`, `requestAirdrop`, `setCluster`, etc.) that wrap the RPC and keep the store in sync.
- **Watchers** – Subscription helpers (`watchAccount`, `watchBalance`, `watchSignature`) that stream
updates into the store and call your listeners.
- **Helpers** – Opinionated utilities for SOL transfers, SPL tokens, and transactions. They handle
mundane tasks like resolving fee payers, refreshing blockhashes, or signing with Wallet Standard
sessions.
```ts
const signature = await client.actions.requestAirdrop(address, 1_000_000_000n); // 1 SOL
console.log(signature.toString());
```

## Transaction helper
### Send SOL

`client.helpers.transaction` handles blockhashes, fee payers, and signing for you.
```ts
const wallet = client.store.getState().wallet;
if (wallet.status !== "connected") throw new Error("Connect wallet first");

const signature = await client.solTransfer.sendTransfer({
amount: 100_000_000n, // 0.1 SOL
authority: wallet.session, // Wallet Standard session
destination: "Ff34MXWdgNsEJ1kJFj9cXmrEe7y2P93b95mGu5CJjBQJ",
});
console.log(signature.toString());
```

### SPL token balance + transfer

```ts
const wallet = client.store.getState().wallet;
if (wallet.status !== "connected") throw new Error("Connect wallet first");

const usdc = client.splToken({ mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" }); // USDC

const balance = await usdc.fetchBalance(wallet.session.account.address);
console.log(`Balance: ${balance.uiAmount}`);

const signature = await usdc.sendTransfer({
amount: 1n,
authority: wallet.session,
destinationOwner: "Ff34MXWdgNsEJ1kJFj9cXmrEe7y2P93b95mGu5CJjBQJ",
});
console.log(signature.toString());
```

### Build and send arbitrary transactions

```ts
const prepared = await client.helpers.transaction.prepare({
authority: walletSession,
instructions: [instruction],
import { getTransferSolInstruction } from "@solana-program/system";

const wallet = client.store.getState().wallet;
if (wallet.status !== "connected") throw new Error("Connect wallet first");

const prepared = await client.transaction.prepare({
authority: wallet.session,
instructions: [
getTransferSolInstruction({
destination: "Ff34MXWdgNsEJ1kJFj9cXmrEe7y2P93b95mGu5CJjBQJ",
lamports: 10_000n,
source: wallet.session.account.address,
}),
],
version: "auto", // defaults to 0 when lookups exist, otherwise 'legacy'
});

const signature = await client.helpers.transaction.send(prepared);
// Inspect or serialize first.
const wire = await client.transaction.toWire(prepared);

// Submit.
const signature = await client.transaction.send(prepared);
console.log(signature.toString());
```

- `prepare` builds a transaction message and refreshes the blockhash.
- `sign` / `toWire` let you collect signatures or emit Base64 manually.
- `send` submits the prepared transaction (or uses `signAndSend` if the wallet supports it).
- `prepareAndSend` runs everything plus an optional simulation/logging pass via `prepareTransaction`.
- Versions default to `0` automatically when any instruction references address lookup tables, otherwise `legacy`; pass `version` if you need to override.
### Watch signature confirmations

```ts
const watcher = client.watchers.watchSignature(
{ signature, commitment: "confirmed" },
(notification) => console.log("Signature update:", notification),
);

Need just the tuning step? Call `client.prepareTransaction` directly with your unsigned message.
// Later…
watcher.abort();
```

## Wallet helpers
## Notes and defaults

Start with connectors: `phantom()`, `solflare()`, `backpack()`, and `autoDiscover()` return Wallet Standard-aware
connectors you can pass to `createClient` or `createWalletRegistry`. Wrap additional wallets with
`createWalletStandardConnector` before registering. The registry powers `client.actions.connectWallet` and the
React hooks package, but you can also query it directly to build your own selectors.
- Wallet connectors: `autoDiscover()` picks up Wallet Standard injectables; compose `phantom()`, `solflare()`, `backpack()`, or `injected()` when you need explicit control.
- Store: built on Zustand; pass `createStore` to `createClient` for custom persistence or server-side stores. `serializeSolanaState` / `deserializeSolanaState` help save and restore cluster + wallet metadata.
- Actions: `fetchAccount`, `fetchBalance`, `setCluster`, `requestAirdrop`, `sendTransaction`, and wallet connect/disconnect keep the store in sync.
- Watchers: `watchAccount`, `watchBalance`, and `watchSignature` stream updates into the store and return an `abort()` handle for cleanup.
- Helpers: `solTransfer`, `splToken`, and `transaction` cover common transfers plus low-level `prepare`/`sign`/`toWire` flows. Transaction versions default to `0` when any instruction references address lookup tables, otherwise `legacy`; override with `version` when needed.

## Scripts

- `pnpm build` – run JS compilation and type definition emit
- `pnpm test:typecheck` – strict type-checking without emit
- `pnpm build` – compile JS and type definitions
- `pnpm test:typecheck` – strict type-checking
- `pnpm lint` / `pnpm format` – Biome-powered linting and formatting

## More resources

- Playground: `examples/vite-react` (run with `pnpm install && pnpm dev`).
- Next.js reference app: `examples/nextjs`.
- Client APIs live in `src/actions.ts`, `src/watchers`, and `src/features/*` for helper internals.