@sundaeswap/wallet-lite 0.1.1 → 0.1.3

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.
Files changed (102) hide show
  1. package/dist/cjs/classes/ReadOnlyProvider.Blockfrost.class.js +22 -12
  2. package/dist/cjs/classes/ReadOnlyProvider.Blockfrost.class.js.map +1 -1
  3. package/dist/cjs/classes/WalletObserver.class.js +105 -17
  4. package/dist/cjs/classes/WalletObserver.class.js.map +1 -1
  5. package/dist/cjs/react-components/RenderWallet.js +2 -1
  6. package/dist/cjs/react-components/RenderWallet.js.map +1 -1
  7. package/dist/cjs/react-components/RenderWalletHandles.js +2 -1
  8. package/dist/cjs/react-components/RenderWalletHandles.js.map +1 -1
  9. package/dist/cjs/react-components/RenderWalletPeerConnect.js +2 -1
  10. package/dist/cjs/react-components/RenderWalletPeerConnect.js.map +1 -1
  11. package/dist/cjs/react-components/RenderWalletState.js +2 -1
  12. package/dist/cjs/react-components/RenderWalletState.js.map +1 -1
  13. package/dist/cjs/react-components/WalletObserverProvider/WalletObserverProvider.js +60 -3
  14. package/dist/cjs/react-components/WalletObserverProvider/WalletObserverProvider.js.map +1 -1
  15. package/dist/cjs/react-components/WalletObserverProvider/hooks/effects/useDerivedState.js +3 -1
  16. package/dist/cjs/react-components/WalletObserverProvider/hooks/effects/useDerivedState.js.map +1 -1
  17. package/dist/cjs/react-components/WalletObserverProvider/hooks/effects/useSyncWalletWithInterval.js +24 -4
  18. package/dist/cjs/react-components/WalletObserverProvider/hooks/effects/useSyncWalletWithInterval.js.map +1 -1
  19. package/dist/cjs/react-components/WalletObserverProvider/hooks/useWalletObserverState.js +38 -36
  20. package/dist/cjs/react-components/WalletObserverProvider/hooks/useWalletObserverState.js.map +1 -1
  21. package/dist/cjs/react-components/contexts/observer/context.js +40 -1
  22. package/dist/cjs/react-components/contexts/observer/context.js.map +1 -1
  23. package/dist/cjs/react-components/contexts/observer/types.js.map +1 -1
  24. package/dist/cjs/react-components/hooks/useWalletData.js +6 -5
  25. package/dist/cjs/react-components/hooks/useWalletData.js.map +1 -1
  26. package/dist/cjs/react-components/hooks/useWalletHandles.js +11 -7
  27. package/dist/cjs/react-components/hooks/useWalletHandles.js.map +1 -1
  28. package/dist/cjs/react-components/hooks/useWalletLoadingState.js +6 -7
  29. package/dist/cjs/react-components/hooks/useWalletLoadingState.js.map +1 -1
  30. package/dist/cjs/react-components/hooks/useWalletPeerConnect.js +18 -12
  31. package/dist/cjs/react-components/hooks/useWalletPeerConnect.js.map +1 -1
  32. package/dist/esm/classes/ReadOnlyProvider.Blockfrost.class.js +11 -6
  33. package/dist/esm/classes/ReadOnlyProvider.Blockfrost.class.js.map +1 -1
  34. package/dist/esm/classes/WalletObserver.class.js +79 -1
  35. package/dist/esm/classes/WalletObserver.class.js.map +1 -1
  36. package/dist/esm/react-components/RenderWallet.js +3 -2
  37. package/dist/esm/react-components/RenderWallet.js.map +1 -1
  38. package/dist/esm/react-components/RenderWalletHandles.js +3 -2
  39. package/dist/esm/react-components/RenderWalletHandles.js.map +1 -1
  40. package/dist/esm/react-components/RenderWalletPeerConnect.js +3 -2
  41. package/dist/esm/react-components/RenderWalletPeerConnect.js.map +1 -1
  42. package/dist/esm/react-components/RenderWalletState.js +3 -2
  43. package/dist/esm/react-components/RenderWalletState.js.map +1 -1
  44. package/dist/esm/react-components/WalletObserverProvider/WalletObserverProvider.js +55 -4
  45. package/dist/esm/react-components/WalletObserverProvider/WalletObserverProvider.js.map +1 -1
  46. package/dist/esm/react-components/WalletObserverProvider/hooks/effects/useDerivedState.js +1 -1
  47. package/dist/esm/react-components/WalletObserverProvider/hooks/effects/useDerivedState.js.map +1 -1
  48. package/dist/esm/react-components/WalletObserverProvider/hooks/effects/useSyncWalletWithInterval.js +2 -2
  49. package/dist/esm/react-components/WalletObserverProvider/hooks/effects/useSyncWalletWithInterval.js.map +1 -1
  50. package/dist/esm/react-components/WalletObserverProvider/hooks/useWalletObserverState.js +4 -4
  51. package/dist/esm/react-components/WalletObserverProvider/hooks/useWalletObserverState.js.map +1 -1
  52. package/dist/esm/react-components/contexts/observer/context.js +36 -0
  53. package/dist/esm/react-components/contexts/observer/context.js.map +1 -1
  54. package/dist/esm/react-components/contexts/observer/types.js.map +1 -1
  55. package/dist/esm/react-components/hooks/useWalletData.js +7 -5
  56. package/dist/esm/react-components/hooks/useWalletData.js.map +1 -1
  57. package/dist/esm/react-components/hooks/useWalletHandles.js +11 -5
  58. package/dist/esm/react-components/hooks/useWalletHandles.js.map +1 -1
  59. package/dist/esm/react-components/hooks/useWalletLoadingState.js +7 -9
  60. package/dist/esm/react-components/hooks/useWalletLoadingState.js.map +1 -1
  61. package/dist/esm/react-components/hooks/useWalletPeerConnect.js +14 -11
  62. package/dist/esm/react-components/hooks/useWalletPeerConnect.js.map +1 -1
  63. package/dist/types/classes/ReadOnlyProvider.Blockfrost.class.d.ts.map +1 -1
  64. package/dist/types/classes/WalletObserver.class.d.ts +11 -0
  65. package/dist/types/classes/WalletObserver.class.d.ts.map +1 -1
  66. package/dist/types/react-components/RenderWallet.d.ts +0 -6
  67. package/dist/types/react-components/RenderWallet.d.ts.map +1 -1
  68. package/dist/types/react-components/RenderWalletHandles.d.ts +0 -5
  69. package/dist/types/react-components/RenderWalletHandles.d.ts.map +1 -1
  70. package/dist/types/react-components/RenderWalletPeerConnect.d.ts +0 -5
  71. package/dist/types/react-components/RenderWalletPeerConnect.d.ts.map +1 -1
  72. package/dist/types/react-components/RenderWalletState.d.ts +1 -7
  73. package/dist/types/react-components/RenderWalletState.d.ts.map +1 -1
  74. package/dist/types/react-components/WalletObserverProvider/WalletObserverProvider.d.ts.map +1 -1
  75. package/dist/types/react-components/WalletObserverProvider/hooks/effects/useDerivedState.d.ts.map +1 -1
  76. package/dist/types/react-components/WalletObserverProvider/hooks/useWalletObserverState.d.ts.map +1 -1
  77. package/dist/types/react-components/contexts/observer/context.d.ts +16 -1
  78. package/dist/types/react-components/contexts/observer/context.d.ts.map +1 -1
  79. package/dist/types/react-components/contexts/observer/types.d.ts +48 -1
  80. package/dist/types/react-components/contexts/observer/types.d.ts.map +1 -1
  81. package/dist/types/react-components/hooks/useWalletHandles.d.ts.map +1 -1
  82. package/dist/types/react-components/hooks/useWalletLoadingState.d.ts +1 -2
  83. package/dist/types/react-components/hooks/useWalletLoadingState.d.ts.map +1 -1
  84. package/dist/types/react-components/hooks/useWalletPeerConnect.d.ts.map +1 -1
  85. package/dist/types/tsconfig.build.tsbuildinfo +1 -1
  86. package/package.json +3 -3
  87. package/src/classes/ReadOnlyProvider.Blockfrost.class.ts +22 -8
  88. package/src/classes/WalletObserver.class.ts +97 -1
  89. package/src/react-components/RenderWallet.tsx +4 -2
  90. package/src/react-components/RenderWalletHandles.tsx +6 -2
  91. package/src/react-components/RenderWalletPeerConnect.tsx +6 -2
  92. package/src/react-components/RenderWalletState.tsx +8 -4
  93. package/src/react-components/WalletObserverProvider/WalletObserverProvider.tsx +92 -4
  94. package/src/react-components/WalletObserverProvider/hooks/effects/useDerivedState.ts +5 -2
  95. package/src/react-components/WalletObserverProvider/hooks/effects/useSyncWalletWithInterval.ts +2 -2
  96. package/src/react-components/WalletObserverProvider/hooks/useWalletObserverState.ts +67 -37
  97. package/src/react-components/contexts/observer/context.ts +71 -1
  98. package/src/react-components/contexts/observer/types.ts +57 -1
  99. package/src/react-components/hooks/useWalletData.ts +5 -5
  100. package/src/react-components/hooks/useWalletHandles.ts +13 -8
  101. package/src/react-components/hooks/useWalletLoadingState.ts +13 -11
  102. package/src/react-components/hooks/useWalletPeerConnect.tsx +20 -11
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sundaeswap/wallet-lite",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
@@ -35,17 +35,16 @@
35
35
  "version": "standard-version"
36
36
  },
37
37
  "dependencies": {
38
- "@babel/cli": "^7.24.7",
39
38
  "@blockfrost/blockfrost-js": "^6.0.0",
40
39
  "@fabianbormann/cardano-peer-connect": "^1.2.17",
41
40
  "@koralabs/adahandle-sdk": "^1.5.5",
41
+ "lodash": "^4.17.21",
42
42
  "rxjs": "^7.8.1"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "react": "^18.3.1",
46
46
  "react-error-boundary": "^4.0.13",
47
47
  "@tanstack/react-query": "^5.51.11",
48
- "lodash": "^4.17.21",
49
48
  "@cardano-sdk/core": "0.45.0",
50
49
  "@cardano-sdk/dapp-connector": "^0.13.3",
51
50
  "@cardano-sdk/util": "^0.15.5",
@@ -60,6 +59,7 @@
60
59
  "@babel/preset-react": "^7.24.7",
61
60
  "@babel/preset-typescript": "^7.24.7",
62
61
  "@happy-dom/global-registrator": "^16.5.3",
62
+ "@babel/cli": "^7.24.7",
63
63
  "@sundaeswap/babel-preset": "^2.0.15",
64
64
  "@tanstack/react-query": "^5.51.11",
65
65
  "@testing-library/dom": "^10.1.0",
@@ -14,37 +14,51 @@ export class ReadOnlyBlockfrostProvider implements ReadOnlyProvider {
14
14
  }
15
15
 
16
16
  async getBalance(address: string, network: 0 | 1) {
17
- const result: Responses["address_content"] = await fetch(
17
+ const response = await fetch(
18
18
  `https://cardano-${network ? "mainnet" : "preview"}.blockfrost.io/api/v0/addresses/${address}`,
19
19
  {
20
20
  headers: {
21
21
  project_id: this.blockfrostProjectId,
22
22
  },
23
23
  },
24
- ).then((res) => res.json());
24
+ );
25
+
26
+ const result = await response.json();
27
+
28
+ if (!response.ok || "error" in result) {
29
+ throw new Error(
30
+ `Blockfrost getBalance failed: ${result.message || result.error || response.statusText}`,
31
+ );
32
+ }
25
33
 
26
34
  // Build our value.
27
- const value = this.__getValueFromAmount(result.amount);
35
+ const value = this.__getValueFromAmount(
36
+ (result as Responses["address_content"]).amount,
37
+ );
28
38
 
29
39
  // Return the cbor.
30
40
  return value.toCbor();
31
41
  }
32
42
 
33
43
  async getUtxos(address: string, network: 0 | 1) {
34
- const result: Responses["address_utxo_content"] = await fetch(
44
+ const response = await fetch(
35
45
  `https://cardano-${network ? "mainnet" : "preview"}.blockfrost.io/api/v0/addresses/${address}/utxos`,
36
46
  {
37
47
  headers: {
38
48
  project_id: this.blockfrostProjectId,
39
49
  },
40
50
  },
41
- ).then((res) => res.json());
51
+ );
52
+
53
+ const result = await response.json();
42
54
 
43
- if ("error" in result) {
44
- return [];
55
+ if (!response.ok || "error" in result) {
56
+ throw new Error(
57
+ `Blockfrost getUtxos failed: ${result.message || result.error || response.statusText}`,
58
+ );
45
59
  }
46
60
 
47
- const formatted = result.map((r) => {
61
+ const formatted = (result as Responses["address_utxo_content"]).map((r) => {
48
62
  return Serialization.TransactionUnspentOutput.fromCore([
49
63
  Serialization.TransactionInput.fromCore({
50
64
  index: r.output_index,
@@ -53,10 +53,20 @@ export class WalletObserver<
53
53
  public peerConnectInstance?: import("@fabianbormann/cardano-peer-connect").DAppPeerConnect;
54
54
 
55
55
  private _performingSync: boolean = false;
56
+ private _syncQueue: Promise<IWalletObserverSync<AssetMetadata> | void> =
57
+ Promise.resolve();
56
58
  private _options: IResolvedWalletObserverOptions<AssetMetadata>;
57
59
 
58
60
  // Caching
59
61
  private _cachedMetadata: Map<string, AssetMetadata> = new Map();
62
+ private _lastBalanceCbor: string | null = null;
63
+ private _lastBalanceMap: WalletBalanceMap<AssetMetadata> | null = null;
64
+ private _lastUsedAddressesCbor: string[] | null = null;
65
+ private _lastUsedAddresses: string[] | null = null;
66
+ private _lastUnusedAddressesCbor: string[] | null = null;
67
+ private _lastUnusedAddresses: string[] | null = null;
68
+ private _lastChangeAddressCbor: string | null = null;
69
+ private _lastChangeAddress: string | null = null;
60
70
 
61
71
  // AbortController for cancelling in-flight metadata fetches when metadataResolver changes
62
72
  private _metadataAbortController: AbortController | null = null;
@@ -120,7 +130,15 @@ export class WalletObserver<
120
130
  *
121
131
  * @returns {Promise<IWalletObserverSync<AssetMetadata>>} - A promise that resolves to the wallet sync data.
122
132
  */
123
- sync = async (): Promise<IWalletObserverSync<AssetMetadata>> => {
133
+ sync = (): Promise<IWalletObserverSync<AssetMetadata>> => {
134
+ const pending = this._syncQueue.then(() => this._doSync());
135
+ // Chain onto queue but swallow rejections in the queue itself
136
+ // so a failed sync doesn't block subsequent syncs.
137
+ this._syncQueue = pending.catch(() => {});
138
+ return pending;
139
+ };
140
+
141
+ private _doSync = async (): Promise<IWalletObserverSync<AssetMetadata>> => {
124
142
  if (!this.api) {
125
143
  throw new Error(
126
144
  "Attempted to perform a sync operation without a connected wallet.",
@@ -320,6 +338,8 @@ export class WalletObserver<
320
338
  if (metadataResolverChanged) {
321
339
  this._metadataAbortController?.abort();
322
340
  this._cachedMetadata = new Map();
341
+ this._lastBalanceCbor = null;
342
+ this._lastBalanceMap = null;
323
343
 
324
344
  // Trigger a new sync if there's an active connection
325
345
  if (this.hasActiveConnection() && !this._performingSync) {
@@ -450,6 +470,8 @@ export class WalletObserver<
450
470
  this._metadataAbortController?.abort();
451
471
  // Clear the cache
452
472
  this._cachedMetadata = new Map();
473
+ this._lastBalanceCbor = null;
474
+ this._lastBalanceMap = null;
453
475
 
454
476
  // Trigger a new sync if there's an active connection
455
477
  if (this.hasActiveConnection()) {
@@ -462,9 +484,21 @@ export class WalletObserver<
462
484
  *
463
485
  * @returns {void}
464
486
  */
487
+ private _clearSyncCaches = (): void => {
488
+ this._lastBalanceCbor = null;
489
+ this._lastBalanceMap = null;
490
+ this._lastUsedAddressesCbor = null;
491
+ this._lastUsedAddresses = null;
492
+ this._lastUnusedAddressesCbor = null;
493
+ this._lastUnusedAddresses = null;
494
+ this._lastChangeAddressCbor = null;
495
+ this._lastChangeAddress = null;
496
+ };
497
+
465
498
  disconnect = (): void => {
466
499
  this.activeWallet = undefined;
467
500
  this.api = undefined;
501
+ this._clearSyncCaches();
468
502
  window.localStorage.removeItem(WalletObserver.PERSISTENCE_CACHE_KEY);
469
503
  this.dispatch(EWalletObserverEvents.DISCONNECT);
470
504
  };
@@ -495,8 +529,20 @@ export class WalletObserver<
495
529
  return e as Error;
496
530
  }
497
531
 
532
+ // Return cached if CBOR hasn't changed
533
+ if (cbor === this._lastChangeAddressCbor && this._lastChangeAddress) {
534
+ const end = performance.now();
535
+ if (this._options.debug) {
536
+ console.log(`getChangeAddress (cached): ${end - start}ms`);
537
+ }
538
+ return this._lastChangeAddress;
539
+ }
540
+
498
541
  const data = Cardano.Address.fromBytes(typedHex(cbor)).toBech32();
499
542
 
543
+ this._lastChangeAddressCbor = cbor;
544
+ this._lastChangeAddress = data;
545
+
500
546
  const end = performance.now();
501
547
  if (this._options.debug) {
502
548
  console.log(`getChangeAddress: ${end - start}ms`);
@@ -530,6 +576,19 @@ export class WalletObserver<
530
576
  } catch (e) {
531
577
  return e as Error;
532
578
  }
579
+
580
+ // Return cached map if CBOR hasn't changed
581
+ if (cbor === this._lastBalanceCbor && this._lastBalanceMap) {
582
+ this.dispatch(EWalletObserverEvents.GET_BALANCE_MAP_END, {
583
+ balanceMap: this._lastBalanceMap,
584
+ });
585
+ const end = performance.now();
586
+ if (this._options.debug) {
587
+ console.log(`getBalanceMap (cached): ${end - start}ms`);
588
+ }
589
+ return this._lastBalanceMap;
590
+ }
591
+
533
592
  const data = Serialization.Value.fromCbor(typedHex(cbor));
534
593
  const multiassetKeys = data.multiasset()?.keys() ?? [];
535
594
 
@@ -554,6 +613,9 @@ export class WalletObserver<
554
613
  }
555
614
  }
556
615
 
616
+ this._lastBalanceCbor = cbor;
617
+ this._lastBalanceMap = balanceMap;
618
+
557
619
  this.dispatch(EWalletObserverEvents.GET_BALANCE_MAP_END, {
558
620
  balanceMap,
559
621
  });
@@ -619,10 +681,27 @@ export class WalletObserver<
619
681
  return e as Error;
620
682
  }
621
683
 
684
+ // Return cached if CBOR hasn't changed
685
+ if (
686
+ this._lastUsedAddressesCbor &&
687
+ this._lastUsedAddresses &&
688
+ cbor.length === this._lastUsedAddressesCbor.length &&
689
+ cbor.every((v, i) => v === this._lastUsedAddressesCbor![i])
690
+ ) {
691
+ const end = performance.now();
692
+ if (this._options.debug) {
693
+ console.log(`getUsedAddresses (cached): ${end - start}ms`);
694
+ }
695
+ return this._lastUsedAddresses;
696
+ }
697
+
622
698
  const data = cbor.map((val) =>
623
699
  Cardano.Address.fromBytes(typedHex(val)).toBech32(),
624
700
  );
625
701
 
702
+ this._lastUsedAddressesCbor = cbor;
703
+ this._lastUsedAddresses = data;
704
+
626
705
  const end = performance.now();
627
706
  if (this._options.debug) {
628
707
  console.log(`getUsedAddresses: ${end - start}ms`);
@@ -656,10 +735,27 @@ export class WalletObserver<
656
735
  return e as Error;
657
736
  }
658
737
 
738
+ // Return cached if CBOR hasn't changed
739
+ if (
740
+ this._lastUnusedAddressesCbor &&
741
+ this._lastUnusedAddresses &&
742
+ cbor.length === this._lastUnusedAddressesCbor.length &&
743
+ cbor.every((v, i) => v === this._lastUnusedAddressesCbor![i])
744
+ ) {
745
+ const end = performance.now();
746
+ if (this._options.debug) {
747
+ console.log(`getUnusedAddresses (cached): ${end - start}ms`);
748
+ }
749
+ return this._lastUnusedAddresses;
750
+ }
751
+
659
752
  const data = cbor.map((val) =>
660
753
  Cardano.Address.fromBytes(typedHex(val)).toBech32(),
661
754
  );
662
755
 
756
+ this._lastUnusedAddressesCbor = cbor;
757
+ this._lastUnusedAddresses = data;
758
+
663
759
  const end = performance.now();
664
760
  if (this._options.debug) {
665
761
  console.log(`getUnusedAddresses: ${end - start}ms`);
@@ -1,5 +1,5 @@
1
1
  import { IAssetAmountMetadata } from "@sundaeswap/asset";
2
- import { ReactElement, ReactNode, Suspense } from "react";
2
+ import { memo, ReactElement, ReactNode, Suspense } from "react";
3
3
 
4
4
  import { ErrorBoundary } from "react-error-boundary";
5
5
  import { useWalletObserver } from "./hooks/useWalletObserver.js";
@@ -26,7 +26,7 @@ export interface IRenderWalletProps<
26
26
  * compose on this and include state for Handles, PeerConnect (CIP-45),
27
27
  * and syncing state (RenderWalletState).
28
28
  */
29
- export const RenderWallet = <
29
+ const RenderWalletInner = <
30
30
  T extends IAssetAmountMetadata = IAssetAmountMetadata,
31
31
  >({
32
32
  render,
@@ -48,3 +48,5 @@ export const RenderWallet = <
48
48
  </ErrorBoundary>
49
49
  );
50
50
  };
51
+
52
+ export const RenderWallet = memo(RenderWalletInner) as typeof RenderWalletInner;
@@ -1,5 +1,5 @@
1
1
  import { IAssetAmountMetadata } from "@sundaeswap/asset";
2
- import { ReactElement, ReactNode, Suspense } from "react";
2
+ import { memo, ReactElement, ReactNode, Suspense } from "react";
3
3
 
4
4
  import { ErrorBoundary } from "react-error-boundary";
5
5
  import { useWalletHandles } from "./hooks/useWalletHandles.js";
@@ -27,7 +27,7 @@ export interface IRenderWalletHandlesProps<
27
27
  * fetching and updating wallet Handles with their extra
28
28
  * metadata.
29
29
  */
30
- export const RenderWalletHandles = <
30
+ const RenderWalletHandlesInner = <
31
31
  T extends IAssetAmountMetadata = IAssetAmountMetadata,
32
32
  >({
33
33
  render,
@@ -52,3 +52,7 @@ export const RenderWalletHandles = <
52
52
  </ErrorBoundary>
53
53
  );
54
54
  };
55
+
56
+ export const RenderWalletHandles = memo(
57
+ RenderWalletHandlesInner,
58
+ ) as typeof RenderWalletHandlesInner;
@@ -1,5 +1,5 @@
1
1
  import { IAssetAmountMetadata } from "@sundaeswap/asset";
2
- import { ReactElement, ReactNode, Suspense } from "react";
2
+ import { memo, ReactElement, ReactNode, Suspense } from "react";
3
3
  import { ErrorBoundary } from "react-error-boundary";
4
4
 
5
5
  import { useWalletObserver } from "./hooks/useWalletObserver.js";
@@ -27,7 +27,7 @@ export interface IRenderWalletPeerConnectProps<
27
27
  * and exposing them to the render function, including a QR Code
28
28
  * element that can be placed in the consuming app.
29
29
  */
30
- export const RenderWalletPeerConnect = <
30
+ const RenderWalletPeerConnectInner = <
31
31
  T extends IAssetAmountMetadata = IAssetAmountMetadata,
32
32
  >({
33
33
  render,
@@ -56,3 +56,7 @@ export const RenderWalletPeerConnect = <
56
56
  </ErrorBoundary>
57
57
  );
58
58
  };
59
+
60
+ export const RenderWalletPeerConnect = memo(
61
+ RenderWalletPeerConnectInner,
62
+ ) as typeof RenderWalletPeerConnectInner;
@@ -1,5 +1,5 @@
1
1
  import { IAssetAmountMetadata } from "@sundaeswap/asset";
2
- import { ReactElement, ReactNode, Suspense } from "react";
2
+ import { memo, ReactElement, ReactNode, Suspense } from "react";
3
3
 
4
4
  import { ErrorBoundary } from "react-error-boundary";
5
5
  import { useWalletLoadingState } from "./hooks/useWalletLoadingState.js";
@@ -8,7 +8,7 @@ import { useWalletObserver } from "./hooks/useWalletObserver.js";
8
8
  export type TRenderWalletStateFunctionState<
9
9
  T extends IAssetAmountMetadata = IAssetAmountMetadata,
10
10
  > = ReturnType<typeof useWalletObserver<T>> &
11
- ReturnType<typeof useWalletLoadingState<T>>;
11
+ ReturnType<typeof useWalletLoadingState>;
12
12
 
13
13
  export type TRenderWalletStateFunction<
14
14
  T extends IAssetAmountMetadata = IAssetAmountMetadata,
@@ -28,7 +28,7 @@ export interface IRenderWalletStateProps<
28
28
  * a sync or connection operation. Useful for displaying
29
29
  * internal operation states of the wallet.
30
30
  */
31
- export const RenderWalletState = <
31
+ const RenderWalletStateInner = <
32
32
  T extends IAssetAmountMetadata = IAssetAmountMetadata,
33
33
  >({
34
34
  render,
@@ -36,7 +36,7 @@ export const RenderWalletState = <
36
36
  fallback,
37
37
  }: IRenderWalletStateProps<T>) => {
38
38
  const state = useWalletObserver<T>();
39
- const loadingState = useWalletLoadingState<T>();
39
+ const loadingState = useWalletLoadingState();
40
40
 
41
41
  return (
42
42
  <ErrorBoundary
@@ -56,3 +56,7 @@ export const RenderWalletState = <
56
56
  </ErrorBoundary>
57
57
  );
58
58
  };
59
+
60
+ export const RenderWalletState = memo(
61
+ RenderWalletStateInner,
62
+ ) as typeof RenderWalletStateInner;
@@ -4,6 +4,9 @@ import { WalletObserver } from "../../classes/WalletObserver.class.js";
4
4
  import {
5
5
  IWalletObserverProviderProps,
6
6
  IWalletObserverState,
7
+ WalletActionsContext,
8
+ WalletConnectionContext,
9
+ WalletDataContext,
7
10
  WalletObserverContext,
8
11
  } from "../contexts/observer/index.js";
9
12
  import { useDerivedState } from "./hooks/effects/useDerivedState.js";
@@ -42,7 +45,87 @@ const WalletObserverProvider: FC<
42
45
  changeAddress: state.changeAddress,
43
46
  });
44
47
 
45
- // Memoize the context value
48
+ // Focused context: stable callbacks and observer ref.
49
+ // Only changes when observer instance is recreated.
50
+ const actionsValue = useMemo(
51
+ () => ({
52
+ observer: observerRef.current,
53
+ observerRef: observerRef,
54
+ connectWallet: state.connectWallet,
55
+ disconnect: state.disconnect,
56
+ syncWallet: state.syncWallet,
57
+ resyncMetadata: state.resyncMetadata,
58
+ }),
59
+ [
60
+ state.connectWallet,
61
+ state.disconnect,
62
+ state.syncWallet,
63
+ state.resyncMetadata,
64
+ ],
65
+ );
66
+
67
+ // Focused context: connection and loading state.
68
+ // Changes on connect/disconnect and sync start/end.
69
+ const connectionValue = useMemo(
70
+ () => ({
71
+ activeWallet: state.activeWallet,
72
+ ready,
73
+ connectingWallet,
74
+ syncingWallet,
75
+ network: state.network,
76
+ isCip45: state.isCip45,
77
+ switching: state.switching,
78
+ isReadOnlyMode: state.isReadOnlyMode,
79
+ willAutoConnect: state.willAutoConnect,
80
+ errorSyncing: state.errorSyncing,
81
+ mainAddress: derivedState.mainAddress,
82
+ stakeAddress: derivedState.stakeAddress,
83
+ }),
84
+ [
85
+ state.activeWallet,
86
+ ready,
87
+ connectingWallet,
88
+ syncingWallet,
89
+ state.network,
90
+ state.isCip45,
91
+ state.switching,
92
+ state.isReadOnlyMode,
93
+ state.willAutoConnect,
94
+ state.errorSyncing,
95
+ derivedState.mainAddress,
96
+ derivedState.stakeAddress,
97
+ ],
98
+ );
99
+
100
+ // Focused context: wallet data that changes every sync cycle.
101
+ const dataValue = useMemo(
102
+ () => ({
103
+ balance: state.balance,
104
+ adaBalance: state.adaBalance,
105
+ usedAddresses: state.usedAddresses,
106
+ unusedAddresses: state.unusedAddresses,
107
+ changeAddress: state.changeAddress,
108
+ feeAddress: state.feeAddress,
109
+ utxos: state.utxos,
110
+ collateral: state.collateral,
111
+ isPending: state.isPending,
112
+ refreshInterval: options?.refreshInterval || 30000,
113
+ }),
114
+ [
115
+ state.balance,
116
+ state.adaBalance,
117
+ state.usedAddresses,
118
+ state.unusedAddresses,
119
+ state.changeAddress,
120
+ state.feeAddress,
121
+ state.utxos,
122
+ state.collateral,
123
+ state.isPending,
124
+ options?.refreshInterval,
125
+ ],
126
+ );
127
+
128
+ // Backward-compatible full context value
46
129
  const contextValue: IWalletObserverState = useMemo(
47
130
  () => ({
48
131
  observerRef: observerRef,
@@ -57,13 +140,12 @@ const WalletObserverProvider: FC<
57
140
  },
58
141
  }),
59
142
  [
60
- options,
143
+ options?.refreshInterval,
61
144
  state,
62
145
  derivedState,
63
146
  connectingWallet,
64
147
  syncingWallet,
65
148
  ready,
66
- observerRef.current,
67
149
  ],
68
150
  );
69
151
 
@@ -83,7 +165,13 @@ const WalletObserverProvider: FC<
83
165
 
84
166
  return (
85
167
  <WalletObserverContext.Provider value={contextValue}>
86
- {children}
168
+ <WalletActionsContext.Provider value={actionsValue}>
169
+ <WalletConnectionContext.Provider value={connectionValue}>
170
+ <WalletDataContext.Provider value={dataValue}>
171
+ {children}
172
+ </WalletDataContext.Provider>
173
+ </WalletConnectionContext.Provider>
174
+ </WalletActionsContext.Provider>
87
175
  </WalletObserverContext.Provider>
88
176
  );
89
177
  };
@@ -11,8 +11,11 @@ export const useDerivedState = (
11
11
  >,
12
12
  ) => {
13
13
  const [stakeAddress, setStakeAddress] = useState<string>();
14
- const address =
15
- state.changeAddress || state.usedAddresses[0] || state.unusedAddresses[0];
14
+ const address = useMemo(
15
+ () =>
16
+ state.changeAddress || state.usedAddresses[0] || state.unusedAddresses[0],
17
+ [state.changeAddress, state.usedAddresses, state.unusedAddresses],
18
+ );
16
19
 
17
20
  useEffect(() => {
18
21
  if (!address) {
@@ -14,8 +14,8 @@ export const useSyncWalletWithInterval = (
14
14
  ) => {
15
15
  useQuery({
16
16
  queryKey: ["useSyncWalletWithInterval"],
17
- queryFn: () => {
18
- syncWalletFn();
17
+ queryFn: async () => {
18
+ await syncWalletFn();
19
19
  return true;
20
20
  },
21
21
  refetchInterval: refreshInterval,
@@ -1,6 +1,12 @@
1
1
  import type { TransactionUnspentOutput } from "@cardano-sdk/core/dist/cjs/Serialization/TransactionUnspentOutput.js";
2
2
  import { AssetAmount, IAssetAmountMetadata } from "@sundaeswap/asset";
3
- import { useCallback, useEffect, useState, useTransition } from "react";
3
+ import {
4
+ useCallback,
5
+ useEffect,
6
+ useMemo,
7
+ useState,
8
+ useTransition,
9
+ } from "react";
4
10
 
5
11
  import { IWalletObserverSync } from "../../../@types/observer.js";
6
12
  import { ReadOnlyApi } from "../../../classes/ReadOnlyApi.class.js";
@@ -77,7 +83,7 @@ export const useWalletObserverState = <
77
83
  setSwitching(() => false);
78
84
  return observer.api;
79
85
  },
80
- [observer, setSwitching, setIsReadOnlyMode],
86
+ [observer],
81
87
  );
82
88
 
83
89
  const resyncMetadata = useCallback(async () => {
@@ -212,39 +218,63 @@ export const useWalletObserverState = <
212
218
  };
213
219
  }, [syncWallet]);
214
220
 
215
- return {
216
- activeWallet,
217
- adaBalance,
218
- balance,
219
- collateral,
220
- connectWallet,
221
- disconnect,
222
- errorSyncing,
223
- feeAddress,
224
- isCip45,
225
- isPending,
226
- isReadOnlyMode,
227
- network,
228
- changeAddress,
229
- resyncMetadata,
230
- setActiveWallet,
231
- setAdaBalance,
232
- setBalance,
233
- setCollateral,
234
- setFeeAddress,
235
- setIsCip45,
236
- setIsReadOnlyMode,
237
- setNetwork,
238
- setSwitching,
239
- setUnusedAddresses,
240
- setUsedAddresses,
241
- setUtxos,
242
- setChangeAddress,
243
- switching,
244
- syncWallet,
245
- unusedAddresses,
246
- usedAddresses,
247
- utxos,
248
- willAutoConnect,
249
- };
221
+ return useMemo(
222
+ () => ({
223
+ activeWallet,
224
+ adaBalance,
225
+ balance,
226
+ collateral,
227
+ connectWallet,
228
+ disconnect,
229
+ errorSyncing,
230
+ feeAddress,
231
+ isCip45,
232
+ isPending,
233
+ isReadOnlyMode,
234
+ network,
235
+ changeAddress,
236
+ resyncMetadata,
237
+ setActiveWallet,
238
+ setAdaBalance,
239
+ setBalance,
240
+ setCollateral,
241
+ setFeeAddress,
242
+ setIsCip45,
243
+ setIsReadOnlyMode,
244
+ setNetwork,
245
+ setSwitching,
246
+ setUnusedAddresses,
247
+ setUsedAddresses,
248
+ setUtxos,
249
+ setChangeAddress,
250
+ switching,
251
+ syncWallet,
252
+ unusedAddresses,
253
+ usedAddresses,
254
+ utxos,
255
+ willAutoConnect,
256
+ }),
257
+ [
258
+ activeWallet,
259
+ adaBalance,
260
+ balance,
261
+ collateral,
262
+ connectWallet,
263
+ disconnect,
264
+ errorSyncing,
265
+ feeAddress,
266
+ isCip45,
267
+ isPending,
268
+ isReadOnlyMode,
269
+ network,
270
+ changeAddress,
271
+ resyncMetadata,
272
+ switching,
273
+ syncWallet,
274
+ unusedAddresses,
275
+ usedAddresses,
276
+ utxos,
277
+ willAutoConnect,
278
+ ],
279
+ );
250
280
  };