diff --git a/src/components/SearchModal/CurrencyList.tsx b/src/components/SearchModal/CurrencyList.tsx
index 43e39fdadf..aaea0b1631 100644
--- a/src/components/SearchModal/CurrencyList.tsx
+++ b/src/components/SearchModal/CurrencyList.tsx
@@ -136,18 +136,15 @@ export function CurrencyRow({
return false
}
- if (isTokenNative(currency, currency.chainId)) {
- return !!favoriteTokens.includeNativeToken
- }
-
if (currency.isToken) {
const addr = (currency as Token).address ?? ''
- const addresses = favoriteTokens?.addresses ?? []
+ const addresses = favoriteTokens ?? []
return !!addresses?.includes(addr) || !!addresses?.includes(addr.toLowerCase())
}
return false
})()
+
const balanceComponent = hideBalance ? (
'******'
) : currencyBalance ? (
diff --git a/src/components/SearchModal/CurrencySearch.tsx b/src/components/SearchModal/CurrencySearch.tsx
index d7529a1f03..7a77c1bdfa 100644
--- a/src/components/SearchModal/CurrencySearch.tsx
+++ b/src/components/SearchModal/CurrencySearch.tsx
@@ -76,7 +76,6 @@ const ButtonClear = styled.div`
gap: 5px;
cursor: pointer;
`
-const MAX_FAVORITE_PAIR = 12
interface CurrencySearchProps {
isOpen: boolean
@@ -254,32 +253,15 @@ export function CurrencySearch({
const handleClickFavorite = useCallback(
(e: React.MouseEvent, currency: any) => {
e.stopPropagation()
-
const address = currency?.wrapped?.address || currency.address
if (!address) return
- const currentList = favoriteTokens?.addresses || []
- const isAddFavorite = isTokenNative(currency, currency.chainId)
- ? !favoriteTokens?.includeNativeToken
- : !currentList.find(el => el === address) // else remove favorite
- const curTotal =
- currentList.filter(address => !!defaultTokens[address]).length + (favoriteTokens?.includeNativeToken ? 1 : 0)
- if (isAddFavorite && curTotal === MAX_FAVORITE_PAIR) return
-
- if (isTokenNative(currency, currency.chainId)) {
- toggleFavoriteToken({
- chainId,
- isNative: true,
- })
- return
- }
-
toggleFavoriteToken({
chainId,
address,
})
},
- [chainId, favoriteTokens, toggleFavoriteToken, defaultTokens],
+ [chainId, toggleFavoriteToken],
)
// menu ui
@@ -292,20 +274,30 @@ export function CurrencySearch({
if (!Object.keys(defaultTokens).length) return
setLoadingCommon(true)
let result: (Token | Currency)[] = []
- if (favoriteTokens?.includeNativeToken) {
- result.push(NativeCurrencies[chainId])
- }
const addressesToFetch: string[] = []
- favoriteTokens?.addresses.forEach(address => {
- if (defaultTokens[address]) {
- result.push(defaultTokens[address])
+
+ favoriteTokens?.forEach(address => {
+ let token
+ Object.entries(defaultTokens).forEach(([add, t]) => {
+ if (add.toLowerCase() === address.toLowerCase()) {
+ token = t
+ }
+ })
+ if (token) {
+ result.push(token)
return
}
addressesToFetch.push(address)
})
+
if (addressesToFetch.length) {
const tokens = await fetchListTokenByAddresses(addressesToFetch, chainId)
- result = result.concat(tokens)
+ // Sort the returned token list to match the order of the passed address list
+ result = result.concat(
+ tokens.sort((x, y) => {
+ return addressesToFetch.indexOf(x.wrapped.address) - addressesToFetch.indexOf(y.wrapped.address)
+ }),
+ )
}
setCommonTokens(result)
} catch (error) {
@@ -393,14 +385,13 @@ export function CurrencySearch({
const removeImportedToken = useCallback(
(token: Token) => {
removeToken(chainId, token.address)
- if (favoriteTokens?.addresses?.includes(token.address))
- // remove in favorite too
- toggleFavoriteToken({
- chainId,
- address: token.address,
- })
+
+ toggleFavoriteToken({
+ chainId,
+ address: token.address,
+ })
},
- [chainId, toggleFavoriteToken, removeToken, favoriteTokens?.addresses],
+ [chainId, toggleFavoriteToken, removeToken],
)
const removeAllImportToken = () => {
diff --git a/src/constants/networks.ts b/src/constants/networks.ts
index b7c8da1862..952e8f8291 100644
--- a/src/constants/networks.ts
+++ b/src/constants/networks.ts
@@ -164,6 +164,7 @@ export const STATIC_FEE_OPTIONS: { [chainId: number]: number[] | undefined } = {
[ChainId.GÖRLI]: [8, 10, 50, 300, 500, 1000],
[ChainId.ZKSYNC]: [8, 10, 50, 300, 500, 1000],
[ChainId.LINEA_TESTNET]: [8, 10, 50, 300, 500, 1000],
+ [ChainId.LINEA]: [8, 10, 50, 300, 500, 1000],
}
export const ONLY_STATIC_FEE_CHAINS = [
@@ -175,6 +176,7 @@ export const ONLY_STATIC_FEE_CHAINS = [
ChainId.GÖRLI,
ChainId.ZKSYNC,
ChainId.LINEA_TESTNET,
+ ChainId.LINEA,
]
// hardcode for unavailable subgraph
diff --git a/src/constants/networks/linea.ts b/src/constants/networks/linea.ts
index fe95642892..cd9ea39456 100644
--- a/src/constants/networks/linea.ts
+++ b/src/constants/networks/linea.ts
@@ -8,7 +8,7 @@ const EMPTY = ''
const EMPTY_ARRAY: any[] = []
const NOT_SUPPORT = null
-const lineaTestnetInfo: EVMNetworkInfo = {
+const lineaInfo: EVMNetworkInfo = {
chainId: ChainId.LINEA,
route: 'linea',
ksSettingRoute: 'linea',
@@ -70,4 +70,4 @@ const lineaTestnetInfo: EVMNetworkInfo = {
geckoTermialId: NOT_SUPPORT,
}
-export default lineaTestnetInfo
+export default lineaInfo
diff --git a/src/pages/App.tsx b/src/pages/App.tsx
index 2aa6c33bf5..3477844850 100644
--- a/src/pages/App.tsx
+++ b/src/pages/App.tsx
@@ -5,7 +5,7 @@ import * as Sentry from '@sentry/react'
import { Suspense, lazy, useEffect } from 'react'
import { isMobile } from 'react-device-detect'
import { AlertTriangle } from 'react-feather'
-import { Navigate, Route, Routes, useLocation, useParams } from 'react-router-dom'
+import { Navigate, Route, Routes, useLocation, useNavigate, useParams } from 'react-router-dom'
import { useNetwork, usePrevious } from 'react-use'
import { Flex, Text } from 'rebass'
import styled from 'styled-components'
@@ -14,6 +14,7 @@ import snow from 'assets/images/snow.png'
import Popups from 'components/Announcement/Popups'
import TopBanner from 'components/Announcement/Popups/TopBanner'
import AppHaveUpdate from 'components/AppHaveUpdate'
+import { ButtonPrimary } from 'components/Button'
import ModalConfirm from 'components/ConfirmModal'
import ErrorBoundary from 'components/ErrorBoundary'
import Footer from 'components/Footer/Footer'
@@ -45,6 +46,22 @@ import ElasticLegacyNotice from './ElasticLegacy/ElasticLegacyNotice'
import Icons from './Icons'
import VerifyAuth from './Verify/VerifyAuth'
+// THIS IS ONLY TEMPORARY, WILL REMOVE IN NEXT VERSION
+const CommingSoonModal = () => {
+ const navigate = useNavigate()
+ return (
+ navigate('/')}>
+
+ Our pools and farms will be available soon!
+
+ navigate('/')}>
+ Ok
+
+
+
+ )
+}
+
// test page for swap only through elastic
const ElasticSwap = lazy(() => import('./ElasticSwap'))
const SwapV2 = lazy(() => import('./SwapV2'))
@@ -331,19 +348,28 @@ export default function App() {
<>
{/* Pools Routes */}
} />
- } />
+ : }
+ />
>
<>
{/* Farms Routes */}
} />
- } />
+ : }
+ />
>
<>
{/* My Pools Routes */}
} />
- } />
+ : }
+ />
>
<>
diff --git a/src/services/ksSetting.ts b/src/services/ksSetting.ts
index 6936e57584..ee7ca0648a 100644
--- a/src/services/ksSetting.ts
+++ b/src/services/ksSetting.ts
@@ -16,6 +16,7 @@ export type KyberSwapConfig = {
elasticClient: ApolloClient
readProvider: AppJsonRpcProvider | undefined
connection: Connection | undefined
+ commonTokens?: string[]
}
export type KyberSwapConfigResponse = {
@@ -25,6 +26,7 @@ export type KyberSwapConfigResponse = {
blockSubgraph: string
classicSubgraph: string
elasticSubgraph: string
+ commonTokens?: string[]
}
export type KyberswapConfigurationResponse = {
diff --git a/src/state/application/hooks.ts b/src/state/application/hooks.ts
index bda0ac3309..bdad33a5c6 100644
--- a/src/state/application/hooks.ts
+++ b/src/state/application/hooks.ts
@@ -434,6 +434,7 @@ function getDefaultConfig(chainId: ChainId): KyberSwapConfigResponse {
blockSubgraph: (evm ? NETWORKS_INFO[chainId] : ethereumInfo).defaultBlockSubgraph,
elasticSubgraph: (evm ? NETWORKS_INFO[chainId] : ethereumInfo).elastic.defaultSubgraph,
classicSubgraph: (evm ? NETWORKS_INFO[chainId] : ethereumInfo).classic.defaultSubgraph,
+ commonTokens: undefined,
}
}
@@ -469,11 +470,13 @@ export const useKyberSwapConfig = (customChainId?: ChainId): KyberSwapConfig =>
elasticClient,
classicClient,
connection: isSolana(chainId) ? new Connection(config.rpc, { commitment: 'confirmed' }) : undefined,
+ commonTokens: config.commonTokens,
}
}, [
config.rpc,
config.isEnableBlockService,
config.prochart,
+ config.commonTokens,
readProvider,
blockClient,
elasticClient,
diff --git a/src/state/application/reducer.ts b/src/state/application/reducer.ts
index 5de933880a..019d850f59 100644
--- a/src/state/application/reducer.ts
+++ b/src/state/application/reducer.ts
@@ -197,6 +197,7 @@ export default createReducer(initialState, builder =>
blockSubgraph,
elasticSubgraph,
classicSubgraph,
+ commonTokens: data.commonTokens,
},
}
}),
diff --git a/src/state/index.ts b/src/state/index.ts
index 20fc52ee46..8dd826d9e0 100644
--- a/src/state/index.ts
+++ b/src/state/index.ts
@@ -41,12 +41,31 @@ import tokenPrices from './tokenPrices'
import topTokens from './topTokens'
import transactions from './transactions/reducer'
import tutorial from './tutorial/reducer'
-import user from './user/reducer'
+import user, { UserState } from './user/reducer'
import vesting from './vesting/reducer'
const PERSISTED_KEYS: string[] = ['user', 'transactions', 'profile']
ENV_LEVEL < ENV_TYPE.PROD && PERSISTED_KEYS.push('customizeDexes')
+// Migrate from old version to new version, prevent lost favorite tokens of user
+const preloadedState: any = load({ states: PERSISTED_KEYS })
+if ('user' in preloadedState) {
+ const userState: UserState = preloadedState.user
+ if (userState.favoriteTokensByChainId) {
+ userState.favoriteTokensByChainIdv2 = Object.entries(userState.favoriteTokensByChainId).reduce(
+ (acc, [chainId, obj]) => {
+ acc[chainId] = {}
+ obj.addresses.forEach((address: string) => {
+ acc[chainId][address.toLowerCase()] = true
+ })
+ return acc
+ },
+ {} as any,
+ )
+ userState.favoriteTokensByChainId = undefined
+ }
+}
+
const store = configureStore({
devTools: process.env.NODE_ENV !== 'production',
reducer: {
@@ -110,7 +129,7 @@ const store = configureStore({
.concat(earningApi.middleware)
.concat(socialApi.middleware)
.concat(tokenApi.middleware),
- preloadedState: load({ states: PERSISTED_KEYS }),
+ preloadedState,
})
const PREFIX_REDUX_PERSIST = 'redux_localstorage_simple_'
diff --git a/src/state/user/actions.ts b/src/state/user/actions.ts
index 0a3fe1802c..23186d7acd 100644
--- a/src/state/user/actions.ts
+++ b/src/state/user/actions.ts
@@ -49,7 +49,9 @@ export const toggleTopTrendingTokens = createAction('user/toggleTopTrendin
export type ToggleFavoriteTokenPayload = {
chainId: ChainId
-} & ({ isNative?: false; address: string } | { isNative: true; address?: never })
+ address: string
+ newValue?: boolean
+}
export const toggleFavoriteToken = createAction('user/toggleFavoriteToken')
export const updateChainId = createAction('user/updateChainId')
export const updateTokenAnalysisSettings = createAction('user/updateTokenAnalysisSettings')
diff --git a/src/state/user/hooks.tsx b/src/state/user/hooks.tsx
index 6e4275450d..74fa4ac391 100644
--- a/src/state/user/hooks.tsx
+++ b/src/state/user/hooks.tsx
@@ -3,6 +3,7 @@ import { useCallback, useMemo } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useGetParticipantInfoQuery } from 'services/kyberAISubscription'
+import { SUGGESTED_BASES } from 'constants/bases'
import { DEFAULT_SLIPPAGE_TESTNET, TERM_FILES_PATH } from 'constants/index'
import { SupportedLocale } from 'constants/locales'
import { PINNED_PAIRS } from 'constants/tokens'
@@ -16,6 +17,7 @@ import {
import useDebounce from 'hooks/useDebounce'
import { ParticipantInfo, ParticipantStatus } from 'pages/TrueSightV2/types'
import { AppDispatch, AppState } from 'state'
+import { useKyberSwapConfig } from 'state/application/hooks'
import { useIsConnectingWallet, useSessionInfo } from 'state/authen/hooks'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
@@ -49,9 +51,11 @@ import {
updateUserSlippageTolerance,
updateUserSlippageToleranceForLineaTestnet,
} from 'state/user/actions'
-import { CROSS_CHAIN_SETTING_DEFAULT, CrossChainSetting, VIEW_MODE, getFavoriteTokenDefault } from 'state/user/reducer'
+import { CROSS_CHAIN_SETTING_DEFAULT, CrossChainSetting, VIEW_MODE } from 'state/user/reducer'
import { isAddress, isChristmasTime } from 'utils'
+const MAX_FAVORITE_LIMIT = 12
+
function serializeToken(token: Token | WrappedTokenInfo): SerializedToken {
return {
chainId: token.chainId,
@@ -395,18 +399,35 @@ export function useToggleTopTrendingTokens(): () => void {
export const useUserFavoriteTokens = (chainId: ChainId) => {
const dispatch = useDispatch()
- const { favoriteTokensByChainId } = useSelector((state: AppState) => state.user)
+ const { favoriteTokensByChainIdv2: favoriteTokensByChainId } = useSelector((state: AppState) => state.user)
+ const { commonTokens } = useKyberSwapConfig(chainId)
+ const defaultTokens = useMemo(() => {
+ return commonTokens || SUGGESTED_BASES[chainId || ChainId.MAINNET].map(e => e.address)
+ }, [commonTokens, chainId])
const favoriteTokens = useMemo(() => {
if (!chainId) return undefined
- return favoriteTokensByChainId
- ? favoriteTokensByChainId[chainId] || getFavoriteTokenDefault(chainId)
- : getFavoriteTokenDefault(chainId)
- }, [chainId, favoriteTokensByChainId])
+ const favoritedTokens = favoriteTokensByChainId?.[chainId] || {}
+ const favoritedTokenAddresses = defaultTokens
+ .filter(address => favoritedTokens[address.toLowerCase()] !== false)
+ .concat(Object.keys(favoritedTokens).filter(address => favoritedTokens[address]))
+
+ return [...new Set(favoritedTokenAddresses.map(a => a.toLowerCase()))]
+ }, [chainId, favoriteTokensByChainId, defaultTokens])
const toggleFavoriteToken = useCallback(
- (payload: ToggleFavoriteTokenPayload) => dispatch(toggleFavoriteTokenAction(payload)),
- [dispatch],
+ (payload: ToggleFavoriteTokenPayload) => {
+ if (!favoriteTokens) return
+ const address = payload.address.toLowerCase()
+ // Is adding favorite and reached max limit
+ if (favoriteTokens.indexOf(address) < 0 && favoriteTokens.length >= MAX_FAVORITE_LIMIT) {
+ return
+ }
+ const newValue = favoriteTokens.indexOf(address) < 0
+
+ dispatch(toggleFavoriteTokenAction({ ...payload, newValue }))
+ },
+ [dispatch, favoriteTokens],
)
return { favoriteTokens, toggleFavoriteToken }
diff --git a/src/state/user/reducer.ts b/src/state/user/reducer.ts
index 7cd19ea5da..5adcce2af2 100644
--- a/src/state/user/reducer.ts
+++ b/src/state/user/reducer.ts
@@ -60,7 +60,7 @@ export type CrossChainSetting = {
enableExpressExecution: boolean
}
-interface UserState {
+export interface UserState {
// the timestamp of the last updateVersion action
lastUpdateVersionTimestamp?: number
@@ -101,7 +101,7 @@ interface UserState {
kyberAIDisplaySettings: {
[k: string]: boolean
}
- favoriteTokensByChainId: Partial<
+ favoriteTokensByChainId?: Partial<
Record<
ChainId,
{
@@ -110,6 +110,14 @@ interface UserState {
}
>
>
+ favoriteTokensByChainIdv2: Partial<
+ Record<
+ ChainId,
+ {
+ [address: string]: boolean
+ }
+ >
+ >
readonly chainId: ChainId
acceptedTermVersion: number | null
viewMode: VIEW_MODE
@@ -181,6 +189,7 @@ const initialState: UserState = {
liquidationsOnCEX: true,
},
favoriteTokensByChainId: {},
+ favoriteTokensByChainIdv2: {},
chainId: ChainId.MAINNET,
acceptedTermVersion: null,
viewMode: VIEW_MODE.GRID,
@@ -296,31 +305,19 @@ export default createReducer(initialState, builder =>
.addCase(toggleKyberAIBanner, state => {
state.showKyberAIBanner = !state.showKyberAIBanner
})
- .addCase(toggleFavoriteToken, (state, { payload: { chainId, isNative, address } }) => {
- if (!state.favoriteTokensByChainId) {
- state.favoriteTokensByChainId = {}
+ .addCase(toggleFavoriteToken, (state, { payload: { chainId, address, newValue } }) => {
+ if (!state.favoriteTokensByChainIdv2) {
+ state.favoriteTokensByChainIdv2 = {}
}
- let favoriteTokens = state.favoriteTokensByChainId[chainId]
- if (!favoriteTokens) {
- favoriteTokens = getFavoriteTokenDefault(chainId)
- state.favoriteTokensByChainId[chainId] = favoriteTokens
+ if (!state.favoriteTokensByChainIdv2[chainId]) {
+ state.favoriteTokensByChainIdv2[chainId] = {}
}
- if (isNative) {
- const previousValue = favoriteTokens.includeNativeToken
- favoriteTokens.includeNativeToken = !previousValue
- return
- }
-
- if (address) {
- // this is intentionally added, to remove compiler error
- const index = favoriteTokens.addresses.findIndex(addr => addr === address)
- if (index === -1) {
- favoriteTokens.addresses.push(address)
- return
- }
- favoriteTokens.addresses.splice(index, 1)
+ const favoriteTokens = state.favoriteTokensByChainIdv2[chainId]
+ const lowercaseAddress = address.toLowerCase()
+ if (favoriteTokens) {
+ favoriteTokens[lowercaseAddress] = newValue !== undefined ? newValue : !favoriteTokens[lowercaseAddress]
}
})
.addCase(updateChainId, (state, { payload: chainId }) => {