@rango-dev/widget-embedded 0.30.1 → 0.31.1-next.0

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 (128) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/components/ConfirmWalletsModal/ConfirmWallets.styles.d.ts +1 -653
  3. package/dist/components/ConfirmWalletsModal/ConfirmWallets.styles.d.ts.map +1 -1
  4. package/dist/components/ConfirmWalletsModal/ConfirmWalletsModal.d.ts.map +1 -1
  5. package/dist/components/ConfirmWalletsModal/WalletList.d.ts.map +1 -1
  6. package/dist/components/CustomDestination/CustomDestination.d.ts +4 -0
  7. package/dist/components/CustomDestination/CustomDestination.d.ts.map +1 -0
  8. package/dist/components/CustomDestination/CustomDestination.styles.d.ts +495 -0
  9. package/dist/components/CustomDestination/CustomDestination.styles.d.ts.map +1 -0
  10. package/dist/components/CustomDestination/CustomDestination.types.d.ts +7 -0
  11. package/dist/components/CustomDestination/CustomDestination.types.d.ts.map +1 -0
  12. package/dist/components/FilterSelector/FilterSelector.d.ts +4 -0
  13. package/dist/components/FilterSelector/FilterSelector.d.ts.map +1 -0
  14. package/dist/components/FilterSelector/FilterSelector.style.d.ts +962 -0
  15. package/dist/components/FilterSelector/FilterSelector.style.d.ts.map +1 -0
  16. package/dist/components/FilterSelector/FilterSelector.type.d.ts +14 -0
  17. package/dist/components/FilterSelector/FilterSelector.type.d.ts.map +1 -0
  18. package/dist/components/FilterSelector/FilterSelectorContent.d.ts +4 -0
  19. package/dist/components/FilterSelector/FilterSelectorContent.d.ts.map +1 -0
  20. package/dist/components/FilterSelector/index.d.ts +2 -0
  21. package/dist/components/FilterSelector/index.d.ts.map +1 -0
  22. package/dist/components/Quote/Quote.styles.d.ts +3 -3
  23. package/dist/components/RefreshModal/RefreshModal.styles.d.ts +3 -3
  24. package/dist/components/SearchInput/SearchInput.d.ts.map +1 -1
  25. package/dist/components/SwapDetails/SwapDetails.d.ts.map +1 -1
  26. package/dist/components/SwapDetails/SwapDetails.styles.d.ts.map +1 -1
  27. package/dist/components/SwapDetailsModal/SwapDetailsModal.WalletState.d.ts.map +1 -1
  28. package/dist/components/TokenList/TokenList.d.ts.map +1 -1
  29. package/dist/containers/Inputs/Inputs.d.ts.map +1 -1
  30. package/dist/containers/Wallets/Wallets.d.ts.map +1 -1
  31. package/dist/containers/WidgetInfo/WidgetInfo.d.ts.map +1 -1
  32. package/dist/containers/WidgetInfo/WidgetInfo.types.d.ts +5 -1
  33. package/dist/containers/WidgetInfo/WidgetInfo.types.d.ts.map +1 -1
  34. package/dist/hooks/useObserveBalanceChanges/index.d.ts +2 -0
  35. package/dist/hooks/useObserveBalanceChanges/index.d.ts.map +1 -0
  36. package/dist/hooks/useObserveBalanceChanges/useObserveBalanceChanges.d.ts +4 -0
  37. package/dist/hooks/useObserveBalanceChanges/useObserveBalanceChanges.d.ts.map +1 -0
  38. package/dist/hooks/usePrepareBlockchainList/usePrepareBlockchainList.helpers.d.ts +2 -2
  39. package/dist/hooks/useTheme.d.ts.map +1 -1
  40. package/dist/hooks/useUpdateQuoteInputs/index.d.ts +2 -0
  41. package/dist/hooks/useUpdateQuoteInputs/index.d.ts.map +1 -0
  42. package/dist/hooks/useUpdateQuoteInputs/useUpdateQuoteInputs.d.ts +3 -0
  43. package/dist/hooks/useUpdateQuoteInputs/useUpdateQuoteInputs.d.ts.map +1 -0
  44. package/dist/index.d.ts +4 -4
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +2 -2
  47. package/dist/index.js.map +4 -4
  48. package/dist/pages/HistoryPage.d.ts.map +1 -1
  49. package/dist/pages/Home.d.ts.map +1 -1
  50. package/dist/pages/WalletsPage.d.ts.map +1 -1
  51. package/dist/services/cacheService.d.ts +21 -0
  52. package/dist/services/cacheService.d.ts.map +1 -0
  53. package/dist/store/notification.d.ts.map +1 -1
  54. package/dist/store/slices/config.d.ts +2 -1
  55. package/dist/store/slices/config.d.ts.map +1 -1
  56. package/dist/store/slices/data.d.ts +2 -1
  57. package/dist/store/slices/data.d.ts.map +1 -1
  58. package/dist/store/utils/data.d.ts +20 -0
  59. package/dist/store/utils/data.d.ts.map +1 -0
  60. package/dist/store/utils/data.test.d.ts +2 -0
  61. package/dist/store/utils/data.test.d.ts.map +1 -0
  62. package/dist/store/utils/index.d.ts +2 -0
  63. package/dist/store/utils/index.d.ts.map +1 -0
  64. package/dist/test-utils/fixtures.d.ts +1 -0
  65. package/dist/test-utils/fixtures.d.ts.map +1 -1
  66. package/dist/types/config.d.ts +5 -0
  67. package/dist/types/config.d.ts.map +1 -1
  68. package/dist/types/event.d.ts +21 -15
  69. package/dist/types/event.d.ts.map +1 -1
  70. package/dist/types/notification.d.ts +1 -0
  71. package/dist/types/notification.d.ts.map +1 -1
  72. package/dist/types/quote.d.ts +9 -1
  73. package/dist/types/quote.d.ts.map +1 -1
  74. package/dist/types/wallets.d.ts +1 -1
  75. package/dist/types/wallets.d.ts.map +1 -1
  76. package/dist/utils/events.d.ts +8 -0
  77. package/dist/utils/events.d.ts.map +1 -0
  78. package/dist/utils/settings.d.ts +1 -1
  79. package/dist/utils/swap.d.ts +1 -1
  80. package/dist/utils/swap.d.ts.map +1 -1
  81. package/dist/widget-embedded.build.json +1 -1
  82. package/package.json +10 -10
  83. package/src/components/ConfirmWalletsModal/ConfirmWallets.styles.ts +1 -49
  84. package/src/components/ConfirmWalletsModal/ConfirmWalletsModal.tsx +61 -179
  85. package/src/components/ConfirmWalletsModal/WalletList.tsx +16 -4
  86. package/src/components/CustomDestination/CustomDestination.styles.ts +45 -0
  87. package/src/components/CustomDestination/CustomDestination.tsx +148 -0
  88. package/src/components/CustomDestination/CustomDestination.types.ts +7 -0
  89. package/src/components/FilterSelector/FilterSelector.style.ts +95 -0
  90. package/src/components/FilterSelector/FilterSelector.tsx +42 -0
  91. package/src/components/FilterSelector/FilterSelector.type.ts +15 -0
  92. package/src/components/FilterSelector/FilterSelectorContent.tsx +60 -0
  93. package/src/components/FilterSelector/index.ts +1 -0
  94. package/src/components/SearchInput/SearchInput.tsx +2 -0
  95. package/src/components/SwapDetails/SwapDetails.styles.ts +10 -0
  96. package/src/components/SwapDetails/SwapDetails.tsx +25 -13
  97. package/src/components/SwapDetailsModal/SwapDetailsModal.WalletState.tsx +28 -13
  98. package/src/components/TokenList/TokenList.tsx +8 -2
  99. package/src/containers/Inputs/Inputs.tsx +1 -0
  100. package/src/containers/Wallets/Wallets.tsx +7 -2
  101. package/src/containers/WidgetInfo/WidgetInfo.tsx +26 -0
  102. package/src/containers/WidgetInfo/WidgetInfo.types.ts +5 -1
  103. package/src/hooks/useObserveBalanceChanges/index.ts +1 -0
  104. package/src/hooks/useObserveBalanceChanges/useObserveBalanceChanges.ts +68 -0
  105. package/src/hooks/useTheme.ts +11 -5
  106. package/src/hooks/useUpdateQuoteInputs/index.ts +1 -0
  107. package/src/hooks/useUpdateQuoteInputs/useUpdateQuoteInputs.ts +50 -0
  108. package/src/index.ts +4 -0
  109. package/src/pages/HistoryPage.tsx +180 -26
  110. package/src/pages/Home.tsx +6 -3
  111. package/src/pages/WalletsPage.tsx +29 -13
  112. package/src/services/cacheService.ts +35 -0
  113. package/src/store/notification.ts +13 -1
  114. package/src/store/quote.ts +1 -1
  115. package/src/store/slices/config.ts +39 -1
  116. package/src/store/slices/data.test.ts +84 -0
  117. package/src/store/slices/data.ts +44 -24
  118. package/src/store/utils/data.test.ts +175 -0
  119. package/src/store/utils/data.ts +117 -0
  120. package/src/store/utils/index.ts +1 -0
  121. package/src/test-utils/fixtures.ts +14 -7
  122. package/src/types/config.ts +3 -1
  123. package/src/types/event.ts +32 -16
  124. package/src/types/notification.ts +1 -0
  125. package/src/types/quote.ts +11 -0
  126. package/src/types/wallets.ts +1 -1
  127. package/src/utils/events.ts +27 -0
  128. package/src/utils/swap.ts +4 -4
@@ -1,16 +1,18 @@
1
1
  // We keep all the received data from server in this slice
2
2
 
3
3
  import type { ConfigSlice } from './config';
4
- import type { Balance, TokenHash } from '../../types';
4
+ import type { CachedEntries } from '../../services/cacheService';
5
+ import type { Balance, Blockchain, TokenHash } from '../../types';
5
6
  import type { Asset, BlockchainMeta, SwapperMeta, Token } from 'rango-sdk';
6
7
  import type { StateCreator } from 'zustand';
7
8
 
9
+ import { cacheService } from '../../services/cacheService';
8
10
  import { httpService as sdk } from '../../services/httpService';
9
11
  import { compareWithSearchFor, containsText } from '../../utils/common';
10
- import { isTokenExcludedInConfig } from '../../utils/configs';
11
12
  import { createTokenHash, isTokenNative } from '../../utils/meta';
12
13
  import { sortLiquiditySourcesByGroupTitle } from '../../utils/settings';
13
14
  import { areTokensEqual, compareTokenBalance } from '../../utils/wallets';
15
+ import { matchTokensFromConfigWithMeta } from '../utils';
14
16
 
15
17
  type BlockchainOptions = {
16
18
  type?: 'source' | 'destination';
@@ -30,6 +32,7 @@ export type FetchStatus = 'loading' | 'success' | 'failed';
30
32
  export interface DataSlice {
31
33
  _blockchainsMapByName: Map<string, BlockchainMeta>;
32
34
  _tokensMapByTokenHash: Map<TokenHash, Token>;
35
+ _tokensMapByBlockchainName: Record<Blockchain, TokenHash[]>;
33
36
  _popularTokens: Token[];
34
37
  _swappers: SwapperMeta[];
35
38
  fetchStatus: FetchStatus;
@@ -48,8 +51,9 @@ export const createDataSlice: StateCreator<
48
51
  DataSlice
49
52
  > = (set, get) => ({
50
53
  // State
51
- _blockchainsMapByName: new Map<string, BlockchainMeta>(),
52
- _tokensMapByTokenHash: new Map<TokenHash, Token>(),
54
+ _blockchainsMapByName: new Map(),
55
+ _tokensMapByTokenHash: new Map(),
56
+ _tokensMapByBlockchainName: {},
53
57
  _popularTokens: [],
54
58
  _swappers: [],
55
59
  fetchStatus: 'loading',
@@ -84,31 +88,41 @@ export const createDataSlice: StateCreator<
84
88
  return list;
85
89
  },
86
90
  tokens: (options) => {
87
- const tokensMapByHashToken = get()._tokensMapByTokenHash;
88
- const tokensFromState = Array.from(tokensMapByHashToken?.values() || []);
91
+ const { _tokensMapByTokenHash, _tokensMapByBlockchainName, config } = get();
92
+ const tokensFromState = Array.from(_tokensMapByTokenHash.values());
89
93
  const blockchainsMapByName = get()._blockchainsMapByName;
90
-
91
- if (!options || !options?.type) {
94
+ if (!options || !options.type) {
92
95
  return tokensFromState;
93
96
  }
94
97
 
95
- const config = get().config;
96
- const supportedTokensConfig =
97
- (options.type === 'source' ? config.from?.tokens : config.to?.tokens) ??
98
- {};
98
+ const configType = options.type === 'source' ? 'from' : 'to';
99
+ const cacheKey: keyof CachedEntries =
100
+ options.type === 'source'
101
+ ? 'supportedSourceTokens'
102
+ : 'supportedDestinationTokens';
103
+
104
+ let supportedTokens = cacheService.get(cacheKey);
105
+ if (!supportedTokens?.length) {
106
+ supportedTokens = matchTokensFromConfigWithMeta({
107
+ type: options.type,
108
+ config: {
109
+ blockchains: config[configType]?.blockchains,
110
+ tokens: config[configType]?.tokens,
111
+ },
112
+ meta: {
113
+ tokensMapByTokenHash: _tokensMapByTokenHash,
114
+ tokensMapByBlockchainName: _tokensMapByBlockchainName,
115
+ },
116
+ });
117
+ cacheService.set(cacheKey, supportedTokens);
118
+ }
119
+
99
120
  const blockchains = get().blockchains({
100
121
  type: options.type,
101
122
  });
102
123
 
103
- const list = tokensFromState
124
+ const list = supportedTokens
104
125
  .filter((token) => {
105
- if (
106
- supportedTokensConfig &&
107
- isTokenExcludedInConfig(token, supportedTokensConfig)
108
- ) {
109
- return false;
110
- }
111
-
112
126
  // If a specific blockchain has passed, we only keep that blockchain's tokens.
113
127
  if (!!options.blockchain && token.blockchain !== options.blockchain) {
114
128
  return false;
@@ -261,17 +275,18 @@ export const createDataSlice: StateCreator<
261
275
  const response = await sdk().getAllMetadata({
262
276
  enableCentralizedSwappers,
263
277
  });
264
-
265
278
  set({ fetchStatus: 'success' });
266
279
  const blockchainsMapByName: Map<string, BlockchainMeta> = new Map<
267
280
  string,
268
281
  BlockchainMeta
269
282
  >();
270
283
 
271
- const tokensMapByHashToken: Map<TokenHash, Token> = new Map<
284
+ const tokensMapByTokenHash: Map<TokenHash, Token> = new Map<
272
285
  TokenHash,
273
286
  Token
274
287
  >();
288
+ const tokensMapByBlockchainName: DataSlice['_tokensMapByBlockchainName'] =
289
+ {};
275
290
  const tokens: Token[] = [];
276
291
  const popularTokens: Token[] = response.popularTokens;
277
292
  const swappers: SwapperMeta[] = response.swappers;
@@ -299,12 +314,17 @@ export const createDataSlice: StateCreator<
299
314
 
300
315
  tokens.forEach((token) => {
301
316
  const tokenHash = createTokenHash(token);
302
- tokensMapByHashToken.set(tokenHash, token);
317
+ if (!tokensMapByBlockchainName[token.blockchain]) {
318
+ tokensMapByBlockchainName[token.blockchain] = [];
319
+ }
320
+ tokensMapByTokenHash.set(tokenHash, token);
321
+ tokensMapByBlockchainName[token.blockchain].push(tokenHash);
303
322
  });
304
323
 
305
324
  set({
306
325
  _blockchainsMapByName: blockchainsMapByName,
307
- _tokensMapByTokenHash: tokensMapByHashToken,
326
+ _tokensMapByTokenHash: tokensMapByTokenHash,
327
+ _tokensMapByBlockchainName: tokensMapByBlockchainName,
308
328
  _popularTokens: popularTokens,
309
329
  _swappers: swappers,
310
330
  });
@@ -0,0 +1,175 @@
1
+ import type { Token } from 'rango-sdk';
2
+
3
+ import { describe, expect, test } from 'vitest';
4
+
5
+ import { createToken } from '../../test-utils/fixtures';
6
+ import { createTokenHash } from '../../utils/meta';
7
+
8
+ import { matchTokensFromConfigWithMeta } from './data'; // Adjust path as necessary
9
+
10
+ type MatchTokensFromConfigWithMetaParam = Parameters<
11
+ typeof matchTokensFromConfigWithMeta
12
+ >[0];
13
+
14
+ const BLOCKCHAIN_A = 'blockchainA';
15
+ const BLOCKCHAIN_B = 'blockchainB';
16
+ const BLOCKCHAIN_C = 'blockchainC';
17
+
18
+ const tokens: Token[] = [
19
+ { ...createToken(), blockchain: BLOCKCHAIN_A },
20
+ { ...createToken(), blockchain: BLOCKCHAIN_A },
21
+ { ...createToken(), blockchain: BLOCKCHAIN_B },
22
+ { ...createToken(), blockchain: BLOCKCHAIN_C },
23
+ ];
24
+
25
+ function createMetaForMockData(
26
+ tokens: Token[]
27
+ ): MatchTokensFromConfigWithMetaParam['meta'] {
28
+ const meta: MatchTokensFromConfigWithMetaParam['meta'] = {
29
+ tokensMapByTokenHash: new Map(),
30
+ tokensMapByBlockchainName: {},
31
+ };
32
+ tokens.forEach((token) => {
33
+ const tokenHash = createTokenHash(token);
34
+ meta.tokensMapByTokenHash.set(tokenHash, token);
35
+ if (!meta.tokensMapByBlockchainName[token.blockchain]) {
36
+ meta.tokensMapByBlockchainName[token.blockchain] = [];
37
+ }
38
+ meta.tokensMapByBlockchainName[token.blockchain].push(tokenHash);
39
+ });
40
+
41
+ return meta;
42
+ }
43
+
44
+ describe('matchTokensFromConfigWithMeta', () => {
45
+ test('should include tokens from config.tokens array that exist in meta', () => {
46
+ const mockData: MatchTokensFromConfigWithMetaParam = {
47
+ type: 'source',
48
+ meta: createMetaForMockData(tokens),
49
+ config: {
50
+ blockchains: undefined,
51
+ tokens: [tokens[0], tokens[1]],
52
+ },
53
+ };
54
+
55
+ const result = matchTokensFromConfigWithMeta(mockData);
56
+ expect(result).toEqual([tokens[0], tokens[1]]);
57
+ });
58
+
59
+ test('If config.blockchains is not defined or is empty, we should include all tokens from the meta for every blockchain that does not have tokens specified in the config, as well as tokens specified in the config that exist in the meta.', () => {
60
+ const mockData: MatchTokensFromConfigWithMetaParam = {
61
+ type: 'source',
62
+ meta: createMetaForMockData(tokens),
63
+ config: {
64
+ blockchains: undefined,
65
+ tokens: {
66
+ [BLOCKCHAIN_A]: { tokens: [tokens[0]], isExcluded: false },
67
+ },
68
+ },
69
+ };
70
+
71
+ const runTestWithConfig = (config: {
72
+ blockchains: MatchTokensFromConfigWithMetaParam['config']['blockchains'];
73
+ }) => {
74
+ mockData.config.blockchains = config.blockchains;
75
+ const result = matchTokensFromConfigWithMeta(mockData);
76
+ /**
77
+ * In this case, the result might differ from what was expected.
78
+ * To pass the test, we reordered the expected result.
79
+ * Changing the order of tokens does not impact the overall behavior of our app,
80
+ * as we have a comprehensive sorting logic applied to tokens within the data slice.
81
+ */
82
+ // eslint-disable-next-line @typescript-eslint/no-magic-numbers
83
+ expect(result).toEqual([tokens[2], tokens[3], tokens[0]]);
84
+ };
85
+
86
+ runTestWithConfig({ blockchains: undefined });
87
+ runTestWithConfig({ blockchains: [] });
88
+ });
89
+
90
+ test('should include tokens from config.tokens object that exist in meta', () => {
91
+ const mockData: MatchTokensFromConfigWithMetaParam = {
92
+ type: 'source',
93
+ meta: createMetaForMockData(tokens),
94
+ config: {
95
+ blockchains: [BLOCKCHAIN_A, BLOCKCHAIN_B],
96
+ tokens: {
97
+ [BLOCKCHAIN_A]: { tokens: [tokens[0]], isExcluded: false },
98
+ [BLOCKCHAIN_B]: { tokens: [tokens[2]], isExcluded: false },
99
+ },
100
+ },
101
+ };
102
+
103
+ const result = matchTokensFromConfigWithMeta(mockData);
104
+ expect(result).toEqual([tokens[0], tokens[2]]);
105
+ });
106
+
107
+ test('should not include tokens from config.tokens whose blockchain does not exist in config.blockchains', () => {
108
+ const mockData: MatchTokensFromConfigWithMetaParam = {
109
+ type: 'source',
110
+ meta: createMetaForMockData(tokens),
111
+ config: {
112
+ blockchains: [BLOCKCHAIN_A],
113
+ tokens: {
114
+ [BLOCKCHAIN_B]: { tokens: [tokens[2]], isExcluded: false },
115
+ },
116
+ },
117
+ };
118
+
119
+ const result = matchTokensFromConfigWithMeta(mockData);
120
+ expect(result).toEqual([tokens[0], tokens[1]]);
121
+ });
122
+
123
+ test('should include all tokens from a blockchain if that blockchain does not have any tokens in config.tokens', () => {
124
+ const mockData: MatchTokensFromConfigWithMetaParam = {
125
+ type: 'source',
126
+ meta: createMetaForMockData(tokens),
127
+ config: {
128
+ blockchains: [BLOCKCHAIN_A],
129
+ tokens: {},
130
+ },
131
+ };
132
+
133
+ const result = matchTokensFromConfigWithMeta(mockData);
134
+ expect(result).toEqual([tokens[0], tokens[1]]);
135
+ });
136
+
137
+ test('should include all tokens from config.blockchains that exist in the meta, except tokens excluded in config.token', () => {
138
+ const mockData: MatchTokensFromConfigWithMetaParam = {
139
+ type: 'source',
140
+ meta: createMetaForMockData(tokens),
141
+ config: {
142
+ blockchains: [BLOCKCHAIN_A],
143
+ tokens: {
144
+ [BLOCKCHAIN_A]: { tokens: [tokens[0]], isExcluded: true },
145
+ },
146
+ },
147
+ };
148
+
149
+ const result = matchTokensFromConfigWithMeta(mockData);
150
+ expect(result).toEqual([tokens[1]]);
151
+ });
152
+
153
+ test('should include all tokens from the meta when config parameters are empty values', () => {
154
+ const mockData: MatchTokensFromConfigWithMetaParam = {
155
+ type: 'source',
156
+ meta: createMetaForMockData(tokens),
157
+ config: {
158
+ blockchains: [],
159
+ tokens: {},
160
+ },
161
+ };
162
+
163
+ const runTestWithConfig = (
164
+ config: MatchTokensFromConfigWithMetaParam['config']
165
+ ) => {
166
+ mockData.config = config;
167
+ const result = matchTokensFromConfigWithMeta(mockData);
168
+ expect(result).toEqual(tokens);
169
+ };
170
+
171
+ runTestWithConfig({ blockchains: [], tokens: {} });
172
+ runTestWithConfig({ blockchains: undefined, tokens: undefined });
173
+ runTestWithConfig({ blockchains: [], tokens: [] });
174
+ });
175
+ });
@@ -0,0 +1,117 @@
1
+ import type { BlockchainAndTokenConfig, TokenHash } from '../../types';
2
+ import type { DataSlice } from '../slices/data';
3
+ import type { Asset, Token } from 'rango-sdk';
4
+
5
+ import { createTokenHash } from '../../utils/meta';
6
+
7
+ /**
8
+ * This function retrieves tokens and blockchains from the widget configuration and combines them with token data from the meta response to determine which tokens should be displayed in the widget.
9
+ * To optimize performance, the function minimizes unnecessary iterations over the meta tokens.
10
+ * The result of this function should be cached and recalculated whenever the widget configuration changes.
11
+ */
12
+ export function matchTokensFromConfigWithMeta(params: {
13
+ type: 'source' | 'destination';
14
+ config: {
15
+ blockchains: BlockchainAndTokenConfig['blockchains'];
16
+ tokens: BlockchainAndTokenConfig['tokens'];
17
+ };
18
+ meta: {
19
+ tokensMapByTokenHash: DataSlice['_tokensMapByTokenHash'];
20
+ tokensMapByBlockchainName: DataSlice['_tokensMapByBlockchainName'];
21
+ };
22
+ }): Token[] {
23
+ const { config, meta } = params;
24
+ const result: Record<TokenHash, Token> = {};
25
+ const configTokens = config.tokens;
26
+
27
+ // Helper function to add tokens to result
28
+ const addTokens = (tokens: (Asset | TokenHash)[]) => {
29
+ tokens.forEach((item) => {
30
+ if (typeof item !== 'string') {
31
+ item = createTokenHash(item);
32
+ }
33
+ const tokenFromMeta = meta.tokensMapByTokenHash.get(item);
34
+ if (tokenFromMeta) {
35
+ result[item] = tokenFromMeta;
36
+ }
37
+ });
38
+ };
39
+
40
+ /**
41
+ * We support two types of configurations for passing tokens in the widget config.
42
+ * We check the type of configuration and calculate the result based on that.
43
+ */
44
+ if (Array.isArray(configTokens)) {
45
+ /**
46
+ * If "config.tokens" is an array,
47
+ * we iterate through it and include any token that exists in the meta data in the result.
48
+ */
49
+ if (configTokens.length > 0) {
50
+ addTokens(configTokens);
51
+ return Object.values(result);
52
+ }
53
+ return Array.from(meta.tokensMapByTokenHash.values());
54
+ }
55
+
56
+ /**
57
+ * From this point onward,
58
+ * the function handles the other type of widget configuration.
59
+ */
60
+ if (!configTokens) {
61
+ return Array.from(meta.tokensMapByTokenHash.values());
62
+ }
63
+
64
+ let configBlockchains: string[];
65
+
66
+ if (config.blockchains?.length) {
67
+ configBlockchains = config.blockchains;
68
+ } else {
69
+ /**
70
+ * If config.blockchains does not exist,
71
+ * we include all tokens from every blockchain in the meta data that are not specified in the tokens from the config.
72
+ */
73
+ configBlockchains = Object.keys(configTokens);
74
+ const configBlockchainsSet = new Set(configBlockchains);
75
+
76
+ Object.keys(meta.tokensMapByBlockchainName).forEach((blockchain) => {
77
+ if (!configBlockchainsSet.has(blockchain)) {
78
+ addTokens(meta.tokensMapByBlockchainName[blockchain]);
79
+ }
80
+ });
81
+ }
82
+ //We iterate over each blockchain and retrieve the related tokens for each one from the configuration.
83
+ configBlockchains.forEach((blockchain) => {
84
+ const targetTokensForBlockchain = configTokens[blockchain];
85
+ /**
86
+ * If no tokens exist for a blockchain,
87
+ * we include all tokens for that blockchain from the meta response.
88
+ */
89
+ if (
90
+ !targetTokensForBlockchain &&
91
+ meta.tokensMapByBlockchainName?.[blockchain]
92
+ ) {
93
+ addTokens(meta.tokensMapByBlockchainName[blockchain]);
94
+ return;
95
+ }
96
+
97
+ if (targetTokensForBlockchain) {
98
+ if (targetTokensForBlockchain.isExcluded) {
99
+ /**
100
+ * If tokens are excluded,
101
+ * we first include all tokens from the meta for that blockchain,
102
+ * then remove the excluded tokens from the result.
103
+ */
104
+ addTokens(meta.tokensMapByBlockchainName[blockchain]);
105
+ targetTokensForBlockchain.tokens.forEach((token) => {
106
+ const tokenHash = createTokenHash(token);
107
+ delete result[tokenHash];
108
+ });
109
+ } else {
110
+ //We retrieve the corresponding token from the meta and include it in the result.
111
+ addTokens(targetTokensForBlockchain.tokens);
112
+ }
113
+ }
114
+ });
115
+
116
+ return Object.values(result);
117
+ }
@@ -0,0 +1 @@
1
+ export * from './data';
@@ -1,5 +1,5 @@
1
1
  import type { AppStoreState } from '../store/app';
2
- import type { TokenHash, WidgetConfig } from '../types';
2
+ import type { Blockchain, TokenHash, WidgetConfig } from '../types';
3
3
  import type { BlockchainMeta, EvmBlockchainMeta, Token } from 'rango-sdk';
4
4
 
5
5
  import { faker } from '@faker-js/faker';
@@ -231,13 +231,20 @@ export function createInitialAppStore() {
231
231
  blockchains: blockchains.map((b) => b.name),
232
232
  });
233
233
 
234
+ const tokensMapByTokenHash: Map<TokenHash, Token> = new Map();
235
+ const tokensMapByBlockchainName: Record<Blockchain, TokenHash[]> = {};
236
+ tokens.forEach((token) => {
237
+ const tokenHash = createTokenHash(token);
238
+ if (!tokensMapByBlockchainName[token.blockchain]) {
239
+ tokensMapByBlockchainName[token.blockchain] = [];
240
+ }
241
+ tokensMapByTokenHash.set(tokenHash, token);
242
+ tokensMapByBlockchainName[token.blockchain].push(tokenHash);
243
+ });
244
+
234
245
  return {
235
- _tokensMapByTokenHash: new Map<TokenHash, Token>(
236
- tokens.map((token) => {
237
- const tokenHash = createTokenHash(token);
238
- return [tokenHash, token];
239
- })
240
- ),
246
+ _tokensMapByTokenHash: tokensMapByTokenHash,
247
+ _tokensMapByBlockchainName: tokensMapByBlockchainName,
241
248
  _blockchainsMapByName: new Map<string, BlockchainMeta>(
242
249
  blockchains.map((meta) => [meta.name, meta])
243
250
  ),
@@ -179,6 +179,8 @@ export type TrezorManifest = {
179
179
  * @property {boolean} customDestination - A boolean value indicating whether the user can input a custom
180
180
  * address for the transaction. If set to true, the widget will allow the user to input a custom
181
181
  * address for the destination.
182
+ * @property {{[blockchain: string]: string }} defaultCustomDestinations - The `defaultCustomDestinations` property is a map of supported `blockchain` names to an arbitrary blockchain address.
183
+ * You could use it to set a default custom destination for some blockchains. e.g. {'BSC': '0x2be.....c1','ETH': '0x2be.....c1'}
182
184
  * @property {string} language - The language property is an optional string that specifies the
183
185
  * default language in which the widget should be displayed. If not provided, the widget will default to the
184
186
  * language of the user's browser.
@@ -219,6 +221,7 @@ export type WidgetConfig = {
219
221
  wallets?: (WalletType | ProviderInterface)[];
220
222
  multiWallets?: boolean;
221
223
  customDestination?: boolean;
224
+ defaultCustomDestinations?: { [blockchain: string]: string };
222
225
  language?: Language;
223
226
  theme?: WidgetTheme;
224
227
  externalWallets?: boolean;
@@ -227,7 +230,6 @@ export type WidgetConfig = {
227
230
  variant?: WidgetVariant;
228
231
  enableCentralizedSwappers?: boolean;
229
232
  signers?: SignersConfig;
230
-
231
233
  // These are likely to change or remove at anytime. Please use with a caution.
232
234
  __UNSTABLE_OR_INTERNAL__?: {
233
235
  walletConnectListedDesktopWalletLink?: string;
@@ -7,6 +7,17 @@ import type {
7
7
 
8
8
  import { WidgetEvents as QueueManagerEvents } from '@rango-dev/queue-manager-rango-preset';
9
9
 
10
+ type EventData<
11
+ T extends QuoteEventTypes | WalletEventTypes | UiEventTypes,
12
+ U extends Record<string, unknown> | null
13
+ > = { type: T; payload: U };
14
+
15
+ export type PreventableEventPayload<
16
+ T extends Record<string, unknown> = Record<string, unknown>
17
+ > = {
18
+ preventDefault: () => void;
19
+ } & T;
20
+
10
21
  type Account = Wallet;
11
22
 
12
23
  export enum QuoteEventTypes {
@@ -19,6 +30,14 @@ export enum WalletEventTypes {
19
30
  DISCONNECT = 'disconnect',
20
31
  }
21
32
 
33
+ /**
34
+ * We use a prefix for interaction type and a name for defining the event name: INTERACTION_EVENT_NAME
35
+ * e.g. CLICK_X_BUTTON or NAVIGATE_WALLET_PAGE
36
+ */
37
+ export enum UiEventTypes {
38
+ CLICK_CONNECT_WALLET = 'clickConnectWallet',
39
+ }
40
+
22
41
  export type QuoteInputUpdateEventPayload = {
23
42
  fromBlockchain?: string;
24
43
  toBlockchain?: string;
@@ -41,31 +60,27 @@ export type DisconnectWalletEventPayload = {
41
60
  walletType: string;
42
61
  };
43
62
 
63
+ export type ClickConnectWalletPayload = PreventableEventPayload;
64
+
44
65
  export type QuoteEventData =
45
- | {
46
- type: QuoteEventTypes.QUOTE_INPUT_UPDATE;
47
- payload: QuoteInputUpdateEventPayload;
48
- }
49
- | {
50
- type: QuoteEventTypes.QUOTE_OUTPUT_UPDATE;
51
- payload: QuoteUpdateEventPayload;
52
- };
66
+ | EventData<QuoteEventTypes.QUOTE_INPUT_UPDATE, QuoteInputUpdateEventPayload>
67
+ | EventData<QuoteEventTypes.QUOTE_OUTPUT_UPDATE, QuoteUpdateEventPayload>;
53
68
 
54
69
  export type WalletEventData =
55
- | {
56
- type: WalletEventTypes.CONNECT;
57
- payload: ConnectWalletEventPayload;
58
- }
59
- | {
60
- type: WalletEventTypes.DISCONNECT;
61
- payload: DisconnectWalletEventPayload;
62
- };
70
+ | EventData<WalletEventTypes.CONNECT, ConnectWalletEventPayload>
71
+ | EventData<WalletEventTypes.DISCONNECT, DisconnectWalletEventPayload>;
72
+
73
+ export type UiEventData = EventData<
74
+ UiEventTypes.CLICK_CONNECT_WALLET,
75
+ ClickConnectWalletPayload
76
+ >;
63
77
 
64
78
  export enum WidgetEvents {
65
79
  RouteEvent = QueueManagerEvents.RouteEvent,
66
80
  StepEvent = QueueManagerEvents.StepEvent,
67
81
  QuoteEvent = 'quoteEvent',
68
82
  WalletEvent = 'walletEvent',
83
+ UiEvent = 'uiEvent',
69
84
  }
70
85
 
71
86
  export type Events = {
@@ -73,6 +88,7 @@ export type Events = {
73
88
  [WidgetEvents.StepEvent]: StepEventData;
74
89
  [WidgetEvents.QuoteEvent]: QuoteEventData;
75
90
  [WidgetEvents.WalletEvent]: WalletEventData;
91
+ [WidgetEvents.UiEvent]: UiEventData;
76
92
  };
77
93
 
78
94
  declare type EventHandler<T = unknown> = (event: T) => void;
@@ -6,6 +6,7 @@ import {
6
6
  } from '@rango-dev/queue-manager-rango-preset';
7
7
 
8
8
  type NotificationRoute = {
9
+ creationTime: number;
9
10
  from: {
10
11
  blockchain: Step['fromBlockchain'];
11
12
  symbol: Step['fromSymbol'];
@@ -3,6 +3,7 @@
3
3
  import type { PriceImpactWarningLevel } from '@rango-dev/ui';
4
4
  import type BigNumber from 'bignumber.js';
5
5
  import type {
6
+ Asset,
6
7
  BlockchainValidationStatus,
7
8
  RouteTag,
8
9
  RoutingResultType,
@@ -131,3 +132,13 @@ export type QuoteErrorResponse = {
131
132
  message: string;
132
133
  options: QuoteError;
133
134
  };
135
+
136
+ export type QuoteInputs = {
137
+ fromBlockchain: string | null;
138
+ toBlockchain: string | null;
139
+ fromToken: Asset | null;
140
+ toToken: Asset | null;
141
+ requestAmount: string;
142
+ };
143
+
144
+ export type UpdateQuoteInputs = (params: Partial<QuoteInputs>) => void;
@@ -13,7 +13,7 @@ export type Balance = {
13
13
  usdValue: string;
14
14
  };
15
15
 
16
- type Blockchain = string;
16
+ export type Blockchain = string;
17
17
  type TokenSymbol = string;
18
18
  type Address = string;
19
19
 
@@ -0,0 +1,27 @@
1
+ import { eventEmitter } from '../services/eventEmitter';
2
+ import { type UiEventData, WidgetEvents } from '../types';
3
+
4
+ type UiEvent = {
5
+ type: UiEventData['type'];
6
+ payload?: Omit<UiEventData['payload'], 'preventDefault'>;
7
+ };
8
+
9
+ export function emitPreventableEvent(event: UiEvent, action: () => void): void {
10
+ let defaultPrevented = false;
11
+
12
+ const extendedPayload = {
13
+ preventDefault() {
14
+ defaultPrevented = true;
15
+ },
16
+ ...(event.payload === undefined && { payload: event.payload }),
17
+ };
18
+
19
+ eventEmitter.emit(WidgetEvents.UiEvent, {
20
+ type: event.type,
21
+ payload: extendedPayload,
22
+ });
23
+
24
+ if (!defaultPrevented) {
25
+ action();
26
+ }
27
+ }
package/src/utils/swap.ts CHANGED
@@ -722,7 +722,7 @@ export function shouldRetrySwap(pendingSwap: PendingSwap) {
722
722
 
723
723
  export function isConfirmSwapDisabled(
724
724
  fetching: boolean,
725
- showCustomDestination: boolean,
725
+ isCustomDestinationOpen: boolean,
726
726
  customDestination: string | null,
727
727
  quote: SelectedQuote | null,
728
728
  selectedWallets: { walletType: string; chain: string }[],
@@ -752,9 +752,9 @@ export function isConfirmSwapDisabled(
752
752
  : false;
753
753
 
754
754
  return (
755
- (!showCustomDestination && !everyWalletSelected) ||
756
- (showCustomDestination && !customDestination) ||
757
- (showCustomDestination &&
755
+ (!isCustomDestinationOpen && !everyWalletSelected) ||
756
+ (isCustomDestinationOpen && !customDestination) ||
757
+ (isCustomDestinationOpen &&
758
758
  !!customDestination &&
759
759
  (!customDestinationIsValid || !everyRequiredWalletSelected))
760
760
  );