@rango-dev/widget-embedded 0.0.0-experimental-1624fbb4-20260708 → 0.0.0-experimental-a84f27a7-20260921
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/CHANGELOG.md +47 -0
- package/dist/QueueManager.d.ts.map +1 -1
- package/dist/components/ConfirmWalletsModal/ConfirmWallets.styles.d.ts +1 -1
- package/dist/components/ConfirmWalletsModal/ConfirmWallets.styles.d.ts.map +1 -1
- package/dist/components/CustomDestination/CustomDestination.styles.d.ts +4 -4
- package/dist/components/CustomDestination/CustomDestination.styles.d.ts.map +1 -1
- package/dist/components/CustomTokenModal/CustomTokenModal.styles.d.ts +169 -169
- package/dist/components/CustomTokenModal/CustomTokenModal.styles.d.ts.map +1 -1
- package/dist/components/FilterSelector/FilterSelector.style.d.ts +1 -1
- package/dist/components/FilterSelector/FilterSelector.style.d.ts.map +1 -1
- package/dist/components/HeaderButtons/HeaderButtons.styles.d.ts +1 -1
- package/dist/components/HeaderButtons/HeaderButtons.styles.d.ts.map +1 -1
- package/dist/components/NamespaceItem/NamespaceItem.d.ts.map +1 -1
- package/dist/components/NamespaceItem/NamespaceItem.styles.d.ts +1 -1
- package/dist/components/NamespaceItem/NamespaceItem.styles.d.ts.map +1 -1
- package/dist/components/NamespaceItem/NamespaceItem.types.d.ts +3 -3
- package/dist/components/NamespaceItem/NamespaceItem.types.d.ts.map +1 -1
- package/dist/components/Quote/Quote.d.ts.map +1 -1
- package/dist/components/Quote/Quote.styles.d.ts +171 -171
- package/dist/components/Quote/Quote.styles.d.ts.map +1 -1
- package/dist/components/Quote/Quote.types.d.ts +0 -2
- package/dist/components/Quote/Quote.types.d.ts.map +1 -1
- package/dist/components/Quote/QuoteCostDetails.d.ts.map +1 -1
- package/dist/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.styles.d.ts +169 -169
- package/dist/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.styles.d.ts.map +1 -1
- package/dist/components/RefreshModal/RefreshModal.styles.d.ts +168 -168
- package/dist/components/RefreshModal/RefreshModal.styles.d.ts.map +1 -1
- package/dist/components/StatefulConnectModal/StatefulConnectModal.d.ts.map +1 -1
- package/dist/components/SwapDetails/SwapDetails.d.ts.map +1 -1
- package/dist/components/SwapDetails/SwapDetails.styles.d.ts +1 -1
- package/dist/components/SwapDetails/SwapDetails.styles.d.ts.map +1 -1
- package/dist/components/SwapDetailsModal/SwapDetailsModal.ConnectWallet.d.ts.map +1 -1
- package/dist/components/SwapDetailsModal/SwapDetailsModal.InstallWallet.d.ts.map +1 -1
- package/dist/components/SwapDetailsModal/SwapDetailsModal.types.d.ts +3 -3
- package/dist/components/SwapDetailsModal/SwapDetailsModal.types.d.ts.map +1 -1
- package/dist/components/SwapMetrics/SwapMetrics.styles.d.ts +1 -1
- package/dist/components/SwapMetrics/SwapMetrics.styles.d.ts.map +1 -1
- package/dist/components/TokenList/TokenList.styles.d.ts +2 -2
- package/dist/components/TokenList/TokenList.styles.d.ts.map +1 -1
- package/dist/components/WalletStatefulConnect/DerivationPath.d.ts.map +1 -1
- package/dist/components/WalletStatefulConnect/DerivationPath.helpers.d.ts +7 -3
- package/dist/components/WalletStatefulConnect/DerivationPath.helpers.d.ts.map +1 -1
- package/dist/components/WalletStatefulConnect/DerivationPath.helpers.test.d.ts +2 -0
- package/dist/components/WalletStatefulConnect/DerivationPath.helpers.test.d.ts.map +1 -0
- package/dist/components/WalletStatefulConnect/DerivationPath.styles.d.ts +169 -169
- package/dist/components/WalletStatefulConnect/DerivationPath.styles.d.ts.map +1 -1
- package/dist/components/WalletStatefulConnect/Detached.d.ts.map +1 -1
- package/dist/components/WalletStatefulConnect/NamespaceDetachedItem.d.ts.map +1 -1
- package/dist/components/WalletStatefulConnect/Namespaces.d.ts.map +1 -1
- package/dist/components/WalletStatefulConnect/Namespaces.styles.d.ts +168 -168
- package/dist/components/WalletStatefulConnect/Namespaces.styles.d.ts.map +1 -1
- package/dist/components/WalletStatefulConnect/Namespaces.types.d.ts +3 -3
- package/dist/components/WalletStatefulConnect/Namespaces.types.d.ts.map +1 -1
- package/dist/constants/errors.d.ts +1 -0
- package/dist/constants/errors.d.ts.map +1 -1
- package/dist/containers/Wallets/Wallets.d.ts.map +1 -1
- package/dist/containers/Wallets/Wallets.helpers.d.ts +2 -2
- package/dist/containers/Wallets/Wallets.helpers.d.ts.map +1 -1
- package/dist/containers/Wallets/Wallets.types.d.ts +4 -4
- package/dist/containers/Wallets/Wallets.types.d.ts.map +1 -1
- package/dist/containers/Wallets/useUpdates.d.ts +1 -1
- package/dist/containers/Wallets/useUpdates.d.ts.map +1 -1
- package/dist/containers/WidgetProvider/WidgetProvider.d.ts.map +1 -1
- package/dist/containers/WidgetProvider/WidgetProvider.helpers.d.ts +5 -0
- package/dist/containers/WidgetProvider/WidgetProvider.helpers.d.ts.map +1 -0
- package/dist/containers/WidgetProvider/WidgetProvider.types.d.ts +1 -1
- package/dist/containers/WidgetProvider/WidgetProvider.types.d.ts.map +1 -1
- package/dist/hooks/useStatefulConnect/useStatefulConnect.d.ts.map +1 -1
- package/dist/hooks/useStatefulConnect/useStatefulConnect.types.d.ts +5 -4
- package/dist/hooks/useStatefulConnect/useStatefulConnect.types.d.ts.map +1 -1
- package/dist/hooks/useWalletList.d.ts.map +1 -1
- package/dist/hooks/useWalletProviders/useWalletProviders.d.ts +2 -2
- package/dist/hooks/useWalletProviders/useWalletProviders.d.ts.map +1 -1
- package/dist/hooks/useWalletProviders/useWalletProviders.helpers.d.ts +2 -3
- package/dist/hooks/useWalletProviders/useWalletProviders.helpers.d.ts.map +1 -1
- package/dist/index.d.ts +10 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +4 -4
- package/dist/store/AppStore.d.ts +2 -2
- package/dist/store/AppStore.d.ts.map +1 -1
- package/dist/store/app.d.ts +2 -2
- package/dist/store/app.d.ts.map +1 -1
- package/dist/store/slices/config.d.ts +5 -5
- package/dist/store/slices/config.d.ts.map +1 -1
- package/dist/test-utils/fixtures.d.ts +2 -2
- package/dist/test-utils/fixtures.d.ts.map +1 -1
- package/dist/types/config.d.ts +25 -4
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/wallets.d.ts +1 -1
- package/dist/types/wallets.d.ts.map +1 -1
- package/dist/utils/common.d.ts +1 -0
- package/dist/utils/common.d.ts.map +1 -1
- package/dist/utils/connectionReporting.d.ts +24 -0
- package/dist/utils/connectionReporting.d.ts.map +1 -0
- package/dist/utils/connectionReporting.test.d.ts +2 -0
- package/dist/utils/connectionReporting.test.d.ts.map +1 -0
- package/dist/utils/errors.d.ts +14 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.test.d.ts +2 -0
- package/dist/utils/errors.test.d.ts.map +1 -0
- package/dist/utils/providers.d.ts +3 -6
- package/dist/utils/providers.d.ts.map +1 -1
- package/dist/utils/swap.d.ts +1 -1
- package/dist/utils/swap.d.ts.map +1 -1
- package/dist/utils/wallets.d.ts +2 -1
- package/dist/utils/wallets.d.ts.map +1 -1
- package/package.json +16 -16
- package/src/QueueManager.tsx +17 -8
- package/src/components/NamespaceItem/NamespaceItem.styles.ts +1 -0
- package/src/components/NamespaceItem/NamespaceItem.tsx +5 -1
- package/src/components/NamespaceItem/NamespaceItem.types.ts +3 -3
- package/src/components/Quote/Quote.tsx +0 -2
- package/src/components/Quote/Quote.types.ts +0 -2
- package/src/components/Quote/QuoteCostDetails.tsx +15 -4
- package/src/components/StatefulConnectModal/StatefulConnectModal.tsx +8 -2
- package/src/components/SwapDetails/SwapDetails.tsx +18 -6
- package/src/components/SwapDetailsModal/SwapDetailsModal.ConnectWallet.tsx +23 -23
- package/src/components/SwapDetailsModal/SwapDetailsModal.InstallWallet.tsx +8 -2
- package/src/components/SwapDetailsModal/SwapDetailsModal.types.ts +3 -3
- package/src/components/TokenList/LoadingTokenList.tsx +2 -2
- package/src/components/WalletStatefulConnect/DerivationPath.helpers.test.ts +98 -0
- package/src/components/WalletStatefulConnect/DerivationPath.helpers.ts +14 -10
- package/src/components/WalletStatefulConnect/DerivationPath.tsx +14 -7
- package/src/components/WalletStatefulConnect/Detached.tsx +17 -8
- package/src/components/WalletStatefulConnect/NamespaceDetachedItem.tsx +2 -1
- package/src/components/WalletStatefulConnect/Namespaces.tsx +6 -12
- package/src/components/WalletStatefulConnect/Namespaces.types.tsx +3 -3
- package/src/constants/errors.ts +3 -0
- package/src/containers/Wallets/Wallets.helpers.ts +4 -4
- package/src/containers/Wallets/Wallets.tsx +20 -3
- package/src/containers/Wallets/Wallets.types.ts +4 -7
- package/src/containers/Wallets/useUpdates.ts +4 -119
- package/src/containers/WidgetProvider/WidgetProvider.helpers.ts +10 -0
- package/src/containers/WidgetProvider/WidgetProvider.tsx +12 -1
- package/src/containers/WidgetProvider/WidgetProvider.types.ts +1 -1
- package/src/hooks/useStatefulConnect/useStatefulConnect.ts +30 -31
- package/src/hooks/useStatefulConnect/useStatefulConnect.types.ts +6 -3
- package/src/hooks/useWalletList.ts +10 -7
- package/src/hooks/useWalletProviders/useWalletProviders.helpers.ts +3 -10
- package/src/hooks/useWalletProviders/useWalletProviders.ts +2 -2
- package/src/index.ts +10 -11
- package/src/store/AppStore.tsx +4 -2
- package/src/store/app.ts +7 -2
- package/src/store/slices/config.ts +36 -25
- package/src/store/slices/data.test.ts +2 -2
- package/src/test-utils/fixtures.ts +6 -2
- package/src/types/config.ts +31 -4
- package/src/types/wallets.ts +1 -1
- package/src/utils/common.ts +6 -0
- package/src/utils/connectionReporting.test.ts +379 -0
- package/src/utils/connectionReporting.ts +94 -0
- package/src/utils/errors.test.ts +125 -0
- package/src/utils/errors.ts +88 -0
- package/src/utils/providers.ts +16 -50
- package/src/utils/swap.ts +8 -1
- package/src/utils/wallets.ts +12 -15
- package/dist/widget-embedded.build.json +0 -1
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import type { DataSlice } from './data';
|
|
2
2
|
import type { SettingsSlice } from './settings';
|
|
3
|
-
import type {
|
|
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 {
|
|
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:
|
|
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:
|
|
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:
|
|
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(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
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
|
-
|
|
117
|
+
WidgetConfigWithoutLegacyProviders,
|
|
108
118
|
ConfigSlice & SettingsSlice & DataSlice,
|
|
109
119
|
ConfigSlice
|
|
110
120
|
> = (initialData, set, get) => {
|
|
111
|
-
const
|
|
121
|
+
const config: WidgetConfigWithoutLegacyProviders = {
|
|
112
122
|
...DEFAULT_CONFIG,
|
|
113
123
|
...initialData,
|
|
114
|
-
}
|
|
124
|
+
};
|
|
125
|
+
const allBuiltProviders = generateProviders(config);
|
|
115
126
|
return {
|
|
116
|
-
config
|
|
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:
|
|
155
|
+
updateConfig: (nextConfig: WidgetConfigWithoutLegacyProviders) => {
|
|
145
156
|
const currentConfig = get().config;
|
|
146
157
|
const {
|
|
147
158
|
_tokensMapByTokenHash: tokensMapByTokenHash,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
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:
|
|
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 {
|
|
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<
|
|
223
|
+
config: Partial<WidgetConfigWithoutLegacyProviders>
|
|
220
224
|
): AppStoreState {
|
|
221
225
|
appStoreState.config = {
|
|
222
226
|
...appStoreState.config,
|
package/src/types/config.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
+
};
|
package/src/types/wallets.ts
CHANGED
package/src/utils/common.ts
CHANGED
|
@@ -294,3 +294,9 @@ export function filterBlockchainsWithAtLeastOneToken(
|
|
|
294
294
|
blockchainsWithAtLeastOneToken.has(blockchain.name)
|
|
295
295
|
);
|
|
296
296
|
}
|
|
297
|
+
|
|
298
|
+
export function detectMobileScreens(): boolean {
|
|
299
|
+
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
|
300
|
+
navigator.userAgent
|
|
301
|
+
);
|
|
302
|
+
}
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import type { ConnectionReportContext } from './connectionReporting';
|
|
2
|
+
|
|
3
|
+
import { WalletConnectionError } from '@hub3js/std/utils';
|
|
4
|
+
import { error as logError } from '@rango-dev/logging-core';
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
CONNECTION_TIMEOUT_MS,
|
|
9
|
+
reportConnectionError,
|
|
10
|
+
trackConnection,
|
|
11
|
+
} from './connectionReporting';
|
|
12
|
+
|
|
13
|
+
vi.mock('@rango-dev/logging-core', () => ({ error: vi.fn() }));
|
|
14
|
+
|
|
15
|
+
const context: ConnectionReportContext = {
|
|
16
|
+
trigger: 'wallet-modal',
|
|
17
|
+
source: 'user',
|
|
18
|
+
walletType: 'phantom',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const namespaceState = {
|
|
22
|
+
network: null,
|
|
23
|
+
connected: false,
|
|
24
|
+
connecting: true,
|
|
25
|
+
connectArgs: null,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function createConnectionError(
|
|
29
|
+
type: 'rejected' | 'locked' | 'timeout' | 'unknown',
|
|
30
|
+
cause?: unknown
|
|
31
|
+
) {
|
|
32
|
+
return new WalletConnectionError({
|
|
33
|
+
walletType: 'phantom',
|
|
34
|
+
namespace: 'solana',
|
|
35
|
+
type,
|
|
36
|
+
state: namespaceState,
|
|
37
|
+
cause,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function catchThrown(run: () => unknown): unknown {
|
|
42
|
+
try {
|
|
43
|
+
run();
|
|
44
|
+
} catch (error) {
|
|
45
|
+
return error;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function createPendingAttempt() {
|
|
52
|
+
let resolve!: (value: string) => void;
|
|
53
|
+
let reject!: (reason: unknown) => void;
|
|
54
|
+
const attempt = new Promise<string>((res, rej) => {
|
|
55
|
+
resolve = res;
|
|
56
|
+
reject = rej;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return { attempt, resolve, reject };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function reportedEvent(call = 0) {
|
|
63
|
+
const [error, data] = vi.mocked(logError).mock.calls[call] ?? [];
|
|
64
|
+
|
|
65
|
+
return { error, data };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
beforeEach(() => {
|
|
69
|
+
vi.mocked(logError).mockReset();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('reporting a connection error', () => {
|
|
73
|
+
test('drop a rejected connection failure without any event', () => {
|
|
74
|
+
catchThrown(() =>
|
|
75
|
+
reportConnectionError(createConnectionError('rejected'), context)
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
expect(logError).not.toHaveBeenCalled();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('drop a locked wallet without any event', () => {
|
|
82
|
+
catchThrown(() =>
|
|
83
|
+
reportConnectionError(createConnectionError('locked'), context)
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
expect(logError).not.toHaveBeenCalled();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('log an unknown connection failure once', () => {
|
|
90
|
+
catchThrown(() =>
|
|
91
|
+
reportConnectionError(createConnectionError('unknown'), context)
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
expect(logError).toHaveBeenCalledTimes(1);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('log a timeout connection failure once', () => {
|
|
98
|
+
catchThrown(() =>
|
|
99
|
+
reportConnectionError(createConnectionError('timeout'), context)
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
expect(logError).toHaveBeenCalledTimes(1);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('log the error itself with its own tags and context', () => {
|
|
106
|
+
const connectionError = createConnectionError('unknown');
|
|
107
|
+
|
|
108
|
+
catchThrown(() => reportConnectionError(connectionError, context));
|
|
109
|
+
|
|
110
|
+
const { error, data } = reportedEvent();
|
|
111
|
+
expect(error).toBe(connectionError);
|
|
112
|
+
expect(data).toEqual({
|
|
113
|
+
tags: {
|
|
114
|
+
name: 'WalletConnectionError',
|
|
115
|
+
type: 'unknown',
|
|
116
|
+
trigger: 'wallet-modal',
|
|
117
|
+
source: 'user',
|
|
118
|
+
walletType: 'phantom',
|
|
119
|
+
namespace: 'solana',
|
|
120
|
+
},
|
|
121
|
+
context: {
|
|
122
|
+
walletType: 'phantom',
|
|
123
|
+
namespace: 'solana',
|
|
124
|
+
state: namespaceState,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('keep the wallet raw error as the cause of the event', () => {
|
|
130
|
+
const walletError = { code: -32603, message: 'Internal error' };
|
|
131
|
+
|
|
132
|
+
catchThrown(() =>
|
|
133
|
+
reportConnectionError(
|
|
134
|
+
createConnectionError('unknown', walletError),
|
|
135
|
+
context
|
|
136
|
+
)
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
expect(reportedEvent().error).toHaveProperty('cause', walletError);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('keep the trigger and source a failure already carries', () => {
|
|
143
|
+
const connectionError = createConnectionError('unknown');
|
|
144
|
+
connectionError.trigger = 'swap-details';
|
|
145
|
+
connectionError.source = 'auto-connect';
|
|
146
|
+
|
|
147
|
+
catchThrown(() => reportConnectionError(connectionError, context));
|
|
148
|
+
|
|
149
|
+
expect(reportedEvent().data).toMatchObject({
|
|
150
|
+
tags: { trigger: 'swap-details', source: 'auto-connect' },
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test('wrap an unexpected error keeping its real message', () => {
|
|
155
|
+
const unexpected = new TypeError('Cannot read properties of undefined');
|
|
156
|
+
|
|
157
|
+
catchThrown(() => reportConnectionError(unexpected, context));
|
|
158
|
+
|
|
159
|
+
const { error, data } = reportedEvent();
|
|
160
|
+
expect(error).toMatchObject({
|
|
161
|
+
name: 'RangoError',
|
|
162
|
+
type: 'unknown',
|
|
163
|
+
message: 'Cannot read properties of undefined',
|
|
164
|
+
cause: unexpected,
|
|
165
|
+
});
|
|
166
|
+
expect(data).toMatchObject({
|
|
167
|
+
tags: {
|
|
168
|
+
name: 'RangoError',
|
|
169
|
+
type: 'unknown',
|
|
170
|
+
trigger: 'wallet-modal',
|
|
171
|
+
source: 'user',
|
|
172
|
+
walletType: 'phantom',
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('log a thrown value that is not an error', () => {
|
|
178
|
+
catchThrown(() => reportConnectionError('boom', context));
|
|
179
|
+
|
|
180
|
+
expect(logError).toHaveBeenCalledTimes(1);
|
|
181
|
+
expect(reportedEvent().error).toMatchObject({ message: 'boom' });
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test('rethrow the original error as the same object', () => {
|
|
185
|
+
const rejected = createConnectionError('rejected');
|
|
186
|
+
const unknown = createConnectionError('unknown');
|
|
187
|
+
const unexpected = new TypeError('boom');
|
|
188
|
+
|
|
189
|
+
expect(catchThrown(() => reportConnectionError(rejected, context))).toBe(
|
|
190
|
+
rejected
|
|
191
|
+
);
|
|
192
|
+
expect(catchThrown(() => reportConnectionError(unknown, context))).toBe(
|
|
193
|
+
unknown
|
|
194
|
+
);
|
|
195
|
+
expect(catchThrown(() => reportConnectionError(unexpected, context))).toBe(
|
|
196
|
+
unexpected
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test('rethrow the original error when logging fails', () => {
|
|
201
|
+
vi.mocked(logError).mockImplementationOnce(() => {
|
|
202
|
+
throw new Error('the log sink is down');
|
|
203
|
+
});
|
|
204
|
+
const connectionError = createConnectionError('unknown');
|
|
205
|
+
|
|
206
|
+
expect(
|
|
207
|
+
catchThrown(() => reportConnectionError(connectionError, context))
|
|
208
|
+
).toBe(connectionError);
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe('tracking a connection attempt', () => {
|
|
213
|
+
beforeEach(() => {
|
|
214
|
+
vi.useFakeTimers();
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
afterEach(() => {
|
|
218
|
+
vi.useRealTimers();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test('resolve with the value of a successful attempt without any event', async () => {
|
|
222
|
+
const { attempt, resolve } = createPendingAttempt();
|
|
223
|
+
|
|
224
|
+
const tracked = trackConnection(attempt, context);
|
|
225
|
+
resolve('connected');
|
|
226
|
+
|
|
227
|
+
await expect(tracked).resolves.toBe('connected');
|
|
228
|
+
expect(logError).not.toHaveBeenCalled();
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test('report a failed attempt and rethrow the same error', async () => {
|
|
232
|
+
const { attempt, reject } = createPendingAttempt();
|
|
233
|
+
const connectionError = createConnectionError('unknown');
|
|
234
|
+
|
|
235
|
+
const tracked = trackConnection(attempt, context);
|
|
236
|
+
reject(connectionError);
|
|
237
|
+
|
|
238
|
+
await expect(tracked).rejects.toBe(connectionError);
|
|
239
|
+
expect(logError).toHaveBeenCalledTimes(1);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test('rethrow a rejection without any event', async () => {
|
|
243
|
+
const { attempt, reject } = createPendingAttempt();
|
|
244
|
+
const connectionError = createConnectionError('rejected');
|
|
245
|
+
|
|
246
|
+
const tracked = trackConnection(attempt, context);
|
|
247
|
+
reject(connectionError);
|
|
248
|
+
|
|
249
|
+
await expect(tracked).rejects.toBe(connectionError);
|
|
250
|
+
expect(logError).not.toHaveBeenCalled();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test('send one timeout event when the attempt is still pending after three minutes', async () => {
|
|
254
|
+
const { attempt } = createPendingAttempt();
|
|
255
|
+
|
|
256
|
+
void trackConnection(attempt, { ...context, namespaces: ['solana'] });
|
|
257
|
+
await vi.advanceTimersByTimeAsync(CONNECTION_TIMEOUT_MS);
|
|
258
|
+
|
|
259
|
+
expect(logError).toHaveBeenCalledTimes(1);
|
|
260
|
+
const { error, data } = reportedEvent();
|
|
261
|
+
expect(error).toBeInstanceOf(WalletConnectionError);
|
|
262
|
+
expect(error).toMatchObject({
|
|
263
|
+
type: 'timeout',
|
|
264
|
+
message: "Your wallet didn't respond.",
|
|
265
|
+
});
|
|
266
|
+
expect(data).toMatchObject({
|
|
267
|
+
tags: {
|
|
268
|
+
name: 'WalletConnectionError',
|
|
269
|
+
type: 'timeout',
|
|
270
|
+
trigger: 'wallet-modal',
|
|
271
|
+
source: 'user',
|
|
272
|
+
walletType: 'phantom',
|
|
273
|
+
namespace: 'solana',
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test('wait for the full three minutes before the timeout event', async () => {
|
|
279
|
+
const { attempt } = createPendingAttempt();
|
|
280
|
+
|
|
281
|
+
void trackConnection(attempt, context);
|
|
282
|
+
await vi.advanceTimersByTimeAsync(CONNECTION_TIMEOUT_MS - 1);
|
|
283
|
+
|
|
284
|
+
expect(logError).not.toHaveBeenCalled();
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test('send at most one timeout event per attempt', async () => {
|
|
288
|
+
const { attempt } = createPendingAttempt();
|
|
289
|
+
|
|
290
|
+
void trackConnection(attempt, context);
|
|
291
|
+
await vi.advanceTimersByTimeAsync(CONNECTION_TIMEOUT_MS * 2);
|
|
292
|
+
|
|
293
|
+
expect(logError).toHaveBeenCalledTimes(1);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test('name every requested namespace on the timeout event', async () => {
|
|
297
|
+
const { attempt } = createPendingAttempt();
|
|
298
|
+
|
|
299
|
+
void trackConnection(attempt, {
|
|
300
|
+
...context,
|
|
301
|
+
namespaces: ['solana', 'evm', 'solana'],
|
|
302
|
+
});
|
|
303
|
+
await vi.advanceTimersByTimeAsync(CONNECTION_TIMEOUT_MS);
|
|
304
|
+
|
|
305
|
+
expect(reportedEvent().data).toMatchObject({
|
|
306
|
+
tags: { namespace: 'evm,solana' },
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test('send no timeout event when the attempt succeeds first', async () => {
|
|
311
|
+
const { attempt, resolve } = createPendingAttempt();
|
|
312
|
+
|
|
313
|
+
const tracked = trackConnection(attempt, context);
|
|
314
|
+
await vi.advanceTimersByTimeAsync(CONNECTION_TIMEOUT_MS - 1);
|
|
315
|
+
resolve('connected');
|
|
316
|
+
await tracked;
|
|
317
|
+
await vi.advanceTimersByTimeAsync(CONNECTION_TIMEOUT_MS * 2);
|
|
318
|
+
|
|
319
|
+
expect(logError).not.toHaveBeenCalled();
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test('send no timeout event when the attempt fails first', async () => {
|
|
323
|
+
const { attempt, reject } = createPendingAttempt();
|
|
324
|
+
|
|
325
|
+
const tracked = trackConnection(attempt, context);
|
|
326
|
+
await vi.advanceTimersByTimeAsync(CONNECTION_TIMEOUT_MS - 1);
|
|
327
|
+
reject(createConnectionError('rejected'));
|
|
328
|
+
await tracked.catch(() => undefined);
|
|
329
|
+
await vi.advanceTimersByTimeAsync(CONNECTION_TIMEOUT_MS * 2);
|
|
330
|
+
|
|
331
|
+
expect(logError).not.toHaveBeenCalled();
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
test('clear the timer once the attempt settles', async () => {
|
|
335
|
+
const { attempt, resolve } = createPendingAttempt();
|
|
336
|
+
|
|
337
|
+
const tracked = trackConnection(attempt, context);
|
|
338
|
+
expect(vi.getTimerCount()).toBe(1);
|
|
339
|
+
resolve('connected');
|
|
340
|
+
await tracked;
|
|
341
|
+
|
|
342
|
+
expect(vi.getTimerCount()).toBe(0);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
test('send no timeout event for auto connect', async () => {
|
|
346
|
+
const { attempt } = createPendingAttempt();
|
|
347
|
+
|
|
348
|
+
void trackConnection(attempt, { ...context, source: 'auto-connect' });
|
|
349
|
+
await vi.advanceTimersByTimeAsync(CONNECTION_TIMEOUT_MS * 2);
|
|
350
|
+
|
|
351
|
+
expect(vi.getTimerCount()).toBe(0);
|
|
352
|
+
expect(logError).not.toHaveBeenCalled();
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
test('keep the attempt going after the timeout event', async () => {
|
|
356
|
+
const { attempt, resolve } = createPendingAttempt();
|
|
357
|
+
|
|
358
|
+
const tracked = trackConnection(attempt, context);
|
|
359
|
+
await vi.advanceTimersByTimeAsync(CONNECTION_TIMEOUT_MS);
|
|
360
|
+
resolve('connected');
|
|
361
|
+
|
|
362
|
+
await expect(tracked).resolves.toBe('connected');
|
|
363
|
+
expect(logError).toHaveBeenCalledTimes(1);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
test('report a real failure that arrives after the timeout event', async () => {
|
|
367
|
+
const { attempt, reject } = createPendingAttempt();
|
|
368
|
+
const connectionError = createConnectionError('unknown');
|
|
369
|
+
|
|
370
|
+
const tracked = trackConnection(attempt, context);
|
|
371
|
+
await vi.advanceTimersByTimeAsync(CONNECTION_TIMEOUT_MS);
|
|
372
|
+
reject(connectionError);
|
|
373
|
+
|
|
374
|
+
await expect(tracked).rejects.toBe(connectionError);
|
|
375
|
+
expect(logError).toHaveBeenCalledTimes(2);
|
|
376
|
+
expect(reportedEvent(0).error).toMatchObject({ type: 'timeout' });
|
|
377
|
+
expect(reportedEvent(1).error).toBe(connectionError);
|
|
378
|
+
});
|
|
379
|
+
});
|