@satoshai/kit 0.7.0 → 0.8.1

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
@@ -11,14 +11,16 @@ Typesafe Stacks wallet & contract interaction library for React. Wagmi-inspired
11
11
  - **`StacksWalletProvider`** — React context provider for wallet state
12
12
  - **`useConnect` / `useDisconnect`** — Connect and disconnect wallets
13
13
  - **`useWallets`** — Configured wallets with availability status
14
- - **`useAddress`** — Access connected wallet address and status
14
+ - **`useAddress`** — Access connected wallet address and status (discriminated union)
15
15
  - **`useSignMessage`** — Sign arbitrary messages
16
16
  - **`useSignStructuredMessage`** — Sign SIP-018 structured data
17
17
  - **`useSignTransaction`** — Sign serialized transactions (sponsored tx flows)
18
- - **`useWriteContract`** — Call smart contracts with post-conditions
18
+ - **`useWriteContract`** — Call smart contracts with post-conditions (typed or untyped)
19
19
  - **`useTransferSTX`** — Native STX transfers
20
20
  - **`useBnsName`** — Resolve BNS v2 names
21
+ - **Typed errors** — `BaseError`, `WalletNotConnectedError`, `WalletNotFoundError`, `UnsupportedMethodError`, `WalletRequestError`
21
22
  - **6 wallets supported** — Xverse, Leather, OKX, Asigna, Fordefi, WalletConnect
23
+ - **WalletConnect session management** — Zombie session detection, wallet-initiated disconnect, and account change events
22
24
  - **Next.js App Router compatible** — `"use client"` directives included
23
25
 
24
26
  ## Install
@@ -75,13 +77,22 @@ Wrap your app to provide wallet context to all hooks.
75
77
  connectModal={true} // optional — defaults to true
76
78
  walletConnect={{ projectId: '...' }} // optional — enables WalletConnect
77
79
  onConnect={(provider, address) => {}} // optional
78
- onAddressChange={(newAddress) => {}} // optional — Xverse account switching
80
+ onAddressChange={(newAddress) => {}} // optional — Xverse/WalletConnect account switching
79
81
  onDisconnect={() => {}} // optional
80
82
  >
81
83
  {children}
82
84
  </StacksWalletProvider>
83
85
  ```
84
86
 
87
+ | Prop | Type | Default | Description |
88
+ |------|------|---------|-------------|
89
+ | `wallets` | `SupportedStacksWallet[]` | All 6 wallets | Wallets to enable. |
90
+ | `connectModal` | `boolean` | `true` | Show `@stacks/connect` modal on `connect()` with no args. |
91
+ | `walletConnect` | `{ projectId, metadata?, chains? }` | — | WalletConnect config. Required when `wallets` includes `'wallet-connect'`. |
92
+ | `onConnect` | `(provider, address) => void` | — | Called after successful connection. |
93
+ | `onAddressChange` | `(newAddress) => void` | — | Called when the connected account changes. |
94
+ | `onDisconnect` | `() => void` | — | Called when the wallet disconnects. |
95
+
85
96
  > If `wallets` includes `'wallet-connect'`, you must provide `walletConnect.projectId` or the provider will throw at mount.
86
97
 
87
98
  > **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.
@@ -111,8 +122,10 @@ When `connectModal` is enabled:
111
122
 
112
123
  ### `useConnect()`
113
124
 
125
+ Connect to a Stacks wallet. Returns a mutation-style object.
126
+
114
127
  ```ts
115
- const { connect, reset, isPending } = useConnect();
128
+ const { connect, reset, error, isPending, isSuccess, isError, isIdle, status } = useConnect();
116
129
 
117
130
  // Open the @stacks/connect modal (when connectModal is enabled, the default)
118
131
  await connect();
@@ -153,8 +166,10 @@ A wallet is `available` when its browser extension is installed. For `wallet-con
153
166
 
154
167
  ### `useDisconnect()`
155
168
 
169
+ Disconnect the current wallet and clear the persisted session.
170
+
156
171
  ```ts
157
- const { disconnect } = useDisconnect();
172
+ const { disconnect, reset, error, isSuccess, isError, isIdle, isPending, status } = useDisconnect();
158
173
 
159
174
  disconnect();
160
175
  disconnect(() => { /* callback after disconnect */ });
@@ -162,24 +177,29 @@ disconnect(() => { /* callback after disconnect */ });
162
177
 
163
178
  ### `useAddress()`
164
179
 
180
+ Read the connected wallet's address and connection status. Returns a **discriminated union** — when `isConnected` is `true`, `address` and `provider` are narrowed to defined values (no null checks needed).
181
+
165
182
  ```ts
166
183
  const { address, isConnected, isConnecting, isDisconnected, provider } = useAddress();
167
184
 
168
185
  if (isConnected) {
169
- console.log(address); // 'SP...' or 'ST...'
186
+ console.log(address); // 'SP...' or 'ST...' — narrowed to string
170
187
  console.log(provider); // 'xverse' | 'leather' | ...
171
188
  }
172
189
  ```
173
190
 
174
191
  ### `useSignMessage()`
175
192
 
193
+ Sign an arbitrary plaintext message.
194
+
176
195
  ```ts
177
- const { signMessage, signMessageAsync, data, error, isPending } = useSignMessage();
196
+ const { signMessage, signMessageAsync, data, error, isPending, reset } = useSignMessage();
178
197
 
179
198
  // Callback style
180
199
  signMessage({ message: 'Hello Stacks' }, {
181
200
  onSuccess: ({ publicKey, signature }) => {},
182
201
  onError: (error) => {},
202
+ onSettled: (data, error) => {},
183
203
  });
184
204
 
185
205
  // Async style
@@ -190,7 +210,7 @@ const { publicKey, signature } = await signMessageAsync({ message: 'Hello Stacks
190
210
 
191
211
  Sign SIP-018 structured data for typed, verifiable off-chain messages.
192
212
 
193
- > **Note:** OKX wallet does not support structured message signing and will throw an error.
213
+ > **Note:** OKX wallet does not support structured message signing and will throw an `UnsupportedMethodError`.
194
214
 
195
215
  ```ts
196
216
  import { tupleCV, stringAsciiCV, uintCV } from '@stacks/transactions';
@@ -222,14 +242,18 @@ const { publicKey, signature } = await signStructuredMessageAsync({
222
242
 
223
243
  ### `useTransferSTX()`
224
244
 
245
+ Transfer native STX tokens. Amount is in **microSTX** (1 STX = 1,000,000 microSTX).
246
+
225
247
  ```ts
226
- const { transferSTX, transferSTXAsync, data, error, isPending } = useTransferSTX();
248
+ const { transferSTX, transferSTXAsync, data, error, isPending, reset } = useTransferSTX();
227
249
 
228
250
  // Callback style
229
251
  transferSTX({
230
252
  recipient: 'SP2...',
231
- amount: 1000000n, // in microSTX
253
+ amount: 1000000n, // 1 STX
232
254
  memo: 'optional memo',
255
+ fee: 2000n, // optional custom fee
256
+ nonce: 42n, // optional custom nonce
233
257
  }, {
234
258
  onSuccess: (txid) => {},
235
259
  onError: (error) => {},
@@ -244,10 +268,14 @@ const txid = await transferSTXAsync({
244
268
 
245
269
  ### `useWriteContract()`
246
270
 
271
+ Call a public function on a Clarity smart contract. Supports two modes:
272
+
273
+ #### Untyped mode (ClarityValue[] args)
274
+
247
275
  ```ts
248
- import { Pc, PostConditionMode } from '@stacks/transactions';
276
+ import { uintCV, Pc, PostConditionMode } from '@stacks/transactions';
249
277
 
250
- const { writeContract, writeContractAsync, data, error, isPending } = useWriteContract();
278
+ const { writeContract, writeContractAsync, data, error, isPending, reset } = useWriteContract();
251
279
 
252
280
  writeContract({
253
281
  address: 'SP...',
@@ -264,14 +292,58 @@ writeContract({
264
292
  });
265
293
  ```
266
294
 
295
+ #### Typed mode (with ABI — autocomplete + type-checked args)
296
+
297
+ When you pass an `abi` object, `functionName` is autocompleted from the ABI's public functions and `args` becomes a named, type-checked object. Use [`@satoshai/abi-cli`](https://github.com/satoshai-dev/abi-cli) to generate typed ABIs from deployed contracts.
298
+
299
+ ```ts
300
+ import { PostConditionMode } from '@stacks/transactions';
301
+ import type { ClarityAbi } from '@satoshai/kit';
302
+
303
+ // 1. Define your ABI (use @satoshai/abi-cli to generate it — https://github.com/satoshai-dev/abi-cli)
304
+ const poolAbi = { functions: [...], ... } as const satisfies ClarityAbi;
305
+
306
+ // 2. Call with full type safety
307
+ const txid = await writeContractAsync({
308
+ abi: poolAbi,
309
+ address: 'SP...',
310
+ contract: 'pool-v1',
311
+ functionName: 'deposit', // autocompleted
312
+ args: { amount: 1000000n }, // named args, type-checked
313
+ pc: { postConditions: [], mode: PostConditionMode.Deny },
314
+ });
315
+ ```
316
+
317
+ #### `createContractConfig()`
318
+
319
+ Pre-bind ABI + address + contract for reuse across multiple calls:
320
+
321
+ ```ts
322
+ import { createContractConfig } from '@satoshai/kit';
323
+
324
+ const pool = createContractConfig({
325
+ abi: poolAbi,
326
+ address: 'SP...',
327
+ contract: 'pool-v1',
328
+ });
329
+
330
+ // Spread into writeContract — functionName and args stay typed
331
+ writeContract({
332
+ ...pool,
333
+ functionName: 'deposit',
334
+ args: { amount: 1000000n },
335
+ pc: { postConditions: [], mode: PostConditionMode.Deny },
336
+ });
337
+ ```
338
+
267
339
  ### `useSignTransaction()`
268
340
 
269
341
  Sign a serialized transaction without automatically broadcasting it. Useful for sponsored transaction flows where a separate service pays the fee.
270
342
 
271
- > **Note:** OKX wallet does not support raw transaction signing and will throw an error.
343
+ > **Note:** OKX wallet does not support raw transaction signing and will throw an `UnsupportedMethodError`.
272
344
 
273
345
  ```ts
274
- const { signTransaction, signTransactionAsync, data, error, isPending } = useSignTransaction();
346
+ const { signTransaction, signTransactionAsync, data, error, isPending, reset } = useSignTransaction();
275
347
 
276
348
  // Callback style
277
349
  signTransaction({ transaction: '0x0100...', broadcast: false }, {
@@ -288,6 +360,8 @@ const { transaction, txid } = await signTransactionAsync({
288
360
 
289
361
  ### `useBnsName()`
290
362
 
363
+ Resolve a BNS v2 primary name for a Stacks address. Returns `null` when no name is registered.
364
+
291
365
  ```ts
292
366
  const { bnsName, isLoading } = useBnsName(address);
293
367
  // bnsName = 'satoshi.btc' | null
@@ -296,14 +370,94 @@ const { bnsName, isLoading } = useBnsName(address);
296
370
  ### Utilities
297
371
 
298
372
  ```ts
299
- import { getNetworkFromAddress, getStacksWallets, getLocalStorageWallet } from '@satoshai/kit';
300
-
373
+ import {
374
+ getNetworkFromAddress,
375
+ getStacksWallets,
376
+ getLocalStorageWallet,
377
+ createContractConfig,
378
+ } from '@satoshai/kit';
379
+
380
+ // Infer network from address prefix
301
381
  getNetworkFromAddress('SP...'); // 'mainnet'
302
382
  getNetworkFromAddress('ST...'); // 'testnet'
303
383
 
384
+ // Detect supported and installed wallets
304
385
  const { supported, installed } = getStacksWallets();
386
+
387
+ // Read persisted wallet session (returns null on server or when empty)
388
+ const session = getLocalStorageWallet();
389
+ // { address: 'SP...', provider: 'xverse' } | null
305
390
  ```
306
391
 
392
+ ## Mutation Hook Return Types
393
+
394
+ All mutation hooks (`useConnect`, `useSignMessage`, `useWriteContract`, etc.) return the same status shape:
395
+
396
+ | Field | Type | Description |
397
+ |-------|------|-------------|
398
+ | `data` | `T \| undefined` | The successful result. |
399
+ | `error` | `BaseError \| null` | The error, if any. |
400
+ | `status` | `'idle' \| 'pending' \| 'error' \| 'success'` | Current mutation status. |
401
+ | `isIdle` | `boolean` | `true` when no operation has been triggered. |
402
+ | `isPending` | `boolean` | `true` while waiting for wallet response. |
403
+ | `isSuccess` | `boolean` | `true` after a successful operation. |
404
+ | `isError` | `boolean` | `true` after a failed operation. |
405
+ | `reset()` | `() => void` | Reset the mutation state back to idle. |
406
+
407
+ Each hook also provides both a **callback** variant (fire-and-forget with `onSuccess`/`onError`/`onSettled` callbacks) and an **async** variant that returns a promise.
408
+
409
+ ## Error Handling
410
+
411
+ All errors thrown by hooks extend `BaseError`. You can catch and narrow them:
412
+
413
+ ```ts
414
+ import {
415
+ BaseError,
416
+ WalletNotConnectedError,
417
+ WalletNotFoundError,
418
+ UnsupportedMethodError,
419
+ WalletRequestError,
420
+ } from '@satoshai/kit';
421
+
422
+ try {
423
+ await signMessageAsync({ message: 'hello' });
424
+ } catch (err) {
425
+ if (err instanceof WalletNotConnectedError) {
426
+ // No wallet connected — prompt user to connect
427
+ } else if (err instanceof UnsupportedMethodError) {
428
+ // Wallet doesn't support this method (e.g. OKX + structured signing)
429
+ console.log(err.method, err.wallet);
430
+ } else if (err instanceof WalletNotFoundError) {
431
+ // Wallet extension not installed
432
+ console.log(err.wallet);
433
+ } else if (err instanceof WalletRequestError) {
434
+ // Wallet rejected or failed — original error in cause
435
+ console.log(err.method, err.wallet, err.cause);
436
+ } else if (err instanceof BaseError) {
437
+ // Any other kit error
438
+ console.log(err.shortMessage);
439
+ console.log(err.walk()); // root cause
440
+ }
441
+ }
442
+ ```
443
+
444
+ | Error | When |
445
+ |-------|------|
446
+ | `WalletNotConnectedError` | A mutation hook is called before connecting. |
447
+ | `WalletNotFoundError` | A wallet's browser extension is not installed (e.g. OKX). |
448
+ | `UnsupportedMethodError` | The wallet doesn't support the requested method. |
449
+ | `WalletRequestError` | The wallet rejected or failed the RPC request. |
450
+
451
+ ## WalletConnect Session Management
452
+
453
+ When using WalletConnect, the kit automatically handles session lifecycle events:
454
+
455
+ - **Zombie session detection** — On app restore, the relay is pinged (10s timeout). If the wallet on the other end doesn't respond, the session is cleaned up and `onDisconnect` fires.
456
+ - **Wallet-initiated disconnect** — If the wallet disconnects via the relay, state is cleaned up automatically.
457
+ - **Account changes** — Listens for `accountsChanged`, `stx_accountChange` (SIP-030), and `stx_accountsChanged` events. When the connected account changes, `onAddressChange` fires.
458
+
459
+ No additional setup is needed — these features activate when `wallets` includes `'wallet-connect'` and a session is active.
460
+
307
461
  ## Supported Wallets
308
462
 
309
463
  All 6 wallets work with both headless (`connect('xverse')`) and modal (`connect()`) modes.
@@ -328,15 +482,15 @@ All 6 wallets work with both headless (`connect('xverse')`) and modal (`connect(
328
482
  | `useWriteContract` | ✓ | ✓ | ✓ | ✓ | ~ | ✓ |
329
483
  | `useTransferSTX` | ✓ | ✓ | ✓ | ✓ | ~ | ✓ |
330
484
 
331
- ✓ Confirmed supported | ✗ Unsupported (throws error) | ? Unverified | ~ Depends on the connected wallet
485
+ ✓ Confirmed supported | ✗ Unsupported (throws `UnsupportedMethodError`) | ? Unverified | ~ Depends on the connected wallet
332
486
 
333
487
  **Notes:**
334
488
 
335
- - **OKX** uses a proprietary API (`window.okxwallet.stacks`) instead of the standard `@stacks/connect` RPC. `useSignStructuredMessage` and `useSignTransaction` are explicitly unsupported and will throw.
489
+ - **OKX** uses a proprietary API (`window.okxwallet.stacks`) instead of the standard `@stacks/connect` RPC. `useSignStructuredMessage` and `useSignTransaction` are explicitly unsupported and will throw `UnsupportedMethodError`.
336
490
  - **Asigna** is a multisig wallet. Transaction-based hooks (`useWriteContract`, `useTransferSTX`) work, but message signing hooks may be limited since there is no multisig message signature standard on Stacks.
337
491
  - **Fordefi** supports transactions and contract calls on Stacks, but their [supported blockchains](https://docs.fordefi.com/docs/supported-blockchains) page does not list Stacks under message signing capabilities.
338
492
  - **WalletConnect** is a relay protocol — all methods are forwarded, but actual support depends on the wallet on the other end.
339
- - **Xverse** and **Leather** implement the full [SIP-030](https://github.com/janniks/sips/blob/main/sips/sip-030/sip-030-wallet-interface.md) interface.
493
+ - **Xverse** and **Leather** support all hooks provided by `@satoshai/kit`. Neither fully implements [SIP-030](https://github.com/janniks/sips/blob/main/sips/sip-030/sip-030-wallet-interface.md) — for example, account change detection uses Xverse's proprietary `XverseProviders.StacksProvider.addListener('accountChange')` API, and Leather does not emit account change events at all.
340
494
 
341
495
  This matrix was compiled from wallet documentation as of March 2026. Sources: [Xverse Sats Connect docs](https://docs.xverse.app/sats-connect/stacks-methods), [Leather developer docs](https://leather.gitbook.io/developers), [Asigna docs](https://asigna.gitbook.io/asigna), [Fordefi docs](https://docs.fordefi.com/docs/supported-blockchains), [@stacks/connect WalletConnect source](https://github.com/stx-labs/connect/tree/main/packages/connect/src/walletconnect).
342
496
 
package/dist/index.cjs CHANGED
@@ -9,6 +9,7 @@ var bnsV2Sdk = require('bns-v2-sdk');
9
9
  // src/errors.ts
10
10
  var BaseError = class extends Error {
11
11
  name = "StacksKitError";
12
+ /** Short, human-readable error summary without details or cause chain. */
12
13
  shortMessage;
13
14
  constructor(shortMessage, options) {
14
15
  const message = [
@@ -18,6 +19,10 @@ var BaseError = class extends Error {
18
19
  super(message, options?.cause ? { cause: options.cause } : void 0);
19
20
  this.shortMessage = shortMessage;
20
21
  }
22
+ /**
23
+ * Walk the error cause chain. If `fn` is provided, returns the first error
24
+ * where `fn` returns `true`; otherwise returns the root cause.
25
+ */
21
26
  walk(fn) {
22
27
  return walk(this, fn);
23
28
  }
@@ -37,6 +42,7 @@ var WalletNotConnectedError = class extends BaseError {
37
42
  };
38
43
  var WalletNotFoundError = class extends BaseError {
39
44
  name = "WalletNotFoundError";
45
+ /** The wallet ID that was not found. */
40
46
  wallet;
41
47
  constructor({ wallet }) {
42
48
  super(`${wallet} wallet not found`, {
@@ -47,7 +53,9 @@ var WalletNotFoundError = class extends BaseError {
47
53
  };
48
54
  var UnsupportedMethodError = class extends BaseError {
49
55
  name = "UnsupportedMethodError";
56
+ /** The SIP-030 method name that is not supported. */
50
57
  method;
58
+ /** The wallet that does not support the method. */
51
59
  wallet;
52
60
  constructor({ method, wallet }) {
53
61
  super(`${method} is not supported by ${wallet} wallet`);
@@ -57,7 +65,9 @@ var UnsupportedMethodError = class extends BaseError {
57
65
  };
58
66
  var WalletRequestError = class extends BaseError {
59
67
  name = "WalletRequestError";
68
+ /** The SIP-030 method name that failed. */
60
69
  method;
70
+ /** The wallet that returned the error. */
61
71
  wallet;
62
72
  constructor({ method, wallet, cause }) {
63
73
  super(`${wallet} wallet request failed`, {
@@ -214,6 +224,102 @@ var extractStacksAddress = (typedProvider, addresses) => {
214
224
  `No valid Stacks address found for ${typedProvider} wallet`
215
225
  );
216
226
  };
227
+
228
+ // src/hooks/use-wallet-connect/use-wallet-connect.helpers.ts
229
+ var getWcUniversalProvider = () => window.WalletConnectProvider?.connector?.provider ?? null;
230
+ var extractStacksAddress2 = (accounts) => {
231
+ for (const entry of accounts) {
232
+ if (typeof entry === "object" && entry !== null && "address" in entry) {
233
+ return entry.address;
234
+ }
235
+ if (typeof entry === "string") {
236
+ if (entry.startsWith("S")) return entry;
237
+ if (entry.startsWith("stacks:")) return entry.split(":")[2] ?? null;
238
+ }
239
+ }
240
+ return null;
241
+ };
242
+ var PING_TIMEOUT_MS = 1e4;
243
+ var pingSession = async () => {
244
+ const wcProvider = getWcUniversalProvider();
245
+ const client = wcProvider?.client;
246
+ const session = wcProvider?.session;
247
+ if (!client || !session) return false;
248
+ try {
249
+ await Promise.race([
250
+ client.ping({ topic: session.topic }),
251
+ new Promise(
252
+ (_, reject) => setTimeout(() => reject(new Error("Ping timeout")), PING_TIMEOUT_MS)
253
+ )
254
+ ]);
255
+ return true;
256
+ } catch {
257
+ return false;
258
+ }
259
+ };
260
+
261
+ // src/hooks/use-wallet-connect/use-wallet-connect.ts
262
+ var useWalletConnect = ({
263
+ address,
264
+ provider,
265
+ onAddressChange,
266
+ onDisconnect
267
+ }) => {
268
+ react.useEffect(() => {
269
+ if (provider !== "wallet-connect" || !address) return;
270
+ let cancelled = false;
271
+ const validateSession = async () => {
272
+ const alive = await pingSession();
273
+ if (cancelled) return;
274
+ if (!alive) {
275
+ const wcProvider = getWcUniversalProvider();
276
+ try {
277
+ await wcProvider?.disconnect();
278
+ } catch {
279
+ }
280
+ connect.clearSelectedProviderId();
281
+ onDisconnect();
282
+ }
283
+ };
284
+ void validateSession();
285
+ return () => {
286
+ cancelled = true;
287
+ };
288
+ }, [provider, address, onDisconnect]);
289
+ react.useEffect(() => {
290
+ if (provider !== "wallet-connect" || !address) return;
291
+ const wcProvider = getWcUniversalProvider();
292
+ if (!wcProvider) return;
293
+ const handleDisconnect = () => {
294
+ connect.clearSelectedProviderId();
295
+ onDisconnect();
296
+ };
297
+ const handleAccountsChanged = (...args) => {
298
+ const accounts = args[0];
299
+ const newAddress = extractStacksAddress2(accounts);
300
+ if (newAddress && newAddress !== address) {
301
+ onAddressChange(newAddress);
302
+ }
303
+ };
304
+ wcProvider.on("disconnect", handleDisconnect);
305
+ wcProvider.on("accountsChanged", handleAccountsChanged);
306
+ wcProvider.on("stx_accountChange", handleAccountsChanged);
307
+ wcProvider.on("stx_accountsChanged", handleAccountsChanged);
308
+ return () => {
309
+ try {
310
+ wcProvider.off("disconnect", handleDisconnect);
311
+ wcProvider.off("accountsChanged", handleAccountsChanged);
312
+ wcProvider.off("stx_accountChange", handleAccountsChanged);
313
+ wcProvider.off("stx_accountsChanged", handleAccountsChanged);
314
+ } catch (error) {
315
+ console.error(
316
+ "Failed to remove WalletConnect listeners:",
317
+ error
318
+ );
319
+ }
320
+ };
321
+ }, [address, provider, onAddressChange, onDisconnect]);
322
+ };
217
323
  var getXverseProductInfo = async () => await window.XverseProviders?.StacksProvider?.getProductInfo?.() ?? null;
218
324
  var shouldSupportAccountChange = (version) => version !== void 0 && version !== "1.0.0";
219
325
  var waitForXverseProvider = async (maxAttempts = 10, initialDelay = 200) => {
@@ -592,6 +698,18 @@ var StacksWalletProvider = ({
592
698
  onAddressChange: handleAddressChange,
593
699
  connect: connect$1
594
700
  });
701
+ const handleWcDisconnect = react.useCallback(() => {
702
+ localStorage.removeItem(LOCAL_STORAGE_STACKS);
703
+ setAddress(void 0);
704
+ setProvider(void 0);
705
+ onDisconnect?.();
706
+ }, [onDisconnect]);
707
+ useWalletConnect({
708
+ address,
709
+ provider,
710
+ onAddressChange: handleAddressChange,
711
+ onDisconnect: handleWcDisconnect
712
+ });
595
713
  const { installed } = getStacksWallets();
596
714
  const configured = wallets ?? [...SUPPORTED_STACKS_WALLETS];
597
715
  const walletInfos = configured.map((w) => ({