Skip to content

Commit e566449

Browse files
satoshai-devclaude
andauthored
docs: move full docs to root README (#50)
* docs: move full docs to root README, slim packages/kit for npm Root README is the GitHub landing page and should contain the full API documentation. packages/kit/README.md (shown on npm) now points to GitHub for docs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: add changeset for docs restructure Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: single source of truth for README Root README.md has the full docs. At publish time, prepublishOnly copies it into packages/kit/ for npm. The copy is gitignored. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 791f8f6 commit e566449

File tree

5 files changed

+323
-325
lines changed

5 files changed

+323
-325
lines changed

.changeset/root-readme-docs.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@satoshai/kit": patch
3+
---
4+
5+
Move full API documentation to root README (GitHub landing page), slim packages/kit README to a pointer for npm

README.md

Lines changed: 316 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,322 @@
66

77
Typesafe Stacks wallet & contract interaction library for React. Wagmi-inspired hook API for connecting wallets, signing messages, and calling contracts on the Stacks blockchain.
88

9-
See the full documentation in [`packages/kit/README.md`](./packages/kit/README.md).
9+
## Features
10+
11+
- **`StacksWalletProvider`** — React context provider for wallet state
12+
- **`useConnect` / `useDisconnect`** — Connect and disconnect wallets
13+
- **`useWallets`** — Configured wallets with availability status
14+
- **`useAddress`** — Access connected wallet address and status
15+
- **`useSignMessage`** — Sign arbitrary messages
16+
- **`useSignStructuredMessage`** — Sign SIP-018 structured data
17+
- **`useSignTransaction`** — Sign serialized transactions (sponsored tx flows)
18+
- **`useWriteContract`** — Call smart contracts with post-conditions
19+
- **`useTransferSTX`** — Native STX transfers
20+
- **`useBnsName`** — Resolve BNS v2 names
21+
- **6 wallets supported** — Xverse, Leather, OKX, Asigna, Fordefi, WalletConnect
22+
- **Next.js App Router compatible**`"use client"` directives included
23+
24+
## Install
25+
26+
```bash
27+
pnpm add @satoshai/kit @stacks/transactions react react-dom
28+
```
29+
30+
## Quick Start
31+
32+
```tsx
33+
import { StacksWalletProvider, useConnect, useAddress, useDisconnect } from '@satoshai/kit';
34+
35+
function App() {
36+
return (
37+
<StacksWalletProvider>
38+
<Wallet />
39+
</StacksWalletProvider>
40+
);
41+
}
42+
43+
function Wallet() {
44+
const { connect, reset, isPending } = useConnect();
45+
const { address, isConnected } = useAddress();
46+
const { disconnect } = useDisconnect();
47+
48+
if (isConnected) {
49+
return (
50+
<div>
51+
<p>Connected: {address}</p>
52+
<button onClick={() => disconnect()}>Disconnect</button>
53+
</div>
54+
);
55+
}
56+
57+
return (
58+
<div>
59+
{isPending && <button onClick={reset}>Cancel</button>}
60+
<button onClick={() => connect()} disabled={isPending}>Connect Wallet</button>
61+
</div>
62+
);
63+
}
64+
```
65+
66+
## API
67+
68+
### `<StacksWalletProvider>`
69+
70+
Wrap your app to provide wallet context to all hooks.
71+
72+
```tsx
73+
<StacksWalletProvider
74+
wallets={['xverse', 'leather', 'wallet-connect']} // optional — defaults to all supported
75+
connectModal={true} // optional — defaults to true
76+
walletConnect={{ projectId: '...' }} // optional — enables WalletConnect
77+
onConnect={(provider, address) => {}} // optional
78+
onAddressChange={(newAddress) => {}} // optional — Xverse account switching
79+
onDisconnect={() => {}} // optional
80+
>
81+
{children}
82+
</StacksWalletProvider>
83+
```
84+
85+
> If `wallets` includes `'wallet-connect'`, you must provide `walletConnect.projectId` or the provider will throw at mount.
86+
87+
> **Important:** Define `wallets` and `walletConnect` outside of your component (or memoize them) so they remain referentially stable across renders. These values are treated as static configuration.
88+
89+
#### `connectModal` (default: `true`)
90+
91+
Controls whether `connect()` with no arguments shows `@stacks/connect`'s built-in wallet selection modal.
92+
93+
```tsx
94+
// Default — modal handles wallet selection
95+
<StacksWalletProvider>
96+
<App /> {/* connect() opens the modal */}
97+
</StacksWalletProvider>
98+
99+
// Headless — manage wallet selection yourself (wagmi-style)
100+
<StacksWalletProvider connectModal={false}>
101+
<App /> {/* connect('xverse') only, connect() with no args is a no-op */}
102+
</StacksWalletProvider>
103+
```
104+
105+
When `connectModal` is enabled:
106+
- `connect()` with no args opens the `@stacks/connect` modal
107+
- `connect('xverse')` with an explicit provider still bypasses the modal
108+
- The `wallets` prop controls which wallets appear in the modal
109+
- All 6 wallets are supported in the modal
110+
- After the user picks a wallet, the kit automatically maps it back and sets state
111+
112+
### `useConnect()`
113+
114+
```ts
115+
const { connect, reset, isPending } = useConnect();
116+
117+
// Open the @stacks/connect modal (when connectModal is enabled, the default)
118+
await connect();
119+
120+
// Or connect to a specific wallet directly
121+
await connect('xverse');
122+
await connect('leather', {
123+
onSuccess: (address, provider) => {},
124+
onError: (error) => {},
125+
});
126+
127+
// Reset stuck connecting state (e.g. when a wallet modal is dismissed)
128+
reset();
129+
```
130+
131+
> **Note:** Some wallets (e.g. OKX) never reject the connection promise when the user closes the popup. Use `reset()` to clear the pending state in those cases.
132+
133+
### `useWallets()`
134+
135+
Returns all configured wallets with their name, icon, download link, and availability status. Metadata is sourced from `@stacks/connect`.
136+
137+
```ts
138+
const { wallets } = useWallets();
139+
// [{ id: 'xverse', name: 'Xverse Wallet', icon: 'data:image/svg+xml;...', webUrl: 'https://xverse.app', available: true }, ...]
140+
141+
{wallets.map(({ id, name, icon, webUrl, available }) => (
142+
<div key={id}>
143+
<button onClick={() => connect(id)} disabled={!available}>
144+
{icon && <img src={icon} alt={name} width={20} height={20} />}
145+
{name}
146+
</button>
147+
{!available && webUrl && <a href={webUrl} target="_blank">Install</a>}
148+
</div>
149+
))}
150+
```
151+
152+
A wallet is `available` when its browser extension is installed. For `wallet-connect`, it's `available` when a `walletConnect.projectId` is provided to the provider.
153+
154+
### `useDisconnect()`
155+
156+
```ts
157+
const { disconnect } = useDisconnect();
158+
159+
disconnect();
160+
disconnect(() => { /* callback after disconnect */ });
161+
```
162+
163+
### `useAddress()`
164+
165+
```ts
166+
const { address, isConnected, isConnecting, isDisconnected, provider } = useAddress();
167+
168+
if (isConnected) {
169+
console.log(address); // 'SP...' or 'ST...'
170+
console.log(provider); // 'xverse' | 'leather' | ...
171+
}
172+
```
173+
174+
### `useSignMessage()`
175+
176+
```ts
177+
const { signMessage, signMessageAsync, data, error, isPending } = useSignMessage();
178+
179+
// Callback style
180+
signMessage({ message: 'Hello Stacks' }, {
181+
onSuccess: ({ publicKey, signature }) => {},
182+
onError: (error) => {},
183+
});
184+
185+
// Async style
186+
const { publicKey, signature } = await signMessageAsync({ message: 'Hello Stacks' });
187+
```
188+
189+
### `useSignStructuredMessage()`
190+
191+
Sign SIP-018 structured data for typed, verifiable off-chain messages.
192+
193+
> **Note:** OKX wallet does not support structured message signing and will throw an error.
194+
195+
```ts
196+
import { tupleCV, stringAsciiCV, uintCV } from '@stacks/transactions';
197+
198+
const { signStructuredMessage, signStructuredMessageAsync, data, error, isPending } = useSignStructuredMessage();
199+
200+
// Callback style
201+
signStructuredMessage({
202+
domain: tupleCV({
203+
name: stringAsciiCV('MyApp'),
204+
version: stringAsciiCV('1.0'),
205+
'chain-id': uintCV(1),
206+
}),
207+
message: tupleCV({
208+
action: stringAsciiCV('authorize'),
209+
amount: uintCV(1000),
210+
}),
211+
}, {
212+
onSuccess: ({ publicKey, signature }) => {},
213+
onError: (error) => {},
214+
});
215+
216+
// Async style
217+
const { publicKey, signature } = await signStructuredMessageAsync({
218+
domain: tupleCV({ ... }),
219+
message: tupleCV({ ... }),
220+
});
221+
```
222+
223+
### `useTransferSTX()`
224+
225+
```ts
226+
const { transferSTX, transferSTXAsync, data, error, isPending } = useTransferSTX();
227+
228+
// Callback style
229+
transferSTX({
230+
recipient: 'SP2...',
231+
amount: 1000000n, // in microSTX
232+
memo: 'optional memo',
233+
}, {
234+
onSuccess: (txid) => {},
235+
onError: (error) => {},
236+
});
237+
238+
// Async style
239+
const txid = await transferSTXAsync({
240+
recipient: 'SP2...',
241+
amount: 1000000n,
242+
});
243+
```
244+
245+
### `useWriteContract()`
246+
247+
```ts
248+
import { Pc, PostConditionMode } from '@stacks/transactions';
249+
250+
const { writeContract, writeContractAsync, data, error, isPending } = useWriteContract();
251+
252+
writeContract({
253+
address: 'SP...',
254+
contract: 'my-contract',
255+
functionName: 'my-function',
256+
args: [uintCV(100)],
257+
pc: {
258+
postConditions: [Pc.principal('SP...').willSendLte(100n).ustx()],
259+
mode: PostConditionMode.Deny,
260+
},
261+
}, {
262+
onSuccess: (txHash) => {},
263+
onError: (error) => {},
264+
});
265+
```
266+
267+
### `useSignTransaction()`
268+
269+
Sign a serialized transaction without automatically broadcasting it. Useful for sponsored transaction flows where a separate service pays the fee.
270+
271+
> **Note:** OKX wallet does not support raw transaction signing and will throw an error.
272+
273+
```ts
274+
const { signTransaction, signTransactionAsync, data, error, isPending } = useSignTransaction();
275+
276+
// Callback style
277+
signTransaction({ transaction: '0x0100...', broadcast: false }, {
278+
onSuccess: ({ transaction, txid }) => {},
279+
onError: (error) => {},
280+
});
281+
282+
// Async style
283+
const { transaction, txid } = await signTransactionAsync({
284+
transaction: '0x0100...',
285+
broadcast: false,
286+
});
287+
```
288+
289+
### `useBnsName()`
290+
291+
```ts
292+
const { bnsName, isLoading } = useBnsName(address);
293+
// bnsName = 'satoshi.btc' | null
294+
```
295+
296+
### Utilities
297+
298+
```ts
299+
import { getNetworkFromAddress, getStacksWallets, getLocalStorageWallet } from '@satoshai/kit';
300+
301+
getNetworkFromAddress('SP...'); // 'mainnet'
302+
getNetworkFromAddress('ST...'); // 'testnet'
303+
304+
const { supported, installed } = getStacksWallets();
305+
```
306+
307+
## Supported Wallets
308+
309+
All 6 wallets work with both headless (`connect('xverse')`) and modal (`connect()`) modes.
310+
311+
| Wallet | ID |
312+
|---|---|
313+
| Xverse | `xverse` |
314+
| Leather | `leather` |
315+
| Asigna | `asigna` |
316+
| Fordefi | `fordefi` |
317+
| WalletConnect | `wallet-connect` |
318+
| OKX | `okx` |
319+
320+
## Peer Dependencies
321+
322+
- `react` ^18 or ^19
323+
- `react-dom` ^18 or ^19
324+
- `@stacks/transactions` >=7.0.0
10325

11326
## License
12327

packages/kit/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
README.md

0 commit comments

Comments
 (0)