diff --git a/package.json b/package.json index 2975c6b132..496988de7b 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,6 @@ "redux-localstorage-simple": "^2.5.1", "styled-components": "^5.3.6", "swiper": "^8.4.4", - "swr": "^2.0.0", "ua-parser-js": "^1.0.33", "util": "^0.12.5", "vite-plugin-env-compatible": "^1.1.1", diff --git a/src/components/SearchModal/CurrencySearch.tsx b/src/components/SearchModal/CurrencySearch.tsx index aed2696ce4..5feae646e4 100644 --- a/src/components/SearchModal/CurrencySearch.tsx +++ b/src/components/SearchModal/CurrencySearch.tsx @@ -7,6 +7,7 @@ import { ChangeEvent, KeyboardEvent, ReactNode, useCallback, useEffect, useMemo, import { Trash } from 'react-feather' import { usePrevious } from 'react-use' import { Flex, Text } from 'rebass' +import ksSettingApi from 'services/ksSetting' import styled from 'styled-components' import Column from 'components/Column' @@ -16,17 +17,12 @@ import { KS_SETTING_API } from 'constants/env' import { Z_INDEXS } from 'constants/styles' import { NativeCurrencies } from 'constants/tokens' import { useActiveWeb3React } from 'hooks' -import { - fetchListTokenByAddresses, - fetchTokenByAddress, - formatAndCacheToken, - useAllTokens, - useFetchERC20TokenFromRPC, -} from 'hooks/Tokens' +import { fetchListTokenByAddresses, formatAndCacheToken, useAllTokens, useFetchERC20TokenFromRPC } from 'hooks/Tokens' import useDebounce from 'hooks/useDebounce' import { useOnClickOutside } from 'hooks/useOnClickOutside' import useTheme from 'hooks/useTheme' import useToggle from 'hooks/useToggle' +import store from 'state' import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo' import { useRemoveUserAddedToken, useUserAddedTokens, useUserFavoriteTokens } from 'state/user/hooks' import { ButtonText, CloseIcon, TYPE } from 'theme' @@ -100,7 +96,9 @@ const fetchTokens = async ( ): Promise => { try { if (search && chainId && isAddress(chainId, search)) { - const token = await fetchTokenByAddress(search, chainId) + const { data: token } = await store.dispatch( + ksSettingApi.endpoints.getTokenByAddress.initiate({ address: search, chainId }), + ) return token ? [token as WrappedTokenInfo] : [] } const params: { query: string; isWhitelisted?: boolean; pageSize: number; page: number; chainIds: string } = { diff --git a/src/constants/index.ts b/src/constants/index.ts index b9e509e058..50f12c5fb9 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -5,7 +5,6 @@ import { v4 as uuid } from 'uuid' import { TransactionFlowState } from 'types/TransactionFlowState' -import { CAMPAIGN_BASE_URL } from './env' import * as ENV from './env' import { MAINNET_NETWORKS, NETWORKS_INFO, SUPPORTED_NETWORKS } from './networks' @@ -124,12 +123,6 @@ export const CAMPAIGN_YOUR_TRANSACTIONS_ITEM_PER_PAGE = 10000 export const ELASTIC_BASE_FEE_UNIT = 100_000 export const KYBERSWAP_SOURCE = '{"source":"kyberswap"}' -export const SWR_KEYS = { - getGrantProgramLeaderBoard: (id: number | string) => `${CAMPAIGN_BASE_URL}/api/v1/competitions/${id}/leaderboard`, - getListGrantPrograms: `${CAMPAIGN_BASE_URL}/api/v1/competitions`, - getGrantProgram: (id: number | string) => `${CAMPAIGN_BASE_URL}/api/v1/competitions/${id}`, -} - // https://www.nasdaq.com/glossary/b/bip export const MAX_NORMAL_SLIPPAGE_IN_BIPS = 1999 export const MAX_DEGEN_SLIPPAGE_IN_BIPS = 5000 diff --git a/src/hooks/Tokens.ts b/src/hooks/Tokens.ts index 729a6406cc..3b2bd81257 100644 --- a/src/hooks/Tokens.ts +++ b/src/hooks/Tokens.ts @@ -4,8 +4,7 @@ import axios from 'axios' import { arrayify } from 'ethers/lib/utils' import { useCallback, useMemo } from 'react' import { useSelector } from 'react-redux' -import ksSettingApi, { useGetTokenListQuery } from 'services/ksSetting' -import useSWR from 'swr' +import ksSettingApi from 'services/ksSetting' import ERC20_INTERFACE, { ERC20_BYTES32_INTERFACE } from 'constants/abis/erc20' import { KS_SETTING_API } from 'constants/env' @@ -13,12 +12,13 @@ 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 store, { AppState } from 'state' +import { 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' import { useUserAddedTokens } from 'state/user/hooks' import { filterTruthy, isAddress } from 'utils' +import { escapeQuoteString } from 'utils/tokenInfo' import useDebounce from './useDebounce' @@ -294,25 +294,6 @@ export const findCacheToken = (address: string) => { return cacheTokens[address] || cacheTokens[address.toLowerCase()] } -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 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 -} - export const fetchListTokenByAddresses = async (address: string[], chainId: ChainId) => { const cached = filterTruthy(address.map(addr => findCacheToken(addr))) if (cached.length === address.length) return cached @@ -322,10 +303,6 @@ export const fetchListTokenByAddresses = async (address: string[], chainId: Chai return filterTruthy(tokens.map(formatAndCacheToken)) as WrappedTokenInfo[] } -// ex: `"BTT_b"` => BTT_b -const escapeQuoteString = (str: string) => - str?.startsWith('"') && str?.endsWith('"') ? str.substring(1, str.length - 1) : str - export const formatAndCacheToken = (rawTokenResponse: TokenInfo) => { try { const tokenResponse = { ...rawTokenResponse } @@ -343,26 +320,6 @@ export const formatAndCacheToken = (rawTokenResponse: TokenInfo) => { } } -function useTokenV2( - tokenAddress?: string, - customChain?: ChainId, -): WrappedTokenInfo | Token | NativeCurrency | undefined | null { - const { chainId: currentChain } = useActiveWeb3React() - const chainId = customChain || currentChain - const address = isAddress(chainId, tokenAddress) - const { data, isValidating } = useSWR( - address.toString() + chainId?.toString(), - async () => { - try { - if (chainId && address) return await fetchTokenByAddress(address, chainId) - } catch (error) {} - return undefined - }, - { revalidateOnFocus: false, shouldRetryOnError: false, revalidateIfStale: false }, - ) - return isValidating ? null : data -} - export function useCurrency(currencyId: string | undefined): Currency | null | undefined { const { chainId } = useActiveWeb3React() const isETH = useMemo( @@ -389,7 +346,11 @@ export function useCurrencyV2(currencyId: string | undefined, customChainId?: Ch const tokenInWhitelist = currencyId ? whitelistTokens[currencyId] || whitelistTokens[currencyId?.toLowerCase()] : undefined - const token = useTokenV2(isETH || tokenInWhitelist ? undefined : currencyId, chainId) + + const { data: token } = ksSettingApi.useGetTokenByAddressQuery( + { address: isAddress(chainId, currencyId) || '', chainId }, + { skip: isETH || !!tokenInWhitelist || !isAddress(chainId, currencyId) }, + ) return useMemo(() => { if (!currencyId) return @@ -399,7 +360,10 @@ export function useCurrencyV2(currencyId: string | undefined, customChainId?: Ch } export const useStableCoins = (chainId: ChainId | undefined) => { - const { data } = useGetTokenListQuery({ chainId: chainId as ChainId, isStable: true }, { skip: !chainId }) + const { data } = ksSettingApi.useGetTokenListQuery( + { chainId: chainId as ChainId, isStable: true }, + { skip: !chainId }, + ) const stableCoins = useMemo(() => { return data?.data?.tokens || [] diff --git a/src/hooks/bridge/useGetBridgeTransfers.ts b/src/hooks/bridge/useGetBridgeTransfers.ts index 1db293b834..4e9d1bc024 100644 --- a/src/hooks/bridge/useGetBridgeTransfers.ts +++ b/src/hooks/bridge/useGetBridgeTransfers.ts @@ -1,5 +1,3 @@ -import useSWR from 'swr' - export enum MultichainTransferStatus { Processing = 0, Success = 1, @@ -21,36 +19,3 @@ export type MultichainTransfer = { createdAt: number isReceiveAnyToken: boolean } - -type Response = { - code: number - message: string - data: { - transfers: MultichainTransfer[] - pagination: { - totalItems: number - } - } -} - -const useGetBridgeTransfers = (swrKey: string | null) => { - return useSWR( - swrKey, - async (url: string) => { - const response = await fetch(url) - if (response.ok) { - const data = await response.json() - if (data) { - return data - } - - throw new Error(`No transfers found with url = ${swrKey}`) - } - - throw new Error(`Fetching bridge transfers failed with url = ${swrKey}`) - }, - { revalidateOnFocus: false, refreshInterval: 5_000 }, - ) -} - -export default useGetBridgeTransfers diff --git a/src/hooks/campaigns/useGetGrantProgram.ts b/src/hooks/campaigns/useGetGrantProgram.ts deleted file mode 100644 index e018b4b07d..0000000000 --- a/src/hooks/campaigns/useGetGrantProgram.ts +++ /dev/null @@ -1,32 +0,0 @@ -import axios from 'axios' -import useSWR from 'swr' - -import { SWR_KEYS } from 'constants/index' -import { GrantProgram } from 'types/grantProgram' - -type Response = { - code: number - message: string - data?: GrantProgram -} - -const useGetGrantProgram = (id = 'latest') => { - return useSWR( - `${SWR_KEYS.getGrantProgram(id)}`, - async (url: string) => { - const response = await axios.get(url) - - if (response?.data?.data) { - return response.data.data - } - - throw new Error(response?.data?.message || 'Something went wrong while fetching the latest grant program') - }, - { - revalidateIfStale: false, - revalidateOnFocus: false, - }, - ) -} - -export default useGetGrantProgram diff --git a/src/hooks/campaigns/useGetGrantPrograms.ts b/src/hooks/campaigns/useGetGrantPrograms.ts deleted file mode 100644 index cbf9f7e718..0000000000 --- a/src/hooks/campaigns/useGetGrantPrograms.ts +++ /dev/null @@ -1,38 +0,0 @@ -import axios from 'axios' -import useSWR from 'swr' - -import { SWR_KEYS } from 'constants/index' -import { GrantProgram } from 'types/grantProgram' - -type Response = { - code: number - message: string - data?: { - totalItems: number - competitions: GrantProgram[] - } -} - -const useGetGrantPrograms = () => { - return useSWR( - SWR_KEYS.getListGrantPrograms, - async (url: string) => { - const { data: response } = await axios({ - method: 'GET', - url, - params: { - page: 1, - pageSize: 100, - }, - }) - - return response - }, - { - revalidateOnFocus: false, - revalidateIfStale: false, - }, - ) -} - -export default useGetGrantPrograms diff --git a/src/hooks/campaigns/useGetLeaderboardGrantProgram.ts b/src/hooks/campaigns/useGetLeaderboardGrantProgram.ts deleted file mode 100644 index aa154f9044..0000000000 --- a/src/hooks/campaigns/useGetLeaderboardGrantProgram.ts +++ /dev/null @@ -1,61 +0,0 @@ -import axios from 'axios' -import { stringify } from 'querystring' -import useSWR, { mutate } from 'swr' - -import { SWR_KEYS } from 'constants/index' -import { ProjectRanking } from 'types/grantProgram' - -export type RankByParam = 'total_participants' | 'total_trades' | 'total_volume' - -type LeaderBoardData = { - totalItems: number - rankings: ProjectRanking[] -} - -type Response = { - code: number - message: string - data?: LeaderBoardData -} - -const generateUrl = ({ id, rankBy, page = 1, pageSize = 5 }: Args) => { - if (!id || !rankBy) { - return '' - } - - return `${SWR_KEYS.getGrantProgramLeaderBoard(id)}?${stringify({ rankBy, page, pageSize })}` -} - -type Args = { id?: number; rankBy: RankByParam; page?: number; pageSize?: number } - -const useGetLeaderboardGrantProgram = (args: Args) => { - const url = generateUrl(args) - const swrData = useSWR( - url, - async (url: string) => { - const response = await axios.get(url) - if (response?.data?.data) { - return response.data.data - } - - throw new Error(response?.data?.message || 'Something went wrong while fetching the leader board data') - }, - { - revalidateOnFocus: false, - revalidateIfStale: false, - }, - ) - - const refresh = () => { - mutate(url, undefined, { - revalidate: true, - }) - } - - return { - swrData, - refresh, - } -} - -export default useGetLeaderboardGrantProgram diff --git a/src/hooks/kyberdao/index.tsx b/src/hooks/kyberdao/index.tsx index 66afe2c20f..9ea1c77e60 100644 --- a/src/hooks/kyberdao/index.tsx +++ b/src/hooks/kyberdao/index.tsx @@ -12,8 +12,6 @@ import kyberDAOApi, { useGetGasRefundRewardInfoQuery, useGetGasRefundTierInfoQuery, } from 'services/kyberDAO' -import useSWR from 'swr' -import useSWRImmutable from 'swr/immutable' import { NotificationType } from 'components/Announcement/type' import DaoABI from 'constants/abis/kyberdao/dao.json' @@ -40,15 +38,7 @@ import { formatUnitsToFixed } from 'utils/formatBalance' import { sendEVMTransaction } from 'utils/sendTransaction' import { ErrorName } from 'utils/sentry' -import { - EligibleTxsInfo, - ProposalDetail, - ProposalStatus, - RewardStats, - StakerAction, - StakerInfo, - VoteInfo, -} from './types' +import { DaoInfo, EligibleTxsInfo } from './types' export function isSupportKyberDao(chainId: ChainId) { return SUPPORTED_NETWORKS.includes(chainId) && NETWORKS_INFO[chainId].kyberDAO @@ -305,12 +295,6 @@ export const useVotingActions = () => { return { vote } } -const fetcher = (url: string) => { - return fetch(url) - .then(res => res.json()) - .then(res => res.data) -} - export function useStakingInfo() { const { account } = useActiveWeb3React() const kyberDaoInfo = useKyberDAOInfo() @@ -323,10 +307,7 @@ export function useStakingInfo() { return delegatedAddress.result?.[0] && delegatedAddress.result?.[0] !== account }, [delegatedAddress, account]) - const { data: stakerActions } = useSWR( - account && kyberDaoInfo?.daoStatsApi + '/stakers/' + account + '/actions', - fetcher, - ) + const { data: stakerActions } = kyberDAOApi.useGetStakerActionsQuery({ account }, { skip: !account }) const [totalSupply, setTotalSupply] = useState() useEffect(() => { @@ -354,8 +335,9 @@ export function useVotingInfo() { RewardDistributorABI, ChainId.MAINNET, ) - const { data: daoInfo } = useSWR(kyberDaoInfo?.daoStatsApi + '/dao-info', fetcher) - const [localStoredDaoInfo, setLocalStoredDaoInfo] = useLocalStorage('kyberdao-daoInfo') + const { data: daoInfo } = kyberDAOApi.useGetDaoInfoQuery({}) + const [localStoredDaoInfo, setLocalStoredDaoInfo] = useLocalStorage('kyberdao-daoInfo') + const [merkleData, setMerkleData] = useState() useEffect(() => { rewardsDistributorContract @@ -382,19 +364,9 @@ export function useVotingInfo() { return merkleDataFileUrl }, [merkleData]) - const { data: userRewards } = useSWRImmutable( - account && merkleDataFileUrl ? { url: merkleDataFileUrl, address: account } : null, - ({ url, address }) => { - return fetch(url) - .then(res => { - return res.json() - }) - .then(res => { - res.userReward = address ? res.userRewards[address] : undefined - delete res.userRewards - return res - }) - }, + const { data: userRewards } = kyberDAOApi.useGetUserRewardsQuery( + { url: merkleDataFileUrl, account }, + { skip: !merkleDataFileUrl || !account }, ) const [claimedRewardAmounts, setClaimedRewardAmounts] = useState() @@ -418,53 +390,23 @@ export function useVotingInfo() { ) }, [claimedRewardAmounts, userRewards?.userReward]) - const { data: proposals } = useSWR( - kyberDaoInfo?.daoStatsApi + '/proposals', - (url: string) => - fetch(url) - .then(res => res.json()) - .then(res => - res.data.map((p: ProposalDetail) => { - let mappedStatus - switch (p.status) { - case 'Succeeded': - case 'Queued': - case 'Finalized': - mappedStatus = ProposalStatus.Approved - break - case 'Expired': - mappedStatus = ProposalStatus.Failed - break - default: - mappedStatus = p.status - break - } - return { ...p, status: mappedStatus } - }), - ), - { - refreshInterval: 15000, - }, - ) + const { data: proposals } = kyberDAOApi.useGetProposalsQuery({}) - const { data: stakerInfo } = useSWR( - daoInfo?.current_epoch && - account && - kyberDaoInfo?.daoStatsApi + '/stakers/' + account + '?epoch=' + daoInfo?.current_epoch, - fetcher, + const { data: stakerInfo } = kyberDAOApi.useGetStakerInfoQuery( + { account, epoch: daoInfo?.current_epoch }, + { skip: !account || !daoInfo?.current_epoch }, ) - const { data: stakerInfoNextEpoch } = useSWR( - daoInfo?.current_epoch && - account && - kyberDaoInfo?.daoStatsApi + '/stakers/' + account + '?epoch=' + (parseFloat(daoInfo?.current_epoch) + 1), - fetcher, + + const { data: stakerInfoNextEpoch } = kyberDAOApi.useGetStakerInfoQuery( + { account, epoch: Number(daoInfo?.current_epoch) + 1 }, + { skip: !account || !daoInfo?.current_epoch }, ) const calculateVotingPower = useCallback( (kncAmount: string, newStakingAmount?: string) => { if (!daoInfo?.total_staked) return '0' const totalStakedKNC = daoInfo?.total_staked - if (parseFloat(totalStakedKNC) === 0) return '0' + if (totalStakedKNC === 0) return '0' const votingPower = newStakingAmount && parseFloat(newStakingAmount) > 0 @@ -481,14 +423,9 @@ export function useVotingInfo() { [daoInfo], ) - const { data: votesInfo } = useSWR( - account ? kyberDaoInfo?.daoStatsApi + '/stakers/' + account + '/votes' : null, - fetcher, - ) + const { data: votesInfo } = kyberDAOApi.useGetStakerVotesQuery({ account }, { skip: !account }) - const { data: rewardStats } = useSWR(kyberDaoInfo?.daoStatsApi + '/api/v1/reward-stats', url => - fetcher(url).then(res => res.rewardStats), - ) + const { data: rewardStats } = kyberDAOApi.useGetRewardStatsQuery({}) const result = { daoInfo: daoInfo || localStoredDaoInfo || undefined, @@ -681,13 +618,3 @@ export const useEligibleTransactions = (page = 1, pageSize = 100): EligibleTxsIn return data?.data } - -export function useProposalInfoById(id?: number): { proposalInfo?: ProposalDetail } { - const kyberDaoInfo = useKyberDAOInfo() - const { data } = useSWRImmutable( - id !== undefined ? kyberDaoInfo?.daoStatsApi + '/proposals/' + id : undefined, - fetcher, - { refreshInterval: 15000 }, - ) - return { proposalInfo: data } -} diff --git a/src/hooks/kyberdao/types.ts b/src/hooks/kyberdao/types.ts index 081eca4f10..4fb96310c8 100644 --- a/src/hooks/kyberdao/types.ts +++ b/src/hooks/kyberdao/types.ts @@ -133,3 +133,13 @@ export interface RewardStats { } apr: string } + +export type DaoInfo = { + current_epoch: number + current_epoch_voted: number + current_epoch_voter: number + epoch_period_in_seconds: number + first_epoch_start_timestamp: number + total_staked: number + total_staker: number +} diff --git a/src/hooks/useAggregatorAPR.ts b/src/hooks/useAggregatorAPR.ts deleted file mode 100644 index 38b3900676..0000000000 --- a/src/hooks/useAggregatorAPR.ts +++ /dev/null @@ -1,47 +0,0 @@ -import useSWR from 'swr' - -import { AGGREGATOR_STATS_API } from 'constants/env' -import { VERSION } from 'constants/v2' - -interface APRResponse { - max_apr: { - value: number - id: string - chain_id: number - is_farm: boolean - type?: VERSION.CLASSIC | VERSION.ELASTIC - } - total_earnings: number -} - -export default function useAggregatorAPR(): APRResponse { - const fetcher = (url: string) => fetch(url).then(r => r.json()) - - const url = `${AGGREGATOR_STATS_API}/api/max-apr-and-total-earning` - - const { data, error } = useSWR(url, fetcher, { - refreshInterval: 10000, - onErrorRetry: (error, _key, _config, revalidate, { retryCount }) => { - // Never retry on 404. - if (error.status === 404) return - - // Only retry up to 10 times. - if (retryCount >= 10) return - - if (error.status === 403) { - // If API return 403, retry after 30 seconds. - setTimeout(() => revalidate({ retryCount }), 30000) - return - } - - // Retry after 5 seconds. - setTimeout(() => revalidate({ retryCount }), 5000) - }, - }) - - if (error) { - console.error(error.message) - } - - return data -} diff --git a/src/hooks/useAggregatorStats.ts b/src/hooks/useAggregatorStats.ts deleted file mode 100644 index 34b5770540..0000000000 --- a/src/hooks/useAggregatorStats.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { ChainId } from '@kyberswap/ks-sdk-core' -import useSWR from 'swr' - -import { KS_SETTING_API } from 'constants/env' -import { NETWORKS_INFO } from 'constants/networks' - -const useLiquiditySources = (chainId: ChainId) => { - return useSWR<{ name: string; logoURL: string; dexId: string; id: number }[]>( - `${KS_SETTING_API}/v1/dexes?chain=${NETWORKS_INFO[chainId].ksSettingRoute}&isEnabled=true&pageSize=100`, - async (url: string) => { - if (!NETWORKS_INFO[chainId].ksSettingRoute) return - - const response = await fetch(url) - if (response.ok) { - const data = await response.json() - if (data && data.data.dexes) { - return data.data.dexes - } - - const err = `no pools found on ${NETWORKS_INFO[chainId].name}` - console.error(err) - throw err - } - - const err = `fetching stats on ${NETWORKS_INFO[chainId].name} failed` - console.error(err) - throw err - }, - ) -} - -export default useLiquiditySources diff --git a/src/hooks/useAggregatorVolume.ts b/src/hooks/useAggregatorVolume.ts deleted file mode 100644 index 31e7733765..0000000000 --- a/src/hooks/useAggregatorVolume.ts +++ /dev/null @@ -1,40 +0,0 @@ -import useSWR from 'swr' - -import { AGGREGATOR_STATS_API } from 'constants/env' - -interface VolumeResponse { - totalVolume: string - last24hVolume: string -} - -export default function useAggregatorVolume(): VolumeResponse { - const fetcher = (url: string) => fetch(url).then(r => r.json()) - - const url = `${AGGREGATOR_STATS_API}/api/volume` - - const { data, error } = useSWR(url, fetcher, { - refreshInterval: 10000, - onErrorRetry: (error, _key, _config, revalidate, { retryCount }) => { - // Never retry on 404. - if (error.status === 404) return - - // Only retry up to 10 times. - if (retryCount >= 10) return - - if (error.status === 403) { - // If API return 403, retry after 30 seconds. - setTimeout(() => revalidate({ retryCount }), 30000) - return - } - - // Retry after 5 seconds. - setTimeout(() => revalidate({ retryCount }), 5000) - }, - }) - - if (error) { - console.error(error.message) - } - - return data -} diff --git a/src/hooks/useBasicChartData.ts b/src/hooks/useBasicChartData.ts index 45c659f1dc..792f6f3265 100644 --- a/src/hooks/useBasicChartData.ts +++ b/src/hooks/useBasicChartData.ts @@ -1,11 +1,10 @@ import { KyberOauth2Api } from '@kybernetwork/oauth2' import { ChainId, Token, WETH } from '@kyberswap/ks-sdk-core' -import axios, { AxiosResponse } from 'axios' +import { AxiosResponse } from 'axios' import { getUnixTime, subHours } from 'date-fns' import { useMemo } from 'react' -import useSWR from 'swr' +import coingeckoApi from 'services/coingecko' -import { AGGREGATOR_API, PRICE_CHART_API } from 'constants/env' import { NETWORKS_INFO } from 'constants/networks' import { useActiveWeb3React } from 'hooks' @@ -67,23 +66,7 @@ const formatData = (res: AxiosResponse) => { return res.data } -const fetchKyberDataSWR = async (url: string) => { - const res = await axios.get(url, { timeout: 5000 }) - return formatData(res) -} - -const fetchKyberDataSWRWithHeader = async (url: string) => { - const res = await KyberOauth2Api.get(url, undefined, { - timeout: 5000, - headers: { - 'accept-version': 'Latest', - }, - }) - - return formatData(res) -} - -const fetchCoingeckoDataSWR = async ([tokenAddresses, chainIds, timeFrame, coingeckoAPI]: [ +export const fetchCoingeckoData = async ([tokenAddresses, chainIds, timeFrame, coingeckoAPI]: [ tokenAddresses: string[], chainIds: ChainId[], timeFrame: any, @@ -104,15 +87,9 @@ export default function useBasicChartData( tokens: (Token | null | undefined)[], timeFrame: LiveDataTimeframeEnum, ): { data: ChartData[]; loading: boolean; error: any } { - const { chainId, networkInfo } = useActiveWeb3React() + const { chainId } = useActiveWeb3React() const coingeckoAPI = useCoingeckoAPI() - const isReverse = useMemo(() => { - if (!tokens || !tokens[0] || !tokens[1] || tokens[0].equals(tokens[1]) || tokens[0].chainId !== tokens[1].chainId) - return false - const [token0] = tokens[0].sortsBefore(tokens[1]) ? [tokens[0], tokens[1]] : [tokens[1], tokens[0]] - return token0 !== tokens[0] - }, [tokens]) const tokenAddresses = useMemo( () => tokens.filter(Boolean).map(token => { @@ -125,123 +102,53 @@ export default function useBasicChartData( const { data: coingeckoData, error: coingeckoError, - isValidating: coingeckoLoading, - } = useSWR( - tokenAddresses[0] && tokenAddresses[1] - ? [tokenAddresses, [tokens[0]?.chainId, tokens[1]?.chainId], timeFrame, coingeckoAPI] - : null, - - fetchCoingeckoDataSWR, - { - shouldRetryOnError: false, - revalidateOnFocus: false, - revalidateIfStale: false, - }, - ) - - const { - data: kyberData, - error: kyberError, - isValidating: kyberLoading, - } = useSWR( - coingeckoError && tokenAddresses[0] && tokenAddresses[1] - ? `${PRICE_CHART_API}/price-chart?chainId=${chainId}&timeWindow=${timeFrame.toLowerCase()}&tokenIn=${ - tokenAddresses[0] - }&tokenOut=${tokenAddresses[1]}` - : null, - fetchKyberDataSWR, + isLoading: coingeckoLoading, + } = coingeckoApi.useFetchCoingeckoDataQuery( { - shouldRetryOnError: false, - revalidateOnFocus: false, - revalidateIfStale: false, + tokenAddresses: tokenAddresses as string[], + chainIds: [tokens[0]?.chainId, tokens[1]?.chainId] as ChainId[], + timeFrame, + coingeckoAPI, }, + { skip: !tokenAddresses[0] || !tokenAddresses[1] }, ) - const isKyberDataNotValid = useMemo(() => { - if (kyberError || kyberData === null) return true - if (kyberData && kyberData.length === 0) return true - if ( - kyberData && - kyberData.length > 0 && - kyberData.every((item: any) => !item.token0Price || item.token0Price === '0') - ) - return true - return false - }, [kyberError, kyberData]) - const chartData = useMemo(() => { - if (!isKyberDataNotValid && kyberData && kyberData.length > 0) { - return kyberData - .sort((a: any, b: any) => parseInt(a.timestamp) - parseInt(b.timestamp)) - .map((item: any) => { - return { - time: parseInt(item.timestamp) * 1000, - value: !isReverse ? item.token0Price : item.token1Price || 0, - } - }) - } else if (coingeckoData && coingeckoData[0]?.prices?.length > 0 && coingeckoData[1]?.prices?.length > 0) { + if (coingeckoData && coingeckoData[0]?.prices?.length > 0 && coingeckoData[1]?.prices?.length > 0) { const [data1, data2] = coingeckoData return data1.prices.map((item: number[]) => { const closestPrice = getClosestPrice(data2.prices, item[0]) return { time: item[0], value: closestPrice > 0 ? parseFloat((item[1] / closestPrice).toPrecision(6)) : 0 } }) } else return [] - }, [kyberData, coingeckoData, isKyberDataNotValid, isReverse]) - - const error = (!!kyberError && !!coingeckoError) || chartData.length === 0 + }, [coingeckoData]) - const { data: liveKyberData } = useSWR( - !isKyberDataNotValid && kyberData && chainId - ? `${AGGREGATOR_API}/${networkInfo.aggregatorRoute}/tokens?ids=${tokenAddresses[0]},${tokenAddresses[1]}` - : null, - fetchKyberDataSWRWithHeader, + const { data: liveCoingeckoData } = coingeckoApi.useFetchCoingeckoDataQuery( { - refreshInterval: 60000, - shouldRetryOnError: false, - revalidateOnFocus: false, - revalidateIfStale: false, - }, - ) - - const { data: liveCoingeckoData } = useSWR( - isKyberDataNotValid && coingeckoData - ? [tokenAddresses, [tokens[0]?.chainId, tokens[1]?.chainId], 'live', coingeckoAPI] - : null, - fetchCoingeckoDataSWR, - { - refreshInterval: 60000, - shouldRetryOnError: false, - revalidateOnFocus: false, - revalidateIfStale: false, + tokenAddresses: tokenAddresses as string[], + chainIds: [tokens[0]?.chainId, tokens[1]?.chainId] as ChainId[], + timeFrame: 'live', + coingeckoAPI, }, + { skip: !coingeckoData, pollingInterval: 60_000 }, ) const latestData = useMemo(() => { - if (isKyberDataNotValid) { - if (liveCoingeckoData) { - const [data1, data2] = liveCoingeckoData - if (data1.prices?.length > 0 && data2.prices?.length > 0) { - const newValue = parseFloat( - (data1.prices[data1.prices.length - 1][1] / data2.prices[data2.prices.length - 1][1]).toPrecision(6), - ) - return { time: new Date().getTime(), value: newValue } - } - } - } else { - if (liveKyberData) { - const value = - liveKyberData && tokenAddresses[0] && tokenAddresses[1] - ? liveKyberData[tokenAddresses[0]]?.price / liveKyberData[tokenAddresses[1]]?.price - : 0 - if (value) return { time: new Date().getTime(), value: value } + if (liveCoingeckoData) { + const [data1, data2] = liveCoingeckoData + if (data1?.prices?.length > 0 && data2?.prices?.length > 0) { + const newValue = parseFloat( + (data1.prices[data1.prices.length - 1][1] / data2.prices[data2.prices.length - 1][1]).toPrecision(6), + ) + return { time: new Date().getTime(), value: newValue } } } return null - }, [liveKyberData, liveCoingeckoData, isKyberDataNotValid, tokenAddresses]) + }, [liveCoingeckoData]) return { data: useMemo(() => (latestData ? [...chartData, latestData] : chartData), [latestData, chartData]), - error: error, - loading: !tokenAddresses[0] || !tokenAddresses[1] || kyberLoading || coingeckoLoading, + error: !!coingeckoError || chartData.length === 0, + loading: !tokenAddresses[0] || !tokenAddresses[1] || coingeckoLoading, } } diff --git a/src/hooks/useClaimReward.ts b/src/hooks/useClaimReward.ts index ac01415f98..be8ca96972 100644 --- a/src/hooks/useClaimReward.ts +++ b/src/hooks/useClaimReward.ts @@ -4,7 +4,7 @@ import { t } from '@lingui/macro' import { captureException } from '@sentry/react' import { BigNumber } from 'ethers' import { useCallback, useEffect, useMemo, useState } from 'react' -import useSWR from 'swr' +import externalApi from 'services/externalApi' import CLAIM_REWARD_ABI from 'constants/abis/claim-reward.json' import { CLAIM_REWARDS_DATA_URL, NETWORKS_INFO } from 'constants/networks' @@ -20,7 +20,7 @@ interface IReward { amounts: string[] proof: string[] } -interface IPhaseData { +export interface IPhaseData { phaseId: number merkleRoot: string tokens: string[] @@ -49,8 +49,9 @@ export default function useClaimReward() { const [rewardAmounts, setRewardAmounts] = useState('0') const [error, setError] = useState(null) const [phaseId, setPhaseId] = useState(0) - const { data } = useSWR(isValid && chainId ? CLAIM_REWARDS_DATA_URL[chainId] : '', (url: string) => - fetch(url).then(r => r.json()), + const { data } = externalApi.useGetClaimRewardsQuery( + { url: CLAIM_REWARDS_DATA_URL[chainId], account }, + { skip: !isValid || !chainId }, ) const userRewards: IUserReward[] = useMemo( () => @@ -140,7 +141,11 @@ export default function useClaimReward() { ) .then((res: boolean) => { if (res) { - return rewardSigningContract.getClaimedAmounts(data.phaseId || 0, account || '', data?.tokens || []) + return rewardSigningContract.getClaimedAmounts( + userReward.phaseId || 0, + account || '', + userReward.tokens || [], + ) } else { throw new Error() } diff --git a/src/hooks/useGasPriceFromDeBank.ts b/src/hooks/useGasPriceFromDeBank.ts index 8c1d4154fb..5cb0433876 100644 --- a/src/hooks/useGasPriceFromDeBank.ts +++ b/src/hooks/useGasPriceFromDeBank.ts @@ -1,4 +1,4 @@ -import useSWR from 'swr' +import externalApi from 'services/externalApi' import { useActiveWeb3React } from 'hooks' import { useETHPrice } from 'state/application/hooks' @@ -9,7 +9,7 @@ export enum GasLevel { FAST = 'fast', } -type Response = Array<{ +export type GasPriceData = Array<{ level: GasLevel front_tx_count: number price: number // in wei @@ -24,7 +24,7 @@ type GasPriceTrackerData = Record< } > -const calculateGasPrices = (resp: Response, currentPrice?: string | number): GasPriceTrackerData => { +const calculateGasPrices = (resp: GasPriceData, currentPrice?: string | number): GasPriceTrackerData => { const levels = [GasLevel.SLOW, GasLevel.NORMAL, GasLevel.FAST] const gasPricesInWei = levels.map(level => resp.find(item => item.level === level)?.price) @@ -58,40 +58,12 @@ const calculateGasPrices = (resp: Response, currentPrice?: string | number): Gas } const useGasPriceFromDeBank = (): GasPriceTrackerData | undefined => { - const { chainId, networkInfo } = useActiveWeb3React() + const { networkInfo } = useActiveWeb3React() const nativeTokenPriceData = useETHPrice() const chainSlug = networkInfo.deBankSlug - const { data, error } = useSWR( - `https://openapi.debank.com/v1/wallet/gas_market?chain_id=${chainSlug}`, - async (url: string) => { - if (!chainSlug) { - const err = `chain (${chainId}) is not supported` - console.error(err) - throw err - } - - const response = await fetch(url) - if (response.ok) { - const data = await response.json() - if (data && Array.isArray(data)) { - return data - } - - const err = `invalid data in chain (${chainSlug})` - console.error(err) - throw err - } - - const err = `fetching data on chain (${chainSlug}) failed` - console.error(err) - throw err - }, - ) - - if (error || !data) { - return undefined - } + const { data } = externalApi.useGetGasPriceQuery({ chainSlug }, { skip: !chainSlug }) + if (!data) return undefined return calculateGasPrices(data, nativeTokenPriceData.currentPrice) } diff --git a/src/pages/About/AboutKyberSwap/index.tsx b/src/pages/About/AboutKyberSwap/index.tsx index 4785130c2d..c8574646e0 100644 --- a/src/pages/About/AboutKyberSwap/index.tsx +++ b/src/pages/About/AboutKyberSwap/index.tsx @@ -3,6 +3,7 @@ import { Edit, FileText, Plus, Repeat } from 'react-feather' import { Link } from 'react-router-dom' import { useMedia } from 'react-use' import { Box, Flex, Text } from 'rebass' +import aggregatorStatsApi from 'services/aggregatorStats' import styled from 'styled-components' import ArbitrumDark from 'assets/images/Arbitrum_HorizontalLogo-dark.svg' @@ -41,7 +42,6 @@ import ZkSyncFull from 'components/Icons/ZkSyncFull' import Loader from 'components/Loader' import { APP_PATHS } from 'constants/index' import { useActiveWeb3React } from 'hooks' -import useAggregatorVolume from 'hooks/useAggregatorVolume' import useChainsConfig from 'hooks/useChainsConfig' import useMixpanel, { MIXPANEL_TYPE } from 'hooks/useMixpanel' import useTheme from 'hooks/useTheme' @@ -170,7 +170,7 @@ function AboutKyberSwap() { const above768 = useMedia('(min-width: 768px)') const above500 = useMedia('(min-width: 500px)') - const aggregatorData = useAggregatorVolume() + const { data: aggregatorData } = aggregatorStatsApi.useGetAggregatorVolumeQuery({}) const { mixpanelHandler } = useMixpanel() diff --git a/src/pages/GrantProgram/EndedPrograms.tsx b/src/pages/GrantProgram/EndedPrograms.tsx index 09de595f07..6cad06535b 100644 --- a/src/pages/GrantProgram/EndedPrograms.tsx +++ b/src/pages/GrantProgram/EndedPrograms.tsx @@ -4,12 +4,12 @@ import { ChevronRight } from 'react-feather' import { useNavigate } from 'react-router-dom' import { useMedia } from 'react-use' import { Flex, Text } from 'rebass' +import campaignApi from 'services/campaign' import styled from 'styled-components' import HourGlass from 'assets/images/hourglass.png' import Loader from 'components/Loader' import { APP_PATHS } from 'constants/index' -import useGetGrantPrograms from 'hooks/campaigns/useGetGrantPrograms' import useTheme from 'hooks/useTheme' import { MEDIA_WIDTHS } from 'theme' import { GrantProgram } from 'types/grantProgram' @@ -167,12 +167,13 @@ const EmptyState = () => { ) } +const now = Date.now() / 1000 + const EndedPrograms: React.FC = () => { - const now = Date.now() / 1000 - const { data, isValidating } = useGetGrantPrograms() - const programs = (data?.data?.competitions || []).filter(prog => prog.endTime < now) + const { data: competitions = [], isLoading } = campaignApi.useGetListGrantProgramsQuery({}) + const programs = competitions.filter(prog => prog.endTime < now) - if (isValidating) { + if (isLoading) { return } diff --git a/src/pages/GrantProgram/LatestProgram.tsx b/src/pages/GrantProgram/LatestProgram.tsx index 29c7d08199..849cb95544 100644 --- a/src/pages/GrantProgram/LatestProgram.tsx +++ b/src/pages/GrantProgram/LatestProgram.tsx @@ -1,9 +1,9 @@ -import useGetGrantProgram from 'hooks/campaigns/useGetGrantProgram' +import campaignApi from 'services/campaign' import SingleProgram from './SingleProgram' const LatestProgram = () => { - const { data } = useGetGrantProgram('latest') + const { data } = campaignApi.useGetGrantProgramQuery({ id: 'latest' }) return } diff --git a/src/pages/GrantProgram/SingleProgram/LeaderBoardSection/Trophy.tsx b/src/pages/GrantProgram/SingleProgram/LeaderBoardSection/Trophy.tsx index 2aed76209e..17f61a3214 100644 --- a/src/pages/GrantProgram/SingleProgram/LeaderBoardSection/Trophy.tsx +++ b/src/pages/GrantProgram/SingleProgram/LeaderBoardSection/Trophy.tsx @@ -1,10 +1,10 @@ import { useEffect, useRef, useState } from 'react' import { useMedia } from 'react-use' import { Box } from 'rebass' +import campaignApi from 'services/campaign' import styled from 'styled-components' import TrophyImage from 'assets/images/campaign_trophy.png' -import useGetLeaderboardGrantProgram from 'hooks/campaigns/useGetLeaderboardGrantProgram' import { MEDIA_WIDTHS } from 'theme' import { ProjectRanking } from 'types/grantProgram' @@ -151,9 +151,7 @@ type Props = { const EmptyRankings: ProjectRanking[] = [] const TrophyWrapper: React.FC = ({ programId, rankByConfig }) => { - const { - swrData: { data }, - } = useGetLeaderboardGrantProgram({ + const { data } = campaignApi.useGetGrantProgramLeaderBoardQuery({ id: programId, rankBy: rankByConfig.param, page: 1, diff --git a/src/pages/GrantProgram/SingleProgram/LeaderBoardSection/index.tsx b/src/pages/GrantProgram/SingleProgram/LeaderBoardSection/index.tsx index db3638c945..384ff8f4eb 100644 --- a/src/pages/GrantProgram/SingleProgram/LeaderBoardSection/index.tsx +++ b/src/pages/GrantProgram/SingleProgram/LeaderBoardSection/index.tsx @@ -2,9 +2,9 @@ import { Trans, t } from '@lingui/macro' import { useEffect, useState } from 'react' import { Info } from 'react-feather' import { Flex, Text } from 'rebass' +import campaignApi from 'services/campaign' import Loader from 'components/Loader' -import useGetLeaderboardGrantProgram, { RankByParam } from 'hooks/campaigns/useGetLeaderboardGrantProgram' import useTheme from 'hooks/useTheme' import { ProjectRanking } from 'types/grantProgram' import { formatDisplayNumber } from 'utils/numbers' @@ -28,7 +28,7 @@ export const ITEMS_PER_PAGE = 5 export type RankByConfig = { extracter: (p: ProjectRanking) => string // used to extract the value - param: RankByParam // used as param in GET request + param: 'total_participants' | 'total_trades' | 'total_volume' title: string } @@ -66,10 +66,8 @@ const LeaderBoardSection: React.FC = ({ programId, showRefreshTimer }) => const [page, setPage] = useState(1) const rankByConfigs = getRankByConfigs() const [rankByConfig, setRankByConfig] = useState(rankByConfigs[0]) - const { - swrData: { data, isValidating, error }, - refresh, - } = useGetLeaderboardGrantProgram({ + + const { data, isLoading, isError, refetch } = campaignApi.useGetGrantProgramLeaderBoardQuery({ id: programId, rankBy: rankByConfig.param, page, @@ -77,7 +75,7 @@ const LeaderBoardSection: React.FC = ({ programId, showRefreshTimer }) => }) const renderLoading = () => { - if (isValidating) { + if (isLoading) { return ( @@ -85,7 +83,7 @@ const LeaderBoardSection: React.FC = ({ programId, showRefreshTimer }) => ) } - if (error) { + if (isError) { return ( = ({ programId, showRefreshTimer }) => color: theme.text, }} > - {JSON.stringify(error) || Something went wrong} + Something went wrong ) @@ -177,7 +175,7 @@ const LeaderBoardSection: React.FC = ({ programId, showRefreshTimer }) => - + = ({ id }) => { const navigate = useNavigate() const theme = useTheme() - const { data, isValidating, error } = useGetGrantProgram(id) + const { data, isLoading, isError } = campaignApi.useGetGrantProgramQuery({ id }) - if (isValidating) { + if (isLoading) { return ( @@ -35,7 +35,7 @@ const SpecificProgram: React.FC = ({ id }) => { ) } - if (error) { + if (isError) { navigate(APP_PATHS.GRANT_PROGRAMS) return ( @@ -48,7 +48,7 @@ const SpecificProgram: React.FC = ({ id }) => { color: theme.text, }} > - {JSON.stringify(error) || Something went wrong} + Something went wrong ) diff --git a/src/pages/KyberDAO/Vote/ProposalItem/Participants.tsx b/src/pages/KyberDAO/Vote/ProposalItem/Participants.tsx index 624ed3c565..65520bb1ae 100644 --- a/src/pages/KyberDAO/Vote/ProposalItem/Participants.tsx +++ b/src/pages/KyberDAO/Vote/ProposalItem/Participants.tsx @@ -2,13 +2,13 @@ import { Trans } from '@lingui/macro' import { BigNumber } from 'ethers' import { useMemo, useState } from 'react' import { Text } from 'rebass' +import kyberDAOApi from 'services/kyberDAO' import styled, { css } from 'styled-components' import Gold from 'assets/svg/gold_icon.svg' import Divider from 'components/Divider' import Modal from 'components/Modal' import Row, { RowBetween, RowFit } from 'components/Row' -import { useProposalInfoById } from 'hooks/kyberdao' import { ProposalType, VoteDetail } from 'hooks/kyberdao/types' import useTheme from 'hooks/useTheme' import { HARDCODED_OPTION_TITLE } from 'pages/KyberDAO/constants' @@ -150,13 +150,15 @@ const VotersListModal = ({ } export default function Participants({ proposalId }: { proposalId?: number }) { - const { proposalInfo } = useProposalInfoById(proposalId) const theme = useTheme() const [modalIndex, setModalIndex] = useState(null) + const { data: proposalInfo } = kyberDAOApi.useGetProposalByIdQuery({ id: proposalId }, { skip: !proposalId }) + const participants = useMemo(() => { if (!proposalInfo?.vote_stats?.votes) return return proposalInfo.vote_stats.votes + .slice() .sort((a, b) => (BigNumber.from(a.power).sub(BigNumber.from(b.power)).gt(0) ? -1 : 1)) .map(v => { return { diff --git a/src/pages/KyberDAO/Vote/ProposalItem/VoteInformation.tsx b/src/pages/KyberDAO/Vote/ProposalItem/VoteInformation.tsx index 124bc409bf..110049717b 100644 --- a/src/pages/KyberDAO/Vote/ProposalItem/VoteInformation.tsx +++ b/src/pages/KyberDAO/Vote/ProposalItem/VoteInformation.tsx @@ -61,8 +61,8 @@ export default function VoteInformation({ proposal }: { proposal: ProposalDetail .multiply(proposal.executor_minimum_quorum) .divide(BIPS_BASE) const { epochNumber, epochStartTimestamp } = getEpochInformation( - daoInfo.epoch_period_in_seconds, - daoInfo.first_epoch_start_timestamp, + daoInfo?.epoch_period_in_seconds ?? 0, + daoInfo?.first_epoch_start_timestamp ?? 0, proposal.start_timestamp, ) diff --git a/src/pages/KyberDAO/Vote/index.tsx b/src/pages/KyberDAO/Vote/index.tsx index de39dfcfc3..8403ba1234 100644 --- a/src/pages/KyberDAO/Vote/index.tsx +++ b/src/pages/KyberDAO/Vote/index.tsx @@ -90,7 +90,7 @@ const TabReward = styled.span<{ active?: boolean }>` } ` -const formatVotingPower = (votingPowerNumber: number) => { +const formatVotingPower = (votingPowerNumber?: number) => { if (votingPowerNumber === undefined) return '--' if (votingPowerNumber === 0) return '0%' if (votingPowerNumber < 0.0001) { diff --git a/src/services/aggregatorStats.ts b/src/services/aggregatorStats.ts new file mode 100644 index 0000000000..1d5b5f3833 --- /dev/null +++ b/src/services/aggregatorStats.ts @@ -0,0 +1,41 @@ +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' + +import { AGGREGATOR_STATS_API } from 'constants/env' +import { VERSION } from 'constants/v2' + +type AggregatorAPR = { + max_apr: { + value: number + id: string + chain_id: number + is_farm: boolean + type?: VERSION.CLASSIC | VERSION.ELASTIC + } + total_earnings: number +} + +type AggregatorVolume = { + totalVolume: string + last24hVolume: string +} + +const aggregatorStatsApi = createApi({ + reducerPath: 'aggregatorStatsApi', + baseQuery: fetchBaseQuery({ + baseUrl: `${AGGREGATOR_STATS_API}`, + }), + endpoints: builder => ({ + getAggregatorAPR: builder.query({ + query: () => ({ + url: '/api/max-apr-and-total-earning', + }), + }), + getAggregatorVolume: builder.query({ + query: () => ({ + url: '/api/volume', + }), + }), + }), +}) + +export default aggregatorStatsApi diff --git a/src/services/campaign.ts b/src/services/campaign.ts index 5afc204014..541841f03f 100644 --- a/src/services/campaign.ts +++ b/src/services/campaign.ts @@ -17,6 +17,7 @@ import { RewardDistribution, } from 'state/campaigns/actions' import { SerializedToken } from 'state/user/actions' +import { GrantProgram, ProjectRanking } from 'types/grantProgram' const getCampaignStatus = ({ endTime, startTime }: CampaignData) => { const now = Date.now() @@ -202,18 +203,18 @@ const formatTxs = (data: any[]) => { const campaignApi = createApi({ reducerPath: 'campaignApi', - baseQuery: fetchBaseQuery({ baseUrl: `${CAMPAIGN_BASE_URL}/api/v1/campaigns` }), + baseQuery: fetchBaseQuery({ baseUrl: `${CAMPAIGN_BASE_URL}/api/v1` }), endpoints: builder => ({ getCampaigns: builder.query({ query: params => ({ params, - url: '', + url: '/campaigns', }), transformResponse: (data: any) => formatListCampaign(data?.data || []), }), getCampaignById: builder.query({ query: campaignId => ({ - url: `/${campaignId}`, + url: `/campaigns/${campaignId}`, }), transformResponse: (data: any) => formatListCampaign(data?.data)?.[0], }), @@ -223,7 +224,7 @@ const campaignApi = createApi({ >({ query: ({ campaignId, ...params }) => ({ params, - url: `/${campaignId}/leaderboard`, + url: `/campaigns/${campaignId}/leaderboard`, }), transformResponse: (data: any) => formatLeaderboardData(data?.data), }), @@ -233,7 +234,7 @@ const campaignApi = createApi({ >({ query: ({ campaignId, ...params }) => ({ params, - url: `/${campaignId}/lucky-winners`, + url: `/campaigns/${campaignId}/lucky-winners`, }), transformResponse: (data: any) => formatLuckyWinners(data?.data || []), }), @@ -243,7 +244,7 @@ const campaignApi = createApi({ >({ query: ({ campaignId, ...params }) => ({ params, - url: `/${campaignId}/proofs`, + url: `/campaigns/${campaignId}/proofs`, }), transformResponse: (data: any) => formatTxs(data?.data || []), }), @@ -251,9 +252,32 @@ const campaignApi = createApi({ query: ({ recaptchaId, ...body }) => ({ body, method: 'POST', - url: `/${recaptchaId}/participants`, + url: `/campaigns/${recaptchaId}/participants`, }), }), + getGrantProgram: builder.query({ + query: ({ id }) => ({ + url: `/competitions/${id}`, + }), + transformResponse: (res: CommonRes) => res.data, + }), + getListGrantPrograms: builder.query({ + query: () => ({ + url: '/competitions', + params: { page: 1, pageSize: 100 }, + }), + transformResponse: (res: CommonPagingRes<{ competitions: GrantProgram[] }>) => res.data.competitions, + }), + getGrantProgramLeaderBoard: builder.query< + CommonPagingData<{ rankings: ProjectRanking[] }>, + { id?: number; rankBy: string; page?: number; pageSize?: number } + >({ + query: ({ id, rankBy, page, pageSize }) => ({ + url: `/competitions/${id}/leaderboard`, + params: { rankBy, page, pageSize }, + }), + transformResponse: (res: CommonPagingRes<{ rankings: ProjectRanking[] }>) => res.data, + }), }), }) diff --git a/src/services/coingecko.ts b/src/services/coingecko.ts index 30ef41c7fe..6ed6458bb6 100644 --- a/src/services/coingecko.ts +++ b/src/services/coingecko.ts @@ -3,6 +3,7 @@ import { createApi } from '@reduxjs/toolkit/query/react' import { baseQueryOauthDynamic } from 'services/baseQueryOauth' import { NETWORKS_INFO } from 'constants/networks' +import { fetchCoingeckoData } from 'hooks/useBasicChartData' export type SecurityInfo = { is_open_source: string @@ -50,6 +51,15 @@ const coingeckoApi = createApi({ }), transformResponse: (data: any, _, arg) => data?.result?.[arg.address.toLowerCase()], }), + fetchCoingeckoData: builder.query< + any, + { tokenAddresses: string[]; chainIds: ChainId[]; timeFrame: any; coingeckoAPI: string } + >({ + queryFn: async ({ tokenAddresses, chainIds, timeFrame, coingeckoAPI }): Promise => { + const data = await fetchCoingeckoData([tokenAddresses, chainIds, timeFrame, coingeckoAPI]) + return { data } + }, + }), }), }) diff --git a/src/services/externalApi.ts b/src/services/externalApi.ts new file mode 100644 index 0000000000..f625e27099 --- /dev/null +++ b/src/services/externalApi.ts @@ -0,0 +1,24 @@ +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' + +import { IPhaseData } from 'hooks/useClaimReward' +import { GasPriceData } from 'hooks/useGasPriceFromDeBank' + +const externalApi = createApi({ + reducerPath: 'externalApi', + baseQuery: fetchBaseQuery({ baseUrl: '' }), + endpoints: builder => ({ + getClaimRewards: builder.query({ + query: ({ url }) => ({ + url, + }), + }), + getGasPrice: builder.query({ + query: ({ chainSlug }) => ({ + url: 'https://openapi.debank.com/v1/wallet/gas_market', + params: { chain_id: chainSlug }, + }), + }), + }), +}) + +export default externalApi diff --git a/src/services/knprotocol.ts b/src/services/knprotocol.ts index 6548037931..212242b842 100644 --- a/src/services/knprotocol.ts +++ b/src/services/knprotocol.ts @@ -5,6 +5,7 @@ import { POOL_FARM_BASE_URL } from 'constants/env' import { RTK_QUERY_TAGS } from 'constants/index' import { NETWORKS_INFO } from 'hooks/useChainsConfig' import { SubgraphFarmV2 } from 'state/farms/elasticv2/types' +import { ElasticPool, RawToken } from 'state/prommPools/useGetElasticPools/useGetElasticPoolsV2' type Token = { id: string @@ -14,6 +15,23 @@ type Token = { priceUSD: string } +export type FarmingPool = { + id: string + pid: string + startTime: string + endTime: string + feeTarget: string + farm: { + id: string // address of fair launch contract + } + rewardTokensIds: string[] + totalRewardAmounts: string[] + pool: ElasticPool + rewardTokens: RawToken[] + stakedTvl: string + apr: string +} + export type ClassicPoolKN = { id: string fee: string @@ -92,6 +110,20 @@ const knProtocolApi = createApi({ }), providesTags: [RTK_QUERY_TAGS.GET_FARM_V2], }), + getFarmPools: builder.query({ + query: (chainId: ChainId) => ({ + url: `/${NETWORKS_INFO[chainId].poolFarmRoute}/api/v1/elastic-new/farm-pools`, + params: { page: 1, perPage: 1000 }, + }), + transformResponse: (res: CommonPagingRes<{ farmPools: FarmingPool[] }>) => res.data.farmPools, + }), + getPoolElastic: builder.query({ + query: ({ chainId, thisParamToForceRefresh }) => ({ + url: `/${NETWORKS_INFO[chainId].poolFarmRoute}/api/v1/elastic-new/pools`, + params: { page: 1, perPage: 1000, includeLowTvl: true, thisParamToForceRefresh }, + }), + transformResponse: (res: CommonPagingRes<{ pools: ElasticPool[] }>) => res.data.pools, + }), getPoolClassic: builder.query<{ data: { pools: ClassicPoolKN[] } }, ChainId>({ query: (chainId: ChainId) => ({ url: `/${NETWORKS_INFO[chainId].poolFarmRoute}/api/v1/classic/pools?includeLowTvl=true&perPage=10000&page=1`, diff --git a/src/services/ksSetting.ts b/src/services/ksSetting.ts index b8e4b75169..5afac7fecf 100644 --- a/src/services/ksSetting.ts +++ b/src/services/ksSetting.ts @@ -1,13 +1,14 @@ import { ApolloClient, NormalizedCacheObject } from '@apollo/client' -import { ChainId } from '@kyberswap/ks-sdk-core' +import { ChainId, NativeCurrency } from '@kyberswap/ks-sdk-core' import { createApi } from '@reduxjs/toolkit/query/react' import baseQueryOauth from 'services/baseQueryOauth' import { KS_SETTING_API } from 'constants/env' import { AppJsonRpcProvider } from 'constants/providers' import { ChainStateMap } from 'hooks/useChainsConfig' -import { TokenInfo } from 'state/lists/wrappedTokenInfo' +import { TokenInfo, WrappedTokenInfo } from 'state/lists/wrappedTokenInfo' import { TopToken } from 'state/topTokens/type' +import { formatTokenInfo } from 'utils/tokenInfo' export type KyberSwapConfig = { rpc: string @@ -46,6 +47,13 @@ export type KyberswapGlobalConfigurationResponse = { } } +type Dex = { + id: number + dexId: string + name: string + logoURL: string +} + export interface TokenListResponse { data: { pagination?: { @@ -79,7 +87,6 @@ const ksSettingApi = createApi({ }, }), }), - getKyberswapGlobalConfiguration: builder.query({ query: () => ({ url: '/configurations/fetch', @@ -103,6 +110,13 @@ const ksSettingApi = createApi({ })), }), + getDexList: builder.query({ + query: ({ chainId }) => ({ + url: `/dexes`, + params: { chain: chainId, isEnabled: true, pageSize: 100 }, + }), + transformResponse: (res: CommonPagingRes<{ dexes: Dex[] }>) => res.data.dexes, + }), getTokenList: builder.query< TokenListResponse, { @@ -120,6 +134,25 @@ const ksSettingApi = createApi({ params: { ...params, chainIds: chainId }, }), }), + getTokenByAddress: builder.query({ + queryFn: async ({ address, chainId }, _api, _extra, fetchWithBQ): Promise => { + const tokenListRes = await fetchWithBQ({ + url: '/tokens', + params: { chainIds: chainId, addresses: address }, + }) + let token = (tokenListRes.data as TokenListResponse)?.data.tokens[0] + if (!token) { + const importTokenRes = await fetchWithBQ({ + url: '/tokens/import', + method: 'POST', + body: { tokens: [{ chainId: chainId.toString(), address }] }, + }) + token = (importTokenRes.data as TokenImportResponse)?.data.tokens[0]?.data + } + const data = token ? formatTokenInfo(token) : undefined + return { data } + }, + }), importToken: builder.mutation>({ query: tokens => ({ url: `/tokens/import`, diff --git a/src/services/kyberDAO.ts b/src/services/kyberDAO.ts index dcd828af16..04e8848358 100644 --- a/src/services/kyberDAO.ts +++ b/src/services/kyberDAO.ts @@ -1,6 +1,15 @@ import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' import { KYBER_DAO_STATS_API } from 'constants/env' +import { + DaoInfo, + ProposalDetail, + ProposalStatus, + RewardStats, + StakerAction, + StakerInfo, + VoteInfo, +} from 'hooks/kyberdao/types' type GasRefundTierInfoResponse = { data: { @@ -79,12 +88,12 @@ export type GasRefundRewardParams = { const kyberDAOApi = createApi({ reducerPath: 'kyberDAO', baseQuery: fetchBaseQuery({ - baseUrl: `${KYBER_DAO_STATS_API}/api/v1`, + baseUrl: `${KYBER_DAO_STATS_API}`, }), endpoints: builder => ({ getGasRefundRewardInfo: builder.query({ query: ({ account, rewardStatus }) => ({ - url: `/stakers/${account}/refunds/total`, + url: `/api/v1/stakers/${account}/refunds/total`, params: { rewardStatus }, }), transformResponse: (response: GasRefundRewardResponse) => ({ @@ -94,13 +103,13 @@ const kyberDAOApi = createApi({ }), getGasRefundEligibleTxsInfo: builder.query({ query: ({ account, pageSize, page }) => ({ - url: `/stakers/${account}/refunds/eligible-transactions`, + url: `/api/v1/stakers/${account}/refunds/eligible-transactions`, params: { pageSize, page }, }), }), getGasRefundTierInfo: builder.query({ query: (account: string) => ({ - url: `/stakers/${account}/refund-info`, + url: `/api/v1/stakers/${account}/refund-info`, }), transformResponse: (response: GasRefundTierInfoResponse): GasRefundTierInfo => ({ userTier: response?.data?.refundInfo?.userTier || 0, @@ -109,14 +118,75 @@ const kyberDAOApi = createApi({ }), getGasRefundNextCycleInfo: builder.query({ query: () => ({ - url: '/gas-refund/program/next-cycle-info', + url: '/api/v1/gas-refund/program/next-cycle-info', }), }), getGasRefundProgramInfo: builder.query({ query: () => ({ - url: '/gas-refund/program/info', + url: '/api/v1/gas-refund/program/info', }), }), + getRewardStats: builder.query({ + query: () => ({ + url: `/api/v1/reward-stats`, + }), + transformResponse: (res: CommonRes<{ rewardStats: RewardStats }>) => res.data.rewardStats, + }), + getUserRewards: builder.query({ + query: ({ url }) => ({ + url, + }), + transformResponse: (res: any, _meta, { account }) => { + if (account) { + res.userReward = res.userRewards[account] + } + delete res.userRewards + return res + }, + }), + getDaoInfo: builder.query({ + query: () => ({ + url: '/dao-info', + }), + transformResponse: (res: CommonRes) => res.data, + }), + getProposals: builder.query({ + query: () => ({ + url: '/proposals', + }), + transformResponse: (res: CommonRes) => + res.data.map(proposal => { + let status = proposal.status + if (['Succeeded', 'Queued', 'Finalized'].includes(proposal.status)) status = ProposalStatus.Approved + if (['Expired'].includes(proposal.status)) status = ProposalStatus.Failed + return { ...proposal, status } + }), + }), + getProposalById: builder.query({ + query: ({ id }) => ({ + url: `/proposals/${id}`, + }), + transformResponse: (res: CommonRes) => res.data, + }), + getStakerInfo: builder.query({ + query: ({ account, epoch }) => ({ + url: `/stakers/${account}`, + params: { epoch }, + }), + transformResponse: (res: CommonRes) => res.data, + }), + getStakerVotes: builder.query({ + query: ({ account }) => ({ + url: `/stakers/${account}/votes`, + }), + transformResponse: (res: CommonRes) => res.data, + }), + getStakerActions: builder.query({ + query: ({ account }) => ({ + url: `/stakers/${account}/actions`, + }), + transformResponse: (res: CommonRes) => res.data, + }), }), }) diff --git a/src/state/customizeDexes/updater.tsx b/src/state/customizeDexes/updater.tsx index ed7bd38e61..174f17ab7f 100644 --- a/src/state/customizeDexes/updater.tsx +++ b/src/state/customizeDexes/updater.tsx @@ -1,11 +1,12 @@ import { ChainId } from '@kyberswap/ks-sdk-core' import { useEffect, useMemo } from 'react' import { useDispatch } from 'react-redux' +import ksSettingApi from 'services/ksSetting' import { isKyberSwapDex } from 'components/swapv2/LiquiditySourcesPanel' import { KYBERSWAP_KS_DEXES_TO_UI_DEXES, KYBERSWAP_UI_DEXES_CUSTOM } from 'constants/dexes' +import { NETWORKS_INFO } from 'constants/networks' import { useActiveWeb3React } from 'hooks' -import useLiquiditySources from 'hooks/useAggregatorStats' import { AppDispatch } from 'state/index' import { uniqueArray } from 'utils/array' @@ -16,7 +17,7 @@ export default function Updater({ customChainId }: { customChainId?: ChainId }): const { chainId: walletChainId } = useActiveWeb3React() const chainId = customChainId || walletChainId - const { data: dexes } = useLiquiditySources(chainId) + const { data: dexes } = ksSettingApi.useGetDexListQuery({ chainId: NETWORKS_INFO[chainId].ksSettingRoute }) // filterout kyberswap dexes, will hardcode const normalizeDexes = useMemo(() => { diff --git a/src/state/farms/elastic/updaters/v2.tsx b/src/state/farms/elastic/updaters/v2.tsx index df8aaf0b40..466434ecc0 100644 --- a/src/state/farms/elastic/updaters/v2.tsx +++ b/src/state/farms/elastic/updaters/v2.tsx @@ -1,77 +1,37 @@ import { CurrencyAmount, Token, TokenAmount, WETH } from '@kyberswap/ks-sdk-core' import { FeeAmount, Pool } from '@kyberswap/ks-sdk-elastic' import { useEffect } from 'react' -import useSWR from 'swr' +import knProtocolApi, { FarmingPool } from 'services/knprotocol' -import { POOL_FARM_BASE_URL } from 'constants/env' import { ZERO_ADDRESS } from 'constants/index' -import { NETWORKS_INFO } from 'constants/networks' import { NativeCurrencies } from 'constants/tokens' import { useActiveWeb3React } from 'hooks' import { useAppDispatch } from 'state/hooks' -import { ElasticPool, RawToken } from 'state/prommPools/useGetElasticPools/useGetElasticPoolsV2' import { isAddressString } from 'utils' import { CommonProps } from '.' import { setFarms, setLoading } from '..' import { ElasticFarm } from '../types' -interface FarmingPool { - id: string - pid: string - startTime: string - endTime: string - feeTarget: string - farm: { - id: string // address of fair launch contract - } - rewardTokensIds: string[] - totalRewardAmounts: string[] - pool: ElasticPool - rewardTokens: RawToken[] - stakedTvl: string - apr: string -} - -interface Response { - code: number - message: string - data: { - farmPools: FarmingPool[] - } -} - -const useGetElasticFarms = () => { - const { chainId } = useActiveWeb3React() - const endpoint = `${POOL_FARM_BASE_URL}/${NETWORKS_INFO[chainId].poolFarmRoute}/api/v1/elastic-new/farm-pools?page=1&perPage=10000` - - return useSWR(endpoint, (url: string) => fetch(url).then(resp => resp.json()), { - revalidateIfStale: false, - revalidateOnFocus: false, - revalidateOnReconnect: false, - }) -} - const FarmUpdaterV2: React.FC = ({}) => { const dispatch = useAppDispatch() const { chainId } = useActiveWeb3React() - const { data, error, isValidating } = useGetElasticFarms() - const farms = data?.data?.farmPools + const { data: farms, isError, isLoading } = knProtocolApi.useGetFarmPoolsQuery(chainId) useEffect(() => { - if (isValidating) { + if (isLoading) { dispatch(setLoading({ chainId, loading: true })) } else { dispatch(setLoading({ chainId, loading: false })) } - }, [chainId, dispatch, isValidating]) + }, [chainId, dispatch, isLoading]) useEffect(() => { - if (error && chainId) { + if (isError && chainId) { dispatch(setFarms({ chainId, farms: [] })) dispatch(setLoading({ chainId, loading: false })) } - }, [error, dispatch, chainId]) + }, [isError, dispatch, chainId]) useEffect(() => { if (farms && chainId) { diff --git a/src/state/index.ts b/src/state/index.ts index 4b605c7c8e..6132a831fc 100644 --- a/src/state/index.ts +++ b/src/state/index.ts @@ -1,5 +1,6 @@ import { configureStore } from '@reduxjs/toolkit' import { load, save } from 'redux-localstorage-simple' +import aggregatorStatsApi from 'services/aggregatorStats' import announcementApi, { publicAnnouncementApi } from 'services/announcement' import blackjackApi from 'services/blackjack' import blockServiceApi from 'services/blockService' @@ -8,6 +9,7 @@ import coingeckoApi from 'services/coingecko' import contractQuery from 'services/contractQuery' import crosschainApi from 'services/crossChain' import earningApi from 'services/earning' +import externalApi from 'services/externalApi' import geckoTerminalApi from 'services/geckoTermial' import identifyApi from 'services/identity' import knProtocolApi from 'services/knprotocol' @@ -95,12 +97,14 @@ const store = configureStore({ pools, farms, vesting, + [aggregatorStatsApi.reducerPath]: aggregatorStatsApi.reducer, [announcementApi.reducerPath]: announcementApi.reducer, [publicAnnouncementApi.reducerPath]: publicAnnouncementApi.reducer, [geckoTerminalApi.reducerPath]: geckoTerminalApi.reducer, [coingeckoApi.reducerPath]: coingeckoApi.reducer, [contractQuery.reducerPath]: contractQuery.reducer, [limitOrderApi.reducerPath]: limitOrderApi.reducer, + [externalApi.reducerPath]: externalApi.reducer, [campaignApi.reducerPath]: campaignApi.reducer, [kyberAIApi.reducerPath]: kyberAIApi.reducer, @@ -134,12 +138,14 @@ const store = configureStore({ .concat(save({ states: PERSISTED_KEYS, debounce: 100 })) .concat(geckoTerminalApi.middleware) .concat(coingeckoApi.middleware) + .concat(externalApi.middleware) .concat(contractQuery.middleware) .concat(limitOrderApi.middleware) .concat(kyberAIApi.middleware) .concat(coinmarketcapApi.middleware) .concat(campaignApi.middleware) .concat(kyberAISubscriptionApi.middleware) + .concat(aggregatorStatsApi.middleware) .concat(announcementApi.middleware) .concat(publicAnnouncementApi.middleware) .concat(kyberDAO.middleware) diff --git a/src/state/lists/wrappedTokenInfo.ts b/src/state/lists/wrappedTokenInfo.ts index 02c703ff6a..1f18208661 100644 --- a/src/state/lists/wrappedTokenInfo.ts +++ b/src/state/lists/wrappedTokenInfo.ts @@ -1,7 +1,7 @@ import { Token } from '@kyberswap/ks-sdk-core' import { MultiChainTokenInfo } from 'pages/Bridge/type' -import { isAddress } from 'utils' +import { isAddress } from 'utils/address' export interface TokenInfo { readonly chainId: number diff --git a/src/state/prommPools/useGetElasticPools/useGetElasticPoolsV2.ts b/src/state/prommPools/useGetElasticPools/useGetElasticPoolsV2.ts index 2a98a31f7a..e0650baef8 100644 --- a/src/state/prommPools/useGetElasticPools/useGetElasticPoolsV2.ts +++ b/src/state/prommPools/useGetElasticPools/useGetElasticPoolsV2.ts @@ -1,7 +1,5 @@ -import useSWRImmutable from 'swr/immutable' +import knProtocolApi from 'services/knprotocol' -import { POOL_FARM_BASE_URL } from 'constants/env' -import { NETWORKS_INFO } from 'constants/networks' import { useActiveWeb3React } from 'hooks' import { useKyberSwapConfig } from 'state/application/hooks' import { ElasticPoolDetail } from 'types/pool' @@ -39,37 +37,23 @@ export type ElasticPool = { farmApr: string } -type Response = { - code: number - message: string - data?: { - pools: Array - } -} - type PoolAccumulator = { [address: string]: ElasticPoolDetail } const useGetElasticPoolsV2 = (): CommonReturn => { const { chainId } = useActiveWeb3React() const { isEnableKNProtocol } = useKyberSwapConfig() - const chainRoute = NETWORKS_INFO[chainId].poolFarmRoute - - const { isValidating, error, data } = useSWRImmutable( - `${POOL_FARM_BASE_URL}/${chainRoute}/api/v1/elastic-new/pools?includeLowTvl=true&page=1&perPage=10000&thisParamToForceRefresh=${isEnableKNProtocol}`, - async (url: string) => { - if (!isEnableKNProtocol) { - return Promise.resolve({}) - } - return fetch(url).then(resp => resp.json()) - }, - { - refreshInterval: isEnableKNProtocol ? 0 : 60_000, - }, + const { + data: pools, + isLoading, + isError, + } = knProtocolApi.useGetPoolElasticQuery( + { chainId, thisParamToForceRefresh: isEnableKNProtocol }, + { skip: !isEnableKNProtocol, pollingInterval: 60_000 }, ) const poolData: PoolAccumulator = - data?.data?.pools.reduce((acc, pool) => { + pools?.reduce((acc, pool) => { acc[pool.id] = { address: pool.id, @@ -111,9 +95,9 @@ const useGetElasticPoolsV2 = (): CommonReturn => { }, {} as PoolAccumulator) || {} return { - isLoading: isValidating, - isError: !!error, data: poolData, + isLoading, + isError, } } diff --git a/src/types/common.d.ts b/src/types/common.d.ts new file mode 100644 index 0000000000..b4ba9b700f --- /dev/null +++ b/src/types/common.d.ts @@ -0,0 +1,16 @@ +type CommonRes = { + code: number + message: string + data: T +} + +type CommonPagingData = T & { + totalItems?: number + pagination?: { totalItems: number } +} + +type CommonPagingRes = { + code: number + message: string + data: CommonPagingData +} diff --git a/src/utils/tokenInfo.ts b/src/utils/tokenInfo.ts index 61b3319df9..560d20acdf 100644 --- a/src/utils/tokenInfo.ts +++ b/src/utils/tokenInfo.ts @@ -2,7 +2,7 @@ import { ChainId, Currency, NativeCurrency, Token, WETH } from '@kyberswap/ks-sd import { ETHER_ADDRESS } from 'constants/index' import { MAP_TOKEN_HAS_MULTI_BY_NETWORK } from 'constants/tokenLists/token-info' -import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo' +import { TokenInfo, WrappedTokenInfo } from 'state/lists/wrappedTokenInfo' /** * hard code: ex: usdt => usdt_e, ... if network has multi symbol same name base on network @@ -65,3 +65,23 @@ export const getTokenSymbolWithHardcode = ( export const getProxyTokenLogo = (logoUrl: string | undefined) => logoUrl ? `https://proxy.kyberswap.com/token-logo?url=${logoUrl}` : '' + +// ex: `"BTT_b"` => BTT_b +export const escapeQuoteString = (str: string) => + str?.startsWith('"') && str?.endsWith('"') ? str.substring(1, str.length - 1) : str + +export const formatTokenInfo = (rawTokenResponse: TokenInfo) => { + try { + const tokenResponse = { ...rawTokenResponse } + tokenResponse.symbol = escapeQuoteString(tokenResponse.symbol) + tokenResponse.name = escapeQuoteString(tokenResponse.name) + + const tokenInfo = new WrappedTokenInfo(tokenResponse) + if (!tokenInfo.decimals && !tokenInfo.symbol && !tokenInfo.name) { + return + } + return tokenInfo + } catch (e) { + return + } +} diff --git a/yarn.lock b/yarn.lock index 3c834aa16c..e227412a3c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17508,13 +17508,6 @@ swiper@^8.4.4: dom7 "^4.0.4" ssr-window "^4.0.2" -swr@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/swr/-/swr-2.0.0.tgz#91d999359e2be92de1a41f6b6711d72be20ffdbd" - integrity sha512-IhUx5yPkX+Fut3h0SqZycnaNLXLXsb2ECFq0Y29cxnK7d8r7auY2JWNbCW3IX+EqXUg3rwNJFlhrw5Ye/b6k7w== - dependencies: - use-sync-external-store "^1.2.0" - symbol-observable@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-2.0.3.tgz#5b521d3d07a43c351055fa43b8355b62d33fd16a" @@ -18306,7 +18299,7 @@ use-sidecar@^1.1.2: detect-node-es "^1.1.0" tslib "^2.0.0" -use-sync-external-store@1.2.0, use-sync-external-store@^1.2.0: +use-sync-external-store@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==