@rango-dev/widget-embedded 0.40.2-next.1 → 0.40.2-next.11

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 (122) hide show
  1. package/dist/components/CustomTokenModal/CustomTokenModal.constants.d.ts +2 -0
  2. package/dist/components/CustomTokenModal/CustomTokenModal.constants.d.ts.map +1 -0
  3. package/dist/components/CustomTokenModal/CustomTokenModal.d.ts.map +1 -1
  4. package/dist/components/CustomTokenModal/CustomTokenModal.types.d.ts +1 -0
  5. package/dist/components/CustomTokenModal/CustomTokenModal.types.d.ts.map +1 -1
  6. package/dist/components/ImportCustomToken/ImportCustomToken.d.ts +4 -0
  7. package/dist/components/ImportCustomToken/ImportCustomToken.d.ts.map +1 -0
  8. package/dist/components/ImportCustomToken/ImportCustomToken.types.d.ts +15 -0
  9. package/dist/components/ImportCustomToken/ImportCustomToken.types.d.ts.map +1 -0
  10. package/dist/components/ImportCustomToken/index.d.ts +2 -0
  11. package/dist/components/ImportCustomToken/index.d.ts.map +1 -0
  12. package/dist/components/Layout/Layout.d.ts.map +1 -1
  13. package/dist/components/Layout/Layout.styles.d.ts +161 -0
  14. package/dist/components/Layout/Layout.styles.d.ts.map +1 -1
  15. package/dist/components/Quote/Quote.styles.d.ts +2 -2
  16. package/dist/components/SearchInput/SearchInput.d.ts.map +1 -1
  17. package/dist/components/SearchInput/SearchInput.types.d.ts +4 -2
  18. package/dist/components/SearchInput/SearchInput.types.d.ts.map +1 -1
  19. package/dist/components/SwapDetailsModal/SwapDetailsModal.types.d.ts +2 -2
  20. package/dist/components/SwapDetailsModal/SwapDetailsModal.types.d.ts.map +1 -1
  21. package/dist/components/TokenList/TokenList.d.ts.map +1 -1
  22. package/dist/components/TokenList/TokenList.styles.d.ts +323 -1
  23. package/dist/components/TokenList/TokenList.styles.d.ts.map +1 -1
  24. package/dist/components/TokenList/TokenList.types.d.ts +6 -1
  25. package/dist/components/TokenList/TokenList.types.d.ts.map +1 -1
  26. package/dist/constants/customTokens.d.ts +1 -0
  27. package/dist/constants/customTokens.d.ts.map +1 -1
  28. package/dist/containers/Inputs/Inputs.d.ts.map +1 -1
  29. package/dist/hooks/useFetchCustomToken.d.ts +8 -4
  30. package/dist/hooks/useFetchCustomToken.d.ts.map +1 -1
  31. package/dist/hooks/usePrepareBlockchainList/usePrepareBlockchainList.helpers.d.ts +2 -2
  32. package/dist/hooks/useSearchCustomTokens/index.d.ts +2 -0
  33. package/dist/hooks/useSearchCustomTokens/index.d.ts.map +1 -0
  34. package/dist/hooks/useSearchCustomTokens/types.d.ts +9 -0
  35. package/dist/hooks/useSearchCustomTokens/types.d.ts.map +1 -0
  36. package/dist/hooks/useSearchCustomTokens/useSearchCustomTokens.constants.d.ts +2 -0
  37. package/dist/hooks/useSearchCustomTokens/useSearchCustomTokens.constants.d.ts.map +1 -0
  38. package/dist/hooks/useSearchCustomTokens/useSearchCustomTokens.d.ts +3 -0
  39. package/dist/hooks/useSearchCustomTokens/useSearchCustomTokens.d.ts.map +1 -0
  40. package/dist/hooks/useSwapInput.d.ts.map +1 -1
  41. package/dist/hooks/useSyncStoresWithConfig.d.ts.map +1 -1
  42. package/dist/hooks/useSyncUrlAndStore/useSyncUrlAndStore.d.ts.map +1 -1
  43. package/dist/index.js +2 -2
  44. package/dist/index.js.map +4 -4
  45. package/dist/pages/AddCustomTokenPage.d.ts.map +1 -1
  46. package/dist/pages/CustomTokensPage.d.ts.map +1 -1
  47. package/dist/pages/SelectSwapItemPage/SelectSwapItemPage.constants.d.ts +3 -0
  48. package/dist/pages/SelectSwapItemPage/SelectSwapItemPage.constants.d.ts.map +1 -0
  49. package/dist/pages/SelectSwapItemPage/SelectSwapItemPage.helpers.d.ts +4 -0
  50. package/dist/pages/SelectSwapItemPage/SelectSwapItemPage.helpers.d.ts.map +1 -0
  51. package/dist/pages/SelectSwapItemPage/SelectSwapItemsPage.d.ts.map +1 -0
  52. package/dist/pages/SelectSwapItemPage/index.d.ts +2 -0
  53. package/dist/pages/SelectSwapItemPage/index.d.ts.map +1 -0
  54. package/dist/store/app.d.ts +1 -1
  55. package/dist/store/app.d.ts.map +1 -1
  56. package/dist/store/quote.d.ts +9 -6
  57. package/dist/store/quote.d.ts.map +1 -1
  58. package/dist/store/slices/settings.d.ts +4 -2
  59. package/dist/store/slices/settings.d.ts.map +1 -1
  60. package/dist/store/slices/wallets.d.ts +5 -1
  61. package/dist/store/slices/wallets.d.ts.map +1 -1
  62. package/dist/store/utils/wallets.d.ts +2 -1
  63. package/dist/store/utils/wallets.d.ts.map +1 -1
  64. package/dist/types/config.d.ts +5 -0
  65. package/dist/types/config.d.ts.map +1 -1
  66. package/dist/types/wallets.d.ts +1 -0
  67. package/dist/types/wallets.d.ts.map +1 -1
  68. package/dist/utils/quote.d.ts +1 -1
  69. package/dist/utils/quote.d.ts.map +1 -1
  70. package/dist/utils/settings.d.ts +1 -1
  71. package/dist/utils/swap.d.ts +1 -0
  72. package/dist/utils/swap.d.ts.map +1 -1
  73. package/dist/utils/wallets.d.ts.map +1 -1
  74. package/dist/widget-embedded.build.json +1 -1
  75. package/package.json +12 -12
  76. package/src/components/AppRoutes.tsx +1 -1
  77. package/src/components/CustomTokenModal/CustomTokenModal.constants.ts +2 -0
  78. package/src/components/CustomTokenModal/CustomTokenModal.tsx +26 -5
  79. package/src/components/CustomTokenModal/CustomTokenModal.types.ts +1 -0
  80. package/src/components/ImportCustomToken/ImportCustomToken.tsx +121 -0
  81. package/src/components/ImportCustomToken/ImportCustomToken.types.ts +18 -0
  82. package/src/components/ImportCustomToken/index.ts +1 -0
  83. package/src/components/Layout/Layout.styles.ts +12 -0
  84. package/src/components/Layout/Layout.tsx +37 -5
  85. package/src/components/NotificationContent/NotificationContent.tsx +2 -2
  86. package/src/components/Quote/QuoteTrigger.tsx +5 -5
  87. package/src/components/QuoteSkeleton/QuoteSummarySkeleton.tsx +2 -2
  88. package/src/components/QuoteSkeleton/StepSkeleton.tsx +2 -2
  89. package/src/components/SearchInput/SearchInput.tsx +13 -10
  90. package/src/components/SearchInput/SearchInput.types.ts +4 -2
  91. package/src/components/SwapDetailsModal/SwapDetailsModal.types.ts +2 -2
  92. package/src/components/TokenList/TokenList.styles.ts +40 -0
  93. package/src/components/TokenList/TokenList.tsx +134 -22
  94. package/src/components/TokenList/TokenList.types.ts +5 -1
  95. package/src/constants/customTokens.ts +3 -0
  96. package/src/containers/Inputs/Inputs.tsx +6 -4
  97. package/src/hooks/useFetchCustomToken.ts +39 -16
  98. package/src/hooks/useSearchCustomTokens/index.ts +1 -0
  99. package/src/hooks/useSearchCustomTokens/types.ts +9 -0
  100. package/src/hooks/useSearchCustomTokens/useSearchCustomTokens.constants.ts +1 -0
  101. package/src/hooks/useSearchCustomTokens/useSearchCustomTokens.ts +81 -0
  102. package/src/hooks/useSwapInput.ts +9 -2
  103. package/src/hooks/useSyncStoresWithConfig.ts +2 -3
  104. package/src/hooks/useSyncUrlAndStore/useSyncUrlAndStore.ts +1 -3
  105. package/src/pages/AddCustomTokenPage.tsx +24 -94
  106. package/src/pages/CustomTokensPage.tsx +1 -0
  107. package/src/pages/SelectSwapItemPage/SelectSwapItemPage.constants.ts +2 -0
  108. package/src/pages/SelectSwapItemPage/SelectSwapItemPage.helpers.ts +51 -0
  109. package/src/pages/{SelectSwapItemsPage.tsx → SelectSwapItemPage/SelectSwapItemsPage.tsx} +54 -13
  110. package/src/pages/SelectSwapItemPage/index.ts +1 -0
  111. package/src/store/app.ts +12 -1
  112. package/src/store/quote.ts +6 -3
  113. package/src/store/slices/settings.ts +11 -4
  114. package/src/store/slices/wallets.ts +106 -2
  115. package/src/store/utils/wallets.ts +31 -2
  116. package/src/types/config.ts +5 -0
  117. package/src/types/wallets.ts +1 -0
  118. package/src/utils/quote.ts +1 -1
  119. package/src/utils/swap.ts +3 -0
  120. package/src/utils/wallets.ts +9 -2
  121. package/dist/pages/SelectSwapItemsPage.d.ts.map +0 -1
  122. /package/dist/pages/{SelectSwapItemsPage.d.ts → SelectSwapItemPage/SelectSwapItemsPage.d.ts} +0 -0
@@ -5,25 +5,21 @@ import {
5
5
  darkTheme,
6
6
  Divider,
7
7
  DoneIcon,
8
- MessageBox,
9
8
  styled,
10
9
  TextField,
11
10
  Typography,
12
11
  } from '@rango-dev/ui';
13
- import { type Token } from 'rango-sdk';
14
- import React, { useEffect, useState } from 'react';
12
+ import React, { useState } from 'react';
15
13
  import { useNavigate, useSearchParams } from 'react-router-dom';
16
14
 
17
15
  import { BlockchainSelectorButton } from '../components/BlockchainSelectorButton';
18
- import { WatermarkedModal } from '../components/common/WatermarkedModal';
19
- import { CustomTokenModal } from '../components/CustomTokenModal';
16
+ import { ImportCustomToken } from '../components/ImportCustomToken';
20
17
  import { Layout, PageContainer } from '../components/Layout';
21
18
  import { navigationRoutes } from '../constants/navigationRoutes';
22
19
  import { SearchParams } from '../constants/searchParams';
23
20
  import { useFetchCustomToken } from '../hooks/useFetchCustomToken';
24
21
  import { useNavigateBack } from '../hooks/useNavigateBack';
25
22
  import { useAppStore } from '../store/AppStore';
26
- import { getContainer } from '../utils/common';
27
23
  import { findBlockchain, isValidTokenAddress } from '../utils/meta';
28
24
 
29
25
  const Content = styled('div', {
@@ -41,68 +37,31 @@ const Content = styled('div', {
41
37
  height: '$40',
42
38
  },
43
39
  });
44
- const CUSTOM_TOKEN_REFRESH_DELAY = 1000;
45
40
 
46
41
  export function AddCustomTokenPage() {
47
42
  const navigate = useNavigate();
48
43
  const [searchParams] = useSearchParams();
49
44
  const navigateBack = useNavigateBack();
50
- const { setCustomToken } = useAppStore();
51
45
  const blockchains = useAppStore().blockchains();
52
46
 
53
47
  const blockchainName = searchParams.get(SearchParams.BLOCKCHAIN) || '';
54
48
  const blockchain = findBlockchain(blockchainName, blockchains);
55
49
  const [address, setAddress] = useState('');
56
- const [isOpenErrorModal, setIsOpenErrorModal] = useState<boolean>(false);
57
- const [token, setToken] = useState<Token>();
58
- const { fetchCustomToken, loading, error } = useFetchCustomToken();
59
- const [networkError, setNetworkError] = useState('');
50
+ const { fetchCustomToken, token, loading, error, resetState } =
51
+ useFetchCustomToken();
60
52
  const isValidAddress =
61
53
  !!blockchain && isValidTokenAddress(blockchain, address);
62
54
  const isImportDisabled = !blockchain || !address || !isValidAddress;
63
55
 
64
- const getCustomToken = async () => {
56
+ const getCustomToken = () => {
65
57
  if (blockchain) {
66
- try {
67
- const res = await fetchCustomToken({
68
- blockchain: blockchainName,
69
- tokenAddress: address,
70
- });
71
- setNetworkError('');
72
- if (!!res) {
73
- setToken(res);
74
- }
75
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
76
- } catch (error: any) {
77
- if (error.message === 'Failed to fetch') {
78
- setNetworkError(i18n.t('Network Error'));
79
- setIsOpenErrorModal(true);
80
- }
81
- }
58
+ void fetchCustomToken({
59
+ blockchain: blockchainName,
60
+ tokenAddress: address,
61
+ });
82
62
  }
83
63
  };
84
64
 
85
- const closeErrorModal = () => {
86
- if (!!error) {
87
- setAddress('');
88
- }
89
- setIsOpenErrorModal(false);
90
- };
91
-
92
- const handleErrorModalButtonClick = async () => {
93
- if (!!networkError) {
94
- setTimeout(async () => {
95
- await getCustomToken();
96
- }, CUSTOM_TOKEN_REFRESH_DELAY);
97
- }
98
- closeErrorModal();
99
- };
100
-
101
- useEffect(() => {
102
- if (!!error) {
103
- setIsOpenErrorModal(true);
104
- }
105
- }, [error]);
106
65
  return (
107
66
  <Layout
108
67
  header={{
@@ -164,50 +123,21 @@ export function AddCustomTokenPage() {
164
123
  {i18n.t('Import')}
165
124
  </Button>
166
125
  </Content>
167
- <WatermarkedModal
168
- open={isOpenErrorModal}
169
- dismissible
170
- onClose={closeErrorModal}
171
- container={getContainer()}>
172
- <MessageBox
173
- title={error?.title || networkError}
174
- type="error"
175
- description={
176
- error?.message || i18n.t('Failed Network, Please retry.')
177
- }>
178
- <Divider size={40} />
179
- <Divider size={10} />
180
- {/* eslint-disable-next-line jsx-id-attribute-enforcement/missing-ids */}
181
- <Button
182
- id={`widget-add-custom-token-${
183
- networkError ? 'retry' : 'add-another'
184
- }-btn`}
185
- variant="contained"
186
- size="large"
187
- type="primary"
188
- fullWidth
189
- onClick={handleErrorModalButtonClick}>
190
- {networkError
191
- ? i18n.t('Retry')
192
- : i18n.t('Add another custom token')}
193
- </Button>
194
- </MessageBox>
195
- </WatermarkedModal>
196
- {blockchain && token && (
197
- <CustomTokenModal
198
- blockchain={blockchain}
199
- token={token}
200
- onSubmitClick={() => {
201
- if (token) {
202
- setCustomToken(token);
203
- setToken(undefined);
204
- navigateBack();
205
- }
206
- }}
207
- onClose={() => setToken(undefined)}
208
- open={!!blockchain && !!token}
209
- />
210
- )}
126
+ <ImportCustomToken
127
+ token={token}
128
+ blockchain={blockchain ?? undefined}
129
+ address={address}
130
+ error={error ?? undefined}
131
+ fetchCustomToken={fetchCustomToken}
132
+ onCloseErrorModal={() => {
133
+ if (error?.type !== 'network-error') {
134
+ setAddress('');
135
+ }
136
+ }}
137
+ onImport={navigateBack}
138
+ onExitErrorModal={resetState}
139
+ onExitImportModal={resetState}
140
+ />
211
141
  </PageContainer>
212
142
  </Layout>
213
143
  );
@@ -108,6 +108,7 @@ export function CustomTokensPage() {
108
108
  type="custom-token"
109
109
  searchedFor={searchedFor}
110
110
  showTitle={false}
111
+ showWarning={false}
111
112
  action={(token) => (
112
113
  <DeleteIconButton
113
114
  id="widget-custom-token-delete-icon-btn"
@@ -0,0 +1,2 @@
1
+ export const MAX_TOKENS_BEFORE_FETCH = 20;
2
+ export const MIN_SEARCH_LENGTH = 3;
@@ -0,0 +1,51 @@
1
+ import type { TokenList } from '../../components/TokenList';
2
+ import type { Token } from 'rango-sdk';
3
+ import type { ComponentProps } from 'react';
4
+
5
+ import {
6
+ MAX_TOKENS_BEFORE_FETCH,
7
+ MIN_SEARCH_LENGTH,
8
+ } from './SelectSwapItemPage.constants';
9
+
10
+ export function shouldSearchForCustomTokens(
11
+ metaSearchResultTokens: Token[],
12
+ query: string,
13
+ blockchain?: string
14
+ ) {
15
+ if (
16
+ blockchain &&
17
+ metaSearchResultTokens.length === 1 &&
18
+ metaSearchResultTokens[0].address === query
19
+ ) {
20
+ return false;
21
+ }
22
+ return (
23
+ metaSearchResultTokens.length < MAX_TOKENS_BEFORE_FETCH &&
24
+ query.trim().length >= MIN_SEARCH_LENGTH
25
+ );
26
+ }
27
+
28
+ export function prepareTokensList(
29
+ tokens: Token[],
30
+ customTokens: Token[],
31
+ query: string,
32
+ loading: boolean,
33
+ blockchain?: string
34
+ ) {
35
+ let modifiedList: ComponentProps<typeof TokenList>['list'] = [...tokens];
36
+
37
+ if (shouldSearchForCustomTokens(tokens, query, blockchain)) {
38
+ modifiedList = loading
39
+ ? [...modifiedList, 'skeleton', 'skeleton', 'skeleton']
40
+ : [
41
+ ...modifiedList,
42
+ ...customTokens.map((customToken) => ({
43
+ ...customToken,
44
+ customToken: true,
45
+ warning: true,
46
+ })),
47
+ ];
48
+ }
49
+
50
+ return modifiedList;
51
+ }
@@ -1,18 +1,24 @@
1
1
  import type { BlockchainMeta, Token } from 'rango-sdk';
2
2
 
3
3
  import { i18n } from '@lingui/core';
4
- import { Divider } from '@rango-dev/ui';
5
- import React, { useState } from 'react';
4
+ import { Divider, Spinner } from '@rango-dev/ui';
5
+ import React, { useEffect, useState } from 'react';
6
6
  import { useNavigate } from 'react-router-dom';
7
7
 
8
- import { BlockchainsSection } from '../components/BlockchainsSection';
9
- import { Layout, PageContainer } from '../components/Layout';
10
- import { SearchInput } from '../components/SearchInput';
11
- import { TokenList } from '../components/TokenList';
12
- import { navigationRoutes } from '../constants/navigationRoutes';
13
- import { useNavigateBack } from '../hooks/useNavigateBack';
14
- import { useAppStore } from '../store/AppStore';
15
- import { useQuoteStore } from '../store/quote';
8
+ import { BlockchainsSection } from '../../components/BlockchainsSection';
9
+ import { Layout, PageContainer } from '../../components/Layout';
10
+ import { SearchInput } from '../../components/SearchInput';
11
+ import { TokenList } from '../../components/TokenList';
12
+ import { navigationRoutes } from '../../constants/navigationRoutes';
13
+ import { useNavigateBack } from '../../hooks/useNavigateBack';
14
+ import { useSearchCustomTokens } from '../../hooks/useSearchCustomTokens';
15
+ import { useAppStore } from '../../store/AppStore';
16
+ import { useQuoteStore } from '../../store/quote';
17
+
18
+ import {
19
+ prepareTokensList,
20
+ shouldSearchForCustomTokens,
21
+ } from './SelectSwapItemPage.helpers';
16
22
 
17
23
  interface PropTypes {
18
24
  type: 'source' | 'destination';
@@ -31,6 +37,12 @@ export function SelectSwapItemsPage(props: PropTypes) {
31
37
  setToBlockchain,
32
38
  } = useQuoteStore();
33
39
  const { getBalanceFor } = useAppStore();
40
+ const {
41
+ fetch,
42
+ loading,
43
+ tokens: customTokens,
44
+ cancel,
45
+ } = useSearchCustomTokens();
34
46
  const [searchedFor, setSearchedFor] = useState<string>('');
35
47
 
36
48
  const selectedBlockchain = type === 'source' ? fromBlockchain : toBlockchain;
@@ -47,6 +59,14 @@ export function SelectSwapItemsPage(props: PropTypes) {
47
59
  getBalanceFor: getBalanceFor,
48
60
  });
49
61
 
62
+ const modifiedTokens = prepareTokensList(
63
+ tokens,
64
+ customTokens,
65
+ searchedFor,
66
+ loading,
67
+ selectedBlockchain?.name
68
+ );
69
+
50
70
  const updateBlockchain = (blockchain: BlockchainMeta) => {
51
71
  if (type === 'source') {
52
72
  setFromBlockchain(blockchain);
@@ -57,9 +77,9 @@ export function SelectSwapItemsPage(props: PropTypes) {
57
77
 
58
78
  const updateToken = (token: Token) => {
59
79
  if (type === 'source') {
60
- setFromToken({ token, meta: { blockchains, tokens } });
80
+ setFromToken({ token, meta: { blockchains } });
61
81
  } else {
62
- setToToken({ token, meta: { blockchains, tokens } });
82
+ setToToken({ token, meta: { blockchains } });
63
83
  }
64
84
  };
65
85
  const types = {
@@ -67,6 +87,18 @@ export function SelectSwapItemsPage(props: PropTypes) {
67
87
  destination: i18n.t('Destination'),
68
88
  };
69
89
 
90
+ useEffect(() => {
91
+ if (
92
+ shouldSearchForCustomTokens(tokens, searchedFor, selectedBlockchain?.name)
93
+ ) {
94
+ fetch(searchedFor, selectedBlockchain?.name ?? undefined);
95
+ }
96
+
97
+ return () => {
98
+ cancel();
99
+ };
100
+ }, [tokens.length, searchedFor, selectedBlockchain?.name]);
101
+
70
102
  return (
71
103
  <Layout
72
104
  header={{
@@ -92,10 +124,19 @@ export function SelectSwapItemsPage(props: PropTypes) {
92
124
  size="large"
93
125
  setValue={() => setSearchedFor('')}
94
126
  onChange={(event) => setSearchedFor(event.target.value)}
127
+ suffix={
128
+ shouldSearchForCustomTokens(
129
+ tokens,
130
+ searchedFor,
131
+ selectedBlockchain?.name
132
+ ) && loading ? (
133
+ <Spinner size={12} color="secondary" />
134
+ ) : undefined
135
+ }
95
136
  />
96
137
  <Divider size={16} />
97
138
  <TokenList
98
- list={tokens}
139
+ list={modifiedTokens}
99
140
  selectedBlockchain={selectedBlockchainName}
100
141
  searchedFor={searchedFor}
101
142
  type={type}
@@ -0,0 +1 @@
1
+ export { SelectSwapItemsPage } from './SelectSwapItemsPage';
package/src/store/app.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { AppStoreState } from './slices/types';
2
1
  import type { WidgetConfig } from '../types';
2
+ import type { AppStoreState } from './slices/types';
3
3
  import type { StateCreator } from 'zustand';
4
4
 
5
5
  import { create } from 'zustand';
@@ -48,6 +48,17 @@ export function createAppStore(initialData?: WidgetConfig) {
48
48
  disabledLiquiditySources: state.disabledLiquiditySources,
49
49
  };
50
50
  },
51
+ version: 1,
52
+ migrate: (persistedState, version) => {
53
+ const state = persistedState as AppStoreState;
54
+ if (version === 0) {
55
+ state._customTokens = state._customTokens.map((token) => ({
56
+ ...token,
57
+ warning: true,
58
+ }));
59
+ }
60
+ return state;
61
+ },
51
62
  }
52
63
  )
53
64
  );
@@ -1,3 +1,4 @@
1
+ import type { TokenData } from '../components/TokenList/TokenList.types';
1
2
  import type {
2
3
  BlockchainMeta,
3
4
  MetaResponse,
@@ -35,7 +36,9 @@ export const getUsdValue = (
35
36
 
36
37
  export type Meta = Pick<MetaResponse, 'blockchains' | 'tokens'>;
37
38
 
38
- export type SetTokenParams = { token: Token; meta: Meta } | { token: null };
39
+ export type SetTokenParams =
40
+ | { token: TokenData; meta: { blockchains: BlockchainMeta[] } }
41
+ | { token: null };
39
42
 
40
43
  type SomeQuoteState = {
41
44
  quotes: MultiRouteResponse | null;
@@ -58,9 +61,9 @@ export interface QuoteState {
58
61
  inputUsdValue: BigNumber | null;
59
62
  outputAmount: BigNumber | null;
60
63
  outputUsdValue: BigNumber | null;
61
- fromToken: Token | null;
64
+ fromToken: TokenData | null;
62
65
  sortStrategy: PreferenceType;
63
- toToken: Token | null;
66
+ toToken: TokenData | null;
64
67
  quoteWalletsConfirmed: boolean;
65
68
  selectedWallets: Wallet[];
66
69
  quoteWarningsConfirmed: boolean;
@@ -1,5 +1,7 @@
1
1
  import type { ConfigSlice } from './config';
2
2
  import type { DataSlice } from './data';
3
+ import type { WalletsSlice } from './wallets';
4
+ import type { TokenData } from '../../components/TokenList/TokenList.types';
3
5
  import type { WidgetConfig } from '../../types';
4
6
  import type { SwapperMeta, Token } from 'rango-sdk';
5
7
  import type { StateCreator } from 'zustand';
@@ -46,13 +48,13 @@ export interface SettingsSlice {
46
48
  ) => void;
47
49
  addPreferredBlockchain: (blockchain: string) => void;
48
50
  updateSettings: (config: WidgetConfig) => void;
49
- setCustomToken: (token: Token) => void;
51
+ setCustomToken: (token: TokenData) => void;
50
52
  deleteCustomToken: (token: Token) => void;
51
53
  customTokens: () => Token[];
52
54
  }
53
55
 
54
56
  export const createSettingsSlice: StateCreator<
55
- SettingsSlice & DataSlice & ConfigSlice,
57
+ SettingsSlice & DataSlice & ConfigSlice & WalletsSlice,
56
58
  [],
57
59
  [],
58
60
  SettingsSlice
@@ -186,10 +188,15 @@ export const createSettingsSlice: StateCreator<
186
188
  }),
187
189
  });
188
190
  },
189
- setCustomToken: (token) =>
191
+ setCustomToken: (token) => {
192
+ void get().fetchCustomTokensBalance({
193
+ tokens: [token],
194
+ connectedWallets: get().connectedWallets,
195
+ });
190
196
  set((state) => ({
191
197
  _customTokens: [token, ...state._customTokens],
192
- })),
198
+ }));
199
+ },
193
200
  deleteCustomToken: (token) =>
194
201
  set((state) => ({
195
202
  _customTokens: state._customTokens.filter(
@@ -1,6 +1,6 @@
1
1
  import type { AppStoreState } from './types';
2
2
  import type { Namespace } from '@rango-dev/wallets-core/namespaces/common';
3
- import type { Token, WalletDetail } from 'rango-sdk';
3
+ import type { Asset, Token, WalletDetail } from 'rango-sdk';
4
4
 
5
5
  import BigNumber from 'bignumber.js';
6
6
 
@@ -24,6 +24,7 @@ import {
24
24
  extractAssetFromBalanceKey,
25
25
  removeBalanceFromAggregatedBalance,
26
26
  updateAggregatedBalanceStateForNewAccount,
27
+ updateBalancesWithNewPrices,
27
28
  } from '../utils/wallets';
28
29
 
29
30
  type WalletAddress = string;
@@ -124,6 +125,10 @@ export interface WalletsSlice {
124
125
  accounts: Wallet[],
125
126
  options?: { retryOnFailedBalances?: boolean }
126
127
  ) => Promise<void>;
128
+ fetchCustomTokensBalance: (params: {
129
+ tokens: Asset[];
130
+ connectedWallets: Wallet[];
131
+ }) => Promise<void>;
127
132
  getBalanceFor: (token: Token) => Balance | null;
128
133
  getBalances: () => BalanceState;
129
134
  getBalancesForWalletAddress: (address: string) => BalanceState;
@@ -259,6 +264,7 @@ export const createWalletsSlice = keepLastUpdated<AppStoreState, WalletsSlice>(
259
264
  return {
260
265
  address: account.address,
261
266
  chain: account.chain,
267
+ isContractWallet: account.isContractWallet,
262
268
  explorerUrl: null,
263
269
  walletType: account.walletType,
264
270
  selected: shouldMarkWalletAsSelected,
@@ -294,6 +300,98 @@ export const createWalletsSlice = keepLastUpdated<AppStoreState, WalletsSlice>(
294
300
  });
295
301
  }
296
302
  },
303
+ fetchCustomTokensBalance: async (params) => {
304
+ const { tokens, connectedWallets } = params;
305
+
306
+ const tokensByBlockchain = tokens.reduce<{ [key: string]: Asset[] }>(
307
+ (acc, asset) => {
308
+ (acc[asset.blockchain] ||= []).push(asset);
309
+ return acc;
310
+ },
311
+ {}
312
+ );
313
+
314
+ const addedWallets = new Set<string>();
315
+
316
+ const tokensByWalletAddress = connectedWallets.reduce<{
317
+ [key: string]: Asset[];
318
+ }>((acc, wallet) => {
319
+ const key = `${wallet.address}-${wallet.chain}`;
320
+ if (addedWallets.has(key)) {
321
+ return acc;
322
+ }
323
+
324
+ addedWallets.add(key);
325
+ if (tokensByBlockchain[wallet.chain]) {
326
+ if (!acc[wallet.address]) {
327
+ acc[wallet.address] = [];
328
+ }
329
+ acc[wallet.address].push(...tokensByBlockchain[wallet.chain]);
330
+ }
331
+ return acc;
332
+ }, {});
333
+
334
+ Object.entries(tokensByWalletAddress).forEach(
335
+ async ([walletAddress, tokens]) => {
336
+ try {
337
+ const { balances } = await httpService().getMultipleTokenBalance({
338
+ assets: tokens.map(({ symbol, address, blockchain }) => ({
339
+ symbol,
340
+ address,
341
+ blockchain,
342
+ })),
343
+ walletAddress,
344
+ });
345
+
346
+ if (balances) {
347
+ let nextBalances: BalanceState = get()._balances;
348
+ let nextAggregatedBalances: AggregatedBalanceState =
349
+ get()._aggregatedBalances;
350
+
351
+ balances.forEach((balance) => {
352
+ if (parseFloat(balance.amount.amount) === 0) {
353
+ return;
354
+ }
355
+
356
+ const WalletDetail = {
357
+ blockChain: balance.asset.blockchain,
358
+ balances: [balance],
359
+ address: walletAddress,
360
+ };
361
+
362
+ updateBalancesWithNewPrices(WalletDetail, nextBalances, get);
363
+
364
+ const balancesForWallet = createBalanceStateForNewAccount(
365
+ WalletDetail,
366
+ get
367
+ );
368
+
369
+ nextAggregatedBalances =
370
+ updateAggregatedBalanceStateForNewAccount(
371
+ nextAggregatedBalances,
372
+ balancesForWallet
373
+ );
374
+
375
+ nextBalances = {
376
+ ...nextBalances,
377
+ ...balancesForWallet,
378
+ };
379
+ });
380
+
381
+ set((state) => ({
382
+ _balances: {
383
+ ...state._balances,
384
+ ...nextBalances,
385
+ },
386
+ _aggregatedBalances: nextAggregatedBalances,
387
+ }));
388
+ }
389
+ } catch (error) {
390
+ console.error(error);
391
+ }
392
+ }
393
+ );
394
+ },
297
395
  setWalletsAsSelected: (wallets) => {
298
396
  const nextConnectedWalletsWithUpdatedSelectedStatus =
299
397
  get().connectedWallets.map((connectedWallet) => {
@@ -331,6 +429,10 @@ export const createWalletsSlice = keepLastUpdated<AppStoreState, WalletsSlice>(
331
429
  get().addConnectedWallet(accounts, namespace);
332
430
 
333
431
  void get().fetchBalances(accounts);
432
+ void get().fetchCustomTokensBalance({
433
+ tokens: get().customTokens(),
434
+ connectedWallets: accounts,
435
+ });
334
436
  },
335
437
  removeBalancesForWallet: (walletType, options) => {
336
438
  let walletsNeedsToBeRemoved = get().connectedWallets.filter(
@@ -561,7 +663,7 @@ export const createWalletsSlice = keepLastUpdated<AppStoreState, WalletsSlice>(
561
663
  }
562
664
  }
563
665
 
564
- let nextBalances: BalanceState = {};
666
+ let nextBalances: BalanceState = get()._balances;
565
667
  let nextAggregatedBalances: AggregatedBalanceState =
566
668
  get()._aggregatedBalances;
567
669
  walletsDetails.forEach((wallet) => {
@@ -569,6 +671,8 @@ export const createWalletsSlice = keepLastUpdated<AppStoreState, WalletsSlice>(
569
671
  return;
570
672
  }
571
673
 
674
+ updateBalancesWithNewPrices(wallet, nextBalances, get);
675
+
572
676
  // Remove old balances for current wallet and blockchain
573
677
  get().removeBalancesForWallet(walletType, {
574
678
  chains: [wallet.blockChain],
@@ -44,8 +44,36 @@ export function extractAssetFromBalanceKey(key: BalanceKey): Asset {
44
44
  };
45
45
  }
46
46
 
47
+ export function updateBalancesWithNewPrices(
48
+ wallet: Omit<WalletDetail, 'failed' | 'explorerUrl'>,
49
+ balanceState: BalanceState,
50
+ store: () => AppStoreState
51
+ ): BalanceState {
52
+ wallet.balances?.forEach((balance) => {
53
+ const usdPrice =
54
+ balance.price ?? store().findToken(balance.asset)?.usdPrice;
55
+ const balancesToUpdate =
56
+ store()._aggregatedBalances[createAssetKey(balance.asset)];
57
+
58
+ balancesToUpdate?.forEach((balanceKey) => {
59
+ if (balanceState[balanceKey]) {
60
+ balanceState[balanceKey] = {
61
+ ...balanceState[balanceKey],
62
+ usdValue: usdPrice
63
+ ? new BigNumber(usdPrice ?? ZERO)
64
+ .multipliedBy(balanceState[balanceKey].amount)
65
+ .toString()
66
+ : '',
67
+ };
68
+ }
69
+ });
70
+ });
71
+
72
+ return balanceState;
73
+ }
74
+
47
75
  export function createBalanceStateForNewAccount(
48
- account: WalletDetail,
76
+ account: Omit<WalletDetail, 'failed' | 'explorerUrl'>,
49
77
  store: () => AppStoreState
50
78
  ): BalanceState {
51
79
  const state: BalanceState = {};
@@ -55,7 +83,8 @@ export function createBalanceStateForNewAccount(
55
83
  const amount = accountBalance.amount.amount;
56
84
  const decimals = accountBalance.amount.decimals;
57
85
 
58
- const usdPrice = store().findToken(accountBalance.asset)?.usdPrice;
86
+ const usdPrice =
87
+ accountBalance.price ?? store().findToken(accountBalance.asset)?.usdPrice;
59
88
  const usdValue = usdPrice
60
89
  ? new BigNumber(usdPrice ?? ZERO).multipliedBy(amount).toString()
61
90
  : '';
@@ -2,6 +2,7 @@ import type { Language, theme } from '@rango-dev/ui';
2
2
  import type { LegacyProviderInterface } from '@rango-dev/wallets-core/dist/legacy/mod';
3
3
  import type { WalletType } from '@rango-dev/wallets-shared';
4
4
  import type { Asset } from 'rango-sdk';
5
+ import type { ReactElement } from 'react';
5
6
 
6
7
  export type WidgetVariant = 'default' | 'expanded' | 'full-expanded';
7
8
 
@@ -273,6 +274,10 @@ export type WidgetConfig = {
273
274
  __UNSTABLE_OR_INTERNAL__?: {
274
275
  walletConnectListedDesktopWalletLink?: string;
275
276
  autoUpdateSettings?: boolean; // If true, settings will be updated automatically based on the configuration.
277
+ swapBoxBanner?: {
278
+ element: ReactElement;
279
+ routes?: string[];
280
+ };
276
281
  };
277
282
  routing?: Routing;
278
283
  };
@@ -5,6 +5,7 @@ export interface Wallet {
5
5
  chain: string;
6
6
  address: string;
7
7
  walletType: WalletType;
8
+ isContractWallet?: boolean;
8
9
  }
9
10
 
10
11
  export type Balance = {