@rango-dev/widget-embedded 0.40.2-next.9 → 0.41.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 (104) 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/Quote/Quote.styles.d.ts +2 -2
  13. package/dist/components/SearchInput/SearchInput.d.ts.map +1 -1
  14. package/dist/components/SearchInput/SearchInput.types.d.ts +4 -2
  15. package/dist/components/SearchInput/SearchInput.types.d.ts.map +1 -1
  16. package/dist/components/SwapDetailsModal/SwapDetailsModal.types.d.ts +2 -2
  17. package/dist/components/SwapDetailsModal/SwapDetailsModal.types.d.ts.map +1 -1
  18. package/dist/components/TokenList/TokenList.d.ts.map +1 -1
  19. package/dist/components/TokenList/TokenList.styles.d.ts +323 -1
  20. package/dist/components/TokenList/TokenList.styles.d.ts.map +1 -1
  21. package/dist/components/TokenList/TokenList.types.d.ts +6 -1
  22. package/dist/components/TokenList/TokenList.types.d.ts.map +1 -1
  23. package/dist/constants/customTokens.d.ts +1 -0
  24. package/dist/constants/customTokens.d.ts.map +1 -1
  25. package/dist/containers/Inputs/Inputs.d.ts.map +1 -1
  26. package/dist/hooks/useFetchCustomToken.d.ts +8 -4
  27. package/dist/hooks/useFetchCustomToken.d.ts.map +1 -1
  28. package/dist/hooks/usePrepareBlockchainList/usePrepareBlockchainList.helpers.d.ts +2 -2
  29. package/dist/hooks/useSearchCustomTokens/index.d.ts +2 -0
  30. package/dist/hooks/useSearchCustomTokens/index.d.ts.map +1 -0
  31. package/dist/hooks/useSearchCustomTokens/types.d.ts +9 -0
  32. package/dist/hooks/useSearchCustomTokens/types.d.ts.map +1 -0
  33. package/dist/hooks/useSearchCustomTokens/useSearchCustomTokens.constants.d.ts +2 -0
  34. package/dist/hooks/useSearchCustomTokens/useSearchCustomTokens.constants.d.ts.map +1 -0
  35. package/dist/hooks/useSearchCustomTokens/useSearchCustomTokens.d.ts +3 -0
  36. package/dist/hooks/useSearchCustomTokens/useSearchCustomTokens.d.ts.map +1 -0
  37. package/dist/hooks/useSyncStoresWithConfig.d.ts.map +1 -1
  38. package/dist/hooks/useSyncUrlAndStore/useSyncUrlAndStore.d.ts.map +1 -1
  39. package/dist/index.js +2 -2
  40. package/dist/index.js.map +4 -4
  41. package/dist/pages/AddCustomTokenPage.d.ts.map +1 -1
  42. package/dist/pages/CustomTokensPage.d.ts.map +1 -1
  43. package/dist/pages/SelectSwapItemPage/SelectSwapItemPage.constants.d.ts +3 -0
  44. package/dist/pages/SelectSwapItemPage/SelectSwapItemPage.constants.d.ts.map +1 -0
  45. package/dist/pages/SelectSwapItemPage/SelectSwapItemPage.helpers.d.ts +4 -0
  46. package/dist/pages/SelectSwapItemPage/SelectSwapItemPage.helpers.d.ts.map +1 -0
  47. package/dist/pages/SelectSwapItemPage/SelectSwapItemsPage.d.ts.map +1 -0
  48. package/dist/pages/SelectSwapItemPage/index.d.ts +2 -0
  49. package/dist/pages/SelectSwapItemPage/index.d.ts.map +1 -0
  50. package/dist/store/app.d.ts +1 -1
  51. package/dist/store/app.d.ts.map +1 -1
  52. package/dist/store/quote.d.ts +9 -6
  53. package/dist/store/quote.d.ts.map +1 -1
  54. package/dist/store/slices/settings.d.ts +4 -2
  55. package/dist/store/slices/settings.d.ts.map +1 -1
  56. package/dist/store/slices/wallets.d.ts +5 -1
  57. package/dist/store/slices/wallets.d.ts.map +1 -1
  58. package/dist/store/utils/wallets.d.ts +2 -1
  59. package/dist/store/utils/wallets.d.ts.map +1 -1
  60. package/dist/utils/quote.d.ts +1 -1
  61. package/dist/utils/quote.d.ts.map +1 -1
  62. package/dist/utils/settings.d.ts +1 -1
  63. package/dist/widget-embedded.build.json +1 -1
  64. package/package.json +12 -12
  65. package/src/components/AppRoutes.tsx +1 -1
  66. package/src/components/CustomTokenModal/CustomTokenModal.constants.ts +2 -0
  67. package/src/components/CustomTokenModal/CustomTokenModal.tsx +26 -5
  68. package/src/components/CustomTokenModal/CustomTokenModal.types.ts +1 -0
  69. package/src/components/ImportCustomToken/ImportCustomToken.tsx +121 -0
  70. package/src/components/ImportCustomToken/ImportCustomToken.types.ts +18 -0
  71. package/src/components/ImportCustomToken/index.ts +1 -0
  72. package/src/components/NotificationContent/NotificationContent.tsx +2 -2
  73. package/src/components/Quote/QuoteTrigger.tsx +5 -5
  74. package/src/components/QuoteSkeleton/QuoteSummarySkeleton.tsx +2 -2
  75. package/src/components/QuoteSkeleton/StepSkeleton.tsx +2 -2
  76. package/src/components/SearchInput/SearchInput.tsx +13 -10
  77. package/src/components/SearchInput/SearchInput.types.ts +4 -2
  78. package/src/components/SwapDetailsModal/SwapDetailsModal.types.ts +2 -2
  79. package/src/components/TokenList/TokenList.styles.ts +40 -0
  80. package/src/components/TokenList/TokenList.tsx +134 -22
  81. package/src/components/TokenList/TokenList.types.ts +5 -1
  82. package/src/constants/customTokens.ts +3 -0
  83. package/src/containers/Inputs/Inputs.tsx +6 -4
  84. package/src/hooks/useFetchCustomToken.ts +39 -16
  85. package/src/hooks/useSearchCustomTokens/index.ts +1 -0
  86. package/src/hooks/useSearchCustomTokens/types.ts +9 -0
  87. package/src/hooks/useSearchCustomTokens/useSearchCustomTokens.constants.ts +1 -0
  88. package/src/hooks/useSearchCustomTokens/useSearchCustomTokens.ts +81 -0
  89. package/src/hooks/useSyncStoresWithConfig.ts +2 -3
  90. package/src/hooks/useSyncUrlAndStore/useSyncUrlAndStore.ts +1 -3
  91. package/src/pages/AddCustomTokenPage.tsx +24 -94
  92. package/src/pages/CustomTokensPage.tsx +1 -0
  93. package/src/pages/SelectSwapItemPage/SelectSwapItemPage.constants.ts +2 -0
  94. package/src/pages/SelectSwapItemPage/SelectSwapItemPage.helpers.ts +51 -0
  95. package/src/pages/{SelectSwapItemsPage.tsx → SelectSwapItemPage/SelectSwapItemsPage.tsx} +54 -13
  96. package/src/pages/SelectSwapItemPage/index.ts +1 -0
  97. package/src/store/app.ts +12 -1
  98. package/src/store/quote.ts +6 -3
  99. package/src/store/slices/settings.ts +11 -4
  100. package/src/store/slices/wallets.ts +105 -2
  101. package/src/store/utils/wallets.ts +31 -2
  102. package/src/utils/quote.ts +1 -1
  103. package/dist/pages/SelectSwapItemsPage.d.ts.map +0 -1
  104. /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;
@@ -295,6 +300,98 @@ export const createWalletsSlice = keepLastUpdated<AppStoreState, WalletsSlice>(
295
300
  });
296
301
  }
297
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
+ },
298
395
  setWalletsAsSelected: (wallets) => {
299
396
  const nextConnectedWalletsWithUpdatedSelectedStatus =
300
397
  get().connectedWallets.map((connectedWallet) => {
@@ -332,6 +429,10 @@ export const createWalletsSlice = keepLastUpdated<AppStoreState, WalletsSlice>(
332
429
  get().addConnectedWallet(accounts, namespace);
333
430
 
334
431
  void get().fetchBalances(accounts);
432
+ void get().fetchCustomTokensBalance({
433
+ tokens: get().customTokens(),
434
+ connectedWallets: accounts,
435
+ });
335
436
  },
336
437
  removeBalancesForWallet: (walletType, options) => {
337
438
  let walletsNeedsToBeRemoved = get().connectedWallets.filter(
@@ -562,7 +663,7 @@ export const createWalletsSlice = keepLastUpdated<AppStoreState, WalletsSlice>(
562
663
  }
563
664
  }
564
665
 
565
- let nextBalances: BalanceState = {};
666
+ let nextBalances: BalanceState = get()._balances;
566
667
  let nextAggregatedBalances: AggregatedBalanceState =
567
668
  get()._aggregatedBalances;
568
669
  walletsDetails.forEach((wallet) => {
@@ -570,6 +671,8 @@ export const createWalletsSlice = keepLastUpdated<AppStoreState, WalletsSlice>(
570
671
  return;
571
672
  }
572
673
 
674
+ updateBalancesWithNewPrices(wallet, nextBalances, get);
675
+
573
676
  // Remove old balances for current wallet and blockchain
574
677
  get().removeBalancesForWallet(walletType, {
575
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
  : '';
@@ -293,7 +293,7 @@ export function getPriceImpact(
293
293
 
294
294
  export const getUniqueBlockchains = (steps: Step[]) => {
295
295
  const set = new Set();
296
- const result: { displayName: string; image: string }[] = [];
296
+ const result: { displayName: string; image?: string }[] = [];
297
297
  steps.forEach((step) => {
298
298
  if (!set.has(step.from.chain.displayName)) {
299
299
  set.add(step.from.chain.displayName);
@@ -1 +0,0 @@
1
- {"version":3,"file":"SelectSwapItemsPage.d.ts","sourceRoot":"","sources":["../../src/pages/SelectSwapItemsPage.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAYxC,UAAU,SAAS;IACjB,IAAI,EAAE,QAAQ,GAAG,aAAa,CAAC;CAChC;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,qBAiGnD"}