Skip to content

Commit

Permalink
Refactor token verification logic and enhance pool liquidity handling (
Browse files Browse the repository at this point in the history
…#1126)

* Refactor token verification logic and enhance pool liquidity handling

* Remove unused ReactQueryDevtools import from App component
  • Loading branch information
trungbach authored Jan 13, 2025
1 parent ac79af2 commit 2ffedda
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 60 deletions.
20 changes: 19 additions & 1 deletion src/helper/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { fromBech32, toBech32 } from '@cosmjs/encoding';
import { Bech32Config } from '@keplr-wallet/types';
import { getSnap } from '@leapwallet/cosmos-snap-provider';
import { CosmosChainId, CustomChainInfo, NetworkChainId } from '@oraichain/common';
import { CosmosChainId, CustomChainInfo, fetchRetry, NetworkChainId } from '@oraichain/common';
import {
BigDecimal,
BSC_SCAN,
Expand Down Expand Up @@ -35,6 +35,7 @@ import { serializeError } from 'serialize-error';
import { store } from 'store/configure';
import { bitcoinChainId, leapSnapId } from './constants';
import { numberWithCommas } from './format';
import { onChainTokenToTokenItem } from 'reducer/onchainTokens';

export interface Tokens {
denom?: string;
Expand Down Expand Up @@ -748,3 +749,20 @@ export const retry = async (fn, retries = 3, delay = 1000) => {
return retry(fn, retries - 1, delay);
}
};

export const inspectTokenFromOraiCommonApi = async (denoms: string[]): Promise<TokenItemType[]> => {
try {
const BASE_URL = 'https://oraicommon.oraidex.io/api/v1/tokens/list';
const TOKEN_LIST = denoms.map(encodeURIComponent).join(',');
const URL = `${BASE_URL}/${TOKEN_LIST}`;
const response = await fetchRetry(URL);
if (response.status !== 200) throw new Error('Failed to fetch token list: ' + response.statusText);

const inspectedTokens = await response.json();
const tokenItemTypes = inspectedTokens.map((info) => onChainTokenToTokenItem(info));
return tokenItemTypes;
} catch (error) {
console.log('Error inspectTokenFromOraiCommonApi: ', error);
return [];
}
};
4 changes: 1 addition & 3 deletions src/initCommon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ export const initializeOraidexCommon = async (
const oraichainTokens = oraidexCommonOg.oraichainTokens;
const otherChainTokens = oraidexCommonOg.otherChainTokens;

const allVerifiedOraichainTokens = allOraichainTokens.filter(
(token) => !addedTokens.find((addedToken) => addedToken.denom === token.denom)
);
const allVerifiedOraichainTokens = allOraichainTokens.filter((token) => token.isVerified);
if (arraysAreDifferent(oraichainTokens, allVerifiedOraichainTokens)) {
dispatch(updateAllOraichainTokens([...oraichainTokens, ...addedTokens]));
}
Expand Down
13 changes: 6 additions & 7 deletions src/layouts/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ import {
WEBSOCKET_RECONNECT_ATTEMPTS,
WEBSOCKET_RECONNECT_INTERVAL
} from '@oraichain/oraidex-common';
import { useWallet } from '@solana/wallet-adapter-react';
import { useTonConnectUI } from '@tonconnect/ui-react';
import { isMobile } from '@walletconnect/browser-utils';
import { TToastType, displayToast } from 'components/Toasts/Toast';
import { useWallet } from '@solana/wallet-adapter-react';
import { displayToast, TToastType } from 'components/Toasts/Toast';
import { ThemeProvider } from 'context/theme-context';
import { TonNetwork } from 'context/ton-provider';
import { getListAddressCosmos, getWalletByNetworkFromStorage, interfaceRequestTron, retry } from 'helper';
import useConfigReducer from 'hooks/useConfigReducer';
import useLoadTokens from 'hooks/useLoadTokens';
import { useTronEventListener } from 'hooks/useTronLink';
import useWalletReducer from 'hooks/useWalletReducer';
import { initializeOraidexCommon, network } from 'initCommon';
import SingletonOraiswapV3 from 'libs/contractSingleton';
import { getCosmWasmClient } from 'libs/cosmjs';
import Keplr from 'libs/keplr';
Expand All @@ -22,15 +25,11 @@ import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import useWebSocket from 'react-use-websocket';
import { setAddressBookList } from 'reducer/addressBook';
import routes from 'routes';
import { persistor, RootState } from 'store/configure';
import { ADDRESS_BOOK_KEY_BACKUP, PERSIST_VER } from 'store/constants';
import './index.scss';
import { initializeOraidexCommon, network } from 'initCommon';
import useLoadTokens from 'hooks/useLoadTokens';
import { useTronEventListener } from 'hooks/useTronLink';
import Menu from './Menu';

import routes from 'routes';
import { NoticeBanner } from './NoticeBanner';
import Sidebar from './Sidebar';

Expand Down
1 change: 1 addition & 0 deletions src/libs/contractSingleton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,7 @@ export function simulateIncentiveAprPosition(
const allOraichainTokens = storage.token.allOraichainTokens || [];
const tokenX = allOraichainTokens.find((token) => extractAddress(token) === poolKey.token_x);
const tokenY = allOraichainTokens.find((token) => extractAddress(token) === poolKey.token_y);
if (!tokenX || !tokenY) return { min: 0, max: 0 };

const positionLiquidityUsdX = ((prices[tokenX?.coinGeckoId] ?? 0) * Number(res.x)) / 10 ** tokenX.decimals;
const positionLiquidityUsdY = ((prices[tokenY?.coinGeckoId] ?? 0) * Number(res.y)) / 10 ** tokenY.decimals;
Expand Down
18 changes: 5 additions & 13 deletions src/pages/Pool-V3/components/PoolList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export enum PoolColumnHeader {
const PoolList = ({ search, filterType }: { search: string; filterType: POOL_TYPE }) => {
const theme = useTheme();
const { data: price } = useCoinGeckoPrices();
const [, setLiquidityPools] = useConfigReducer('liquidityPools');
const [poolLiquiditiesFromCache, setLiquidityPools] = useConfigReducer('liquidityPools');
const [volumnePools, setVolumnePools] = useConfigReducer('volumnePools');
const [aprInfo, setAprInfo] = useConfigReducer('aprPools');
const [openTooltip, setOpenTooltip] = useState(false);
Expand Down Expand Up @@ -137,7 +137,6 @@ const PoolList = ({ search, filterType }: { search: string; filterType: POOL_TYP

useEffect(() => {
if (Object.values(poolLiquidities).length > 0) {
const totalLiqudity = Object.values(poolLiquidities).reduce((acc, cur) => acc + cur, 0);
setLiquidityPools(poolLiquidities);
}
}, [poolLiquidities, dataPool]);
Expand All @@ -153,7 +152,9 @@ const PoolList = ({ search, filterType }: { search: string; filterType: POOL_TYP
const { type, totalLiquidity: liquidityV2, volume24Hour: volumeV2, liquidityAddr, poolKey } = item || {};

const showLiquidity =
type === POOL_TYPE.V3 ? poolLiquidities?.[item?.poolKey] : toDisplay(Math.trunc(liquidityV2 || 0).toString());
type === POOL_TYPE.V3
? poolLiquiditiesFromCache?.[item?.poolKey]
: toDisplay(Math.trunc(liquidityV2 || 0).toString());
const showVolume = type === POOL_TYPE.V3 ? volumeV3 : toDisplay(volumeV2 || 0);

let showApr = aprInfo?.[poolKey] || aprInfo?.[liquidityAddr] || {};
Expand Down Expand Up @@ -412,16 +413,7 @@ const PoolList = ({ search, filterType }: { search: string; filterType: POOL_TYP
</div>
</div>
<LoadingBox loading={loading} styles={{ minHeight: '60vh', height: 'fit-content' }}>
<div className={styles.list}>
{filteredPool?.length > 0
? renderList()
: !loading && (
<div className={styles.nodata}>
{theme === 'light' ? <NoData /> : <NoDataDark />}
<span>{!dataPool.length ? 'No Pools!' : !filteredPool.length && 'No Matched Pools!'}</span>
</div>
)}
</div>
<div className={styles.list}>{filteredPool?.length > 0 && renderList()}</div>

<div className={styles.paginate}>
{totalPages > 1 && (
Expand Down
25 changes: 13 additions & 12 deletions src/pages/Pool-V3/hooks/useGetPoolLiquidityVolume.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,30 @@ export const useGetPoolLiquidityVolume = (prices: CoinGeckoPrices<string>) => {
() => getPoolsLiqudityAndVolumeAmount(),
{
refetchOnWindowFocus: false,
placeholderData: []
placeholderData: [],
cacheTime: 5 * 60 * 1000
}
);

const { data: dataHours } = useQuery<any[]>(
['pool-v3-liquidty-volume-hourly', prices],
getPoolsVolumeByTokenLatest24h,
{
refetchOnWindowFocus: true,
refetchOnWindowFocus: false,
placeholderData: [],
cacheTime: 5 * 60 * 1000
}
);

useEffect(() => {
if (data.length === 0 || Object.keys(prices).length === 0 || dataHours.length === 0) return;
const newPoolLiquidities: Record<string, number> = {};
const newPoolVolumes: Record<string, number> = {};

data.forEach((item) => {
setPoolLiquidities((prevState) => ({
...prevState,
[item.id]:
(item.totalValueLockedTokenX / 10 ** item.tokenX.decimals) * (prices[item.tokenX.coingeckoId] ?? 0) +
(item.totalValueLockedTokenY / 10 ** item.tokenY.decimals) * (prices[item.tokenY.coingeckoId] ?? 0)
}));
newPoolLiquidities[item.id] =
(item.totalValueLockedTokenX / 10 ** item.tokenX.decimals) * (prices[item.tokenX.coingeckoId] ?? 0) +
(item.totalValueLockedTokenY / 10 ** item.tokenY.decimals) * (prices[item.tokenY.coingeckoId] ?? 0);

let poolVolumeInUsd = 0;
const poolHourData = dataHours.find((dataHour) => dataHour.id === item.id);
Expand All @@ -54,11 +55,11 @@ export const useGetPoolLiquidityVolume = (prices: CoinGeckoPrices<string>) => {
(Number(volume24hByToken.volumeTokenY) / 10 ** item.tokenY.decimals) * (prices[item.tokenY.coingeckoId] ?? 0);
}

setPoolVolume((prevState) => ({
...prevState,
[item.id]: poolVolumeInUsd
}));
newPoolVolumes[item.id] = poolVolumeInUsd;
});

setPoolLiquidities(newPoolLiquidities);
setPoolVolume(newPoolVolumes);
}, [data, prices, dataHours]);

return {
Expand Down
48 changes: 24 additions & 24 deletions src/pages/Pool-V3/hooks/useGetPoolList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,35 @@ import { parseAssetInfo } from '@oraichain/oraidex-common';
import { PoolWithPoolKey } from '@oraichain/oraidex-contracts-sdk/build/OraiswapV3.types';
import { useQuery } from '@tanstack/react-query';
import { CoinGeckoPrices } from 'hooks/useCoingecko';
import { getTokenInspectorInstance } from 'initTokenInspector';
import SingletonOraiswapV3 from 'libs/contractSingleton';
import { getPools } from 'pages/Pools/hooks';
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { onChainTokenToTokenItem } from 'reducer/onchainTokens';
import { addToOraichainTokens } from 'reducer/token';
import { RootState } from 'store/configure';
import { RootState, store } from 'store/configure';
import { PoolInfoResponse } from 'types/pool';
import { calcPrice } from '../components/PriceRangePlot/utils';
import { extractAddress, formatPoolData } from '../helpers/format';
import { inspectTokenFromOraiCommonApi } from 'helper';

export const useGetPoolList = (coingeckoPrices: CoinGeckoPrices<string>) => {
const [prices, setPrices] = useState<CoinGeckoPrices<string>>(coingeckoPrices);
const [dataPool, setDataPool] = useState([...Array(0)]);
const dispatch = useDispatch();
const allOraichainTokens = useSelector((state: RootState) => state.token.allOraichainTokens || []);

const [prices, setPrices] = useState<CoinGeckoPrices<string>>(coingeckoPrices);
const [dataPool, setDataPool] = useState([...Array(0)]);
const {
data: poolList,
refetch: refetchPoolList,
isLoading: isLoadingGetPoolList
} = useQuery<(PoolWithPoolKey | PoolInfoResponse)[]>(['pool-v3-pools', coingeckoPrices], () => getPoolList(), {
refetchOnWindowFocus: false
// cacheTime: 5 * 60 * 1000,
});
} = useQuery<(PoolWithPoolKey | PoolInfoResponse)[]>(
['pool-v3-pools', Object.keys(coingeckoPrices).length],
getPoolList,
{
refetchOnWindowFocus: false,
cacheTime: 5 * 60 * 1000,
enabled: Object.keys(coingeckoPrices).length > 0
}
);

useEffect(() => {
if (!poolList || poolList?.length === 0 || Object.keys(coingeckoPrices).length === 0) return;
Expand All @@ -43,15 +46,15 @@ export const useGetPoolList = (coingeckoPrices: CoinGeckoPrices<string>) => {
const tokenY = allOraichainTokens.find((token) => extractAddress(token) === pool.pool_key.token_y);

if (!tokenX || !tokenY) continue;
if (tokenX && !prices[tokenX.coinGeckoId]) {
if (!prices[tokenX.coinGeckoId]) {
if (prices[tokenY.coinGeckoId]) {
// calculate price of X in Y from current sqrt price
const price = calcPrice(pool.pool.current_tick_index, true, tokenX.decimals, tokenY.decimals);
newPrice[tokenX.coinGeckoId || tokenX.denom] = price * prices[tokenY.coinGeckoId];
}
}

if (tokenY && !prices[tokenY.coinGeckoId]) {
if (!prices[tokenY.coinGeckoId]) {
if (prices[tokenX.coinGeckoId]) {
// calculate price of Y in X from current sqrt price
const price = calcPrice(pool.pool.current_tick_index, false, tokenX.decimals, tokenY.decimals);
Expand All @@ -64,7 +67,7 @@ export const useGetPoolList = (coingeckoPrices: CoinGeckoPrices<string>) => {
}, [poolList]);

useEffect(() => {
if (!poolList || poolList.length === 0 || Object.keys(coingeckoPrices).length === 0) return;
if (!poolList || poolList.length === 0) return;

(async function formatListPools() {
const tokenAddresses = new Set<string>();
Expand All @@ -85,19 +88,16 @@ export const useGetPoolList = (coingeckoPrices: CoinGeckoPrices<string>) => {
}
});

const HMSTR_DENOM = 'factory/orai17hyr3eg92fv34fdnkend48scu32hn26gqxw3hnwkfy904lk9r09qqzty42/HMSTR';
if (tokenAddresses.has(HMSTR_DENOM)) tokenAddresses.delete(HMSTR_DENOM);
if (tokenAddresses.size > 0) {
if (
!(
tokenAddresses.size === 1 &&
// deprecate HMSTR token
tokenAddresses.has('factory/orai17hyr3eg92fv34fdnkend48scu32hn26gqxw3hnwkfy904lk9r09qqzty42/HMSTR')
)
) {
const tokenInspector = await getTokenInspectorInstance();
const extendedInfos = await tokenInspector.inspectMultipleTokens([...tokenAddresses]);
const convertToTokensType = extendedInfos.map((info) => onChainTokenToTokenItem(info));
dispatch(addToOraichainTokens(convertToTokensType));
const tokenAddressesArray = [...tokenAddresses];
const tokenChunks = [];
for (let i = 0; i < tokenAddressesArray.length; i += 30) {
const chunk = tokenAddressesArray.slice(i, i + 30);
tokenChunks.push(inspectTokenFromOraiCommonApi(chunk));
}
dispatch(addToOraichainTokens(tokenChunks.flat()));
}

const listPools = (poolList || []).map((p) => formatPoolData(p));
Expand Down

0 comments on commit 2ffedda

Please sign in to comment.