@rango-dev/widget-embedded 0.0.0-experimental-1624fbb4-20260708 → 0.0.0-experimental-99f1781b-20260920

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 (190) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/dist/QueueManager.d.ts.map +1 -1
  3. package/dist/components/ConfirmWalletsModal/ConfirmWallets.styles.d.ts +1 -1
  4. package/dist/components/ConfirmWalletsModal/ConfirmWallets.styles.d.ts.map +1 -1
  5. package/dist/components/CustomDestination/CustomDestination.styles.d.ts +4 -4
  6. package/dist/components/CustomDestination/CustomDestination.styles.d.ts.map +1 -1
  7. package/dist/components/CustomTokenModal/CustomTokenModal.styles.d.ts +169 -169
  8. package/dist/components/CustomTokenModal/CustomTokenModal.styles.d.ts.map +1 -1
  9. package/dist/components/FilterSelector/FilterSelector.style.d.ts +1 -1
  10. package/dist/components/FilterSelector/FilterSelector.style.d.ts.map +1 -1
  11. package/dist/components/HeaderButtons/HeaderButtons.styles.d.ts +1 -1
  12. package/dist/components/HeaderButtons/HeaderButtons.styles.d.ts.map +1 -1
  13. package/dist/components/NamespaceItem/NamespaceItem.d.ts.map +1 -1
  14. package/dist/components/NamespaceItem/NamespaceItem.styles.d.ts +1 -1
  15. package/dist/components/NamespaceItem/NamespaceItem.styles.d.ts.map +1 -1
  16. package/dist/components/NamespaceItem/NamespaceItem.types.d.ts +3 -3
  17. package/dist/components/NamespaceItem/NamespaceItem.types.d.ts.map +1 -1
  18. package/dist/components/Quote/Quote.d.ts.map +1 -1
  19. package/dist/components/Quote/Quote.styles.d.ts +171 -171
  20. package/dist/components/Quote/Quote.styles.d.ts.map +1 -1
  21. package/dist/components/Quote/Quote.types.d.ts +0 -2
  22. package/dist/components/Quote/Quote.types.d.ts.map +1 -1
  23. package/dist/components/Quote/QuoteCostDetails.d.ts.map +1 -1
  24. package/dist/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.styles.d.ts +169 -169
  25. package/dist/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.styles.d.ts.map +1 -1
  26. package/dist/components/RefreshModal/RefreshModal.styles.d.ts +168 -168
  27. package/dist/components/RefreshModal/RefreshModal.styles.d.ts.map +1 -1
  28. package/dist/components/StatefulConnectModal/StatefulConnectModal.d.ts +2 -1
  29. package/dist/components/StatefulConnectModal/StatefulConnectModal.d.ts.map +1 -1
  30. package/dist/components/SwapDetails/SwapDetails.d.ts.map +1 -1
  31. package/dist/components/SwapDetails/SwapDetails.styles.d.ts +1 -1
  32. package/dist/components/SwapDetails/SwapDetails.styles.d.ts.map +1 -1
  33. package/dist/components/SwapDetailsModal/SwapDetailsModal.ConnectWallet.d.ts.map +1 -1
  34. package/dist/components/SwapDetailsModal/SwapDetailsModal.InstallWallet.d.ts.map +1 -1
  35. package/dist/components/SwapDetailsModal/SwapDetailsModal.types.d.ts +3 -3
  36. package/dist/components/SwapDetailsModal/SwapDetailsModal.types.d.ts.map +1 -1
  37. package/dist/components/SwapMetrics/SwapMetrics.styles.d.ts +1 -1
  38. package/dist/components/SwapMetrics/SwapMetrics.styles.d.ts.map +1 -1
  39. package/dist/components/TokenList/TokenList.styles.d.ts +2 -2
  40. package/dist/components/TokenList/TokenList.styles.d.ts.map +1 -1
  41. package/dist/components/WalletStatefulConnect/DerivationPath.d.ts.map +1 -1
  42. package/dist/components/WalletStatefulConnect/DerivationPath.helpers.d.ts +7 -3
  43. package/dist/components/WalletStatefulConnect/DerivationPath.helpers.d.ts.map +1 -1
  44. package/dist/components/WalletStatefulConnect/DerivationPath.helpers.test.d.ts +2 -0
  45. package/dist/components/WalletStatefulConnect/DerivationPath.helpers.test.d.ts.map +1 -0
  46. package/dist/components/WalletStatefulConnect/DerivationPath.styles.d.ts +169 -169
  47. package/dist/components/WalletStatefulConnect/DerivationPath.styles.d.ts.map +1 -1
  48. package/dist/components/WalletStatefulConnect/Detached.d.ts.map +1 -1
  49. package/dist/components/WalletStatefulConnect/Detached.types.d.ts +2 -1
  50. package/dist/components/WalletStatefulConnect/Detached.types.d.ts.map +1 -1
  51. package/dist/components/WalletStatefulConnect/NamespaceDetachedItem.d.ts.map +1 -1
  52. package/dist/components/WalletStatefulConnect/Namespaces.d.ts.map +1 -1
  53. package/dist/components/WalletStatefulConnect/Namespaces.styles.d.ts +168 -168
  54. package/dist/components/WalletStatefulConnect/Namespaces.styles.d.ts.map +1 -1
  55. package/dist/components/WalletStatefulConnect/Namespaces.types.d.ts +3 -3
  56. package/dist/components/WalletStatefulConnect/Namespaces.types.d.ts.map +1 -1
  57. package/dist/constants/errors.d.ts +1 -0
  58. package/dist/constants/errors.d.ts.map +1 -1
  59. package/dist/containers/Wallets/Wallets.constants.d.ts +3 -0
  60. package/dist/containers/Wallets/Wallets.constants.d.ts.map +1 -0
  61. package/dist/containers/Wallets/Wallets.d.ts.map +1 -1
  62. package/dist/containers/Wallets/Wallets.helpers.d.ts +2 -2
  63. package/dist/containers/Wallets/Wallets.helpers.d.ts.map +1 -1
  64. package/dist/containers/Wallets/Wallets.helpers.test.d.ts +2 -0
  65. package/dist/containers/Wallets/Wallets.helpers.test.d.ts.map +1 -0
  66. package/dist/containers/Wallets/Wallets.types.d.ts +6 -4
  67. package/dist/containers/Wallets/Wallets.types.d.ts.map +1 -1
  68. package/dist/containers/Wallets/useUpdates.d.ts +1 -1
  69. package/dist/containers/Wallets/useUpdates.d.ts.map +1 -1
  70. package/dist/containers/WidgetProvider/WidgetProvider.d.ts.map +1 -1
  71. package/dist/containers/WidgetProvider/WidgetProvider.helpers.d.ts +5 -0
  72. package/dist/containers/WidgetProvider/WidgetProvider.helpers.d.ts.map +1 -0
  73. package/dist/containers/WidgetProvider/WidgetProvider.types.d.ts +1 -1
  74. package/dist/containers/WidgetProvider/WidgetProvider.types.d.ts.map +1 -1
  75. package/dist/hooks/useConnectWithLogging/index.d.ts +2 -0
  76. package/dist/hooks/useConnectWithLogging/index.d.ts.map +1 -0
  77. package/dist/hooks/useConnectWithLogging/useConnectWithLogging.d.ts +11 -0
  78. package/dist/hooks/useConnectWithLogging/useConnectWithLogging.d.ts.map +1 -0
  79. package/dist/hooks/useConnectWithLogging/useConnectWithLogging.test.d.ts +2 -0
  80. package/dist/hooks/useConnectWithLogging/useConnectWithLogging.test.d.ts.map +1 -0
  81. package/dist/hooks/useStatefulConnect/useStatefulConnect.d.ts +2 -1
  82. package/dist/hooks/useStatefulConnect/useStatefulConnect.d.ts.map +1 -1
  83. package/dist/hooks/useStatefulConnect/useStatefulConnect.types.d.ts +5 -4
  84. package/dist/hooks/useStatefulConnect/useStatefulConnect.types.d.ts.map +1 -1
  85. package/dist/hooks/useWalletList.d.ts.map +1 -1
  86. package/dist/hooks/useWalletProviders/useWalletProviders.d.ts +2 -2
  87. package/dist/hooks/useWalletProviders/useWalletProviders.d.ts.map +1 -1
  88. package/dist/hooks/useWalletProviders/useWalletProviders.helpers.d.ts +2 -3
  89. package/dist/hooks/useWalletProviders/useWalletProviders.helpers.d.ts.map +1 -1
  90. package/dist/index.d.ts +10 -7
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js +3 -2
  93. package/dist/index.js.map +4 -4
  94. package/dist/libs/connectLogging/connectLogging.constants.d.ts +9 -0
  95. package/dist/libs/connectLogging/connectLogging.constants.d.ts.map +1 -0
  96. package/dist/libs/connectLogging/connectLogging.d.ts +12 -0
  97. package/dist/libs/connectLogging/connectLogging.d.ts.map +1 -0
  98. package/dist/libs/connectLogging/connectLogging.test.d.ts +2 -0
  99. package/dist/libs/connectLogging/connectLogging.test.d.ts.map +1 -0
  100. package/dist/libs/connectLogging/connectLogging.types.d.ts +57 -0
  101. package/dist/libs/connectLogging/connectLogging.types.d.ts.map +1 -0
  102. package/dist/libs/connectLogging/index.d.ts +3 -0
  103. package/dist/libs/connectLogging/index.d.ts.map +1 -0
  104. package/dist/pages/WalletsPage.d.ts.map +1 -1
  105. package/dist/store/AppStore.d.ts +2 -2
  106. package/dist/store/AppStore.d.ts.map +1 -1
  107. package/dist/store/app.d.ts +2 -2
  108. package/dist/store/app.d.ts.map +1 -1
  109. package/dist/store/slices/config.d.ts +5 -5
  110. package/dist/store/slices/config.d.ts.map +1 -1
  111. package/dist/test-utils/fixtures.d.ts +2 -2
  112. package/dist/test-utils/fixtures.d.ts.map +1 -1
  113. package/dist/types/config.d.ts +25 -4
  114. package/dist/types/config.d.ts.map +1 -1
  115. package/dist/types/wallets.d.ts +1 -1
  116. package/dist/types/wallets.d.ts.map +1 -1
  117. package/dist/utils/common.d.ts +1 -0
  118. package/dist/utils/common.d.ts.map +1 -1
  119. package/dist/utils/errors.d.ts +14 -0
  120. package/dist/utils/errors.d.ts.map +1 -0
  121. package/dist/utils/errors.test.d.ts +2 -0
  122. package/dist/utils/errors.test.d.ts.map +1 -0
  123. package/dist/utils/providers.d.ts +3 -6
  124. package/dist/utils/providers.d.ts.map +1 -1
  125. package/dist/utils/swap.d.ts +1 -1
  126. package/dist/utils/swap.d.ts.map +1 -1
  127. package/dist/utils/wallets.d.ts +2 -1
  128. package/dist/utils/wallets.d.ts.map +1 -1
  129. package/package.json +16 -16
  130. package/src/QueueManager.tsx +17 -16
  131. package/src/components/ConfirmWalletsModal/WalletList.tsx +2 -2
  132. package/src/components/NamespaceItem/NamespaceItem.styles.ts +1 -0
  133. package/src/components/NamespaceItem/NamespaceItem.tsx +5 -1
  134. package/src/components/NamespaceItem/NamespaceItem.types.ts +3 -3
  135. package/src/components/Quote/Quote.tsx +0 -2
  136. package/src/components/Quote/Quote.types.ts +0 -2
  137. package/src/components/Quote/QuoteCostDetails.tsx +15 -4
  138. package/src/components/StatefulConnectModal/StatefulConnectModal.tsx +4 -2
  139. package/src/components/SwapDetails/SwapDetails.tsx +17 -8
  140. package/src/components/SwapDetailsModal/SwapDetailsModal.ConnectWallet.tsx +12 -13
  141. package/src/components/SwapDetailsModal/SwapDetailsModal.InstallWallet.tsx +8 -2
  142. package/src/components/SwapDetailsModal/SwapDetailsModal.types.ts +3 -3
  143. package/src/components/TokenList/LoadingTokenList.tsx +2 -2
  144. package/src/components/WalletStatefulConnect/DerivationPath.helpers.test.ts +98 -0
  145. package/src/components/WalletStatefulConnect/DerivationPath.helpers.ts +14 -10
  146. package/src/components/WalletStatefulConnect/DerivationPath.tsx +14 -7
  147. package/src/components/WalletStatefulConnect/Detached.tsx +17 -10
  148. package/src/components/WalletStatefulConnect/Detached.types.ts +2 -1
  149. package/src/components/WalletStatefulConnect/NamespaceDetachedItem.tsx +2 -1
  150. package/src/components/WalletStatefulConnect/Namespaces.tsx +6 -12
  151. package/src/components/WalletStatefulConnect/Namespaces.types.tsx +3 -3
  152. package/src/constants/errors.ts +3 -0
  153. package/src/containers/Wallets/Wallets.constants.ts +14 -0
  154. package/src/containers/Wallets/Wallets.helpers.test.ts +67 -0
  155. package/src/containers/Wallets/Wallets.helpers.ts +9 -9
  156. package/src/containers/Wallets/Wallets.tsx +20 -3
  157. package/src/containers/Wallets/Wallets.types.ts +7 -7
  158. package/src/containers/Wallets/useUpdates.ts +7 -117
  159. package/src/containers/WidgetProvider/WidgetProvider.helpers.ts +10 -0
  160. package/src/containers/WidgetProvider/WidgetProvider.tsx +12 -1
  161. package/src/containers/WidgetProvider/WidgetProvider.types.ts +1 -1
  162. package/src/hooks/useConnectWithLogging/index.ts +1 -0
  163. package/src/hooks/useConnectWithLogging/useConnectWithLogging.test.ts +138 -0
  164. package/src/hooks/useConnectWithLogging/useConnectWithLogging.ts +36 -0
  165. package/src/hooks/useStatefulConnect/useStatefulConnect.ts +36 -29
  166. package/src/hooks/useStatefulConnect/useStatefulConnect.types.ts +6 -3
  167. package/src/hooks/useWalletList.ts +10 -7
  168. package/src/hooks/useWalletProviders/useWalletProviders.helpers.ts +3 -10
  169. package/src/hooks/useWalletProviders/useWalletProviders.ts +2 -2
  170. package/src/index.ts +10 -11
  171. package/src/libs/connectLogging/connectLogging.constants.ts +17 -0
  172. package/src/libs/connectLogging/connectLogging.test.ts +918 -0
  173. package/src/libs/connectLogging/connectLogging.ts +359 -0
  174. package/src/libs/connectLogging/connectLogging.types.ts +70 -0
  175. package/src/libs/connectLogging/index.ts +5 -0
  176. package/src/pages/WalletsPage.tsx +1 -0
  177. package/src/store/AppStore.tsx +4 -2
  178. package/src/store/app.ts +7 -2
  179. package/src/store/slices/config.ts +36 -25
  180. package/src/store/slices/data.test.ts +2 -2
  181. package/src/test-utils/fixtures.ts +6 -2
  182. package/src/types/config.ts +31 -4
  183. package/src/types/wallets.ts +1 -1
  184. package/src/utils/common.ts +6 -0
  185. package/src/utils/errors.test.ts +78 -0
  186. package/src/utils/errors.ts +69 -0
  187. package/src/utils/providers.ts +16 -50
  188. package/src/utils/swap.ts +8 -1
  189. package/src/utils/wallets.ts +12 -15
  190. package/dist/widget-embedded.build.json +0 -1
@@ -0,0 +1,359 @@
1
+ import type {
2
+ ConnectFailureCategory,
3
+ ConnectionAttempt,
4
+ ConnectionFailureDetails,
5
+ ConnectLogContext,
6
+ ConnectLogEvent,
7
+ ConnectLogTags,
8
+ FailureCategory,
9
+ LoggedConnectionAttempt,
10
+ } from './connectLogging.types';
11
+ import type { WalletType } from '@hub3js/core';
12
+ import type { WalletErrorCode } from '@hub3js/std/utils';
13
+ import type {
14
+ ConnectResult,
15
+ NamespaceInputForConnect,
16
+ } from '@rango-dev/wallets-react';
17
+
18
+ import {
19
+ USER_REJECTION_ERROR_CODE,
20
+ WALLET_LOCKED_ERROR_CODE,
21
+ } from '@hub3js/std/utils';
22
+ import { error as logError } from '@rango-dev/logging-core';
23
+ import {
24
+ AutoConnectionAttemptError,
25
+ WalletConnectionAttemptError,
26
+ } from '@rango-dev/wallets-react';
27
+
28
+ import {
29
+ ATTEMPT_ERROR_NAMES,
30
+ CONNECT_TIMEOUT,
31
+ CONNECT_TIMEOUT_MESSAGE,
32
+ CONNECTION_ERROR_NAME,
33
+ UNREADABLE_FAILURE_MESSAGE,
34
+ UNREADABLE_FAILURE_NAME,
35
+ } from './connectLogging.constants';
36
+
37
+ /** An attempt, as read from what it failed with. */
38
+ type FailedAttempt = Omit<LoggedConnectionAttempt, 'requestedNamespaces'> & {
39
+ errors: unknown[];
40
+ /** Read from the failure, so any value at all. */
41
+ requestedNamespaces: unknown[];
42
+ };
43
+
44
+ /**
45
+ * Failures come from wallet bundles the widget doesn't control, so any read can
46
+ * throw. Each field is read on its own, so one nobody can read costs only itself.
47
+ */
48
+ function readOrStandIn<Value>(read: () => Value, standIn: Value): Value {
49
+ try {
50
+ return read();
51
+ } catch {
52
+ return standIn;
53
+ }
54
+ }
55
+
56
+ /** A field of any value at all, or `standIn` when reading it throws. */
57
+ function readField(value: unknown, field: string, standIn?: unknown): unknown {
58
+ return readOrStandIn(
59
+ () =>
60
+ typeof value === 'object' && value !== null
61
+ ? (value as Record<string, unknown>)[field]
62
+ : undefined,
63
+ standIn
64
+ );
65
+ }
66
+
67
+ /** The field when it's a string, `standIn` when it can't be read as one. */
68
+ function readString(value: unknown, field: string): string | undefined;
69
+ function readString(value: unknown, field: string, standIn: string): string;
70
+ function readString(
71
+ value: unknown,
72
+ field: string,
73
+ standIn?: string
74
+ ): string | undefined {
75
+ const found = readField(value, field, standIn);
76
+
77
+ return typeof found === 'string' ? found : standIn;
78
+ }
79
+
80
+ function readCode(value: unknown): WalletErrorCode | undefined {
81
+ const code = readField(value, 'code');
82
+
83
+ return typeof code === 'number' || typeof code === 'string'
84
+ ? code
85
+ : undefined;
86
+ }
87
+
88
+ function isString(value: unknown): value is string {
89
+ return typeof value === 'string';
90
+ }
91
+
92
+ // By name, not `instanceof`: a host may bundle more than one copy of the packages.
93
+ function isConnectionError(failure: unknown): boolean {
94
+ return readString(failure, 'name') === CONNECTION_ERROR_NAME;
95
+ }
96
+
97
+ /**
98
+ * Anything other than an attempt error is read as an attempt with that value as
99
+ * its only failure, requesting what the caller asked for.
100
+ */
101
+ function readAttempt(
102
+ thrown: unknown,
103
+ attempt: LoggedConnectionAttempt
104
+ ): FailedAttempt {
105
+ const errors = ATTEMPT_ERROR_NAMES.includes(readString(thrown, 'name') ?? '')
106
+ ? readField(thrown, 'errors')
107
+ : undefined;
108
+ if (!Array.isArray(errors)) {
109
+ return { ...attempt, errors: [thrown] };
110
+ }
111
+
112
+ const requested = readField(thrown, 'requestedNamespaces');
113
+
114
+ return {
115
+ ...attempt,
116
+ errors,
117
+ requestedNamespaces: Array.isArray(requested)
118
+ ? requested
119
+ : attempt.requestedNamespaces,
120
+ };
121
+ }
122
+
123
+ function classifyFailure(failure: unknown): FailureCategory {
124
+ if (!isConnectionError(failure)) {
125
+ return 'unknown';
126
+ }
127
+
128
+ // A failure nobody can check against the ignored kinds is worth seeing.
129
+ const code = readCode(failure);
130
+ if (code === USER_REJECTION_ERROR_CODE) {
131
+ return 'rejected';
132
+ }
133
+ if (code === WALLET_LOCKED_ERROR_CODE) {
134
+ return 'locked';
135
+ }
136
+
137
+ return 'wallet';
138
+ }
139
+
140
+ /**
141
+ * The category of an attempt's event, or `null` when it has no logged failures.
142
+ */
143
+ function resolveCategory(
144
+ categories: FailureCategory[]
145
+ ): Exclude<ConnectFailureCategory, 'timeout'> | null {
146
+ if (categories.includes('unknown')) {
147
+ return 'unknown';
148
+ }
149
+ if (categories.includes('wallet')) {
150
+ return 'wallet';
151
+ }
152
+
153
+ return null;
154
+ }
155
+
156
+ /** Sorted, deduplicated and comma-joined. An empty string counts as none. */
157
+ function joinSorted(values: (string | undefined)[]): string | undefined {
158
+ const present = new Set(values.filter((value): value is string => !!value));
159
+
160
+ return present.size ? [...present].sort().join(',') : undefined;
161
+ }
162
+
163
+ function buildTags(params: {
164
+ attempt: FailedAttempt;
165
+ category: ConnectFailureCategory;
166
+ /** Absent when no namespace can be blamed: the requested ones are tagged. */
167
+ blamedFailures?: unknown[];
168
+ }): ConnectLogTags {
169
+ const { attempt, category, blamedFailures } = params;
170
+ const requested = attempt.requestedNamespaces.map((request) => ({
171
+ namespace: readString(request, 'namespace'),
172
+ network: readString(request, 'network'),
173
+ }));
174
+ const namespaces = blamedFailures
175
+ ? blamedFailures.map((failure) => readString(failure, 'namespace'))
176
+ : requested.map(({ namespace }) => namespace);
177
+ const networks = requested
178
+ .filter(({ namespace }) => !!namespace && namespaces.includes(namespace))
179
+ .map(({ network }) => network);
180
+
181
+ const namespacesTag = joinSorted(namespaces);
182
+ const networkTag = joinSorted(networks);
183
+
184
+ return {
185
+ walletType: attempt.walletType,
186
+ ...(namespacesTag ? { namespaces: namespacesTag } : {}),
187
+ ...(networkTag ? { network: networkTag } : {}),
188
+ trigger: attempt.trigger,
189
+ category,
190
+ };
191
+ }
192
+
193
+ function describeFailure(failure: unknown): ConnectionFailureDetails {
194
+ const namespace = isConnectionError(failure)
195
+ ? readString(failure, 'namespace')
196
+ : undefined;
197
+ const code = readCode(failure);
198
+
199
+ return {
200
+ ...(namespace !== undefined ? { namespace } : {}),
201
+ name: readString(failure, 'name', UNREADABLE_FAILURE_NAME),
202
+ message: readString(failure, 'message', UNREADABLE_FAILURE_MESSAGE),
203
+ ...(code !== undefined ? { code } : {}),
204
+ };
205
+ }
206
+
207
+ function buildContext(attempt: FailedAttempt): ConnectLogContext {
208
+ return {
209
+ failures: attempt.errors.map(describeFailure),
210
+ requestedNamespaces: attempt.requestedNamespaces
211
+ .map((request) => readString(request, 'namespace'))
212
+ .filter(isString),
213
+ };
214
+ }
215
+
216
+ /**
217
+ * What the attempt failed with, unchanged. `connect` and auto-connect only ever
218
+ * fail with errors, but logging-core takes nothing else, so any other value is
219
+ * logged as the one-failure attempt it's read as.
220
+ */
221
+ function toLoggedError(
222
+ thrown: unknown,
223
+ attempt: LoggedConnectionAttempt
224
+ ): Error {
225
+ if (thrown instanceof Error) {
226
+ return thrown;
227
+ }
228
+
229
+ const AttemptError =
230
+ attempt.trigger === 'auto-connect'
231
+ ? AutoConnectionAttemptError
232
+ : WalletConnectionAttemptError;
233
+
234
+ return new AttemptError({
235
+ errors: [thrown],
236
+ requestedNamespaces: attempt.requestedNamespaces,
237
+ });
238
+ }
239
+
240
+ /**
241
+ * Builds the event for an attempt that failed with `thrown`, or `null` when
242
+ * every failure is a rejection or a locked wallet.
243
+ */
244
+ function buildConnectFailureEvent(
245
+ thrown: unknown,
246
+ attempt: LoggedConnectionAttempt
247
+ ): ConnectLogEvent | null {
248
+ const failedAttempt = readAttempt(thrown, attempt);
249
+ const categories = failedAttempt.errors.map(classifyFailure);
250
+ const category = resolveCategory(categories);
251
+ if (!category) {
252
+ return null;
253
+ }
254
+
255
+ return {
256
+ error: toLoggedError(thrown, attempt),
257
+ tags: buildTags({
258
+ attempt: failedAttempt,
259
+ category,
260
+ blamedFailures:
261
+ category === 'wallet'
262
+ ? failedAttempt.errors.filter(
263
+ (_, index) => categories[index] === 'wallet'
264
+ )
265
+ : undefined,
266
+ }),
267
+ context: buildContext(failedAttempt),
268
+ };
269
+ }
270
+
271
+ function buildTimeoutError(
272
+ requestedNamespaces: NamespaceInputForConnect[]
273
+ ): WalletConnectionAttemptError {
274
+ return new WalletConnectionAttemptError({
275
+ errors: [],
276
+ requestedNamespaces: requestedNamespaces.map(({ namespace, network }) => ({
277
+ namespace,
278
+ network,
279
+ })),
280
+ message: CONNECT_TIMEOUT_MESSAGE,
281
+ });
282
+ }
283
+
284
+ function buildConnectTimeoutEvent(attempt: ConnectionAttempt): ConnectLogEvent {
285
+ const error = buildTimeoutError(attempt.requestedNamespaces);
286
+ const timedOut: FailedAttempt = {
287
+ ...attempt,
288
+ errors: [],
289
+ requestedNamespaces: error.requestedNamespaces,
290
+ };
291
+
292
+ return {
293
+ error,
294
+ tags: buildTags({ attempt: timedOut, category: 'timeout' }),
295
+ context: buildContext(timedOut),
296
+ };
297
+ }
298
+
299
+ // Logging must never change what the caller sees, so an event that can't be built or sent is dropped.
300
+ function dropIfItThrows(send: () => void): void {
301
+ try {
302
+ send();
303
+ } catch {
304
+ // Dropped on purpose.
305
+ }
306
+ }
307
+
308
+ /** Logs an attempt that failed with `thrown`, unless it's ignored. Never throws. */
309
+ function logConnectFailure(
310
+ thrown: unknown,
311
+ attempt: LoggedConnectionAttempt
312
+ ): void {
313
+ dropIfItThrows(() => {
314
+ const event = buildConnectFailureEvent(thrown, attempt);
315
+ if (event) {
316
+ logError(event.error, { tags: event.tags, context: event.context });
317
+ }
318
+ });
319
+ }
320
+
321
+ /** Logs the error of an `AUTO_CONNECT_FAILED` event. Never throws. */
322
+ export function logAutoConnectFailure(
323
+ walletType: WalletType,
324
+ thrown: unknown
325
+ ): void {
326
+ logConnectFailure(thrown, {
327
+ walletType,
328
+ trigger: 'auto-connect',
329
+ requestedNamespaces: [],
330
+ });
331
+ }
332
+
333
+ /**
334
+ * Runs an attempt, logs whatever it fails with, and logs a timeout if it's
335
+ * still pending after 3 minutes. The timer follows the attempt, not a
336
+ * component, and never cancels it. What `connect` throws is rethrown unchanged.
337
+ */
338
+ export async function runConnectWithLogging(
339
+ attempt: ConnectionAttempt,
340
+ connect: () => Promise<ConnectResult[]>
341
+ ): Promise<ConnectResult[]> {
342
+ const timeoutId = setTimeout(
343
+ () =>
344
+ dropIfItThrows(() => {
345
+ const { error, tags, context } = buildConnectTimeoutEvent(attempt);
346
+ logError(error, { tags, context });
347
+ }),
348
+ CONNECT_TIMEOUT
349
+ );
350
+
351
+ try {
352
+ return await connect();
353
+ } catch (thrown) {
354
+ logConnectFailure(thrown, attempt);
355
+ throw thrown;
356
+ } finally {
357
+ clearTimeout(timeoutId);
358
+ }
359
+ }
@@ -0,0 +1,70 @@
1
+ import type { WalletType } from '@hub3js/core';
2
+ import type { WalletErrorCode } from '@hub3js/std/utils';
3
+ import type { NamespaceInputForConnect } from '@rango-dev/wallets-react';
4
+
5
+ /** Where in the widget the user started a connection attempt from. */
6
+ export type ConnectTrigger =
7
+ | 'wallets-page'
8
+ | 'confirm-wallets-modal'
9
+ | 'swap-details-reconnect'
10
+ | 'swap-details-switch-network'
11
+ | 'queue-switch-network';
12
+
13
+ /**
14
+ * The trigger a logged attempt is tagged with. `auto-connect` is kept out of
15
+ * `ConnectTrigger`, so nothing can start a timed attempt as auto-connect.
16
+ */
17
+ export type ConnectLogTrigger = ConnectTrigger | 'auto-connect';
18
+
19
+ /** The trigger as a prop, declared once for every layer it travels through. */
20
+ export type WithConnectTrigger = { trigger?: ConnectTrigger };
21
+
22
+ /**
23
+ * What one connection failure of an attempt is. Rejected and locked failures
24
+ * are set aside; the others are the attempt's logged failures: `wallet` is a
25
+ * wallet connection error worth logging, `unknown` is anything else.
26
+ */
27
+ export type FailureCategory = 'rejected' | 'locked' | 'wallet' | 'unknown';
28
+
29
+ export type ConnectFailureCategory = 'wallet' | 'unknown' | 'timeout';
30
+
31
+ /** What the caller knows about an attempt before it settles. */
32
+ export type ConnectionAttempt<Trigger = ConnectTrigger> = {
33
+ walletType: WalletType;
34
+ trigger: Trigger;
35
+ /** In request order. */
36
+ requestedNamespaces: NamespaceInputForConnect[];
37
+ };
38
+
39
+ export type LoggedConnectionAttempt = ConnectionAttempt<ConnectLogTrigger>;
40
+
41
+ /**
42
+ * One connection failure of an attempt, without anything that identifies the
43
+ * user.
44
+ */
45
+ export type ConnectionFailureDetails = {
46
+ namespace?: string;
47
+ name?: string;
48
+ message: string;
49
+ code?: WalletErrorCode;
50
+ };
51
+
52
+ export type ConnectLogTags = {
53
+ walletType: WalletType;
54
+ namespaces?: string;
55
+ network?: string;
56
+ trigger: ConnectLogTrigger;
57
+ category: ConnectFailureCategory;
58
+ };
59
+
60
+ export type ConnectLogContext = {
61
+ failures: ConnectionFailureDetails[];
62
+ requestedNamespaces: string[];
63
+ };
64
+
65
+ /** What is logged for one failed connection attempt. */
66
+ export type ConnectLogEvent = {
67
+ error: Error;
68
+ tags: ConnectLogTags;
69
+ context: ConnectLogContext;
70
+ };
@@ -0,0 +1,5 @@
1
+ export { logAutoConnectFailure, runConnectWithLogging } from './connectLogging';
2
+ export type {
3
+ ConnectTrigger,
4
+ WithConnectTrigger,
5
+ } from './connectLogging.types';
@@ -99,6 +99,7 @@ export function WalletsPage() {
99
99
  <StatefulConnectModal
100
100
  id="widget-state-full-connect-modal"
101
101
  wallet={selectedWalletToConnect}
102
+ trigger="wallets-page"
102
103
  onClose={() => {
103
104
  setSelectedWalletToConnect(undefined);
104
105
  }}
@@ -1,4 +1,4 @@
1
- import type { WidgetConfig } from '../types';
1
+ import type { WidgetConfigWithoutLegacyProviders } from '../types';
2
2
  import type { PropsWithChildren } from 'react';
3
3
 
4
4
  import React, { createContext, useContext, useEffect, useRef } from 'react';
@@ -6,7 +6,9 @@ import React, { createContext, useContext, useEffect, useRef } from 'react';
6
6
  import { createAppStore } from './app';
7
7
 
8
8
  type AppStore = ReturnType<typeof createAppStore>;
9
- type AppStoreProviderProps = PropsWithChildren<{ config?: WidgetConfig }>;
9
+ type AppStoreProviderProps = PropsWithChildren<{
10
+ config?: WidgetConfigWithoutLegacyProviders;
11
+ }>;
10
12
 
11
13
  export const AppStoreContext = createContext<AppStore | null>(null);
12
14
 
package/src/store/app.ts CHANGED
@@ -1,4 +1,7 @@
1
- import type { WidgetConfig } from '../types';
1
+ import type {
2
+ WidgetConfig,
3
+ WidgetConfigWithoutLegacyProviders,
4
+ } from '../types';
2
5
  import type { AppStoreState } from './slices/types';
3
6
  import type { StateCreator } from 'zustand';
4
7
 
@@ -21,7 +24,9 @@ export type StateCreatorWithInitialData<
21
24
 
22
25
  export type { AppStoreState };
23
26
 
24
- export function createAppStore(initialData?: WidgetConfig) {
27
+ export function createAppStore(
28
+ initialData?: WidgetConfigWithoutLegacyProviders
29
+ ) {
25
30
  return create<AppStoreState>()(
26
31
  persist(
27
32
  (...a) => ({
@@ -1,13 +1,20 @@
1
1
  import type { DataSlice } from './data';
2
2
  import type { SettingsSlice } from './settings';
3
- import type { WidgetConfig } from '../../types';
3
+ import type {
4
+ WidgetConfig,
5
+ WidgetConfigWithoutLegacyProviders,
6
+ } from '../../types';
4
7
  import type { StateCreatorWithInitialData } from '../app';
5
8
  import type { VersionedProviders } from '@hub3js/core/utils';
6
9
 
7
- import { allProviders as getAllProviders } from '@rango-dev/provider-all';
10
+ import {
11
+ allProviders as getAllProviders,
12
+ WalletTypes,
13
+ } from '@rango-dev/provider-all';
8
14
 
9
15
  import { cacheService } from '../../services/cacheService';
10
16
  import {
17
+ configWalletsToWalletName,
11
18
  matchAndGenerateProviders,
12
19
  type ProvidersOptions,
13
20
  } from '../../utils/providers';
@@ -27,7 +34,7 @@ function makeProvidersOptionsFromConfig(
27
34
  return options;
28
35
  }
29
36
 
30
- export const DEFAULT_CONFIG: WidgetConfig = {
37
+ export const DEFAULT_CONFIG: WidgetConfigWithoutLegacyProviders = {
31
38
  apiKey: '',
32
39
  title: undefined,
33
40
  multiWallets: true,
@@ -62,13 +69,13 @@ const DEFAULT_CAMPAIGN_MODE: CampaignMode = {
62
69
 
63
70
  export interface ConfigSlice {
64
71
  // What user can set directly.
65
- config: WidgetConfig;
72
+ config: WidgetConfigWithoutLegacyProviders;
66
73
  campaignMode: CampaignMode;
67
74
  isInCampaignMode: () => boolean;
68
75
  getLiquiditySources: () => string[];
69
76
  getDisabledLiquiditySources: () => string[];
70
77
  excludeLiquiditySources: () => boolean;
71
- updateConfig: (config: WidgetConfig) => void;
78
+ updateConfig: (config: WidgetConfigWithoutLegacyProviders) => void;
72
79
  updateCampaignMode: <K extends keyof CampaignMode>(
73
80
  name: K,
74
81
  value: CampaignMode[K]
@@ -84,36 +91,40 @@ export interface ConfigSlice {
84
91
  getAvailableProviders: () => VersionedProviders[];
85
92
  }
86
93
 
87
- function generateProviders(config: WidgetConfig) {
88
- const options = makeProvidersOptionsFromConfig(config);
89
- const envs = {
90
- walletconnect2: {
91
- WC_PROJECT_ID: options?.walletConnectProjectId || '',
92
- DISABLE_MODAL_AND_OPEN_LINK:
93
- options?.walletConnectListedDesktopWalletLink,
94
- },
95
- selectedProviders: config.wallets,
96
- tonConnect: options?.tonConnect?.manifestUrl
97
- ? { manifestUrl: options?.tonConnect.manifestUrl }
98
- : undefined,
99
- };
100
- const allProviders = getAllProviders(envs);
94
+ function generateProviders(
95
+ config: WidgetConfigWithoutLegacyProviders
96
+ ): VersionedProviders[] {
97
+ const allProviders = getAllProviders();
101
98
  const allBuiltProviders = allProviders.map((build) => build());
102
99
 
103
- return allBuiltProviders;
100
+ const providerNames = configWalletsToWalletName(allBuiltProviders);
101
+
102
+ const filteredProviders = allBuiltProviders.filter((_, index) => {
103
+ if (
104
+ providerNames[index] === WalletTypes.LEDGER_WALLET &&
105
+ (!config.ledgerWallet?.apiKey || !config.ledgerWallet?.dAppIdentifier)
106
+ ) {
107
+ return false;
108
+ }
109
+
110
+ return true;
111
+ });
112
+
113
+ return filteredProviders;
104
114
  }
105
115
 
106
116
  export const createConfigSlice: StateCreatorWithInitialData<
107
- WidgetConfig,
117
+ WidgetConfigWithoutLegacyProviders,
108
118
  ConfigSlice & SettingsSlice & DataSlice,
109
119
  ConfigSlice
110
120
  > = (initialData, set, get) => {
111
- const allBuiltProviders = generateProviders({
121
+ const config: WidgetConfigWithoutLegacyProviders = {
112
122
  ...DEFAULT_CONFIG,
113
123
  ...initialData,
114
- });
124
+ };
125
+ const allBuiltProviders = generateProviders(config);
115
126
  return {
116
- config: { ...DEFAULT_CONFIG, ...initialData },
127
+ config,
117
128
  iframe: DEFAULT_IFRAME_CONFIGS,
118
129
  campaignMode: DEFAULT_CAMPAIGN_MODE,
119
130
  allProviders: allBuiltProviders,
@@ -141,7 +152,7 @@ export const createConfigSlice: StateCreatorWithInitialData<
141
152
  },
142
153
 
143
154
  // Actions
144
- updateConfig: (nextConfig: WidgetConfig) => {
155
+ updateConfig: (nextConfig: WidgetConfigWithoutLegacyProviders) => {
145
156
  const currentConfig = get().config;
146
157
  const {
147
158
  _tokensMapByTokenHash: tokensMapByTokenHash,
@@ -1,4 +1,4 @@
1
- import type { WidgetConfig } from '../../types';
1
+ import type { WidgetConfigWithoutLegacyProviders } from '../../types';
2
2
  import type { AppStoreState } from '../app';
3
3
  import type { EvmBlockchainMeta, Token } from 'rango-sdk';
4
4
 
@@ -18,7 +18,7 @@ let appStoreState: AppStoreState;
18
18
  let customTokens: [Token, Token, Token];
19
19
  let rangoBlockchain: EvmBlockchainMeta;
20
20
 
21
- const DEFAULT_CONFIG: WidgetConfig = {
21
+ const DEFAULT_CONFIG: WidgetConfigWithoutLegacyProviders = {
22
22
  apiKey: '',
23
23
  walletConnectProjectId: 'e24844c5deb5193c1c14840a7af6a40b',
24
24
  title: undefined,
@@ -1,5 +1,9 @@
1
1
  import type { AppStoreState } from '../store/app';
2
- import type { Blockchain, TokenHash, WidgetConfig } from '../types';
2
+ import type {
3
+ Blockchain,
4
+ TokenHash,
5
+ WidgetConfigWithoutLegacyProviders,
6
+ } from '../types';
3
7
  import type { BlockchainMeta, EvmBlockchainMeta, Token } from 'rango-sdk';
4
8
 
5
9
  import { faker } from '@faker-js/faker';
@@ -216,7 +220,7 @@ export function createInitialAppStore() {
216
220
 
217
221
  export function updateAppStoreConfig(
218
222
  appStoreState: AppStoreState,
219
- config: Partial<WidgetConfig>
223
+ config: Partial<WidgetConfigWithoutLegacyProviders>
220
224
  ): AppStoreState {
221
225
  appStoreState.config = {
222
226
  ...appStoreState.config,
@@ -1,7 +1,6 @@
1
- import type { Provider } from '@hub3js/core';
1
+ import type { Provider, WalletType } from '@hub3js/core';
2
2
  import type { Language, theme } from '@rango-dev/ui';
3
- import type { LegacyProviderInterface } from '@rango-dev/wallets-core/legacy';
4
- import type { WalletType } from '@rango-dev/wallets-shared';
3
+ import type { ProviderInterface } from '@rango-dev/wallets-react';
5
4
  import type { Asset } from 'rango-sdk';
6
5
  import type { ReactElement } from 'react';
7
6
 
@@ -186,6 +185,21 @@ export type TonConnectConfig = {
186
185
  manifestUrl?: string;
187
186
  };
188
187
 
188
+ /**
189
+ * Configuration for the Ledger wallet integration.
190
+ *
191
+ * @property {string} dAppIdentifier - Unique identifier of your dApp, registered with Ledger to authorize the integration.
192
+ * @property {string} apiKey - API key issued by Ledger, used to authenticate requests from your dApp.
193
+ * @property {'fatal' | 'error' | 'warn' | 'info' | 'debug'} [loggerLevel] - Verbosity of the Ledger logger.
194
+ * @property {boolean} [hideButton] - When `true`, hides the built-in Ledger floating button.
195
+ */
196
+ export type LedgerWalletConfig = {
197
+ dAppIdentifier: string;
198
+ apiKey: string;
199
+ loggerLevel?: 'fatal' | 'error' | 'warn' | 'info' | 'debug';
200
+ hideButton?: boolean;
201
+ };
202
+
189
203
  /**
190
204
  * The type WidgetConfig defines the configuration options for a widget, including API key, affiliate
191
205
  * reference, amount, blockchain and token configurations, liquidity sources, wallet types, language,
@@ -254,13 +268,18 @@ export type WidgetConfig = {
254
268
  title?: string;
255
269
  walletConnectProjectId?: string;
256
270
  trezorManifest?: TrezorManifest;
271
+ ledgerWallet?: LedgerWalletConfig;
257
272
  tonConnect?: TonConnectConfig;
258
273
  affiliate?: WidgetAffiliate;
259
274
  amount?: number;
260
275
  from?: BlockchainAndTokenConfig;
261
276
  to?: BlockchainAndTokenConfig;
262
277
  liquiditySources?: string[];
263
- wallets?: (WalletType | LegacyProviderInterface | Provider)[];
278
+ /**
279
+ * Passing a `ProviderInterface` is deprecated. Pass a hub `Provider` instead.
280
+ * Legacy providers will be removed in future versions.
281
+ */
282
+ wallets?: (WalletType | ProviderInterface | Provider)[];
264
283
  multiWallets?: boolean;
265
284
  customDestination?: boolean;
266
285
  defaultCustomDestinations?: { [blockchain: string]: string };
@@ -289,3 +308,11 @@ export type WidgetConfig = {
289
308
  };
290
309
  routing?: Routing;
291
310
  };
311
+
312
+ // TODO: remove this when ProviderInterface gets removed
313
+ export type WidgetConfigWithoutLegacyProviders = WidgetConfig & {
314
+ wallets?: Exclude<
315
+ NonNullable<WidgetConfig['wallets']>[number],
316
+ ProviderInterface
317
+ >[];
318
+ };