@satoshai/kit 0.3.1 → 0.4.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/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { WalletConnect, setSelectedProviderId, request, getSelectedProvider, clearSelectedProviderId } from '@stacks/connect';
1
+ import { WalletConnect, DEFAULT_PROVIDERS, WALLET_CONNECT_PROVIDER, clearSelectedProviderId, request, getSelectedProviderId, getSelectedProvider, setSelectedProviderId } from '@stacks/connect';
2
2
  import { createContext, useState, useRef, useEffect, useCallback, useMemo, useContext } from 'react';
3
3
  import { jsx } from 'react/jsx-runtime';
4
4
  import { PostConditionMode, postConditionToHex, cvToHex } from '@stacks/transactions';
@@ -8,10 +8,17 @@ import { getPrimaryName } from 'bns-v2-sdk';
8
8
  var STACKS_TO_STACKS_CONNECT_PROVIDERS = {
9
9
  xverse: "XverseProviders.BitcoinProvider",
10
10
  leather: "LeatherProvider",
11
+ okx: "OkxStacksProvider",
11
12
  asigna: "AsignaProvider",
12
13
  fordefi: "FordefiProviders.UtxoProvider",
13
14
  "wallet-connect": "WalletConnectProvider"
14
15
  };
16
+ var STACKS_CONNECT_TO_STACKS_PROVIDERS = Object.fromEntries(
17
+ Object.entries(STACKS_TO_STACKS_CONNECT_PROVIDERS).map(([kit, connect]) => [
18
+ connect,
19
+ kit
20
+ ])
21
+ );
15
22
 
16
23
  // src/constants/storage-keys.ts
17
24
  var LOCAL_STORAGE_STACKS = "@satoshai/kit";
@@ -20,10 +27,10 @@ var LOCAL_STORAGE_STACKS = "@satoshai/kit";
20
27
  var SUPPORTED_STACKS_WALLETS = [
21
28
  "xverse",
22
29
  "leather",
23
- "okx",
24
30
  "asigna",
25
31
  "fordefi",
26
- "wallet-connect"
32
+ "wallet-connect",
33
+ "okx"
27
34
  ];
28
35
 
29
36
  // src/utils/get-stacks-wallets.ts
@@ -35,7 +42,7 @@ var getStacksWallets = () => {
35
42
  return { supported, installed };
36
43
  };
37
44
  var checkIfStacksProviderIsInstalled = (wallet) => {
38
- if (typeof window === "undefined") return true;
45
+ if (typeof window === "undefined") return wallet === "wallet-connect";
39
46
  switch (wallet) {
40
47
  case "xverse":
41
48
  return !!window.XverseProviders;
@@ -90,6 +97,38 @@ var getOKXStacksAddress = async () => {
90
97
  provider: "okx"
91
98
  };
92
99
  };
100
+ var OKX_PROVIDER_META = {
101
+ id: "OkxStacksProvider",
102
+ name: "OKX Wallet",
103
+ icon: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiICAgICB4bWxuczp4b2RtPSJodHRwOi8vd3d3LmNvcmVsLmNvbS9jb3JlbGRyYXcvb2RtLzIwMDMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjUwMCAyNTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAyNTAwIDI1MDA7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KICAgIC5zdDB7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7fQogICAgLnN0MXtmaWxsOiNGRkZGRkY7fQo8L3N0eWxlPgo8ZyBpZD0iTGF5ZXJfeDAwMjBfMSI+CiAgICA8ZyBpZD0iXzIxODczODEzMjM4NTYiPgogICAgICAgIDxyZWN0IHk9IjAiIGNsYXNzPSJzdDAiIHdpZHRoPSIyNTAwIiBoZWlnaHQ9IjI1MDAiPjwvcmVjdD4KICAgICAgICA8Zz4KICAgICAgICAgICAgPHBhdGggY2xhc3M9InN0MSIgZD0iTTE0NjMsMTAxNWgtNDA0Yy0xNywwLTMxLDE0LTMxLDMxdjQwNGMwLDE3LDE0LDMxLDMxLDMxaDQwNGMxNywwLDMxLTE0LDMxLTMxdi00MDQgICAgIEMxNDk0LDEwMjksMTQ4MCwxMDE1LDE0NjMsMTAxNXoiPjwvcGF0aD4KICAgICAgICAgICAgPHBhdGggY2xhc3M9InN0MSIgZD0iTTk5Niw1NDlINTkyYy0xNywwLTMxLDE0LTMxLDMxdjQwNGMwLDE3LDE0LDMxLDMxLDMxaDQwNGMxNywwLDMxLTE0LDMxLTMxVjU4MEMxMDI3LDU2MywxMDEzLDU0OSw5OTYsNTQ5eiI+PC9wYXRoPgogICAgICAgICAgICA8cGF0aCBjbGFzcz0ic3QxIiBkPSJNMTkzMCw1NDloLTQwNGMtMTcsMC0zMSwxNC0zMSwzMXY0MDRjMCwxNywxNCwzMSwzMSwzMWg0MDRjMTcsMCwzMS0xNCwzMS0zMVY1ODAgICAgIEMxOTYxLDU2MywxOTQ3LDU0OSwxOTMwLDU0OXoiPjwvcGF0aD4KICAgICAgICAgICAgPHBhdGggY2xhc3M9InN0MSIgZD0iTTk5NiwxNDgySDU5MmMtMTcsMC0zMSwxNC0zMSwzMXY0MDRjMCwxNywxNCwzMSwzMSwzMWg0MDRjMTcsMCwzMS0xNCwzMS0zMXYtNDA0ICAgICBDMTAyNywxNDk2LDEwMTMsMTQ4Miw5OTYsMTQ4MnoiPjwvcGF0aD4KICAgICAgICAgICAgPHBhdGggY2xhc3M9InN0MSIgZD0iTTE5MzAsMTQ4MmgtNDA0Yy0xNywwLTMxLDE0LTMxLDMxdjQwNGMwLDE3LDE0LDMxLDMxLDMxaDQwNGMxNywwLDMxLTE0LDMxLTMxdi00MDQgICAgIEMxOTYxLDE0OTYsMTk0NywxNDgyLDE5MzAsMTQ4MnoiPjwvcGF0aD4KICAgICAgICA8L2c+CiAgICA8L2c+CjwvZz4KPC9zdmc+",
104
+ webUrl: "https://www.okx.com/"
105
+ };
106
+ var registerOkxProvider = () => {
107
+ if (typeof window === "undefined") return;
108
+ if (!window.OkxStacksProvider) {
109
+ window.OkxStacksProvider = {
110
+ request: async (method) => {
111
+ if (method === "getAddresses") {
112
+ const data = await getOKXStacksAddress();
113
+ return {
114
+ result: {
115
+ addresses: [
116
+ { address: data.address, symbol: "STX" }
117
+ ]
118
+ }
119
+ };
120
+ }
121
+ throw new Error(
122
+ `OKX adapter: unsupported method "${method}". Use connect('okx') for direct OKX SDK access.`
123
+ );
124
+ }
125
+ };
126
+ }
127
+ };
128
+ var unregisterOkxProvider = () => {
129
+ if (typeof window === "undefined") return;
130
+ delete window.OkxStacksProvider;
131
+ };
93
132
  var extractStacksAddress = (typedProvider, addresses) => {
94
133
  if (!addresses.length) {
95
134
  throw new Error(`No addresses provided for ${typedProvider} wallet`);
@@ -221,12 +260,21 @@ var getLocalStorageWallet = () => {
221
260
  return null;
222
261
  }
223
262
  };
263
+ var PROVIDER_META_BY_KIT_ID = Object.fromEntries(
264
+ [...DEFAULT_PROVIDERS, WALLET_CONNECT_PROVIDER, OKX_PROVIDER_META].map(
265
+ (p) => [
266
+ STACKS_CONNECT_TO_STACKS_PROVIDERS[p.id],
267
+ { name: p.name, icon: p.icon ?? "", webUrl: p.webUrl ?? "" }
268
+ ]
269
+ )
270
+ );
224
271
  var StacksWalletContext = createContext(
225
272
  void 0
226
273
  );
227
274
  var StacksWalletProvider = ({
228
275
  children,
229
276
  wallets,
277
+ connectModal = true,
230
278
  walletConnect,
231
279
  onConnect,
232
280
  onAddressChange,
@@ -236,15 +284,23 @@ var StacksWalletProvider = ({
236
284
  const [provider, setProvider] = useState();
237
285
  const [isConnecting, setIsConnecting] = useState(false);
238
286
  const connectGenRef = useRef(0);
287
+ const wasConnectedRef = useRef(false);
239
288
  const wcInitRef = useRef(null);
289
+ const walletConnectRef = useRef(walletConnect);
290
+ walletConnectRef.current = walletConnect;
240
291
  const walletsKey = wallets?.join(",");
292
+ if (wallets?.includes("wallet-connect") && !walletConnect?.projectId) {
293
+ throw new Error(
294
+ 'StacksWalletProvider: "wallet-connect" is listed in wallets but no walletConnect.projectId was provided.'
295
+ );
296
+ }
241
297
  useEffect(() => {
242
- if (wallets?.includes("wallet-connect") && !walletConnect?.projectId) {
243
- throw new Error(
244
- 'StacksWalletProvider: "wallet-connect" is listed in wallets but no walletConnect.projectId was provided.'
245
- );
298
+ const okxConfigured = !wallets || wallets.includes("okx");
299
+ if (connectModal && okxConfigured) {
300
+ registerOkxProvider();
301
+ return () => unregisterOkxProvider();
246
302
  }
247
- }, [walletsKey, walletConnect?.projectId]);
303
+ }, [connectModal, walletsKey]);
248
304
  useEffect(() => {
249
305
  const loadPersistedWallet = async () => {
250
306
  const persisted = getLocalStorageWallet();
@@ -284,6 +340,65 @@ var StacksWalletProvider = ({
284
340
  }, [walletConnect?.projectId]);
285
341
  const connect = useCallback(
286
342
  async (providerId, options) => {
343
+ if (connectModal && !providerId) {
344
+ const gen2 = ++connectGenRef.current;
345
+ setIsConnecting(true);
346
+ try {
347
+ clearSelectedProviderId();
348
+ const requestOptions = {
349
+ forceWalletSelect: true,
350
+ // OKX at the end so it appears last among installed wallets
351
+ defaultProviders: [
352
+ ...DEFAULT_PROVIDERS,
353
+ OKX_PROVIDER_META
354
+ ]
355
+ };
356
+ if (wallets) {
357
+ requestOptions.approvedProviderIds = wallets.map(
358
+ (w) => STACKS_TO_STACKS_CONNECT_PROVIDERS[w]
359
+ );
360
+ }
361
+ const wc2 = walletConnectRef.current;
362
+ if (wc2?.projectId) {
363
+ requestOptions.walletConnect = buildWalletConnectConfig(
364
+ wc2.projectId,
365
+ wc2.metadata,
366
+ wc2.chains
367
+ );
368
+ }
369
+ const data = await request(
370
+ requestOptions,
371
+ "getAddresses",
372
+ {}
373
+ );
374
+ if (connectGenRef.current !== gen2) return;
375
+ const selectedId = getSelectedProviderId();
376
+ const resolvedProvider = selectedId ? STACKS_CONNECT_TO_STACKS_PROVIDERS[selectedId] : void 0;
377
+ if (!resolvedProvider) {
378
+ throw new Error(
379
+ `Unknown provider returned from @stacks/connect modal: ${selectedId ?? "none"}`
380
+ );
381
+ }
382
+ const extractedAddress = extractStacksAddress(
383
+ resolvedProvider,
384
+ data.addresses
385
+ );
386
+ setAddress(extractedAddress);
387
+ setProvider(resolvedProvider);
388
+ options?.onSuccess?.(extractedAddress, resolvedProvider);
389
+ } catch (error) {
390
+ if (connectGenRef.current !== gen2) return;
391
+ console.error("Failed to connect wallet:", error);
392
+ getSelectedProvider()?.disconnect?.();
393
+ clearSelectedProviderId();
394
+ options?.onError?.(error);
395
+ } finally {
396
+ if (connectGenRef.current === gen2) {
397
+ setIsConnecting(false);
398
+ }
399
+ }
400
+ return;
401
+ }
287
402
  const typedProvider = SUPPORTED_STACKS_WALLETS.find(
288
403
  (wallet) => wallet === providerId
289
404
  );
@@ -303,7 +418,8 @@ var StacksWalletProvider = ({
303
418
  options?.onError?.(error);
304
419
  return;
305
420
  }
306
- if (typedProvider === "wallet-connect" && !walletConnect?.projectId) {
421
+ const wc = walletConnectRef.current;
422
+ if (typedProvider === "wallet-connect" && !wc?.projectId) {
307
423
  const error = new Error(
308
424
  "WalletConnect requires a project ID. Please provide walletConnect.projectId to the StacksWalletProvider."
309
425
  );
@@ -325,10 +441,10 @@ var StacksWalletProvider = ({
325
441
  setSelectedProviderId(
326
442
  STACKS_TO_STACKS_CONNECT_PROVIDERS[typedProvider]
327
443
  );
328
- const wcConfig = typedProvider === "wallet-connect" && walletConnect ? buildWalletConnectConfig(
329
- walletConnect.projectId,
330
- walletConnect.metadata,
331
- walletConnect.chains
444
+ const wcConfig = typedProvider === "wallet-connect" && wc ? buildWalletConnectConfig(
445
+ wc.projectId,
446
+ wc.metadata,
447
+ wc.chains
332
448
  ) : void 0;
333
449
  if (wcConfig) {
334
450
  if (wcInitRef.current) await wcInitRef.current;
@@ -365,7 +481,7 @@ var StacksWalletProvider = ({
365
481
  }
366
482
  }
367
483
  },
368
- [walletConnect]
484
+ [connectModal, walletsKey]
369
485
  );
370
486
  const reset = useCallback(() => {
371
487
  connectGenRef.current++;
@@ -392,24 +508,35 @@ var StacksWalletProvider = ({
392
508
  );
393
509
  }, [address, provider]);
394
510
  useEffect(() => {
395
- if (!address || !provider || !onConnect) return;
396
- onConnect(provider, address);
511
+ const isConnected = !!address && !!provider;
512
+ if (isConnected && !wasConnectedRef.current) {
513
+ onConnect?.(provider, address);
514
+ }
515
+ wasConnectedRef.current = isConnected;
397
516
  }, [address, provider, onConnect]);
398
- useXverse({
399
- address,
400
- provider,
401
- onAddressChange: (newAddress) => {
517
+ const handleAddressChange = useCallback(
518
+ (newAddress) => {
402
519
  setAddress(newAddress);
403
520
  onAddressChange?.(newAddress);
404
521
  },
522
+ [onAddressChange]
523
+ );
524
+ useXverse({
525
+ address,
526
+ provider,
527
+ onAddressChange: handleAddressChange,
405
528
  connect
406
529
  });
407
530
  const { installed } = getStacksWallets();
408
531
  const configured = wallets ?? [...SUPPORTED_STACKS_WALLETS];
409
532
  const walletInfos = configured.map((w) => ({
410
533
  id: w,
534
+ name: PROVIDER_META_BY_KIT_ID[w]?.name ?? w,
535
+ icon: PROVIDER_META_BY_KIT_ID[w]?.icon ?? "",
536
+ webUrl: PROVIDER_META_BY_KIT_ID[w]?.webUrl ?? "",
411
537
  available: w === "wallet-connect" ? !!walletConnect?.projectId : installed.includes(w)
412
538
  }));
539
+ const walletInfosKey = walletInfos.map((w) => `${w.id}:${w.available}`).join(",");
413
540
  const value = useMemo(() => {
414
541
  const walletState = isConnecting ? { status: "connecting", address: void 0, provider: void 0 } : address && provider ? { status: "connected", address, provider } : {
415
542
  status: "disconnected",
@@ -423,7 +550,7 @@ var StacksWalletProvider = ({
423
550
  reset,
424
551
  wallets: walletInfos
425
552
  };
426
- }, [address, provider, isConnecting, connect, disconnect, reset, walletInfos]);
553
+ }, [address, provider, isConnecting, connect, disconnect, reset, walletInfosKey]);
427
554
  return /* @__PURE__ */ jsx(StacksWalletContext.Provider, { value, children });
428
555
  };
429
556
  var useStacksWalletContext = () => {
@@ -739,20 +866,28 @@ var useBnsName = (address) => {
739
866
  setIsLoading(false);
740
867
  return;
741
868
  }
869
+ let cancelled = false;
742
870
  const fetchBnsName = async () => {
743
871
  setIsLoading(true);
744
872
  try {
745
873
  const network = getNetworkFromAddress(address);
746
874
  const result = await getPrimaryName({ address, network });
875
+ if (cancelled) return;
747
876
  const fullName = result ? `${result.name}.${result.namespace}` : null;
748
877
  setBnsName(fullName);
749
878
  } catch {
879
+ if (cancelled) return;
750
880
  setBnsName(null);
751
881
  } finally {
752
- setIsLoading(false);
882
+ if (!cancelled) {
883
+ setIsLoading(false);
884
+ }
753
885
  }
754
886
  };
755
887
  void fetchBnsName();
888
+ return () => {
889
+ cancelled = true;
890
+ };
756
891
  }, [address]);
757
892
  return { bnsName, isLoading };
758
893
  };