diff --git a/src/components/SearchModal/CurrencySearch.tsx b/src/components/SearchModal/CurrencySearch.tsx index 018c2bea086..028a815d907 100644 --- a/src/components/SearchModal/CurrencySearch.tsx +++ b/src/components/SearchModal/CurrencySearch.tsx @@ -24,6 +24,7 @@ import { useSelectedListInfo } from '../../state/lists/hooks' import { useToken } from '../../hooks/Tokens' import { useTokenComparator } from './sorting' import { useTranslation } from 'react-i18next' +import { TokenList } from '@uniswap/token-lists' interface CurrencySearchProps { isOpen: boolean @@ -63,13 +64,13 @@ export function CurrencySearch({ // ChainId.RINKEBY BUSD const availableTokensArray = isCrossChain ? availableTokens - .filter(a => a.name !== 'BUSD') - .map((x: any) => { - return new Token(x.chainId, x.address, x.decimals, x.symbol, x.name) - }) - : availableTokens.map((x: any) => { + .filter(a => a.name !== 'BUSD') + .map((x: any) => { return new Token(x.chainId, x.address, x.decimals, x.symbol, x.name) }) + : availableTokens.map((x: any) => { + return new Token(x.chainId, x.address, x.decimals, x.symbol, x.name) + }) const defaultTokenList = DEFAULT_TOKEN_LIST.filter((x: any) => x.chainId === chainId).map((x: any) => { return new Token(x.chainId, x.address, x.decimals, x.symbol, x.name) @@ -97,7 +98,7 @@ export function CurrencySearch({ ) }, [isAddressSearch, searchToken, searchQuery, defaultTokenList, chainId, availableTokensArray]) - const filteredSortedTokens: Token[] = useMemo(() => { + let filteredSortedTokens: Token[] = useMemo(() => { if (searchToken) return [searchToken] const sorted = filteredTokens.sort(tokenComparator) const symbolMatch = searchQuery @@ -153,8 +154,29 @@ export function CurrencySearch({ }, [filteredSortedTokens, handleCurrencySelect, searchQuery] ) - - const selectedListInfo = useSelectedListInfo() + + let selectedListInfo = useSelectedListInfo() + const newSelectedList = useSelectedListInfo() + //let totalList = useRef(() => { + + if (selectedListInfo === undefined) { + selectedListInfo = {} as { current: TokenList | null; pending: TokenList | null; loading: boolean; } + } + let newList: Token[] = [] + + let tokenMergeLength = 0 + if (newSelectedList && newSelectedList.current && newSelectedList.current.tokens && filteredSortedTokens) { + tokenMergeLength = newSelectedList.current.tokens.length + filteredSortedTokens.length + } + if (selectedListInfo.current?.tokens?.length !== tokenMergeLength) { + const arrSortedTokens: Token[] = [] + selectedListInfo?.current?.tokens?.map(token => { + arrSortedTokens.push(new Token(token.chainId, token.address, token.decimals, token.symbol, token.name)) + + }) + newList = arrSortedTokens.concat(filteredSortedTokens) + filteredSortedTokens = newList + } return ( @@ -197,7 +219,7 @@ export function CurrencySearch({ void }) { + const dispatch = useDispatch() const listsByUrl = useSelector(state => state.lists.byUrl) const selectedListUrl = useSelectedListUrl() - const dispatch = useDispatch() + const { current: list, pendingUpdate: pending } = listsByUrl[listUrl] const isSelected = listUrl === selectedListUrl @@ -133,8 +134,8 @@ const ListRow = memo(function ListRow({ listUrl, onBack }: { listUrl: string; on {list.logoURI ? ( ) : ( -
- )} +
+ )} - + {/* */} + {list.tokens.length} tokens @@ -193,22 +195,22 @@ const ListRow = memo(function ListRow({ listUrl, onBack }: { listUrl: string; on Selected ) : ( - <> - - Select + <> + + Select - - )} + + )} ) }) @@ -278,8 +280,8 @@ export function ListSelect({ onDismiss, onBack }: { onDismiss: () => void; onBac return l1.name.toLowerCase() < l2.name.toLowerCase() ? -1 : l1.name.toLowerCase() === l2.name.toLowerCase() - ? 0 - : 1 + ? 0 + : 1 } if (l1) return -1 if (l2) return 1 diff --git a/src/constants/lists.ts b/src/constants/lists.ts index 4ffd2ff9a5d..294b82350cb 100644 --- a/src/constants/lists.ts +++ b/src/constants/lists.ts @@ -1,4 +1,71 @@ + + +const ls = localStorage.getItem('redux_localstorage_simple_crosschain') +export let MAIN_CURRENCY = 'ETH' +export let CHAIN_ID = 1 +if(ls) { + MAIN_CURRENCY = JSON.parse(ls).currentChain.symbol +} + +export const DEFAULT_ACTIVE_LIST_URLS: string[] = [] +export function getListTokensByChain(currency: string, chainId: number) { +CHAIN_ID = chainId +const DEFAULT_LIST_OF_LISTS: string[] = listTokensFomChains[`${currency}List`] +MAIN_CURRENCY = currency +return {DEFAULT_LIST_OF_LISTS, DEFAULT_TOKEN_LIST_URL, DEFAULT_ACTIVE_LIST_URLS, MAIN_CURRENCY, CHAIN_ID} +} + + // the Uniswap Default token list lives here export const DEFAULT_TOKEN_LIST_URL = 'tokens.uniswap.eth' -export const DEFAULT_LIST_OF_LISTS: string[] = [] +// from uniswap exchanges +const COMPOUND_LIST = 'https://raw.githubusercontent.com/compound-finance/token-list/master/compound.tokenlist.json' +const UMA_LIST = 'https://umaproject.org/uma.tokenlist.json' +const AAVE_LIST = 'tokenlist.aave.eth' +const SYNTHETIX_LIST = 'synths.snx.eth' +const WRAPPED_LIST = 'wrapped.tokensoft.eth' +const SET_LIST = 'https://raw.githubusercontent.com/SetProtocol/uniswap-tokenlist/main/set.tokenlist.json' +const OPYN_LIST = 'https://raw.githubusercontent.com/opynfinance/opyn-tokenlist/master/opyn-v1.tokenlist.json' +const ROLL_LIST = 'https://app.tryroll.com/tokens.json' +const COINGECKO_LIST = 'https://tokens.coingecko.com/uniswap/all.json' +const CMC_ALL_LIST = 'defi.cmc.eth' +const CMC_STABLECOIN = 'stablecoin.cmc.eth' +const KLEROS_LIST = 't2crtokens.eth' +const GEMINI_LIST = 'https://www.gemini.com/uniswap/manifest.json' +// const BA_LIST = 'https://raw.githubusercontent.com/The-Blockchain-Association/sec-notice-list/master/ba-sec-list.json' + +// from https://pangolin.exchange/ exchanges +const AVAX_DEFI = 'https://raw.githubusercontent.com/zeroexchange/app/master/tokens-of-exchanges/avalanche/defi.tokenlist.json' +const AVAX_TOP_15 = 'https://raw.githubusercontent.com/zeroexchange/app/master/tokens-of-exchanges/avalanche/top15.tokenlist.json' +const AVAX_ETH = 'https://raw.githubusercontent.com/zeroexchange/app/master/tokens-of-exchanges/avalanche/aeb.tokenlist.json' +const AVAX_STABLECOINS = 'https://raw.githubusercontent.com/zeroexchange/app/master/tokens-of-exchanges/avalanche/stablecoin.tokenlist.json' + + +// used to mark unsupported tokens, these are hosted lists of unsupported tokens +export const UNSUPPORTED_LIST_URLS: string[] = [] +const listTokensFomChains = { + ETHList: [ + COMPOUND_LIST, + AAVE_LIST, + SYNTHETIX_LIST, + UMA_LIST, + WRAPPED_LIST, + SET_LIST, + OPYN_LIST, + ROLL_LIST, + COINGECKO_LIST, + CMC_ALL_LIST, + CMC_STABLECOIN, + KLEROS_LIST, + GEMINI_LIST, + ...UNSUPPORTED_LIST_URLS // need to load unsupported tokens as well + ], + AVAXList: [ + AVAX_DEFI, + AVAX_ETH, + AVAX_STABLECOINS, + AVAX_TOP_15 + ], + BNBList: [] +} diff --git a/src/state/crosschain/hooks.ts b/src/state/crosschain/hooks.ts index 5284bb27424..eb55756d1a6 100644 --- a/src/state/crosschain/hooks.ts +++ b/src/state/crosschain/hooks.ts @@ -30,6 +30,7 @@ import { initialState } from './reducer' import { useActiveWeb3React } from '../../hooks' import { useEffect } from 'react' import useGasPrice from 'hooks/useGasPrice' +import { switchNewList } from 'state/lists/actions' // import { afterWrite } from '@popperjs/core' @@ -564,6 +565,9 @@ export function useCrossChain() { chain: GetCurrentChain(currentChainName) }) ) + dispatch( + switchNewList({ newCurrency: GetCurrentChain(currentChainName)?.symbol ?? 'ETH', chainNumber: (!chainId) ? 1 : chainId }) + ) dispatch(setTransferAmount({ amount: '' })) UpdateOwnTokenBalance().catch(console.error) UpdateFee().catch(console.error) diff --git a/src/state/lists/actions.ts b/src/state/lists/actions.ts index 9114ad540bb..c8edf590f2c 100644 --- a/src/state/lists/actions.ts +++ b/src/state/lists/actions.ts @@ -1,6 +1,6 @@ import { ActionCreatorWithPayload, createAction } from '@reduxjs/toolkit' import { TokenList, Version } from '@uniswap/token-lists' - +type ChainType = {newCurrency: string, chainNumber: number} export const fetchTokenList: Readonly<{ pending: ActionCreatorWithPayload<{ url: string; requestId: string }> fulfilled: ActionCreatorWithPayload<{ url: string; tokenList: TokenList; requestId: string }> @@ -16,3 +16,4 @@ export const addList = createAction('lists/addList') export const removeList = createAction('lists/removeList') export const selectList = createAction('lists/selectList') export const rejectVersionUpdate = createAction('lists/rejectVersionUpdate') +export const switchNewList = createAction('lists/switchNewList') diff --git a/src/state/lists/reducer.ts b/src/state/lists/reducer.ts index fd85f0839ac..35ad028b5fa 100644 --- a/src/state/lists/reducer.ts +++ b/src/state/lists/reducer.ts @@ -1,22 +1,25 @@ import { createReducer } from '@reduxjs/toolkit' import { getVersionUpgrade, VersionUpgrade } from '@uniswap/token-lists' import { TokenList } from '@uniswap/token-lists/dist/types' -import { DEFAULT_LIST_OF_LISTS, DEFAULT_TOKEN_LIST_URL } from '../../constants/lists' +import { getListTokensByChain, MAIN_CURRENCY, CHAIN_ID } from '../../constants/lists' import { updateVersion } from '../global/actions' -import { acceptListUpdate, addList, fetchTokenList, removeList, selectList } from './actions' +import { acceptListUpdate, addList, fetchTokenList, removeList, selectList, switchNewList } from './actions' +const { DEFAULT_TOKEN_LIST_URL, DEFAULT_LIST_OF_LISTS } = getListTokensByChain(MAIN_CURRENCY, CHAIN_ID) export interface ListsState { readonly byUrl: { readonly [url: string]: { readonly current: TokenList | null readonly pendingUpdate: TokenList | null readonly loadingRequestId: string | null - readonly error: string | null + readonly error: string | null, + readonly firstCurrency: string | null } } // this contains the default list of lists from the last time the updateVersion was called, i.e. the app was reloaded readonly lastInitializedDefaultListOfLists?: string[] readonly selectedListUrl: string | undefined + readonly mainCurrency: string } type ListState = ListsState['byUrl'][string] @@ -25,7 +28,8 @@ const NEW_LIST_STATE: ListState = { error: null, current: null, loadingRequestId: null, - pendingUpdate: null + pendingUpdate: null, + firstCurrency: null } type Mutable = { -readonly [P in keyof T]: T[P] extends ReadonlyArray ? U[] : T[P] } @@ -38,7 +42,8 @@ const initialState: ListsState = { return memo }, {}) }, - selectedListUrl: DEFAULT_TOKEN_LIST_URL + selectedListUrl: DEFAULT_TOKEN_LIST_URL, + mainCurrency: MAIN_CURRENCY } export default createReducer(initialState, builder => @@ -49,7 +54,8 @@ export default createReducer(initialState, builder => pendingUpdate: null, ...state.byUrl[url], loadingRequestId: requestId, - error: null + error: null, + firstCurrency: null } }) .addCase(fetchTokenList.fulfilled, (state, { payload: { requestId, tokenList, url } }) => { @@ -66,7 +72,8 @@ export default createReducer(initialState, builder => loadingRequestId: null, error: null, current: current, - pendingUpdate: tokenList + pendingUpdate: tokenList, + firstCurrency: MAIN_CURRENCY } } } else { @@ -75,7 +82,8 @@ export default createReducer(initialState, builder => loadingRequestId: null, error: null, current: tokenList, - pendingUpdate: null + pendingUpdate: null, + firstCurrency: MAIN_CURRENCY } } }) @@ -90,7 +98,8 @@ export default createReducer(initialState, builder => loadingRequestId: null, error: errorMessage, current: null, - pendingUpdate: null + pendingUpdate: null, + firstCurrency: null } }) .addCase(selectList, (state, { payload: url }) => { @@ -123,19 +132,40 @@ export default createReducer(initialState, builder => current: state.byUrl[url].pendingUpdate } }) + .addCase(switchNewList, (state, { payload: {newCurrency, chainNumber} }) => { + if (MAIN_CURRENCY !== newCurrency) { + const newList = getListTokensByChain(newCurrency, chainNumber) + //if(state.lastInitializedDefaultListOfLists) { + const keys = Object.keys(state.byUrl) + keys.map( url => { + delete state.byUrl[url] + }) + + // } + + state.lastInitializedDefaultListOfLists = newList.DEFAULT_LIST_OF_LISTS + state.mainCurrency = newList.MAIN_CURRENCY + newList.DEFAULT_LIST_OF_LISTS.forEach(listUrl => { + state.byUrl[listUrl] = NEW_LIST_STATE + }) + } + } + + ) .addCase(updateVersion, state => { // state loaded from localStorage, but new lists have never been initialized + const newList = getListTokensByChain(MAIN_CURRENCY, CHAIN_ID) if (!state.lastInitializedDefaultListOfLists) { state.byUrl = initialState.byUrl - state.selectedListUrl = DEFAULT_TOKEN_LIST_URL + state.selectedListUrl = newList.DEFAULT_TOKEN_LIST_URL } else if (state.lastInitializedDefaultListOfLists) { const lastInitializedSet = state.lastInitializedDefaultListOfLists.reduce>( (s, l) => s.add(l), new Set() ) - const newListOfListsSet = DEFAULT_LIST_OF_LISTS.reduce>((s, l) => s.add(l), new Set()) + const newListOfListsSet = newList.DEFAULT_LIST_OF_LISTS.reduce>((s, l) => s.add(l), new Set()) - DEFAULT_LIST_OF_LISTS.forEach(listUrl => { + newList.DEFAULT_LIST_OF_LISTS.forEach(listUrl => { if (!lastInitializedSet.has(listUrl)) { state.byUrl[listUrl] = NEW_LIST_STATE } @@ -148,12 +178,13 @@ export default createReducer(initialState, builder => }) } - state.lastInitializedDefaultListOfLists = DEFAULT_LIST_OF_LISTS + state.lastInitializedDefaultListOfLists = newList.DEFAULT_LIST_OF_LISTS + state.mainCurrency = newList.MAIN_CURRENCY if (!state.selectedListUrl) { - state.selectedListUrl = DEFAULT_TOKEN_LIST_URL - if (!state.byUrl[DEFAULT_TOKEN_LIST_URL]) { - state.byUrl[DEFAULT_TOKEN_LIST_URL] = NEW_LIST_STATE + state.selectedListUrl = newList.DEFAULT_TOKEN_LIST_URL + if (!state.byUrl[newList.DEFAULT_TOKEN_LIST_URL]) { + state.byUrl[newList.DEFAULT_TOKEN_LIST_URL] = NEW_LIST_STATE } } }) diff --git a/src/state/swap/hooks.ts b/src/state/swap/hooks.ts index 93eb89b5202..92c6f5b6dce 100644 --- a/src/state/swap/hooks.ts +++ b/src/state/swap/hooks.ts @@ -19,6 +19,7 @@ import useParsedQueryString from '../../hooks/useParsedQueryString' import useToggledVersion from '../../hooks/useToggledVersion' import { useUserSlippageTolerance } from '../user/hooks' import { useV1Trade } from '../../data/V1' + export function useSwapState(): AppState['swap'] { return useSelector(state => state.swap) @@ -55,8 +56,9 @@ export function useSwapActionHandlers(): { }, [dispatch] ) - + const onSwitchTokens = useCallback(() => { + dispatch(switchCurrencies()) }, [dispatch])