From 742d1020b285d92f591025028a77e5759e62615c Mon Sep 17 00:00:00 2001 From: ltthienn <132639843+ltthienn@users.noreply.github.com> Date: Wed, 22 Nov 2023 21:37:30 +0700 Subject: [PATCH 01/14] E2E - Update failed tests (#2396) --- cypress.config.ts | 1 + cypress/e2e/selectors/constants.cy.ts | 10 +++++----- cypress/e2e/specs/intercept.e2e.cy.ts | 10 +++++----- cypress/e2e/specs/swap-page.e2e.cy.ts | 15 +++++++-------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/cypress.config.ts b/cypress.config.ts index ff52e33d50..a2e2a3f104 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -156,5 +156,6 @@ export default defineConfig({ }) }, specPattern: 'cypress/e2e/specs/*.e2e.cy.ts', + excludeSpecPattern: '*/*/**/zap.e2e.cy.ts', }, }) diff --git a/cypress/e2e/selectors/constants.cy.ts b/cypress/e2e/selectors/constants.cy.ts index e10804a1fd..7ce18b6ae6 100644 --- a/cypress/e2e/selectors/constants.cy.ts +++ b/cypress/e2e/selectors/constants.cy.ts @@ -12,11 +12,11 @@ export enum TAG { } export const TOKEN_SYMBOLS = { - Ethereum: ['BAND', 'wstETH', 'USDT', 'USDC', '1INCH'], - Arbitrum: ['ANGLE', 'ARB', 'USDT', 'USDC.e', 'BOB'], - Optimism: ['BOND', 'wstETH', 'USDT', 'USDC', 'BOB'], - Avalanche: ['AAVE.e', 'sAVAX', 'USDT.e', 'USDC.e', 'BUSD.e'], - BNB: ['RICE', 'BUSD', 'USDT', 'USDC', 'BOB'], + Ethereum: ['BAND', 'wstETH', 'USDT', 'AAVE', '1INCH'], + Arbitrum: ['ANGLE', 'ARB', 'USDT', 'ANGLE', 'BOB'], + Optimism: ['BOND', 'WETH', 'USDT', 'AAVE', 'BOB'], + Avalanche: ['AAVE.e', 'sAVAX', 'USDT.e', 'ANGLE', 'BUSD.e'], + BNB: ['RICE', 'BUSD', 'USDT', 'AAVE', 'BOB'], } export const NETWORK_LIST = [ diff --git a/cypress/e2e/specs/intercept.e2e.cy.ts b/cypress/e2e/specs/intercept.e2e.cy.ts index 3bfb40ae59..384570e5c7 100644 --- a/cypress/e2e/specs/intercept.e2e.cy.ts +++ b/cypress/e2e/specs/intercept.e2e.cy.ts @@ -1,19 +1,19 @@ -/* eslint-disable @typescript-eslint/no-empty-function */ import { FarmPage } from "../pages/farm-page.po.cy"; import { SwapPage, TokenCatalog } from "../pages/swap-page.po.cy" import { DEFAULT_URL, TAG, } from "../selectors/constants.cy" -import { HeaderLocators } from "../selectors/selectors.cy" const tokenCatalog = new TokenCatalog() const farm = new FarmPage() describe('Intercept', { tags: TAG.regression }, () => { - before(() => { + beforeEach(() => { SwapPage.open(DEFAULT_URL) }) afterEach(() => { - cy.reload() + cy.clearCookies(); + cy.clearLocalStorage(); }) + describe('Swap', () => { it('Should get route successfully', () => { cy.intercept('GET', '**/routes?**').as('get-route') @@ -60,7 +60,7 @@ describe('Intercept', { tags: TAG.regression }, () => { cy.intercept('GET', '**/pools?**').as('get-pool-list') SwapPage.goToFarmPage() cy.get('[data-testid=farm-block]') - .should(_ => {}) + .should(_ => { }) .then($list => { if ($list.length) { cy.wait('@get-pool-list', { timeout: 5000 }).its('response.statusCode').should('equal', 200) diff --git a/cypress/e2e/specs/swap-page.e2e.cy.ts b/cypress/e2e/specs/swap-page.e2e.cy.ts index 3e7f7856b0..dd96372409 100644 --- a/cypress/e2e/specs/swap-page.e2e.cy.ts +++ b/cypress/e2e/specs/swap-page.e2e.cy.ts @@ -37,15 +37,18 @@ describe(`Token Catalog on ${NETWORK}`, { tags: TAG.regression }, () => { it('Should be selected tokenOut in favorite tokens list successfully', () => { SwapPage.selectTokenOut().getFavoriteTokens(arr => { - tokenCatalog.selectFavoriteToken(arr[2]) + tokenCatalog.selectFavoriteToken(arr[0]) SwapPage.getCurrentTokenOut(text => { - expect(text).to.equal(arr[2]) + expect(text).to.equal(arr[0]) }) }) }) }) describe('Remove/add token with favorite tokens list', () => { + afterEach(() => { + tokenCatalog.closePopup() + }) it('Should be removed tokenIn from favorite tokens list', () => { SwapPage.selectTokenIn().getFavoriteTokens(arr => { tokenCatalog.removeFavoriteToken(arr[1]) @@ -54,7 +57,6 @@ describe(`Token Catalog on ${NETWORK}`, { tags: TAG.regression }, () => { expect(list).not.to.include.members([arr[1]]) }) }) - tokenCatalog.closePopup() }) it('Should be added tokenIn to favorite tokens list', () => { @@ -62,7 +64,6 @@ describe(`Token Catalog on ${NETWORK}`, { tags: TAG.regression }, () => { tokenCatalog.getFavoriteTokens(list => { expect(list).to.include.members([tokenSymbols[0]]) }) - tokenCatalog.closePopup() }) it('Should be removed tokenOut from favorite tokens list', () => { @@ -73,15 +74,13 @@ describe(`Token Catalog on ${NETWORK}`, { tags: TAG.regression }, () => { expect(list).not.to.include.members([arr[2]]) }) }) - tokenCatalog.closePopup() }) it('Should be added tokenOut to favorite tokens list', () => { - SwapPage.selectTokenOut().addFavoriteToken([tokenSymbols[0]]) + SwapPage.selectTokenOut().addFavoriteToken([tokenSymbols[4]]) tokenCatalog.getFavoriteTokens(list => { - expect(list).to.include.members([tokenSymbols[0]]) + expect(list).to.include.members([tokenSymbols[4]]) }) - tokenCatalog.closePopup() }) }) From 90b4293d71ca21e25d65f49d988d11dd345a5fa6 Mon Sep 17 00:00:00 2001 From: viet-nv Date: Thu, 23 Nov 2023 07:03:41 +0700 Subject: [PATCH 02/14] remove add/increase liq page --- src/pages/App.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/pages/App.tsx b/src/pages/App.tsx index 8f7ad90159..f0d2882e73 100644 --- a/src/pages/App.tsx +++ b/src/pages/App.tsx @@ -57,10 +57,10 @@ const PoolFinder = lazy(() => import('./PoolFinder')) const ElasticRemoveLiquidity = lazy(() => import('pages/RemoveLiquidityProAmm')) const RedirectCreatePool = lazy(() => import('pages/CreatePool/RedirectCreatePool')) -const RedirectElasticCreatePool = lazy(() => import('pages/AddLiquidityV2/RedirectElasticCreatePool')) +// const RedirectElasticCreatePool = lazy(() => import('pages/AddLiquidityV2/RedirectElasticCreatePool')) const AddLiquidity = lazy(() => import('pages/AddLiquidity')) -const ElasticIncreaseLiquidity = lazy(() => import('pages/IncreaseLiquidity')) +// const ElasticIncreaseLiquidity = lazy(() => import('pages/IncreaseLiquidity')) const RemoveLiquidity = lazy(() => import('pages/RemoveLiquidity')) @@ -189,6 +189,7 @@ const RoutesWithNetworkPrefix = () => { {!ELASTIC_NOT_SUPPORTED[chainId] && ( <> + {/* } @@ -197,6 +198,7 @@ const RoutesWithNetworkPrefix = () => { path={`${APP_PATHS.ELASTIC_INCREASE_LIQ}/:currencyIdA?/:currencyIdB?/:feeAmount?/:tokenId?`} element={} /> + */} } /> )} @@ -308,8 +310,11 @@ export default function App() { <> {/* These are old routes and will soon be deprecated - Check: RoutesWithNetworkParam */} + {/* } /> } /> + */} + } /> } /> From 9629e81e20547ce911d2f87de51196cb1a075be5 Mon Sep 17 00:00:00 2001 From: viet-nv Date: Thu, 23 Nov 2023 07:08:25 +0700 Subject: [PATCH 03/14] disable quickzap too --- src/components/ElasticZap/QuickZap.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/ElasticZap/QuickZap.tsx b/src/components/ElasticZap/QuickZap.tsx index 95eff6f835..e5bd9cd165 100644 --- a/src/components/ElasticZap/QuickZap.tsx +++ b/src/components/ElasticZap/QuickZap.tsx @@ -96,7 +96,8 @@ export const QuickZapButton = ({ const isZapAvailable = !!(networkInfo as EVMNetworkInfo).elastic.zap const theme = useTheme() - return ( + const tmp = true + return tmp ? null : ( Date: Thu, 23 Nov 2023 09:49:56 +0700 Subject: [PATCH 04/14] fix: limit order gasless cancel show wrong status (#2397) --- .../swapv2/LimitOrder/ListOrder/useRequestCancelOrder.tsx | 8 ++++++-- src/components/swapv2/LimitOrder/Modals/CancelButtons.tsx | 7 ++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/components/swapv2/LimitOrder/ListOrder/useRequestCancelOrder.tsx b/src/components/swapv2/LimitOrder/ListOrder/useRequestCancelOrder.tsx index 9997d89b3f..9834d4d3b6 100644 --- a/src/components/swapv2/LimitOrder/ListOrder/useRequestCancelOrder.tsx +++ b/src/components/swapv2/LimitOrder/ListOrder/useRequestCancelOrder.tsx @@ -254,8 +254,12 @@ export const useProcessCancelOrder = ({ if (signal.aborted) return setCancelStatus(gasLessCancel ? CancelStatus.COUNTDOWN : CancelStatus.WAITING) const expired = data?.orders?.[0]?.operatorSignatureExpiredAt - if (expired) setExpiredTime(expired) - else onDismiss() + if (expired) { + setExpiredTime(expired) + if (expired * 1000 < Date.now()) { + isEdit ? onDismiss() : setCancelStatus(CancelStatus.CANCEL_DONE) + } + } else onDismiss() } catch (error) { if (signal.aborted) return setExpiredTime(0) diff --git a/src/components/swapv2/LimitOrder/Modals/CancelButtons.tsx b/src/components/swapv2/LimitOrder/Modals/CancelButtons.tsx index 76463ef465..8d1f6c6789 100644 --- a/src/components/swapv2/LimitOrder/Modals/CancelButtons.tsx +++ b/src/components/swapv2/LimitOrder/Modals/CancelButtons.tsx @@ -166,11 +166,7 @@ const CancelButtons = ({ ) - const propsGasless = { - color: cancelType === CancelOrderType.GAS_LESS_CANCEL ? theme.primary : undefined, - height: '40px', - width: '100%', - } + const propsGasless = { height: '40px', width: '100%' } const propsHardCancel = { style: { height: '40px', width: '100%' }, disabled: disabledHardCancel } if (isCountDown) @@ -217,6 +213,7 @@ const CancelButtons = ({ > onSetType(CancelOrderType.GAS_LESS_CANCEL)} $disabled={disabledGasLessCancel} disabled={disabledGasLessCancel} From 13318cf3bc0f9928a18fd15659fe59be9db82f64 Mon Sep 17 00:00:00 2001 From: Khac Kien Date: Thu, 23 Nov 2023 11:34:37 +0700 Subject: [PATCH 05/14] Call import token if token not found (#2387) * Call import token if token not found * Add transformResponse * Update minor layout * onChangeCurrencyOut compare ignore case * Remove _signal * Remove ununsed importToken --- src/components/SearchModal/CurrencySearch.tsx | 24 ++----------------- src/hooks/Tokens.ts | 20 ++++++++++++---- src/pages/CreatePool/styled.tsx | 16 ++++++------- src/pages/PartnerSwap/index.tsx | 4 +++- src/pages/RemoveLiquidity/styled.tsx | 2 +- src/services/ksSetting.ts | 18 +++++++++++++- 6 files changed, 46 insertions(+), 38 deletions(-) diff --git a/src/components/SearchModal/CurrencySearch.tsx b/src/components/SearchModal/CurrencySearch.tsx index 0e5069f09a..077492b1ab 100644 --- a/src/components/SearchModal/CurrencySearch.tsx +++ b/src/components/SearchModal/CurrencySearch.tsx @@ -7,7 +7,6 @@ import { ChangeEvent, KeyboardEvent, ReactNode, useCallback, useEffect, useMemo, import { Trash } from 'react-feather' import { usePrevious } from 'react-use' import { Flex, Text } from 'rebass' -import { useImportTokenMutation } from 'services/ksSetting' import styled from 'styled-components' import Column from 'components/Column' @@ -102,7 +101,7 @@ const fetchTokens = async ( ): Promise => { try { if (search && chainId && isAddress(chainId, search)) { - const token = await fetchTokenByAddress(search, chainId, signal) + const token = await fetchTokenByAddress(search, chainId) return token ? [token as WrappedTokenInfo] : [] } const params: { query: string; isWhitelisted?: boolean; pageSize: number; page: number; chainIds: string } = { @@ -312,7 +311,6 @@ export function CurrencySearch({ }, [fetchFavoriteTokenFromAddress]) const abortControllerRef = useRef(new AbortController()) - const [importTokensToKsSettings] = useImportTokenMutation() const fetchListTokens = useCallback( async (page?: number) => { const nextPage = (page ?? pageCount) + 1 @@ -335,15 +333,6 @@ export function CurrencySearch({ symbol: rawToken.symbol || 'UNKNOWN', }), ) - - importTokensToKsSettings([ - { - chainId: String(rawToken.chainId), - address: rawToken.address, - }, - ]).catch(err => { - console.error('import token err', err) - }) } } } else { @@ -354,16 +343,7 @@ export function CurrencySearch({ setFetchedTokens(current => (nextPage === 1 ? [] : current).concat(tokens)) setHasMoreToken(tokens.length === PAGE_SIZE && !!debouncedQuery) }, - [ - isImportedTab, - chainId, - debouncedQuery, - defaultTokens, - fetchERC20TokenFromRPC, - isQueryValidEVMAddress, - pageCount, - importTokensToKsSettings, - ], + [isImportedTab, chainId, debouncedQuery, defaultTokens, fetchERC20TokenFromRPC, isQueryValidEVMAddress, pageCount], ) const [hasMoreToken, setHasMoreToken] = useState(false) diff --git a/src/hooks/Tokens.ts b/src/hooks/Tokens.ts index eb0b016ecd..729a6406cc 100644 --- a/src/hooks/Tokens.ts +++ b/src/hooks/Tokens.ts @@ -4,7 +4,7 @@ import axios from 'axios' import { arrayify } from 'ethers/lib/utils' import { useCallback, useMemo } from 'react' import { useSelector } from 'react-redux' -import { useGetTokenListQuery } from 'services/ksSetting' +import ksSettingApi, { useGetTokenListQuery } from 'services/ksSetting' import useSWR from 'swr' import ERC20_INTERFACE, { ERC20_BYTES32_INTERFACE } from 'constants/abis/erc20' @@ -13,7 +13,7 @@ import { ETHER_ADDRESS, ZERO_ADDRESS } from 'constants/index' import { NativeCurrencies } from 'constants/tokens' import { useActiveWeb3React } from 'hooks/index' import { useBytes32TokenContract, useMulticallContract, useTokenReadingContract } from 'hooks/useContract' -import { AppState } from 'state' +import store, { AppState } from 'state' import { TokenAddressMap } from 'state/lists/reducer' import { TokenInfo, WrappedTokenInfo } from 'state/lists/wrappedTokenInfo' import { NEVER_RELOAD, useMultipleContractSingleData, useSingleCallResult } from 'state/multicall/hooks' @@ -294,12 +294,22 @@ export const findCacheToken = (address: string) => { return cacheTokens[address] || cacheTokens[address.toLowerCase()] } -export const fetchTokenByAddress = async (address: string, chainId: ChainId, signal?: AbortSignal) => { +export const fetchTokenByAddress = async (address: string, chainId: ChainId) => { if (address === ZERO_ADDRESS) return NativeCurrencies[chainId] const findToken = findCacheToken(address) if (findToken && findToken.chainId === chainId) return findToken - const response = await axios.get(`${KS_SETTING_API}/v1/tokens?query=${address}&chainIds=${chainId}`, { signal }) - const token = response?.data?.data?.tokens?.[0] + const tokenListRes = await store.dispatch( + ksSettingApi.endpoints.getTokenList.initiate({ chainId, addresses: address }), + ) + let token = tokenListRes.data?.data.tokens[0] + if (!token) { + const importTokenRes = await store.dispatch( + ksSettingApi.endpoints.importToken.initiate([{ chainId: chainId.toString(), address }]), + ) + if ('data' in importTokenRes) { + token = importTokenRes.data?.data.tokens[0] + } + } return token ? formatAndCacheToken(token) : undefined } diff --git a/src/pages/CreatePool/styled.tsx b/src/pages/CreatePool/styled.tsx index b0c334df18..1db87af3bb 100644 --- a/src/pages/CreatePool/styled.tsx +++ b/src/pages/CreatePool/styled.tsx @@ -5,37 +5,37 @@ import { AutoColumn } from 'components/Column' import NumericalInput from 'components/NumericalInput' export const PageWrapper = styled.div` - padding: 16px 16px 100px; + padding: 28px 16px 100px; width: 100%; @media only screen and (min-width: 768px) { - padding: 16px 16px 100px; + padding: 24px 16px 100px; } @media only screen and (min-width: 1000px) { - padding: 16px 32px 100px; + padding: 24px 32px 100px; } @media only screen and (min-width: 1366px) { - padding: 16px 215px 50px; + padding: 24px 215px 50px; } @media only screen and (min-width: 1440px) { - padding: 16px 252px 50px; + padding: 24px 252px 50px; } ` export const Container = styled.div` max-width: 936px; margin: 0 auto; - padding: 24px 20px; + padding: 4px 20px 24px; background: ${({ theme }) => theme.background}; box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 24px 32px rgba(0, 0, 0, 0.01); - border-radius: 8px; + border-radius: 20px; @media only screen and (min-width: 1000px) { - padding: 24px; + padding: 4px 24px 24px; } ` diff --git a/src/pages/PartnerSwap/index.tsx b/src/pages/PartnerSwap/index.tsx index 3c81ae1f27..25bab79478 100644 --- a/src/pages/PartnerSwap/index.tsx +++ b/src/pages/PartnerSwap/index.tsx @@ -178,7 +178,9 @@ export default function Swap() { const onChangeCurrencyOut = useCallback( (c: Currency) => { const value = c.isNative ? c.symbol || c.wrapped.address : c.wrapped.address - if (value === inputTokenFromParam) searchParams.set('inputCurrency', outputTokenFromParam || '') + if (value.toLowerCase() === inputTokenFromParam?.toLowerCase()) { + searchParams.set('inputCurrency', outputTokenFromParam || '') + } searchParams.set('outputCurrency', value) setSearchParams(searchParams) }, diff --git a/src/pages/RemoveLiquidity/styled.tsx b/src/pages/RemoveLiquidity/styled.tsx index 1863998b39..2f64ba4e44 100644 --- a/src/pages/RemoveLiquidity/styled.tsx +++ b/src/pages/RemoveLiquidity/styled.tsx @@ -30,7 +30,7 @@ export const Container = styled.div` background: ${({ theme }) => theme.background}; box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 24px 32px rgba(0, 0, 0, 0.01); - border-radius: 8px; + border-radius: 20px; @media only screen and (min-width: 1000px) { padding: 4px 24px 24px; diff --git a/src/services/ksSetting.ts b/src/services/ksSetting.ts index 8ba01c68f9..c9b2fde851 100644 --- a/src/services/ksSetting.ts +++ b/src/services/ksSetting.ts @@ -50,13 +50,22 @@ export type KyberswapGlobalConfigurationResponse = { export interface TokenListResponse { data: { - pageination: { + pagination?: { totalItems: number } tokens: Array } } +export interface TokenImportResponse { + data: { + tokens: { + data: T + errorMsg: string + }[] + } +} + const ksSettingApi = createApi({ reducerPath: 'ksSettingConfigurationApi', baseQuery: baseQueryOauth({ @@ -119,6 +128,13 @@ const ksSettingApi = createApi({ body: { tokens }, method: 'POST', }), + transformResponse: (response: TokenImportResponse): TokenListResponse => { + const tokens: TokenInfo[] = response.data.tokens.map(token => ({ + ...token.data, + chainId: Number(token.data.chainId), + })) + return { data: { tokens } } + }, }), getTopTokens: builder.query, { chainId: number; page: number }>({ query: params => ({ From 7f539fa2bcaba0c2a44662e4db1df77554d79b35 Mon Sep 17 00:00:00 2001 From: Khac Kien Date: Thu, 23 Nov 2023 13:25:01 +0700 Subject: [PATCH 06/14] Separate slippage tolerance settings between Liquidity Actions & Swap (#2388) * Add state user / poolSlippageTolerance Remove dummies Minor Update setting slippage Separate slippage * KEP-1931 Separate degen mode * Update logic more clear --- src/components/SwapForm/SlippageSetting.tsx | 5 +- .../SwapSettingsPanel/SlippageSetting.tsx | 6 +- .../swapv2/SwapSettingsPanel/index.tsx | 2 +- src/hooks/usePageLocation.ts | 16 ++++ src/pages/CrossChain/SwapForm/index.tsx | 1 - src/state/user/actions.ts | 6 ++ src/state/user/hooks.tsx | 80 ++++++++++++++++--- src/state/user/reducer.ts | 38 ++++++++- src/state/user/updater.tsx | 60 +++++++++++--- 9 files changed, 176 insertions(+), 38 deletions(-) create mode 100644 src/hooks/usePageLocation.ts diff --git a/src/components/SwapForm/SlippageSetting.tsx b/src/components/SwapForm/SlippageSetting.tsx index 9aa9e4c8d1..2eb8ceffc3 100644 --- a/src/components/SwapForm/SlippageSetting.tsx +++ b/src/components/SwapForm/SlippageSetting.tsx @@ -23,13 +23,12 @@ type Props = { isStablePairSwap: boolean rightComponent?: ReactNode tooltip?: ReactNode - isCrossChain?: boolean } -const SlippageSetting = ({ isStablePairSwap, rightComponent, tooltip, isCrossChain }: Props) => { +const SlippageSetting = ({ isStablePairSwap, rightComponent, tooltip }: Props) => { const theme = useTheme() const [expanded, setExpanded] = useState(false) - const { setRawSlippage, rawSlippage, isSlippageControlPinned } = useSlippageSettingByPage(isCrossChain) + const { rawSlippage, setRawSlippage, isSlippageControlPinned } = useSlippageSettingByPage() const defaultRawSlippage = getDefaultSlippage(isStablePairSwap) const isWarningSlippage = checkWarningSlippage(rawSlippage, isStablePairSwap) diff --git a/src/components/swapv2/SwapSettingsPanel/SlippageSetting.tsx b/src/components/swapv2/SwapSettingsPanel/SlippageSetting.tsx index 5fe6d34c57..f239003dae 100644 --- a/src/components/swapv2/SwapSettingsPanel/SlippageSetting.tsx +++ b/src/components/swapv2/SwapSettingsPanel/SlippageSetting.tsx @@ -29,12 +29,10 @@ const Message = styled.div` type Props = { shouldShowPinButton?: boolean - isCrossChain?: boolean } -const SlippageSetting: React.FC = ({ shouldShowPinButton = true, isCrossChain = false }) => { - const { rawSlippage, setRawSlippage, isSlippageControlPinned, togglePinSlippage } = - useSlippageSettingByPage(isCrossChain) +const SlippageSetting: React.FC = ({ shouldShowPinButton = true }) => { + const { rawSlippage, setRawSlippage, isSlippageControlPinned, togglePinSlippage } = useSlippageSettingByPage() const isStablePairSwap = useCheckStablePairSwap() const slippageStatus = checkRangeSlippage(rawSlippage, isStablePairSwap) diff --git a/src/components/swapv2/SwapSettingsPanel/index.tsx b/src/components/swapv2/SwapSettingsPanel/index.tsx index 38b7d9c9e2..a3f12990ec 100644 --- a/src/components/swapv2/SwapSettingsPanel/index.tsx +++ b/src/components/swapv2/SwapSettingsPanel/index.tsx @@ -112,7 +112,7 @@ const SettingsPanel: React.FC = ({ Advanced Settings - + {isSwapPage && } {isSwapPage && ( diff --git a/src/hooks/usePageLocation.ts b/src/hooks/usePageLocation.ts new file mode 100644 index 0000000000..f0daf0b52b --- /dev/null +++ b/src/hooks/usePageLocation.ts @@ -0,0 +1,16 @@ +import { useLocation } from 'react-router-dom' + +import { APP_PATHS } from 'constants/index' + +const SWAP_PAGE_URLS = [APP_PATHS.SWAP, APP_PATHS.PARTNER_SWAP, APP_PATHS.LIMIT, APP_PATHS.CROSS_CHAIN] + +const usePageLocation = () => { + const location = useLocation() + + return { + isSwapPage: SWAP_PAGE_URLS.some(url => location.pathname.startsWith(url)), + isCrossChain: location.pathname.startsWith(APP_PATHS.CROSS_CHAIN), + } +} + +export default usePageLocation diff --git a/src/pages/CrossChain/SwapForm/index.tsx b/src/pages/CrossChain/SwapForm/index.tsx index f12c8c1edf..825b13296e 100644 --- a/src/pages/CrossChain/SwapForm/index.tsx +++ b/src/pages/CrossChain/SwapForm/index.tsx @@ -349,7 +349,6 @@ export default function SwapForm() { diff --git a/src/state/user/actions.ts b/src/state/user/actions.ts index dec2df2baa..81be2ce543 100644 --- a/src/state/user/actions.ts +++ b/src/state/user/actions.ts @@ -22,11 +22,17 @@ export interface SerializedPair { export const updateUserDegenMode = createAction<{ userDegenMode: boolean; isStablePairSwap: boolean }>( 'user/updateUserDegenMode', ) +export const updatePoolDegenMode = createAction<{ poolDegenMode: boolean; isStablePairSwap: boolean }>( + 'user/updatePoolDegenMode', +) export const toggleUseAggregatorForZap = createAction('user/toggleUseAggregatorForZap') export const updateUserLocale = createAction<{ userLocale: SupportedLocale }>('user/updateUserLocale') export const updateUserSlippageTolerance = createAction<{ userSlippageTolerance: number }>( 'user/updateUserSlippageTolerance', ) +export const updatePoolSlippageTolerance = createAction<{ poolSlippageTolerance: number }>( + 'user/updatePoolSlippageTolerance', +) export const updateUserDeadline = createAction<{ userDeadline: number }>('user/updateUserDeadline') export const addSerializedToken = createAction<{ serializedToken: SerializedToken }>('user/addSerializedToken') diff --git a/src/state/user/hooks.tsx b/src/state/user/hooks.tsx index 73f1d70937..7bef45192c 100644 --- a/src/state/user/hooks.tsx +++ b/src/state/user/hooks.tsx @@ -15,6 +15,7 @@ import { useStaticFeeFactoryContract, } from 'hooks/useContract' import useDebounce from 'hooks/useDebounce' +import usePageLocation from 'hooks/usePageLocation' import { ParticipantInfo, ParticipantStatus } from 'pages/TrueSightV2/types' import { AppDispatch, AppState } from 'state' import { useKyberSwapConfig } from 'state/application/hooks' @@ -43,6 +44,8 @@ import { toggleTradeRoutes, toggleUseAggregatorForZap, updateAcceptedTermVersion, + updatePoolDegenMode, + updatePoolSlippageTolerance, updateTokenAnalysisSettings, updateUserDeadline, updateUserDegenMode, @@ -121,15 +124,38 @@ export function useIsAcceptedTerm(): [boolean, (isAcceptedTerm: boolean) => void } export function useDegenModeManager(): [boolean, () => void] { + const [swapDegenMode, toggleSwapDegenMode] = useSwapDegenMode() + const [poolDegenMode, togglePoolDegenMode] = usePoolDegenMode() + + const { isSwapPage } = usePageLocation() + if (isSwapPage) { + return [swapDegenMode, toggleSwapDegenMode] + } + return [poolDegenMode, togglePoolDegenMode] +} + +export function useSwapDegenMode(): [boolean, () => void] { const dispatch = useDispatch() - const degenMode = useSelector(state => state.user.userDegenMode) const isStablePairSwap = useCheckStablePairSwap() - const toggleSetDegenMode = useCallback(() => { - dispatch(updateUserDegenMode({ userDegenMode: !degenMode, isStablePairSwap })) - }, [degenMode, dispatch, isStablePairSwap]) + const userDegenMode = useSelector(state => state.user.userDegenMode) + const toggleUserDegenMode = useCallback(() => { + dispatch(updateUserDegenMode({ userDegenMode: !userDegenMode, isStablePairSwap })) + }, [userDegenMode, dispatch, isStablePairSwap]) - return [degenMode, toggleSetDegenMode] + return [userDegenMode, toggleUserDegenMode] +} + +export function usePoolDegenMode(): [boolean, () => void] { + const dispatch = useDispatch() + const isStablePairSwap = useCheckStablePairSwap() + + const poolDegenMode = useSelector(state => state.user.poolDegenMode) + const togglePoolDegenMode = useCallback(() => { + dispatch(updatePoolDegenMode({ poolDegenMode: !poolDegenMode, isStablePairSwap })) + }, [poolDegenMode, dispatch, isStablePairSwap]) + + return [poolDegenMode, togglePoolDegenMode] } export function useAggregatorForZapSetting(): [boolean, () => void] { @@ -146,21 +172,44 @@ export function useAggregatorForZapSetting(): [boolean, () => void] { } export function useUserSlippageTolerance(): [number, (slippage: number) => void] { + const [swapSlippageTolerance, setSwapSlippageTolerance] = useSwapSlippageTolerance() + const [poolSlippageTolerance, setPoolSlippageTolerance] = usePoolSlippageTolerance() + + const { isSwapPage } = usePageLocation() + if (isSwapPage) { + return [swapSlippageTolerance, setSwapSlippageTolerance] + } + return [poolSlippageTolerance, setPoolSlippageTolerance] +} + +export function useSwapSlippageTolerance(): [number, (slippage: number) => void] { const dispatch = useDispatch() const userSlippageTolerance = useSelector(state => { return state.user.userSlippageTolerance }) - const setUserSlippageTolerance = useCallback( (userSlippageTolerance: number) => { dispatch(updateUserSlippageTolerance({ userSlippageTolerance })) }, [dispatch], ) - return [userSlippageTolerance, setUserSlippageTolerance] } +export function usePoolSlippageTolerance(): [number, (slippage: number) => void] { + const dispatch = useDispatch() + const poolSlippageTolerance = useSelector(state => { + return state.user.poolSlippageTolerance + }) + const setPoolSlippageTolerance = useCallback( + (poolSlippageTolerance: number) => { + dispatch(updatePoolSlippageTolerance({ poolSlippageTolerance })) + }, + [dispatch], + ) + return [poolSlippageTolerance, setPoolSlippageTolerance] +} + export function useUserTransactionTTL(): [number, (slippage: number) => void] { const dispatch = useDispatch() const userDeadline = useSelector(state => { @@ -461,10 +510,12 @@ export const useCrossChainSetting = () => { return { setting, setExpressExecutionMode, setRawSlippage, toggleSlippageControlPinned } } -export const useSlippageSettingByPage = (isCrossChain = false) => { +export const useSlippageSettingByPage = () => { const dispatch = useDispatch() + const { isCrossChain } = usePageLocation() + const [rawSlippageTolerance, setRawSlippageTolerance] = useUserSlippageTolerance() + const isPinSlippageSwap = useAppSelector(state => state.user.isSlippageControlPinned) - const [rawSlippageSwap, setRawSlippageSwap] = useUserSlippageTolerance() const togglePinSlippageSwap = () => { dispatch(pinSlippageControl(!isSlippageControlPinned)) } @@ -475,12 +526,17 @@ export const useSlippageSettingByPage = (isCrossChain = false) => { toggleSlippageControlPinned: togglePinnedSlippageCrossChain, } = useCrossChainSetting() + const rawSlippage = isCrossChain ? rawSlippageSwapCrossChain : rawSlippageTolerance + const setRawSlippage = isCrossChain ? setRawSlippageCrossChain : setRawSlippageTolerance const isSlippageControlPinned = isCrossChain ? isPinSlippageCrossChain : isPinSlippageSwap - const rawSlippage = isCrossChain ? rawSlippageSwapCrossChain : rawSlippageSwap - const setRawSlippage = isCrossChain ? setRawSlippageCrossChain : setRawSlippageSwap const togglePinSlippage = isCrossChain ? togglePinnedSlippageCrossChain : togglePinSlippageSwap - return { setRawSlippage, rawSlippage, isSlippageControlPinned, togglePinSlippage } + return { + rawSlippage, + setRawSlippage, + isSlippageControlPinned, + togglePinSlippage, + } } const participantDefault = { diff --git a/src/state/user/reducer.ts b/src/state/user/reducer.ts index 75e1d0ab09..373102e0ea 100644 --- a/src/state/user/reducer.ts +++ b/src/state/user/reducer.ts @@ -35,6 +35,8 @@ import { toggleUseAggregatorForZap, updateAcceptedTermVersion, updateChainId, + updatePoolDegenMode, + updatePoolSlippageTolerance, updateTokenAnalysisSettings, updateUserDeadline, updateUserDegenMode, @@ -64,10 +66,13 @@ export interface UserState { userDegenMode: boolean userDegenModeAutoDisableTimestamp: number + poolDegenMode: boolean + poolDegenModeAutoDisableTimestamp: number useAggregatorForZap: boolean - // user defined slippage tolerance in bips, used in all txns - userSlippageTolerance: number + // user defined slippage tolerance in bips, used in SWAP page + userSlippageTolerance: number // For SWAP page + poolSlippageTolerance: number // For POOL and other pages // deadline set by user in minutes, used in all txns userDeadline: number @@ -149,11 +154,14 @@ export const CROSS_CHAIN_SETTING_DEFAULT = { } const initialState: UserState = { - userDegenMode: false, - useAggregatorForZap: true, + userDegenMode: false, // For SWAP page userDegenModeAutoDisableTimestamp: 0, + poolDegenMode: false, // For POOL and other pages + poolDegenModeAutoDisableTimestamp: 0, + useAggregatorForZap: true, userLocale: null, userSlippageTolerance: INITIAL_ALLOWED_SLIPPAGE, + poolSlippageTolerance: INITIAL_ALLOWED_SLIPPAGE, userDeadline: DEFAULT_DEADLINE_FROM_NOW, tokens: {}, pairs: {}, @@ -230,6 +238,24 @@ export default createReducer(initialState, builder => } state.timestamp = currentTimestamp() }) + .addCase(updatePoolDegenMode, (state, action) => { + state.poolDegenMode = action.payload.poolDegenMode + if (action.payload.poolDegenMode) { + state.poolDegenModeAutoDisableTimestamp = Date.now() + AUTO_DISABLE_DEGEN_MODE_MINUTES * 60 * 1000 + } else { + // If max slippage <= 19.99%, no need update slippage. + if (state.poolSlippageTolerance <= MAX_NORMAL_SLIPPAGE_IN_BIPS) { + return + } + // Else, update to default slippage. + if (action.payload.isStablePairSwap) { + state.poolSlippageTolerance = Math.min(state.poolSlippageTolerance, DEFAULT_SLIPPAGE_STABLE_PAIR_SWAP) + } else { + state.poolSlippageTolerance = Math.min(state.poolSlippageTolerance, DEFAULT_SLIPPAGE) + } + } + state.timestamp = currentTimestamp() + }) .addCase(updateUserLocale, (state, action) => { state.userLocale = action.payload.userLocale state.timestamp = currentTimestamp() @@ -238,6 +264,10 @@ export default createReducer(initialState, builder => state.userSlippageTolerance = action.payload.userSlippageTolerance state.timestamp = currentTimestamp() }) + .addCase(updatePoolSlippageTolerance, (state, action) => { + state.poolSlippageTolerance = action.payload.poolSlippageTolerance + state.timestamp = currentTimestamp() + }) .addCase(updateUserDeadline, (state, action) => { state.userDeadline = action.payload.userDeadline state.timestamp = currentTimestamp() diff --git a/src/state/user/updater.tsx b/src/state/user/updater.tsx index 5a15e1c5df..7fe80e4f38 100644 --- a/src/state/user/updater.tsx +++ b/src/state/user/updater.tsx @@ -5,31 +5,65 @@ import { useInterval } from 'react-use' import { DEFAULT_SLIPPAGE, DEFAULT_SLIPPAGE_STABLE_PAIR_SWAP, MAX_NORMAL_SLIPPAGE_IN_BIPS } from 'constants/index' import { AppDispatch, AppState } from 'state/index' import { useCheckStablePairSwap } from 'state/swap/hooks' -import { useUserSlippageTolerance } from 'state/user/hooks' +import { usePoolSlippageTolerance, useSwapSlippageTolerance } from 'state/user/hooks' -import { updateUserDegenMode } from './actions' +import { updatePoolDegenMode, updateUserDegenMode } from './actions' export default function Updater(): null { const dispatch = useDispatch() - const degenMode = useSelector(state => state.user.userDegenMode) - const userDegenModeAutoDisableTimestamp = useSelector< + const isStablePairSwap = useCheckStablePairSwap() + + const swapDegenMode = useSelector(state => state.user.userDegenMode) + const swapDegenModeAutoDisableTimestamp = useSelector< AppState, AppState['user']['userDegenModeAutoDisableTimestamp'] >(state => state.user.userDegenModeAutoDisableTimestamp) - const isStablePairSwap = useCheckStablePairSwap() - const [rawSlippage, setRawSlippage] = useUserSlippageTolerance() - const autoDisableDegenMode = useCallback(() => { - if (degenMode && userDegenModeAutoDisableTimestamp <= Date.now()) { + const poolDegenMode = useSelector(state => state.user.poolDegenMode) + const poolDegenModeAutoDisableTimestamp = useSelector< + AppState, + AppState['user']['poolDegenModeAutoDisableTimestamp'] + >(state => state.user.poolDegenModeAutoDisableTimestamp) + + const [swapSlippageTolerance, setSwapSlippageTolerance] = useSwapSlippageTolerance() + const [poolSlippageTolerance, setPoolSlippageTolerance] = usePoolSlippageTolerance() + + const autoDisableSwapDegenMode = useCallback(() => { + if (swapDegenMode && swapDegenModeAutoDisableTimestamp <= Date.now()) { dispatch(updateUserDegenMode({ userDegenMode: false, isStablePairSwap })) - if (rawSlippage > MAX_NORMAL_SLIPPAGE_IN_BIPS) { - if (isStablePairSwap) setRawSlippage(DEFAULT_SLIPPAGE_STABLE_PAIR_SWAP) - else setRawSlippage(DEFAULT_SLIPPAGE) + if (swapSlippageTolerance > MAX_NORMAL_SLIPPAGE_IN_BIPS) { + if (isStablePairSwap) setSwapSlippageTolerance(DEFAULT_SLIPPAGE_STABLE_PAIR_SWAP) + else setSwapSlippageTolerance(DEFAULT_SLIPPAGE) + } + } + }, [ + swapDegenMode, + dispatch, + isStablePairSwap, + swapSlippageTolerance, + setSwapSlippageTolerance, + swapDegenModeAutoDisableTimestamp, + ]) + + const autoDisablePoolDegenMode = useCallback(() => { + if (poolDegenMode && poolDegenModeAutoDisableTimestamp <= Date.now()) { + dispatch(updatePoolDegenMode({ poolDegenMode: false, isStablePairSwap })) + if (poolSlippageTolerance > MAX_NORMAL_SLIPPAGE_IN_BIPS) { + if (isStablePairSwap) setPoolSlippageTolerance(DEFAULT_SLIPPAGE_STABLE_PAIR_SWAP) + else setPoolSlippageTolerance(DEFAULT_SLIPPAGE) } } - }, [degenMode, dispatch, isStablePairSwap, rawSlippage, setRawSlippage, userDegenModeAutoDisableTimestamp]) + }, [ + poolDegenMode, + dispatch, + isStablePairSwap, + poolSlippageTolerance, + setPoolSlippageTolerance, + poolDegenModeAutoDisableTimestamp, + ]) - useInterval(autoDisableDegenMode, 1_000) + useInterval(autoDisableSwapDegenMode, 1_000) + useInterval(autoDisablePoolDegenMode, 1_000) return null } From ae3168bed86862b9eb3a41d9f6057082f14e6663 Mon Sep 17 00:00:00 2001 From: Khac Kien Date: Thu, 23 Nov 2023 14:59:39 +0700 Subject: [PATCH 07/14] KEP-1929 Prevent noti on partner-swap page (#2398) --- src/components/Announcement/helper.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/Announcement/helper.ts b/src/components/Announcement/helper.ts index 5dbd31a435..88fe91026a 100644 --- a/src/components/Announcement/helper.ts +++ b/src/components/Announcement/helper.ts @@ -23,13 +23,16 @@ export const ackAnnouncementPopup = (id: string | number) => { export const formatNumberOfUnread = (num: number | undefined) => (num ? (num > 10 ? '10+' : num + '') : null) +const NO_NOTI_PAGES = [APP_PATHS.IAM_CONSENT, APP_PATHS.IAM_LOGIN, APP_PATHS.IAM_LOGOUT, APP_PATHS.PARTNER_SWAP] + export const isPopupCanShow = ( popupInfo: PopupItemType, chainId: ChainId, account: string | undefined, ) => { - if ([APP_PATHS.IAM_CONSENT, APP_PATHS.IAM_LOGIN, APP_PATHS.IAM_LOGOUT].includes(window.location.pathname)) + if (NO_NOTI_PAGES.some(path => window.location.pathname.startsWith(path))) { return false + } const { templateBody = {}, metaMessageId } = popupInfo.content const { endAt, startAt, chainIds = [] } = templateBody as AnnouncementTemplatePopup From ef8ce26765a5039dd92b731f753ee54125c5524a Mon Sep 17 00:00:00 2001 From: XiaoYhun Date: Fri, 24 Nov 2023 09:44:21 +0700 Subject: [PATCH 08/14] temporary hide voting rewards stat (#2400) --- src/pages/KyberDAO/Vote/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/KyberDAO/Vote/index.tsx b/src/pages/KyberDAO/Vote/index.tsx index 420006e3da..25af746fa9 100644 --- a/src/pages/KyberDAO/Vote/index.tsx +++ b/src/pages/KyberDAO/Vote/index.tsx @@ -117,7 +117,7 @@ export default function Vote() { claimedRewardAmount, stakerInfo, stakerInfoNextEpoch, - rewardStats: { knc, usd, apr }, + // rewardStats: { knc, usd, apr }, } = useVotingInfo() const kncPrice = useKNCPrice() @@ -236,7 +236,7 @@ export default function Vote() { - + {/* @@ -259,7 +259,7 @@ export default function Vote() { ~{(+usd?.toFixed(0)).toLocaleString() ?? '--'} USD - + */} From b2ee1ac86f4f8a28950a13b33198739be22e40a0 Mon Sep 17 00:00:00 2001 From: Nam Nguyen Date: Fri, 24 Nov 2023 10:50:28 +0700 Subject: [PATCH 09/14] improvement: validate mock address (#2401) --- src/constants/env.ts | 6 ++++-- src/utils/address.ts | 29 +++++++++++++++++++++++++++++ src/utils/index.ts | 30 +++--------------------------- 3 files changed, 36 insertions(+), 29 deletions(-) create mode 100644 src/utils/address.ts diff --git a/src/constants/env.ts b/src/constants/env.ts index a11cbe6734..6413cb64c9 100644 --- a/src/constants/env.ts +++ b/src/constants/env.ts @@ -1,6 +1,8 @@ +import { ChainId } from '@kyberswap/ks-sdk-core' import invariant from 'tiny-invariant' import { PrivateAnnouncementType } from 'components/Announcement/type' +import { isAddressString } from 'utils/address' import { ENV_TYPE } from './type' @@ -162,8 +164,8 @@ export const getAnnouncementsTemplateIds = (type: keyof TemplateConfig) => { } const mock = localStorage.getItem('mock')?.split(',') ?? [] -export const MOCK_ACCOUNT_EVM = mock[0] ?? '' -export const MOCK_ACCOUNT_SOLANA = mock[1] ?? '' +export const MOCK_ACCOUNT_EVM = isAddressString(ChainId.MAINNET, mock[0]?.trim()) +export const MOCK_ACCOUNT_SOLANA = isAddressString(ChainId.SOLANA, mock[1]?.trim()) const isSupportTestNet = ENV_LEVEL < ENV_TYPE.PROD && new URLSearchParams(window.location.search).get('test') export const CROSS_CHAIN_CONFIG = { diff --git a/src/utils/address.ts b/src/utils/address.ts new file mode 100644 index 0000000000..365df0b211 --- /dev/null +++ b/src/utils/address.ts @@ -0,0 +1,29 @@ +import { ChainId, Token } from '@kyberswap/ks-sdk-core' +import { PublicKey } from '@solana/web3.js' + +export const isWalletAddressSolana = async (addr: string) => { + try { + if (!addr) return false + const publicKey = new PublicKey(addr) + return await PublicKey.isOnCurve(publicKey.toBytes()) + } catch (err) { + return false + } +} + +// returns the checksummed address if the address is valid, otherwise returns false +export function isAddress(chainId: ChainId, value: any): string | false { + try { + return new Token(chainId, value, 0).address + } catch { + return false + } +} + +export function isAddressString(chainId: ChainId, value: any): string { + try { + return new Token(chainId, value, 0).address + } catch { + return '' + } +} diff --git a/src/utils/index.ts b/src/utils/index.ts index 5edd8718db..b7e5ad6e05 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,8 +1,7 @@ import { ApolloClient, NormalizedCacheObject } from '@apollo/client' import { BigNumber } from '@ethersproject/bignumber' -import { ChainId, Currency, CurrencyAmount, Percent, Token, WETH } from '@kyberswap/ks-sdk-core' +import { ChainId, Currency, CurrencyAmount, Percent, WETH } from '@kyberswap/ks-sdk-core' import { WalletReadyState } from '@solana/wallet-adapter-base' -import { PublicKey } from '@solana/web3.js' import dayjs from 'dayjs' import JSBI from 'jsbi' import Numeral from 'numeral' @@ -26,32 +25,9 @@ import store from 'state' import { GroupedTxsByHash, TransactionDetails } from 'state/transactions/type' import { chunk } from 'utils/array' -export const isWalletAddressSolana = async (addr: string) => { - try { - if (!addr) return false - const publicKey = new PublicKey(addr) - return await PublicKey.isOnCurve(publicKey.toBytes()) - } catch (err) { - return false - } -} +import { isAddress, isAddressString, isWalletAddressSolana } from './address' -// returns the checksummed address if the address is valid, otherwise returns false -export function isAddress(chainId: ChainId, value: any): string | false { - try { - return new Token(chainId, value, 0).address - } catch { - return false - } -} - -export function isAddressString(chainId: ChainId, value: any): string { - try { - return new Token(chainId, value, 0).address - } catch { - return '' - } -} +export { isAddress, isWalletAddressSolana, isAddressString } export function getEtherscanLink( chainId: ChainId, From be3b1d83add8940d2bcf2396621c458b90935986 Mon Sep 17 00:00:00 2001 From: Nguyen Van Viet Date: Fri, 24 Nov 2023 15:08:10 +0700 Subject: [PATCH 10/14] support elastic hack (#2402) --- src/pages/RemoveLiquidityProAmm/index.tsx | 54 ++++++++++++++++------- src/state/farms/elastic/hooks.ts | 9 +++- src/state/farms/elasticv2/updater.tsx | 12 +++-- src/state/user/hooks.tsx | 4 +- 4 files changed, 56 insertions(+), 23 deletions(-) diff --git a/src/pages/RemoveLiquidityProAmm/index.tsx b/src/pages/RemoveLiquidityProAmm/index.tsx index aa625764f9..8481d2fb62 100644 --- a/src/pages/RemoveLiquidityProAmm/index.tsx +++ b/src/pages/RemoveLiquidityProAmm/index.tsx @@ -1,7 +1,7 @@ import { BigNumber } from '@ethersproject/bignumber' import { TransactionResponse } from '@ethersproject/providers' import { ZERO } from '@kyberswap/ks-sdk-classic' -import { Currency, CurrencyAmount, Percent, WETH } from '@kyberswap/ks-sdk-core' +import { ChainId, Currency, CurrencyAmount, Percent, WETH } from '@kyberswap/ks-sdk-core' import { FeeAmount, NonfungiblePositionManager } from '@kyberswap/ks-sdk-elastic' import { Trans, t } from '@lingui/macro' import { captureException } from '@sentry/react' @@ -29,6 +29,7 @@ import ProAmmPooledTokens from 'components/ProAmm/ProAmmPooledTokens' import { RowBetween } from 'components/Row' import Slider from 'components/Slider' import { SLIPPAGE_EXPLANATION_URL } from 'components/SlippageWarningNote' +import Toggle from 'components/Toggle' import { MouseoverTooltip, TextDashed } from 'components/Tooltip' import TransactionConfirmationModal, { ConfirmationModalContent, @@ -154,7 +155,15 @@ function Remove({ tokenId }: { tokenId: BigNumber }) { const { position } = useProAmmPositionsFromTokenId(tokenId) const positionManager = useProAmmNFTPositionManagerReadingContract() const theme = useTheme() + const [claimFee, setIsClaimFee] = useState(false) + const { networkInfo, account, chainId, isEVM } = useActiveWeb3React() + useEffect(() => { + if (chainId === ChainId.LINEA || chainId === ChainId.SCROLL) { + setIsClaimFee(true) + } + }, [chainId]) + const { library } = useWeb3React() const toggleWalletModal = useWalletModalToggle() const [removeLiquidityError, setRemoveLiquidityError] = useState('') @@ -317,9 +326,9 @@ function Remove({ tokenId }: { tokenId: BigNumber }) { amount1Min.quotient.toString(), deadline.toString(), buildFlagsForFarmV21({ - isClaimFee: !!feeValue0?.greaterThan('0') && !!feeValue1?.greaterThan('0'), + isClaimFee: claimFee && !!feeValue0?.greaterThan('0') && !!feeValue1?.greaterThan('0'), isSyncFee: !!feeValue0?.greaterThan('0') && !!feeValue1?.greaterThan('0'), - isClaimReward: true, + isClaimReward: claimFee, isReceiveNative: !receiveWETH, }), ] @@ -330,7 +339,7 @@ function Remove({ tokenId }: { tokenId: BigNumber }) { amount0Min.quotient.toString(), amount1Min.quotient.toString(), deadline.toString(), - feeValue0?.greaterThan('0'), + claimFee ? feeValue0?.greaterThan('0') : 0, !receiveWETH, ] : [ @@ -340,7 +349,7 @@ function Remove({ tokenId }: { tokenId: BigNumber }) { amount1Min.quotient.toString(), deadline.toString(), !receiveWETH, - [feeValue0?.greaterThan('0'), true], + [claimFee && feeValue0?.greaterThan('0'), claimFee], ] const gasEstimation = await contract.estimateGas.removeLiquidity(...params) @@ -409,7 +418,7 @@ function Remove({ tokenId }: { tokenId: BigNumber }) { recipient: account, deadline: deadline.toString(), isRemovingLiquid: true, - havingFee: !(feeValue0.equalTo(JSBI.BigInt('0')) && feeValue1.equalTo(JSBI.BigInt('0'))), + havingFee: claimFee && !(feeValue0.equalTo(JSBI.BigInt('0')) && feeValue1.equalTo(JSBI.BigInt('0'))), }, }) const txn = { @@ -434,6 +443,7 @@ function Remove({ tokenId }: { tokenId: BigNumber }) { }) }) .catch((error: any) => { + console.log('error', error) setAttemptingTxn(false) if (!didUserReject(error)) { @@ -467,8 +477,8 @@ function Remove({ tokenId }: { tokenId: BigNumber }) { Removing {liquidityValue0?.toSignificant(6)} {liquidityValue0?.currency?.symbol} and{' '} {liquidityValue1?.toSignificant(6)} {liquidityValue1?.currency?.symbol} - {feeValue0?.greaterThan(ZERO) || feeValue1?.greaterThan(ZERO) ?
: ''} - {feeValue0?.greaterThan(ZERO) || feeValue1?.greaterThan(ZERO) + {claimFee && (feeValue0?.greaterThan(ZERO) || feeValue1?.greaterThan(ZERO)) ?
: ''} + {claimFee && (feeValue0?.greaterThan(ZERO) || feeValue1?.greaterThan(ZERO)) ? `Collecting fee of ${feeValue0?.toSignificant(6)} ${ feeValue0?.currency?.symbol } and ${feeValue1?.toSignificant(6)} ${feeValue1?.currency?.symbol}` @@ -523,13 +533,15 @@ function Remove({ tokenId }: { tokenId: BigNumber }) { title={t`Remove Amount`} /> {positionSDK ? ( - + claimFee ? ( + + ) : null ) : ( )} @@ -780,6 +792,18 @@ function Remove({ tokenId }: { tokenId: BigNumber }) { /> + + + + Claim Your Fees Earned + + { + setIsClaimFee(prev => !prev) + }} + /> + {slippageStatus === SLIPPAGE_STATUS.HIGH && ( diff --git a/src/state/farms/elastic/hooks.ts b/src/state/farms/elastic/hooks.ts index 6ad61e329b..84dacd894b 100644 --- a/src/state/farms/elastic/hooks.ts +++ b/src/state/farms/elastic/hooks.ts @@ -1,5 +1,5 @@ import { defaultAbiCoder } from '@ethersproject/abi' -import { Currency, CurrencyAmount, Token } from '@kyberswap/ks-sdk-core' +import { ChainId, Currency, CurrencyAmount, Token } from '@kyberswap/ks-sdk-core' import { FeeAmount, Position, computePoolAddress } from '@kyberswap/ks-sdk-elastic' import { t } from '@lingui/macro' import { BigNumber } from 'ethers' @@ -677,6 +677,7 @@ const getUserInfoFragment = farmInterface.getFunction('getUserInfo') export function useJoinedPositions() { const positions = useDepositedNfts() const { farms } = useElasticFarms() + const { chainId } = useActiveWeb3React() const params = useMemo(() => { return (farms || []).map(farm => { @@ -762,6 +763,10 @@ export function useJoinedPositions() { } else { rewardPendings[pid][i] = rewardPendings[pid][i].add(amount) } + + // TODO: + if (chainId !== ChainId.LINEA && chainId !== ChainId.SCROLL) + rewardPendings[pid][i] = CurrencyAmount.fromRawAmount(currency, 0) }) } } @@ -774,7 +779,7 @@ export function useJoinedPositions() { } }) return userInfo - }, [result, params, farms, positions]) + }, [result, params, chainId, farms, positions]) } export function useUserInfoByFarm(farmAddress: string): UserInfo { diff --git a/src/state/farms/elasticv2/updater.tsx b/src/state/farms/elasticv2/updater.tsx index 61bacf0727..959bceccb4 100644 --- a/src/state/farms/elasticv2/updater.tsx +++ b/src/state/farms/elasticv2/updater.tsx @@ -2,7 +2,7 @@ import { gql, useLazyQuery } from '@apollo/client' import { defaultAbiCoder } from '@ethersproject/abi' import { getCreate2Address } from '@ethersproject/address' import { keccak256 } from '@ethersproject/solidity' -import { CurrencyAmount, Token, WETH } from '@kyberswap/ks-sdk-core' +import { ChainId, CurrencyAmount, Token, WETH } from '@kyberswap/ks-sdk-core' import { FeeAmount, Pool, Position } from '@kyberswap/ks-sdk-elastic' import { BigNumber } from 'ethers' import { Interface } from 'ethers/lib/utils' @@ -419,9 +419,13 @@ export default function ElasticFarmV2Updater({ interval = true }: { interval?: b +stakedPos.amount0.toExact() * (prices[stakedPos.amount0.currency.wrapped.address] || 0) + +stakedPos.amount1.toExact() * (prices[stakedPos.amount1.currency.wrapped.address] || 0) - const unclaimedRewards = farm.totalRewards.map((rw, i) => - CurrencyAmount.fromRawAmount(rw.currency, item.currentUnclaimedRewards[i].toString()), - ) + // TODO: temporary set 0: item.currentUnclaimedRewards[i].toString() + const unclaimedRewards = + chainId === ChainId.LINEA || chainId === ChainId.SCROLL + ? farm.totalRewards.map((rw, i) => + CurrencyAmount.fromRawAmount(rw.currency, item.currentUnclaimedRewards[i].toString()), + ) + : farm.totalRewards.map((rw, _i) => CurrencyAmount.fromRawAmount(rw.currency, 0)) const unclaimedRewardsUsd = unclaimedRewards.reduce( (total, item) => total + +item.toExact() * (prices[item.currency.wrapped.address] || 0), diff --git a/src/state/user/hooks.tsx b/src/state/user/hooks.tsx index 7bef45192c..78fc322394 100644 --- a/src/state/user/hooks.tsx +++ b/src/state/user/hooks.tsx @@ -4,7 +4,7 @@ import { useDispatch, useSelector } from 'react-redux' import { useGetParticipantInfoQuery, useLazyGetParticipantInfoQuery } from 'services/kyberAISubscription' import { SUGGESTED_BASES } from 'constants/bases' -import { TERM_FILES_PATH } from 'constants/index' +import { INITIAL_ALLOWED_SLIPPAGE, TERM_FILES_PATH } from 'constants/index' import { SupportedLocale } from 'constants/locales' import { PINNED_PAIRS } from 'constants/tokens' import { useActiveWeb3React } from 'hooks' @@ -199,7 +199,7 @@ export function useSwapSlippageTolerance(): [number, (slippage: number) => void] export function usePoolSlippageTolerance(): [number, (slippage: number) => void] { const dispatch = useDispatch() const poolSlippageTolerance = useSelector(state => { - return state.user.poolSlippageTolerance + return state.user.poolSlippageTolerance || INITIAL_ALLOWED_SLIPPAGE }) const setPoolSlippageTolerance = useCallback( (poolSlippageTolerance: number) => { From 9323de7613d8a6bdfe3155acb3fa248d4c44a973 Mon Sep 17 00:00:00 2001 From: Khac Kien Date: Mon, 27 Nov 2023 14:34:18 +0700 Subject: [PATCH 11/14] Fix issues for iframe partner-swap (#2390) * Remove history tab in "qs" for CrossChain tab * Check customChainId for Edit/CancelOrderModal * Update useTokensBalanceOfAnotherChain by rtkQuery Update * Fix rawToken not by customChainId * Revert using new variable array * Get dex by chainId --- src/components/SearchModal/CurrencyList.tsx | 2 +- src/components/SearchModal/CurrencySearch.tsx | 2 +- src/components/TradeRouting/index.tsx | 2 +- .../swapv2/LimitOrder/EditOrderModal.tsx | 8 +- .../swapv2/LimitOrder/ListOrder/index.tsx | 8 +- .../LimitOrder/Modals/CancelOrderModal.tsx | 13 ++- .../LimitOrder/useFetchActiveAllOrders.ts | 5 +- src/hooks/bridge/index.ts | 79 +++++++++---------- .../CrossChain/TransfersHistory/index.tsx | 17 ++-- src/services/contractQuery.ts | 26 ++++++ src/state/index.ts | 3 + src/state/wallet/hooks.ts | 1 + 12 files changed, 107 insertions(+), 59 deletions(-) create mode 100644 src/services/contractQuery.ts diff --git a/src/components/SearchModal/CurrencyList.tsx b/src/components/SearchModal/CurrencyList.tsx index 92595cacbb..d28dfe9ced 100644 --- a/src/components/SearchModal/CurrencyList.tsx +++ b/src/components/SearchModal/CurrencyList.tsx @@ -230,7 +230,7 @@ function CurrencyList({ const canShowBalance = customChainId && customChainId !== chainId ? isEVM(customChainId) === isEVM(chainId) : true const { favoriteTokens } = useUserFavoriteTokens(customChainId) - const Row: any = useCallback( + const Row = useCallback( function TokenRow({ style, currency, currencyBalance }: TokenRowProps) { const isSelected = Boolean(currency && selectedCurrency?.equals(currency)) const otherSelected = Boolean(currency && otherCurrency?.equals(currency)) diff --git a/src/components/SearchModal/CurrencySearch.tsx b/src/components/SearchModal/CurrencySearch.tsx index 077492b1ab..77f04ce421 100644 --- a/src/components/SearchModal/CurrencySearch.tsx +++ b/src/components/SearchModal/CurrencySearch.tsx @@ -173,7 +173,7 @@ export function CurrencySearch({ return (debouncedQuery ? filterTokens(chainId, tokenImports, debouncedQuery) : tokenImports).sort(tokenComparator) }, [debouncedQuery, chainId, tokenImports, tokenComparator]) - const fetchERC20TokenFromRPC = useFetchERC20TokenFromRPC() + const fetchERC20TokenFromRPC = useFetchERC20TokenFromRPC(chainId) // input eth => output filter weth, input weth => output filter eth const filterWrapFunc = useCallback( diff --git a/src/components/TradeRouting/index.tsx b/src/components/TradeRouting/index.tsx index 7ea22b3387..4b95a5261b 100644 --- a/src/components/TradeRouting/index.tsx +++ b/src/components/TradeRouting/index.tsx @@ -41,7 +41,7 @@ const RouteRow = ({ route, chainId, backgroundColor }: RouteRowProps) => { const contentRef = useRef(null) const shadowRef = useRef(null) - const allDexes = useAllDexes() + const allDexes = useAllDexes(chainId) const handleShadow = useShadow(scrollRef, shadowRef, contentRef) useEffect(() => { diff --git a/src/components/swapv2/LimitOrder/EditOrderModal.tsx b/src/components/swapv2/LimitOrder/EditOrderModal.tsx index 95c5e2bb5e..78d921c638 100644 --- a/src/components/swapv2/LimitOrder/EditOrderModal.tsx +++ b/src/components/swapv2/LimitOrder/EditOrderModal.tsx @@ -1,3 +1,4 @@ +import { ChainId } from '@kyberswap/ks-sdk-core' import { Trans } from '@lingui/macro' import { ethers } from 'ethers' import { useEffect, useMemo, useRef, useState } from 'react' @@ -42,6 +43,7 @@ enum Steps { export default function EditOrderModal({ onSubmit, onDismiss, + customChainId, order, note, isOpen, @@ -50,6 +52,7 @@ export default function EditOrderModal({ }: { onSubmit: CancelOrderFunction onDismiss: () => void + customChainId?: ChainId order: LimitOrder note: string isOpen: boolean @@ -60,8 +63,8 @@ export default function EditOrderModal({ const [step, setStep] = useState(Steps.EDIT_ORDER) const { status, makingAmount, takingAmount, makerAsset, takerAsset, filledTakingAmount, expiredAt } = order - const currencyIn = useCurrencyV2(makerAsset) ?? undefined - const currencyOut = useCurrencyV2(takerAsset) ?? undefined + const currencyIn = useCurrencyV2(makerAsset, customChainId) ?? undefined + const currencyOut = useCurrencyV2(takerAsset, customChainId) ?? undefined const inputAmount = currencyIn ? ethers.utils.formatUnits(makingAmount, currencyIn.decimals) : '' const outputAmount = currencyOut ? ethers.utils.formatUnits(takingAmount, currencyOut.decimals) : '' @@ -193,6 +196,7 @@ export default function EditOrderModal({ note={note} orderInfo={order} defaultExpire={defaultExpire} + useUrlParams /> )} diff --git a/src/components/swapv2/LimitOrder/ListOrder/index.tsx b/src/components/swapv2/LimitOrder/ListOrder/index.tsx index 9ece36f48a..221729d447 100644 --- a/src/components/swapv2/LimitOrder/ListOrder/index.tsx +++ b/src/components/swapv2/LimitOrder/ListOrder/index.tsx @@ -189,12 +189,14 @@ export default function ListLimitOrder({ customChainId }: { customChainId?: Chai setCurPage(1) } + const isPartnerSwap = window.location.pathname.includes(APP_PATHS.PARTNER_SWAP) const navigate = useNavigate() const onSelectTab = (type: LimitOrderStatus) => { setOrderType(type) onReset() - if (!window.location.pathname.includes(APP_PATHS.PARTNER_SWAP)) + if (!isPartnerSwap) { navigate({ search: stringify(qs) }, { replace: true }) + } } const onChangeKeyword = (val: string) => { @@ -296,7 +298,7 @@ export default function ListLimitOrder({ customChainId }: { customChainId?: Chai }, [totalOrderNotCancelling, orders, ordersUpdating]) const upToSmall = useMedia(`(max-width: ${MEDIA_WIDTHS.upToSmall}px)`) - const subscribeBtn = !window.location.pathname.includes(APP_PATHS.PARTNER_SWAP) && ( + const subscribeBtn = !isPartnerSwap && ( @@ -405,6 +408,7 @@ export default function ListLimitOrder({ customChainId }: { customChainId?: Chai void flowState: TransactionFlowState isOpen: boolean }) { - const currencyIn = useCurrencyV2(order?.makerAsset) || undefined - const currencyOut = useCurrencyV2(order?.takerAsset) || undefined + const currencyIn = useCurrencyV2(order?.makerAsset, customChainId) || undefined + const currencyOut = useCurrencyV2(order?.takerAsset, customChainId) || undefined const { tradeInfo: marketPrice } = useBaseTradeInfoLimitOrder(currencyIn, currencyOut) const { @@ -56,7 +59,11 @@ function CancelOrderModal({ takerAssetDecimals, } = order ?? ({} as LimitOrder) - const { orders = [], ordersSoftCancel = [], supportCancelGaslessAllOrders } = useAllActiveOrders(!isCancelAll) + const { + orders = [], + ordersSoftCancel = [], + supportCancelGaslessAllOrders, + } = useAllActiveOrders(!isCancelAll, customChainId) const isOrderSupportGaslessCancel = useIsSupportSoftCancelOrder() const { orderSupportGasless, chainSupportGasless } = isOrderSupportGaslessCancel(order) diff --git a/src/components/swapv2/LimitOrder/useFetchActiveAllOrders.ts b/src/components/swapv2/LimitOrder/useFetchActiveAllOrders.ts index 1819a8fd6f..892a25077f 100644 --- a/src/components/swapv2/LimitOrder/useFetchActiveAllOrders.ts +++ b/src/components/swapv2/LimitOrder/useFetchActiveAllOrders.ts @@ -1,3 +1,4 @@ +import { ChainId } from '@kyberswap/ks-sdk-core' import { useCallback, useMemo } from 'react' import { useGetLOConfigQuery, useGetListOrdersQuery } from 'services/limitOrder' @@ -18,10 +19,10 @@ export const useIsSupportSoftCancelOrder = () => { ) } -export default function useAllActiveOrders(disabled = false) { +export default function useAllActiveOrders(disabled = false, customChainId?: ChainId) { const { account, chainId } = useActiveWeb3React() const { data } = useGetListOrdersQuery( - { chainId, maker: account, status: LimitOrderStatus.ACTIVE, pageSize: 100 }, + { chainId: customChainId ?? chainId, maker: account, status: LimitOrderStatus.ACTIVE, pageSize: 100 }, { skip: !account || disabled }, ) diff --git a/src/hooks/bridge/index.ts b/src/hooks/bridge/index.ts index 92ee2ceacc..42b7f95aad 100644 --- a/src/hooks/bridge/index.ts +++ b/src/hooks/bridge/index.ts @@ -1,6 +1,8 @@ +import { Contract } from '@ethersproject/contracts' import { ChainId, Currency, CurrencyAmount } from '@kyberswap/ks-sdk-core' import JSBI from 'jsbi' import { useCallback, useEffect, useMemo, useState } from 'react' +import contractQuery from 'services/contractQuery' import ERC20_INTERFACE from 'constants/abis/erc20' import { NativeCurrencies } from 'constants/tokens' @@ -45,7 +47,7 @@ export const useEthBalanceOfAnotherChain = (chainId: ChainId | undefined) => { return balance } -export const useTokenBalanceOfAnotherChain = (chainId: ChainId | undefined, token: WrappedTokenInfo | undefined) => { +export const useTokenBalanceOfAnotherChain = (chainId?: ChainId, token?: WrappedTokenInfo) => { const isNative = isTokenNative(token, chainId) const param = useMemo(() => (token && !isNative ? [token] : []), [token, isNative]) @@ -57,56 +59,49 @@ export const useTokenBalanceOfAnotherChain = (chainId: ChainId | undefined, toke }, [balances, ethBalance, isNative]) } -const EMPTY_ARRAY: TokenAmountLoading[] = [] +export type FetchBalancesArg = { + account?: string + tokens: Currency[] + multicallContract: Contract | null + chainId?: ChainId +} + +export const fetchBalancesQuery = async ({ account, tokens, multicallContract }: FetchBalancesArg) => { + if (!account || !tokens.length || !multicallContract) return [] + const calls: CallParam[] = tokens.map(token => ({ + callData: ERC20_INTERFACE.encodeFunctionData('balanceOf', [account]), + target: token.wrapped.address, + fragment: 'balanceOf', + key: token.wrapped.address, + })) + const { results } = await fetchChunk( + multicallContract, + calls.map(call => ({ address: call.target, callData: call.callData })), + undefined as any, + ) + const result = formatResult(results, calls) + const balances = tokens.map(token => { + const balance = result[token.wrapped.address] + return [balance ? CurrencyAmount.fromRawAmount(token, JSBI.BigInt(balance)) : undefined, false] + }) + return balances as TokenAmountLoading[] +} export const useTokensBalanceOfAnotherChain = ( chainId: ChainId | undefined, tokens: Currency[], ): [TokenAmountLoading[], boolean] => { - const [balances, setBalances] = useState([]) const { account } = useActiveWeb3React() const multicallContract = useMulticallContract(chainId) - const [loading, setLoading] = useState(false) - - const getBalance = useCallback(async () => { - try { - setBalances(EMPTY_ARRAY) - if (!chainId || !account || !tokens.length || !multicallContract) { - return - } - setLoading(true) - const calls: CallParam[] = tokens.map(token => ({ - callData: ERC20_INTERFACE.encodeFunctionData('balanceOf', [account]), - target: token.wrapped.address, - fragment: 'balanceOf', - key: token.wrapped.address, - })) - - const { results } = await fetchChunk( - multicallContract, - calls.map(e => ({ address: e.target, callData: e.callData })), - undefined as any, - ) - const result = formatResult(results, calls) - const balances = tokens.map(token => { - const balance = result[token.wrapped.address] - return [balance ? CurrencyAmount.fromRawAmount(token, JSBI.BigInt(balance)) : undefined, false] - }) - setBalances(balances as TokenAmountLoading[]) - return - } catch (error) { - console.error('get balance chain err', { chainId, error }) - } finally { - setLoading(false) - } - }, [chainId, tokens, account, multicallContract]) - - useEffect(() => { - getBalance() - }, [getBalance]) + const { data: balances = [], isLoading } = contractQuery.useFetchBalancesQuery({ + account, + tokens, + multicallContract, + chainId, + }) - return [balances, loading] + return [balances, isLoading] } type TokenList = { anytoken: string; underlying: string }[] diff --git a/src/pages/CrossChain/TransfersHistory/index.tsx b/src/pages/CrossChain/TransfersHistory/index.tsx index 57ce2677eb..5f2a951b66 100644 --- a/src/pages/CrossChain/TransfersHistory/index.tsx +++ b/src/pages/CrossChain/TransfersHistory/index.tsx @@ -22,21 +22,28 @@ type Props = { } const BridgeHistory: React.FC = ({ className }) => { + const navigate = useNavigate() + const theme = useTheme() const qs = useParsedQueryString<{ tab: CrossChainTab }>() - const [activeTab, setTab] = useState(qs.tab || CrossChainTab.ROUTE) const isShowTradeRoutes = useShowTradeRoutes() - const navigate = useNavigate() + const isPartnerSwap = window.location.pathname.includes(APP_PATHS.PARTNER_SWAP) + + const [activeTab, setTab] = useState( + isPartnerSwap ? CrossChainTab.ROUTE : qs.tab || CrossChainTab.ROUTE, + ) + const onClickTab = (tab: CrossChainTab) => { - navigate({ search: `tab=${tab}` }, { replace: true }) + if (!isPartnerSwap) { + navigate({ search: `tab=${tab}` }, { replace: true }) + } setTab(tab) } - const theme = useTheme() return (
- {!window.location.pathname.includes(APP_PATHS.PARTNER_SWAP) && ( + {!isPartnerSwap && ( ({ + fetchBalances: builder.query({ + queryFn: async (arg: FetchBalancesArg) => { + const data = await fetchBalancesQuery(arg) + return { data } + }, + serializeQueryArgs: ({ queryArgs }) => { + const { account, tokens, chainId } = queryArgs + return { account, tokens, chainId } + }, + keepUnusedDataFor: 10, + }), + }), +}) + +export const { useFetchBalancesQuery } = contractQuery + +export default contractQuery diff --git a/src/state/index.ts b/src/state/index.ts index 9f0ee714a2..4b605c7c8e 100644 --- a/src/state/index.ts +++ b/src/state/index.ts @@ -5,6 +5,7 @@ import blackjackApi from 'services/blackjack' import blockServiceApi from 'services/blockService' import campaignApi from 'services/campaign' import coingeckoApi from 'services/coingecko' +import contractQuery from 'services/contractQuery' import crosschainApi from 'services/crossChain' import earningApi from 'services/earning' import geckoTerminalApi from 'services/geckoTermial' @@ -98,6 +99,7 @@ const store = configureStore({ [publicAnnouncementApi.reducerPath]: publicAnnouncementApi.reducer, [geckoTerminalApi.reducerPath]: geckoTerminalApi.reducer, [coingeckoApi.reducerPath]: coingeckoApi.reducer, + [contractQuery.reducerPath]: contractQuery.reducer, [limitOrderApi.reducerPath]: limitOrderApi.reducer, [campaignApi.reducerPath]: campaignApi.reducer, @@ -132,6 +134,7 @@ const store = configureStore({ .concat(save({ states: PERSISTED_KEYS, debounce: 100 })) .concat(geckoTerminalApi.middleware) .concat(coingeckoApi.middleware) + .concat(contractQuery.middleware) .concat(limitOrderApi.middleware) .concat(kyberAIApi.middleware) .concat(coinmarketcapApi.middleware) diff --git a/src/state/wallet/hooks.ts b/src/state/wallet/hooks.ts index db9b8116fe..c327906aab 100644 --- a/src/state/wallet/hooks.ts +++ b/src/state/wallet/hooks.ts @@ -107,6 +107,7 @@ export function useTokenBalancesWithLoadingIndicator( const isFetchOtherChain = chainId !== currentChain const balancesCurrentChain = useTokensBalance(isFetchOtherChain ? EMPTY_ARRAY : tokens, chainId) + const [balancesOtherChain, isLoadingAnotherChain] = useTokensBalanceOfAnotherChain( chainId, isFetchOtherChain ? tokens : EMPTY_ARRAY, From 83abea0276a07a4a52e88493fc861d3146ea49ec Mon Sep 17 00:00:00 2001 From: Khac Kien Date: Mon, 27 Nov 2023 16:49:46 +0700 Subject: [PATCH 12/14] Update input value when click switch button swap page (#2403) --- src/components/SwapForm/index.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/SwapForm/index.tsx b/src/components/SwapForm/index.tsx index f8d0c3a341..a5a6d7ee28 100644 --- a/src/components/SwapForm/index.tsx +++ b/src/components/SwapForm/index.tsx @@ -245,7 +245,12 @@ const SwapForm: React.FC = props => { )} )} - currencyIn && onChangeCurrencyOut(currencyIn)} /> + { + currencyIn && onChangeCurrencyOut(currencyIn) + routeSummary && onUserInput(routeSummary.parsedAmountOut.toExact()) + }} + /> From d7211914afe93773a8405008021cdcb2f9806ef0 Mon Sep 17 00:00:00 2001 From: Nguyen Van Viet Date: Tue, 28 Nov 2023 09:40:27 +0700 Subject: [PATCH 13/14] chore: clean up token defination to reduce work when expand chain (#2405) --- src/constants/bases.ts | 160 +++--------------- src/constants/tokens.ts | 215 +++--------------------- src/hooks/useAllCurrencyCombinations.ts | 17 +- src/state/user/hooks.tsx | 17 +- src/state/user/reducer.ts | 6 - 5 files changed, 50 insertions(+), 365 deletions(-) diff --git a/src/constants/bases.ts b/src/constants/bases.ts index 808d2b0641..60b043790d 100644 --- a/src/constants/bases.ts +++ b/src/constants/bases.ts @@ -1,7 +1,5 @@ import { ChainId, Token, WETH } from '@kyberswap/ks-sdk-core' -import { AMPL, COMP, DAI, MKR, USDC, USDT, WBTC_ARBITRUM } from './tokens' - // a list of tokens by chain type ChainTokenList = { readonly [chainId in ChainId]: Token[] @@ -33,152 +31,44 @@ const WETH_ONLY: ChainTokenList = { } // used to construct intermediary pairs for trading +// => Only used for elastic swap export const BASES_TO_CHECK_TRADES_AGAINST: ChainTokenList = { ...WETH_ONLY, [ChainId.MAINNET]: [ WETH[ChainId.MAINNET], - DAI[ChainId.MAINNET], - USDC[ChainId.MAINNET], - USDT[ChainId.MAINNET], - COMP, - MKR, - ], - [ChainId.MUMBAI]: [...WETH_ONLY[ChainId.MUMBAI], DAI[ChainId.MUMBAI], USDC[ChainId.MUMBAI], USDT[ChainId.MUMBAI]], - [ChainId.MATIC]: [ - ...WETH_ONLY[ChainId.MATIC], - DAI[ChainId.MATIC], - USDC[ChainId.MATIC], - USDT[ChainId.MATIC], - new Token(ChainId.MATIC, '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619', 18, 'ETH', 'Ether'), - ], - - [ChainId.BSCTESTNET]: [ - ...WETH_ONLY[ChainId.BSCTESTNET], - DAI[ChainId.BSCTESTNET], - USDC[ChainId.BSCTESTNET], - USDT[ChainId.BSCTESTNET], - ], - [ChainId.BSCMAINNET]: [ - ...WETH_ONLY[ChainId.BSCMAINNET], - DAI[ChainId.BSCMAINNET], - USDC[ChainId.BSCMAINNET], - USDT[ChainId.BSCMAINNET], - new Token(ChainId.BSCMAINNET, '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56', 18, 'BUSD', 'BUSD'), - ], - [ChainId.AVAXTESTNET]: [ - ...WETH_ONLY[ChainId.AVAXTESTNET], - DAI[ChainId.AVAXTESTNET], - USDC[ChainId.AVAXTESTNET], - USDT[ChainId.AVAXTESTNET], - ], - [ChainId.AVAXMAINNET]: [ - ...WETH_ONLY[ChainId.AVAXMAINNET], - DAI[ChainId.AVAXMAINNET], - USDC[ChainId.AVAXMAINNET], - USDT[ChainId.AVAXMAINNET], + new Token(ChainId.MAINNET, '0x6B175474E89094C44Da98b954EedeAC495271d0F', 18, 'DAI', 'DAI'), + new Token(ChainId.MAINNET, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6, 'USDC', 'USD Coin'), + new Token(ChainId.MAINNET, '0xdAC17F958D2ee523a2206206994597C13D831ec7', 6, 'USDT', 'Tether USD'), ], - [ChainId.FANTOM]: [...WETH_ONLY[ChainId.FANTOM], DAI[ChainId.FANTOM], USDC[ChainId.FANTOM], USDT[ChainId.FANTOM]], - [ChainId.CRONOS]: [...WETH_ONLY[ChainId.CRONOS], DAI[ChainId.CRONOS], USDC[ChainId.CRONOS], USDT[ChainId.CRONOS]], - [ChainId.AURORA]: [...WETH_ONLY[ChainId.AURORA], DAI[ChainId.AURORA], USDC[ChainId.AURORA], USDT[ChainId.AURORA]], - [ChainId.BTTC]: [ - ...WETH_ONLY[ChainId.BTTC], - DAI[ChainId.BTTC], - USDC[ChainId.BTTC], - USDT[ChainId.BTTC], - new Token(ChainId.BTTC, '0xdB28719F7f938507dBfe4f0eAe55668903D34a15', 6, 'USDT_t', 'USDT_t'), - new Token(ChainId.BTTC, '0xE887512ab8BC60BcC9224e1c3b5Be68E26048B8B', 6, 'USDT_e', 'USDT_e'), - new Token(ChainId.BTTC, '0xedf53026aea60f8f75fca25f8830b7e2d6200662', 6, 'TRX', 'TRX'), - ], - [ChainId.OPTIMISM]: [...WETH_ONLY[ChainId.OPTIMISM], USDC[ChainId.OPTIMISM], USDT[ChainId.OPTIMISM]], - [ChainId.SOLANA]: [...WETH_ONLY[ChainId.SOLANA], DAI[ChainId.SOLANA], USDC[ChainId.SOLANA], USDT[ChainId.SOLANA]], -} - -/** - * Some tokens can only be swapped via certain pairs, so we override the list of bases that are considered for these - * tokens. - */ -export const CUSTOM_BASES: { [chainId in ChainId]?: { [tokenAddress: string]: Token[] } } = { - [ChainId.MAINNET]: { - [AMPL.address]: [DAI[ChainId.MAINNET], WETH[ChainId.MAINNET]], - }, -} - -// used for display common base in currency search -export const SUGGESTED_BASES: ChainTokenList = { - ...WETH_ONLY, - [ChainId.MAINNET]: [ - ...WETH_ONLY[ChainId.MAINNET], - DAI[ChainId.MAINNET], - USDC[ChainId.MAINNET], - USDT[ChainId.MAINNET], - new Token(ChainId.MAINNET, '0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0', 18, 'wstETH', 'Wrapped stETH (Lido)'), - ], - [ChainId.GÖRLI]: [...WETH_ONLY[ChainId.GÖRLI], DAI[ChainId.GÖRLI], USDC[ChainId.GÖRLI], USDT[ChainId.GÖRLI]], [ChainId.MATIC]: [ - ...WETH_ONLY[ChainId.MATIC], - DAI[ChainId.MATIC], - USDC[ChainId.MATIC], - USDT[ChainId.MATIC], - new Token(ChainId.MATIC, '0xa3Fa99A148fA48D14Ed51d610c367C61876997F1', 18, 'MAI', 'MAI'), - new Token(ChainId.MATIC, '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4', 18, 'stMATIC', 'Staked MATIC (PoS)'), + WETH[ChainId.MATIC], + new Token(ChainId.MATIC, '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', 18, 'DAI', 'DAI'), + new Token(ChainId.MATIC, '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', 6, 'USDC', 'USD Coin'), + new Token(ChainId.MATIC, '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', 6, 'USDT', 'Tether USD'), + new Token(ChainId.MATIC, '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619', 18, 'WETH', 'Ether'), ], [ChainId.BSCMAINNET]: [ - ...WETH_ONLY[ChainId.BSCMAINNET], - DAI[ChainId.BSCMAINNET], - USDC[ChainId.BSCMAINNET], - USDT[ChainId.BSCMAINNET], + WETH[ChainId.BSCMAINNET], + new Token(ChainId.BSCMAINNET, '0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3', 18, 'DAI', 'DAI'), + new Token(ChainId.BSCMAINNET, '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', 18, 'USDC', 'USDC'), + new Token(ChainId.BSCMAINNET, '0x55d398326f99059fF775485246999027B3197955', 18, 'USDT', 'Tether USD'), new Token(ChainId.BSCMAINNET, '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56', 18, 'BUSD', 'BUSD'), ], [ChainId.AVAXMAINNET]: [ - ...WETH_ONLY[ChainId.AVAXMAINNET], - USDC[ChainId.AVAXMAINNET], - USDT[ChainId.AVAXMAINNET], - new Token(ChainId.AVAXMAINNET, '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7', 6, 'USDt', 'TetherToken'), - new Token(ChainId.AVAXMAINNET, '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', 6, 'USDC', 'USD Coin'), - new Token(ChainId.AVAXMAINNET, '0x2b2C81e08f1Af8835a78Bb2A90AE924ACE0eA4bE', 18, 'sAVAX', 'Staked AVAX'), - ], - [ChainId.FANTOM]: [...WETH_ONLY[ChainId.FANTOM], DAI[ChainId.FANTOM], USDC[ChainId.FANTOM], USDT[ChainId.FANTOM]], - [ChainId.CRONOS]: [...WETH_ONLY[ChainId.CRONOS], DAI[ChainId.CRONOS], USDC[ChainId.CRONOS], USDT[ChainId.CRONOS]], - [ChainId.AURORA]: [ - ...WETH_ONLY[ChainId.AURORA], - DAI[ChainId.AURORA], - USDC[ChainId.AURORA], - USDT[ChainId.AURORA], - new Token(ChainId.AURORA, '0x8BEc47865aDe3B172A928df8f990Bc7f2A3b9f79', 18, 'Aurora', 'Aurora'), + WETH[ChainId.AVAXMAINNET], + new Token(ChainId.AVAXMAINNET, '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70', 18, 'DAI', 'DAI'), + new Token(ChainId.AVAXMAINNET, '0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664', 6, 'USDC.e', 'USDC.e'), + new Token(ChainId.AVAXMAINNET, '0xc7198437980c041c805A1EDcbA50c1Ce5db95118', 6, 'USDT.e', 'Tether USD'), ], - [ChainId.ARBITRUM]: [ - ...WETH_ONLY[ChainId.ARBITRUM], - new Token(ChainId.ARBITRUM, '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', 6, 'USDC', 'USDC'), - DAI[ChainId.ARBITRUM], - USDC[ChainId.ARBITRUM], // USDC.e - USDT[ChainId.ARBITRUM], - WBTC_ARBITRUM, - new Token(ChainId.ARBITRUM, '0x5979D7b546E38E414F7E9822514be443A4800529', 18, 'wstETH', 'Lido Wrapped Staked ETH'), + [ChainId.FANTOM]: [ + WETH[ChainId.FANTOM], + new Token(ChainId.FANTOM, '0x91a40C733c97a6e1BF876EaF9ed8c08102eB491f', 18, 'DAI', 'DAI'), + new Token(ChainId.FANTOM, '0x28a92dde19D9989F39A49905d7C9C2FAc7799bDf', 6, 'USDC', 'USD Coin'), + new Token(ChainId.FANTOM, '0xcc1b99dDAc1a33c201a742A1851662E87BC7f22C', 6, 'fUSDT', 'Tether USD'), ], - [ChainId.BTTC]: [...WETH_ONLY[ChainId.BTTC], DAI[ChainId.BTTC], USDC[ChainId.BTTC], USDT[ChainId.BTTC]], [ChainId.OPTIMISM]: [ - ...WETH_ONLY[ChainId.OPTIMISM], - USDC[ChainId.OPTIMISM], - new Token(ChainId.OPTIMISM, '0x4200000000000000000000000000000000000042', 18, 'OP', 'Optimism'), - USDT[ChainId.OPTIMISM], - DAI[ChainId.OPTIMISM], - new Token(ChainId.OPTIMISM, '0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb', 18, 'wstETH', 'Lido Wrapped Staked ETH'), - ], - [ChainId.SOLANA]: [...WETH_ONLY[ChainId.SOLANA], USDC[ChainId.SOLANA], USDT[ChainId.SOLANA]], - [ChainId.ZKSYNC]: [ - ...WETH_ONLY[ChainId.ZKSYNC], - USDC[ChainId.ZKSYNC], - USDT[ChainId.ZKSYNC], - new Token(ChainId.ZKSYNC, '0xfC7E56298657B002b3e656400E746b7212912757', 6, 'zkUSD', 'zkUSD'), - new Token(ChainId.ZKSYNC, '0x8e86e46278518efc1c5ced245cba2c7e3ef11557', 6, 'USD+', 'USD+'), - new Token(ChainId.ZKSYNC, '0x503234f203fc7eb888eec8513210612a43cf6115', 18, 'LUSD', 'LUSD'), - new Token(ChainId.ZKSYNC, '0xbbeb516fb02a01611cbbe0453fe3c580d7281011', 8, 'wBTC', 'wBTC'), - ], - [ChainId.ZKEVM]: [...WETH_ONLY[ChainId.ZKEVM], USDC[ChainId.ZKEVM], USDT[ChainId.ZKEVM], DAI[ChainId.ZKEVM]], - [ChainId.LINEA]: [ - ...WETH_ONLY[ChainId.LINEA], - new Token(ChainId.LINEA, '0x7d43aabc515c356145049227cee54b608342c0ad', 18, 'BUSD', 'BUSD'), + WETH[ChainId.OPTIMISM], + new Token(ChainId.OPTIMISM, '0x7F5c764cBc14f9669B88837ca1490cCa17c31607', 6, 'USDC', 'USD Coin'), + new Token(ChainId.OPTIMISM, '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', 6, 'USDT', 'Tether USD'), ], - [ChainId.BASE]: [...WETH_ONLY[ChainId.BASE], USDC[ChainId.BASE], DAI[ChainId.BASE]], - [ChainId.SCROLL]: [...WETH_ONLY[ChainId.SCROLL], USDT[ChainId.SCROLL], USDC[ChainId.SCROLL], DAI[ChainId.SCROLL]], } diff --git a/src/constants/tokens.ts b/src/constants/tokens.ts index f2161ddeb6..e20ca60e35 100644 --- a/src/constants/tokens.ts +++ b/src/constants/tokens.ts @@ -206,174 +206,6 @@ export const CORRELATED_COINS_ADDRESS: { [chainId in ChainId]: string[][] } = { [ChainId.SOLANA_DEVNET]: [], } -export const DAI: { [chainId in ChainId]: Token } = { - [ChainId.MAINNET]: new Token(ChainId.MAINNET, '0x6B175474E89094C44Da98b954EedeAC495271d0F', 18, 'DAI', 'DAI'), - [ChainId.GÖRLI]: new Token(ChainId.GÖRLI, '0x1BBeeEdCF32dc2c1Ebc2F138e3FC7f3DeCD44D6A', 18, 'DAI', 'DAI'), - [ChainId.MATIC]: new Token(ChainId.MATIC, '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', 18, 'DAI', 'DAI'), - [ChainId.MUMBAI]: new Token(ChainId.MUMBAI, '0x5e2de02472aC02736b43054f095837725A5870eF', 18, 'DAI', 'DAI'), - [ChainId.BSCTESTNET]: new Token(ChainId.BSCTESTNET, '0xBb843a2296F9AA49070EB2Dcd482f23548238f65', 18, 'DAI', 'DAI'), - [ChainId.BSCMAINNET]: new Token(ChainId.BSCMAINNET, '0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3', 18, 'DAI', 'DAI'), - [ChainId.AVAXTESTNET]: new Token(ChainId.AVAXTESTNET, '0xE50c0F38a1890Db49d64ac1C4A5B4fe2f02f819d', 18, 'DAI', 'DAI'), - [ChainId.AVAXMAINNET]: new Token(ChainId.AVAXMAINNET, '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70', 18, 'DAI', 'DAI'), - [ChainId.FANTOM]: new Token(ChainId.FANTOM, '0x91a40C733c97a6e1BF876EaF9ed8c08102eB491f', 18, 'DAI', 'DAI'), - [ChainId.CRONOS]: new Token(ChainId.CRONOS, '0xF2001B145b43032AAF5Ee2884e456CCd805F677D', 18, 'DAI', 'DAI'), - [ChainId.ARBITRUM]: new Token( - ChainId.ARBITRUM, - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - 18, - 'DAI_e', - 'DAI_Ethereum', - ), - [ChainId.BTTC]: new Token(ChainId.BTTC, '0xe7dC549AE8DB61BDE71F22097BEcc8dB542cA100', 18, 'DAI', 'DAI'), - [ChainId.AURORA]: new Token(ChainId.AURORA, '0xe3520349F477A5F6EB06107066048508498A291b', 18, 'DAI', 'DAI'), - [ChainId.ZKEVM]: new Token(ChainId.ZKEVM, '0xC5015b9d9161Dca7e18e32f6f25C4aD850731Fd4', 18, 'DAI', 'Dai'), - [ChainId.LINEA]: new Token(ChainId.LINEA, '0x4AF15ec2A0BD43Db75dd04E62FAA3B8EF36b00d5', 18, 'DAI', 'Dai'), - [ChainId.BASE]: new Token(ChainId.BASE, '0x50c5725949a6f0c72e6c4a641f24049a917db0cb', 18, 'DAI', 'Dai'), - - [ChainId.OPTIMISM]: new Token(ChainId.OPTIMISM, '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', 18, 'DAI', 'DAI'), - [ChainId.ZKSYNC]: new Token(ChainId.ZKSYNC, '0x4BEf76b6b7f2823C6c1f4FcfEACD85C24548ad7e', 18, 'DAI', 'Dai'), - [ChainId.SOLANA]: new Token( - ChainId.SOLANA, - 'EjmyN6qEC1Tf1JxiG1ae7UTJhUxSwk1TCWNWqxWV4J6o', - 8, - 'DAI', - 'DAI (Wormhole)', - ), - [ChainId.SOLANA_DEVNET]: new Token( - ChainId.SOLANA_DEVNET, - 'EjmyN6qEC1Tf1JxiG1ae7UTJhUxSwk1TCWNWqxWV4J6o', - 8, - 'DAI', - 'DAI (Wormhole)', - ), - // TDOD: Get real DAI - [ChainId.SCROLL]: new Token(ChainId.SCROLL, '0x50c5725949a6f0c72e6c4a641f24049a917db0cb', 18, 'DAI', 'Dai'), -} - -export const USDC: { [chainId in ChainId]: Token } = { - [ChainId.MAINNET]: new Token(ChainId.MAINNET, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6, 'USDC', 'USD Coin'), - [ChainId.GÖRLI]: new Token(ChainId.GÖRLI, '0x8e9Bd30D15420bAe4B7EC0aC014B7ECeE864373C', 18, 'USDC', 'USD Coin'), - [ChainId.MATIC]: new Token(ChainId.MATIC, '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', 6, 'USDC', 'USD Coin'), - [ChainId.MUMBAI]: new Token(ChainId.MUMBAI, '0x2CeC76B26A8d96BF3072D34A01BB3a4edE7c06BE', 6, 'USDC', 'USD Coin'), - [ChainId.BSCTESTNET]: new Token(ChainId.BSCTESTNET, '0xb448B701807E644f141a4E4a269aD2F567526505', 6, 'USDC', 'USDC'), - [ChainId.BSCMAINNET]: new Token(ChainId.BSCMAINNET, '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', 18, 'USDC', 'USDC'), - [ChainId.AVAXTESTNET]: new Token( - ChainId.AVAXTESTNET, - '0x5973774202E8b0ad563A69D502bb0e670e7d00Dd', - 6, - 'USDC', - 'USDC', - ), - [ChainId.AVAXMAINNET]: new Token( - ChainId.AVAXMAINNET, - '0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664', - 6, - 'USDC.e', - 'USDC', - ), - [ChainId.FANTOM]: new Token(ChainId.FANTOM, '0x28a92dde19D9989F39A49905d7C9C2FAc7799bDf', 6, 'USDC', 'USD Coin'), - [ChainId.CRONOS]: new Token(ChainId.CRONOS, '0xc21223249CA28397B4B6541dfFaEcC539BfF0c59', 6, 'USDC', 'USD Coin'), - [ChainId.ARBITRUM]: new Token(ChainId.ARBITRUM, '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', 6, 'USDC', 'USD Coin'), - [ChainId.BTTC]: new Token(ChainId.BTTC, '0xCa424b845497f7204D9301bd13Ff87C0E2e86FCF', 18, 'USDC_b', 'USD Coin_BSC'), - [ChainId.AURORA]: new Token(ChainId.AURORA, '0xB12BFcA5A55806AaF64E99521918A4bf0fC40802', 6, 'USDC', 'USD Coin'), - [ChainId.OPTIMISM]: new Token(ChainId.OPTIMISM, '0x7F5c764cBc14f9669B88837ca1490cCa17c31607', 6, 'USDC', 'USD Coin'), - [ChainId.ZKSYNC]: new Token(ChainId.ZKSYNC, '0x3355df6D4c9C3035724Fd0e3914dE96A5a83aaf4', 6, 'USDC', 'USD Coin'), - [ChainId.SOLANA]: new Token(ChainId.SOLANA, 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', 6, 'USDC', 'USD Coin'), - [ChainId.SOLANA_DEVNET]: new Token( - ChainId.SOLANA_DEVNET, - 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', - 6, - 'USDC', - 'USD Coin', - ), - [ChainId.ZKEVM]: new Token(ChainId.ZKEVM, '0xA8CE8aee21bC2A48a5EF670afCc9274C7bbbC035', 6, 'USDC', 'USD Coin'), - [ChainId.LINEA]: new Token(ChainId.LINEA, '0x176211869cA2b568f2A7D4EE941E073a821EE1ff', 6, 'USDC', 'USD Coin'), - [ChainId.BASE]: new Token(ChainId.BASE, '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA', 6, 'USDC', 'USD Coin'), - // TODO: verify this USDC on Scroll - [ChainId.SCROLL]: new Token(ChainId.SCROLL, '0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4', 6, 'USDC', 'USD Coin'), -} - -export const USDT: { [chainId in ChainId]: Token } = { - [ChainId.MAINNET]: new Token(ChainId.MAINNET, '0xdAC17F958D2ee523a2206206994597C13D831ec7', 6, 'USDT', 'Tether USD'), - [ChainId.GÖRLI]: new Token(ChainId.GÖRLI, '0x2bf64acf7ead856209749d0d125e9ade2d908e7f', 18, 'USDT', 'Tether USD'), - [ChainId.MATIC]: new Token(ChainId.MATIC, '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', 6, 'USDT', 'Tether USD'), - [ChainId.MUMBAI]: new Token(ChainId.MUMBAI, '0x064B91Bda6d178DfE03835de9450BFe78201c43F', 6, 'USDT', 'Tether USD'), - [ChainId.BSCTESTNET]: new Token( - ChainId.BSCTESTNET, - '0x3d8f2Ada8e97e4eF19e4ccBf6ec1Ca52900406aA', - 6, - 'USDT', - 'Tether USD', - ), - [ChainId.BSCMAINNET]: new Token( - ChainId.BSCMAINNET, - '0x55d398326f99059fF775485246999027B3197955', - 18, - 'USDT', - 'Tether USD', - ), - [ChainId.AVAXTESTNET]: new Token( - ChainId.AVAXTESTNET, - '0x42296280d753ecdfafe9dbdfa912c9e6221a4e05', - 18, - 'USDT', - 'Tether USD', - ), - [ChainId.AVAXMAINNET]: new Token( - ChainId.AVAXMAINNET, - '0xc7198437980c041c805A1EDcbA50c1Ce5db95118', - 6, - 'USDT.e', - 'Tether USD', - ), - [ChainId.FANTOM]: new Token(ChainId.FANTOM, '0xcc1b99dDAc1a33c201a742A1851662E87BC7f22C', 6, 'fUSDT', 'Tether USD'), - [ChainId.CRONOS]: new Token(ChainId.CRONOS, '0x66e428c3f67a68878562e79A0234c1F83c208770', 6, 'USDT', 'Tether USD'), - [ChainId.ARBITRUM]: new Token( - ChainId.ARBITRUM, - '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', - 6, - 'USDT', - 'Tether USD', - ), - [ChainId.BTTC]: new Token(ChainId.BTTC, '0x9B5F27f6ea9bBD753ce3793a07CbA3C74644330d', 18, 'USDT_b', 'Tether USD_BSC'), - [ChainId.AURORA]: new Token(ChainId.AURORA, '0x4988a896b1227218e4A686fdE5EabdcAbd91571f', 6, 'USDT', 'Tether USD'), - [ChainId.OPTIMISM]: new Token( - ChainId.OPTIMISM, - '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', - 6, - 'USDT', - 'Tether USD', - ), - [ChainId.ZKEVM]: new Token(ChainId.ZKEVM, '0x1E4a5963aBFD975d8c9021ce480b42188849D41d', 6, 'USDT', 'Tether USD'), - [ChainId.LINEA]: new Token(ChainId.LINEA, '0xA219439258ca9da29E9Cc4cE5596924745e12B93', 6, 'USDT', 'Tether USD'), - - [ChainId.ZKSYNC]: new Token(ChainId.ZKSYNC, '0x493257fd37edb34451f62edf8d2a0c418852ba4c', 6, 'USDT', 'Tether USD'), - [ChainId.SOLANA]: new Token(ChainId.SOLANA, 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', 6, 'USDT', 'Tether USD'), - [ChainId.SOLANA_DEVNET]: new Token( - ChainId.SOLANA_DEVNET, - 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', - 6, - 'USDT', - 'Tether USD', - ), - - // not existed - [ChainId.BASE]: new Token(ChainId.BASE, '0xA219439258ca9da29E9Cc4cE5596924745e12B93', 6, 'USDT', 'Tether USD'), - // TODO: verify this address - [ChainId.SCROLL]: new Token(ChainId.SCROLL, '0xf55BEC9cafDbE8730f096Aa55dad6D22d44099Df', 6, 'USDT', 'Tether USD'), -} - -export const COMP = new Token(ChainId.MAINNET, '0xc00e94Cb662C3520282E6f5717214004A7f26888', 18, 'COMP', 'Compound') -export const MKR = new Token(ChainId.MAINNET, '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2', 18, 'MKR', 'Maker') -export const AMPL = new Token(ChainId.MAINNET, '0xD46bA6D942050d489DBd938a2C909A5d5039A161', 9, 'AMPL', 'Ampleforth') -export const WBTC_ARBITRUM = new Token( - ChainId.ARBITRUM, - '0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f', - 8, - 'WBTC', - 'Wrapped BTC', -) - export const KNC_ADDRESS = '0xdeFA4e8a7bcBA345F687a2f1456F5Edd9CE97202' export const KNCL_ADDRESS = '0xdd974D5C2e2928deA5F71b9825b8b646686BD200' @@ -412,35 +244,30 @@ export const KNC: { [chainId in ChainId]: Token } = { ), } -export const PINNED_PAIRS: { readonly [chainId in ChainId]?: [Token, Token][] } = { - [ChainId.MAINNET]: [ - [ - new Token(ChainId.MAINNET, '0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643', 8, 'cDAI', 'Compound Dai'), - new Token(ChainId.MAINNET, '0x39AA39c021dfbaE8faC545936693aC917d5E7563', 8, 'cUSDC', 'Compound USD Coin'), - ], - [USDC[ChainId.MAINNET], USDT[ChainId.MAINNET]], - [DAI[ChainId.MAINNET], USDT[ChainId.MAINNET]], - ], -} - export const DEFAULT_OUTPUT_TOKEN_BY_CHAIN: Partial> = { - [ChainId.MAINNET]: USDT[ChainId.MAINNET], - [ChainId.MATIC]: USDT[ChainId.MATIC], + [ChainId.MAINNET]: new Token(ChainId.MAINNET, '0xdAC17F958D2ee523a2206206994597C13D831ec7', 6, 'USDT', 'Tether USD'), + [ChainId.MATIC]: new Token(ChainId.MATIC, '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', 6, 'USDT', 'Tether USD'), [ChainId.BSCMAINNET]: new Token(ChainId.BSCMAINNET, '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56', 18, 'BUSD', 'BUSD'), - [ChainId.AVAXMAINNET]: USDC[ChainId.AVAXMAINNET], // USDC.e - [ChainId.FANTOM]: USDC[ChainId.FANTOM], - [ChainId.CRONOS]: USDC[ChainId.CRONOS], + [ChainId.AVAXMAINNET]: new Token( + ChainId.AVAXMAINNET, + '0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664', + 6, + 'USDC.e', + 'USDC.e', + ), + [ChainId.FANTOM]: new Token(ChainId.FANTOM, '0x28a92dde19D9989F39A49905d7C9C2FAc7799bDf', 6, 'USDC', 'USD Coin'), + [ChainId.CRONOS]: new Token(ChainId.CRONOS, '0xc21223249CA28397B4B6541dfFaEcC539BfF0c59', 6, 'USDC', 'USD Coin'), [ChainId.ARBITRUM]: new Token(ChainId.ARBITRUM, '0x912CE59144191C1204E64559FE8253a0e49E6548', 18, 'ARB', 'Arbitrum'), - [ChainId.OPTIMISM]: USDC[ChainId.OPTIMISM], - [ChainId.AURORA]: USDC[ChainId.AURORA], - [ChainId.BTTC]: USDT[ChainId.BTTC], // USDT_b - [ChainId.SOLANA]: USDC[ChainId.SOLANA], - [ChainId.GÖRLI]: USDT[ChainId.GÖRLI], - [ChainId.ZKSYNC]: USDC[ChainId.ZKSYNC], - [ChainId.ZKEVM]: USDT[ChainId.ZKEVM], - [ChainId.LINEA]: USDC[ChainId.LINEA], - [ChainId.BASE]: USDC[ChainId.BASE], - [ChainId.SCROLL]: USDT[ChainId.SCROLL], + [ChainId.OPTIMISM]: new Token(ChainId.OPTIMISM, '0x7F5c764cBc14f9669B88837ca1490cCa17c31607', 6, 'USDC', 'USD Coin'), + [ChainId.AURORA]: new Token(ChainId.AURORA, '0xB12BFcA5A55806AaF64E99521918A4bf0fC40802', 6, 'USDC', 'USD Coin'), + [ChainId.BTTC]: new Token(ChainId.BTTC, '0x9B5F27f6ea9bBD753ce3793a07CbA3C74644330d', 18, 'USDT_b', 'Tether USD_BSC'), + [ChainId.GÖRLI]: new Token(ChainId.GÖRLI, '0x2bf64acf7ead856209749d0d125e9ade2d908e7f', 18, 'USDT', 'Tether USD'), + [ChainId.ZKSYNC]: new Token(ChainId.ZKSYNC, '0x3355df6D4c9C3035724Fd0e3914dE96A5a83aaf4', 6, 'USDC', 'USD Coin'), + [ChainId.ZKEVM]: new Token(ChainId.ZKEVM, '0x1E4a5963aBFD975d8c9021ce480b42188849D41d', 6, 'USDT', 'Tether USD'), + [ChainId.LINEA]: new Token(ChainId.LINEA, '0x176211869cA2b568f2A7D4EE941E073a821EE1ff', 6, 'USDC', 'USD Coin'), + [ChainId.BASE]: new Token(ChainId.BASE, '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA', 6, 'USDC', 'USD Coin'), + [ChainId.SCROLL]: new Token(ChainId.SCROLL, '0xf55BEC9cafDbE8730f096Aa55dad6D22d44099Df', 6, 'USDT', 'Tether USD'), + [ChainId.SOLANA]: new Token(ChainId.SOLANA, 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', 6, 'USDC', 'USD Coin'), } export const DEFAULT_SWAP_FEE_STABLE_PAIRS = 4 diff --git a/src/hooks/useAllCurrencyCombinations.ts b/src/hooks/useAllCurrencyCombinations.ts index 9a3e81e4be..18cce397d2 100644 --- a/src/hooks/useAllCurrencyCombinations.ts +++ b/src/hooks/useAllCurrencyCombinations.ts @@ -1,7 +1,7 @@ import { Currency, Token } from '@kyberswap/ks-sdk-core' import { useMemo } from 'react' -import { BASES_TO_CHECK_TRADES_AGAINST, CUSTOM_BASES } from 'constants/bases' +import { BASES_TO_CHECK_TRADES_AGAINST } from 'constants/bases' import { useActiveWeb3React } from './index' @@ -59,21 +59,6 @@ export function useAllCurrencyCombinations(currencyA?: Currency, currencyB?: Cur ] .filter((tokens): tokens is [Token, Token] => Boolean(tokens[0] && tokens[1])) .filter(([t0, t1]) => t0.address !== t1.address) - .filter(([tokenA, tokenB]) => { - if (!chainId) return true - const customBases = CUSTOM_BASES[chainId] - if (!customBases) return true - - const customBasesA: Token[] | undefined = customBases[tokenA.address] - const customBasesB: Token[] | undefined = customBases[tokenB.address] - - if (!customBasesA && !customBasesB) return true - - if (customBasesA && !customBasesA.find(base => tokenB.equals(base))) return false - if (customBasesB && !customBasesB.find(base => tokenA.equals(base))) return false - - return true - }) : [] }, [chainId, currencyA, currencyB]) } diff --git a/src/state/user/hooks.tsx b/src/state/user/hooks.tsx index 78fc322394..402902fb0e 100644 --- a/src/state/user/hooks.tsx +++ b/src/state/user/hooks.tsx @@ -3,10 +3,8 @@ import { useCallback, useMemo } from 'react' import { useDispatch, useSelector } from 'react-redux' import { useGetParticipantInfoQuery, useLazyGetParticipantInfoQuery } from 'services/kyberAISubscription' -import { SUGGESTED_BASES } from 'constants/bases' import { INITIAL_ALLOWED_SLIPPAGE, TERM_FILES_PATH } from 'constants/index' import { SupportedLocale } from 'constants/locales' -import { PINNED_PAIRS } from 'constants/tokens' import { useActiveWeb3React } from 'hooks' import { useAllTokens } from 'hooks/Tokens' import { @@ -322,9 +320,6 @@ export function useLiquidityPositionTokenPairs(): [Token, Token][] { const { chainId } = useActiveWeb3React() const allTokens = useAllTokens() - // pinned pairs - const pinnedPairs = useMemo(() => (chainId ? PINNED_PAIRS[chainId] ?? [] : []), [chainId]) - const { data: userLiquidityPositions } = useUserLiquidityPositions() // get pairs that has liquidity @@ -360,10 +355,7 @@ export function useLiquidityPositionTokenPairs(): [Token, Token][] { }) }, [savedSerializedPairs, chainId]) - const combinedList = useMemo( - () => userPairs.concat(generatedPairs).concat(pinnedPairs), - [generatedPairs, pinnedPairs, userPairs], - ) + const combinedList = useMemo(() => userPairs.concat(generatedPairs), [generatedPairs, userPairs]) return useMemo(() => { // dedupes pairs of tokens in the combined list @@ -428,19 +420,16 @@ export const useUserFavoriteTokens = (customChain?: ChainId) => { const dispatch = useDispatch() 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 const favoritedTokens = favoriteTokensByChainId?.[chainId] || {} - const favoritedTokenAddresses = defaultTokens + const favoritedTokenAddresses = (commonTokens || []) .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]) + }, [chainId, favoriteTokensByChainId, commonTokens]) const toggleFavoriteToken = useCallback( (payload: ToggleFavoriteTokenPayload) => { diff --git a/src/state/user/reducer.ts b/src/state/user/reducer.ts index 373102e0ea..9a21ecf3d4 100644 --- a/src/state/user/reducer.ts +++ b/src/state/user/reducer.ts @@ -1,7 +1,6 @@ import { ChainId } from '@kyberswap/ks-sdk-core' import { createReducer } from '@reduxjs/toolkit' -import { SUGGESTED_BASES } from 'constants/bases' import { DEFAULT_DEADLINE_FROM_NOW, DEFAULT_SLIPPAGE, @@ -142,11 +141,6 @@ function pairKey(token0Address: string, token1Address: string) { return `${token0Address};${token1Address}` } -export const getFavoriteTokenDefault = (chainId: ChainId) => ({ - addresses: SUGGESTED_BASES[chainId].map(e => e.address), - includeNativeToken: true, -}) - export const CROSS_CHAIN_SETTING_DEFAULT = { isSlippageControlPinned: true, slippageTolerance: INITIAL_ALLOWED_SLIPPAGE, From 998ced3e1e781e6b29443f66cff3a2d5d6f6ed09 Mon Sep 17 00:00:00 2001 From: Danh Date: Tue, 28 Nov 2023 14:34:41 +0700 Subject: [PATCH 14/14] hotfix: crash kyberAI when wrong chain --- .../components/SearchWithDropDown.tsx | 262 ++++++++++-------- 1 file changed, 142 insertions(+), 120 deletions(-) diff --git a/src/pages/TrueSightV2/components/SearchWithDropDown.tsx b/src/pages/TrueSightV2/components/SearchWithDropDown.tsx index d1681bc0b1..e28ee8c4be 100644 --- a/src/pages/TrueSightV2/components/SearchWithDropDown.tsx +++ b/src/pages/TrueSightV2/components/SearchWithDropDown.tsx @@ -1,5 +1,5 @@ import { Trans, t } from '@lingui/macro' -import React, { ReactNode, useCallback, useEffect, useRef, useState } from 'react' +import React, { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { X } from 'react-feather' import Skeleton, { SkeletonTheme } from 'react-loading-skeleton' import { useLocation, useNavigate } from 'react-router-dom' @@ -322,11 +322,14 @@ const SearchWithDropdown = () => { { skip: debouncedSearch === '' }, ) const [history, setHistory] = useLocalStorage>('kyberai-search-history') - const saveToHistory = (token: ITokenSearchResult) => { - if (!(history && history.some(t => t.assetId === token.assetId))) { - setHistory([token, ...(history || [])].slice(0, 3)) - } - } + const saveToHistory = useCallback( + (token: ITokenSearchResult) => { + if (!(history && history.some(t => t.assetId === token.assetId))) { + setHistory([token, ...(history || [])].slice(0, 3)) + } + }, + [history, setHistory], + ) const [getTokenData] = useLazySearchTokenQuery() useEffect(() => { @@ -396,132 +399,151 @@ const SearchWithDropdown = () => { } }, []) - const DropdownContent = () => ( -
- {isLoading ? ( - <> - - - - - ) : haveSearchResult ? ( - <> - - {searchResult.map(item => ( - { - setExpanded(false) - saveToHistory(item) - mixpanelHandler(MIXPANEL_TYPE.KYBERAI_SEARCH_TOKEN_SUCCESS, { - token_name: item.symbol?.toUpperCase(), - source: pathname.includes(APP_PATHS.KYBERAI_EXPLORE) - ? 'explore' - : KYBERAI_LISTYPE_TO_MIXPANEL[listType], - search_term: search, - }) - }} - /> - ))} - - - ) : noSearchResult ? ( - <> - - - - Oops, we couldnt find your token! We will regularly add new tokens that have achieved a certain trading - volume - - - - - ) : ( - <> - {history && ( - - - Search History - - } - > - {history.slice(0, 3).map((item, index) => ( - setExpanded(false)} /> - ))} + const content = useMemo(() => { + return ( +
+ {isLoading ? ( + <> + + - )} - - - Bullish Tokens - - } - > - {isBullishLoading ? ( - - ) : ( - top5bullish?.data?.slice(0, 3).map((item, index) => ( + + ) : haveSearchResult ? ( + <> + + {searchResult.map(item => ( { setExpanded(false) - saveToHistory(formatTokenType(item)) + saveToHistory(item) mixpanelHandler(MIXPANEL_TYPE.KYBERAI_SEARCH_TOKEN_SUCCESS, { token_name: item.symbol?.toUpperCase(), source: pathname.includes(APP_PATHS.KYBERAI_EXPLORE) ? 'explore' : KYBERAI_LISTYPE_TO_MIXPANEL[listType], - token_type: 'bullish', + search_term: debouncedSearch, }) }} /> - )) - )} - - - - Bearish Tokens - - } - > - {isBearishLoading ? ( - - ) : ( - top5bearish?.data?.slice(0, 3).map((item, index) => ( - { - setExpanded(false) - saveToHistory(formatTokenType(item)) - mixpanelHandler(MIXPANEL_TYPE.KYBERAI_SEARCH_TOKEN_SUCCESS, { - token_name: item.symbol?.toUpperCase(), - source: pathname.includes(APP_PATHS.KYBERAI_EXPLORE) - ? 'explore' - : KYBERAI_LISTYPE_TO_MIXPANEL[listType], - token_type: 'bearish', - }) - }} - /> - )) + ))} + + + ) : noSearchResult ? ( + <> + + + + Oops, we couldnt find your token! We will regularly add new tokens that have achieved a certain + trading volume + + + + + ) : ( + <> + {history && ( + + + Search History + + } + > + {history.slice(0, 3).map((item, index) => ( + setExpanded(false)} /> + ))} + )} - - - )} -
- ) + + + Bullish Tokens + + } + > + {isBullishLoading ? ( + + ) : ( + top5bullish?.data?.slice(0, 3).map((item, index) => ( + { + setExpanded(false) + saveToHistory(formatTokenType(item)) + mixpanelHandler(MIXPANEL_TYPE.KYBERAI_SEARCH_TOKEN_SUCCESS, { + token_name: item.symbol?.toUpperCase(), + source: pathname.includes(APP_PATHS.KYBERAI_EXPLORE) + ? 'explore' + : KYBERAI_LISTYPE_TO_MIXPANEL[listType], + token_type: 'bullish', + }) + }} + /> + )) + )} + + + + Bearish Tokens + + } + > + {isBearishLoading ? ( + + ) : ( + top5bearish?.data?.slice(0, 3).map((item, index) => ( + { + setExpanded(false) + saveToHistory(formatTokenType(item)) + mixpanelHandler(MIXPANEL_TYPE.KYBERAI_SEARCH_TOKEN_SUCCESS, { + token_name: item.symbol?.toUpperCase(), + source: pathname.includes(APP_PATHS.KYBERAI_EXPLORE) + ? 'explore' + : KYBERAI_LISTYPE_TO_MIXPANEL[listType], + token_type: 'bearish', + }) + }} + /> + )) + )} + + + )} +
+ ) + }, [ + above768, + haveSearchResult, + isBearishLoading, + isLoading, + theme, + listType, + isBullishLoading, + pathname, + saveToHistory, + history, + noSearchResult, + searchResult, + top5bullish, + top5bearish, + mixpanelHandler, + debouncedSearch, + ]) return ( <> @@ -549,7 +571,7 @@ const SearchWithDropdown = () => { - + {content} {expanded && }