Skip to content

Commit

Permalink
refactor useAssets with chain-registry util functions
Browse files Browse the repository at this point in the history
  • Loading branch information
cmaaawscodepipelinedeploy committed Apr 17, 2024
1 parent 2b33881 commit b747303
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 103 deletions.
13 changes: 8 additions & 5 deletions examples/asset-list/components/asset-list/AssetListSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import React from 'react';
import { Text, Box } from '@interchain-ui/react';
import AssetsOverview from './AssetsOverview';
import { useChain } from '@cosmos-kit/react';
import { useAssets } from '../../hooks';
import { ChainName } from 'cosmos-kit';
import { useAssets, usePrices } from '@/hooks';


interface AssetListSectionProps {
chainName: ChainName;
Expand All @@ -12,7 +13,9 @@ interface AssetListSectionProps {

export const AssetListSection = ({ chainName }: AssetListSectionProps) => {
const { isWalletConnected } = useChain(chainName);
const { data, isLoading, refetch } = useAssets(chainName);
const { data: assets = [], isLoading, refetch } = useAssets(chainName);

const { data: prices = {} } = usePrices(chainName);

if (!isWalletConnected) {
return (
Expand Down Expand Up @@ -45,9 +48,9 @@ export const AssetListSection = ({ chainName }: AssetListSectionProps) => {
return (
<Box maxWidth="$containerMd" marginX="auto" marginBottom="$17">
<AssetsOverview
isLoading={isLoading || !data}
assets={data?.assets ?? []}
prices={data?.prices ?? {}}
isLoading={isLoading || !assets}
assets={assets}
prices={prices}
selectedChainName={chainName}
refetch={refetch}
/>
Expand Down
25 changes: 25 additions & 0 deletions examples/asset-list/hooks/newFunctionPlacedToChainRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { CoinGeckoUSD } from '@/utils/types';
import { AssetList } from '@chain-registry/types';
import { DenomPriceMap } from '@chain-registry/utils';

// put some may have issue function. will put to chain registry later, we will see.

// replace mapCoinGeckoPricesToDenoms in chain-registry, use gecko_id to map denom, will have duplicate asset , cause original mapCoinGeckoPricesToDenoms function can not use
export const mapCoinGeckoPricesToDenomsM = (assetLists: AssetList[], prices: Record<string, CoinGeckoUSD>): DenomPriceMap => {
const symbolCoinGeckoIdMap = assetLists.flatMap(a => a.assets).reduce((map: any, asset) => {
if (asset.coingecko_id) {
map[asset.symbol] = prices[asset.coingecko_id]
}
return map
}, {})

const final = assetLists.flatMap(a => a.assets).reduce((map: any, asset) => {
if (asset.symbol && symbolCoinGeckoIdMap[asset.symbol]) {
map[asset.base] = symbolCoinGeckoIdMap[asset.symbol].usd
}
return map
}, {})

return final
}

157 changes: 59 additions & 98 deletions examples/asset-list/hooks/queries/useAssets.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
import { PrettyAsset } from '@/components';
import { Coin } from '@cosmjs/stargate';
import { useChain } from '@cosmos-kit/react';
import { UseQueryResult } from '@tanstack/react-query';
import BigNumber from 'bignumber.js';
import { useEffect, useMemo } from 'react';
import { useChainUtils } from '../useChainUtils';
import { useQueryHooks } from './useQueryHooks';
import { usePrices } from './usePrices';
import { useTopTokens } from './useTopTokens';
import { getPagination } from './useTotalAssets';
import { useChain } from "@cosmos-kit/react"
import { useQueryHooks } from "./useQueryHooks";
import { UseQueryResult } from "@tanstack/react-query";
import { getPagination } from "./useTotalAssets";
import { useMemo } from "react";
import { useOsmosisToken } from "./useOsmosisToken";
import { Coin } from "osmo-query/dist/codegen/cosmos/base/v1beta1/coin";
import {
getAssetByDenom,
getChainNameByDenom,
convertBaseUnitToDollarValue,
getAssetBySymbol,
convertBaseUnitToDisplayUnit
} from '@chain-registry/utils'
import { useGeckoPrices } from './useGeckoPrices';
import { getAssetsByChainName } from '@/utils/local-chain-registry'
import BigNumber from "bignumber.js";

(BigInt.prototype as any).toJSON = function () {
return this.toString();
};

const MAX_TOKENS_TO_SHOW = 50;

export const useAssets = (chainName: string) => {
const { address } = useChain(chainName);

const { cosmosQuery, isReady, isFetching } = useQueryHooks(chainName);
// address for balance
const { address } = useChain(chainName)

const allBalancesQuery: UseQueryResult<Coin[]> =
// init cosmos query client for balance
const { cosmosQuery, isReady } = useQueryHooks(chainName);
const { data: allBalances = [], isLoading: isAllBalanceLoading, refetch: refetchAllBalances }: UseQueryResult<Coin[]> =
cosmosQuery.bank.v1beta1.useAllBalances({
request: {
address: address || '',
Expand All @@ -33,99 +39,54 @@ export const useAssets = (chainName: string) => {
},
});

const pricesQuery = usePrices(chainName);
const topTokensQuery = useTopTokens();
// get osmosis token
const { data: topTokens = [], isLoading: isTopTokensLoading } = useOsmosisToken();

const dataQueries = {
allBalances: allBalancesQuery,
topTokens: topTokensQuery,
prices: pricesQuery,
};
// get gecko prices, get price map
const { data: priceMap = null, isLoading: isGeckoPricesLading } = useGeckoPrices()

const queriesToReset = [dataQueries.allBalances];
const queriesToRefetch = [dataQueries.allBalances];
const data = useMemo(() => {

useEffect(() => {
queriesToReset.forEach((query) => query.remove());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [chainName]);
if (priceMap) {

const queries = Object.values(dataQueries);
const isInitialFetching = queries.some(({ isLoading }) => isLoading);
const isRefetching = queries.some(({ isRefetching }) => isRefetching);
const isLoading = isFetching || isInitialFetching || isRefetching;
const assets = getAssetsByChainName(chainName)

type AllQueries = typeof dataQueries;
return topTokens.map((token) => {

type QueriesData = {
[Key in keyof AllQueries]: NonNullable<AllQueries[Key]['data']>;
};
const { denom, symbol } = token

const {
ibcAssets,
getAssetByDenom,
convRawToDispAmount,
calcCoinDollarValue,
denomToSymbol,
getPrettyChainName,
} = useChainUtils(chainName);
const asset = getAssetByDenom(assets, token.denom, chainName)

const amount = allBalances.find((balance: Coin) => balance.denom === denom)?.amount || '0'

let dollarValue = '0'
const assetBySymbol = getAssetBySymbol(assets, symbol)
if (assetBySymbol) {
dollarValue = convertBaseUnitToDollarValue(assets, priceMap, symbol, amount)
}

let displayAmount = '0'
if (assetBySymbol) {
displayAmount = convertBaseUnitToDisplayUnit(assets, symbol, amount)
}

const prettyChainName = getChainNameByDenom(assets, denom) || ''

const data = useMemo(() => {
if (isLoading) return;

const queriesData = Object.fromEntries(
Object.entries(dataQueries).map(([key, query]) => [key, query.data])
) as QueriesData;

const { allBalances, prices, topTokens } = queriesData;

const nativeAndIbcBalances: Coin[] = allBalances.filter(
({ denom }) => !denom.startsWith('gamm') && prices[denom]
);

const emptyBalances: Coin[] = ibcAssets
.filter(({ base }) => {
const notInBalances = !nativeAndIbcBalances.find(
({ denom }) => denom === base
);
return notInBalances && prices[base];
})
.filter((asset) => {
const isWithinLimit = ibcAssets.length <= MAX_TOKENS_TO_SHOW;
return isWithinLimit || topTokens.includes(asset.symbol);
})
.map((asset) => ({ denom: asset.base, amount: '0' }));

const finalAssets = [...nativeAndIbcBalances, ...emptyBalances]
.map(({ amount, denom }) => {
const asset = getAssetByDenom(denom);
const symbol = denomToSymbol(denom);
const dollarValue = calcCoinDollarValue(prices, { amount, denom });
return {
symbol,
logoUrl: asset.logo_URIs?.png || asset.logo_URIs?.svg,
prettyChainName: getPrettyChainName(denom),
displayAmount: convRawToDispAmount(denom, amount),
logoUrl: asset?.logo_URIs?.png || asset?.logo_URIs?.svg,
prettyChainName,
displayAmount,
dollarValue,
amount,
denom,
};
})
.sort((a, b) =>
new BigNumber(a.dollarValue).lt(b.dollarValue) ? 1 : -1
);

return {
prices,
allBalances,
assets: finalAssets as PrettyAsset[],
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isLoading]);

const refetch = () => {
queriesToRefetch.forEach((query) => query.refetch());
};

return { data, isLoading, refetch };
};
}).sort((a, b) => BigNumber(a.displayAmount).lte(b.displayAmount) ? 1 : -1)
}
return []
}, [topTokens, chainName, allBalances, priceMap])

const isLoading = [isAllBalanceLoading, isTopTokensLoading, isGeckoPricesLading].some(isLoading => isLoading === true)

return { data, isLoading, refetch: refetchAllBalances }
}
41 changes: 41 additions & 0 deletions examples/asset-list/hooks/queries/useGeckoPrices.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { DenomPriceMap } from '@chain-registry/utils';
import { useQuery } from '@tanstack/react-query';
import { handleError } from './useTopTokens';
import { CoinGeckoUSD } from '@/utils/types';
import mainnetAssets from 'chain-registry/mainnet/assets'
import { mapCoinGeckoPricesToDenomsM } from '../newFunctionPlacedToChainRegistry';
import { assets } from '@/utils/local-chain-registry'

const GECKO_PRICE_QUERY_KEY = 'gecko_prices'

const getGeckoIds = () => {
const geckoIds: string[] = []
mainnetAssets.forEach(assetList => {
assetList.assets.forEach(asset => {
if (asset.coingecko_id) {
geckoIds.push(asset.coingecko_id)
}
})
})
return geckoIds
}

const fetchPrices = async (
geckoIds: string[]
): Promise<Record<string, CoinGeckoUSD>> => {
const url = `https://api.coingecko.com/api/v3/simple/price?ids=${geckoIds.join()}&vs_currencies=usd`;

return fetch(url)
.then(handleError)
.then((res) => res.json());
};

export const useGeckoPrices = () => {
const geckoIds = getGeckoIds()
return useQuery({
queryKey: [GECKO_PRICE_QUERY_KEY, geckoIds],
queryFn: () => fetchPrices(geckoIds),
select: (geckoPrices): DenomPriceMap => mapCoinGeckoPricesToDenomsM(assets, geckoPrices),
staleTime: Infinity,
});
};
44 changes: 44 additions & 0 deletions examples/asset-list/hooks/queries/useOsmosisToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useQuery } from '@tanstack/react-query';

type Token = {
price: number;
denom: string;
symbol: string;
liquidity: number;
volume_24h: number;
volume_24h_change: number;
name: string;
price_24h_change: number;
price_7d_change: number;
exponent: number;
display: string;
};

export const handleError = (resp: Response) => {
if (!resp.ok) throw Error(resp.statusText);
return resp;
};

const fetchTokens = async (): Promise<Token[]> => {
const url = 'https://api-osmosis.imperator.co/tokens/v2/all';
return fetch(url)
.then(handleError)
.then((res) => res.json());
};

const MAX_TOP_TOKENS = 60;

const filterTopTokens = (tokens: Token[]) => {
return tokens
.sort((a, b) => b.liquidity - a.liquidity)
.slice(0, MAX_TOP_TOKENS)
};

export const useOsmosisToken = () => {
return useQuery({
queryKey: ['osmosis_tokens'],
queryFn: fetchTokens,
select: filterTopTokens,
staleTime: Infinity,
});
};
38 changes: 38 additions & 0 deletions examples/asset-list/utils/local-chain-registry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { assets, chains, ibc } from 'chain-registry'
import { Asset } from '@chain-registry/types'
import { getChainNameByDenom } from '@chain-registry/utils'

export { assets, chains, ibc }

export const getAssetsByChainName = (chainName: string) => {
return assets.filter(a => a.chain_name === chainName)
}

export const isNativeAsset = (targetAsset: Asset, chainName: string) => {
const assetsByChain = assets.filter(a => a.chain_name === chainName)
const chain_name = getChainNameByDenom(assetsByChain, targetAsset.base)
switch (true) {
case targetAsset.base.startsWith('factory/'):
return false;

case targetAsset.base.startsWith('ft') && chain_name === 'bitsong':
return false;

case targetAsset.base.startsWith('erc20/'):
return true;

case targetAsset.base.startsWith('ibc/'):
return false;

case targetAsset.base.startsWith('cw20:'):
return true;

default:
if (!targetAsset.traces || !targetAsset.traces.length) {
// asset.type_asset = 'sdk.coin'
return true;
}
return false;
}
}

0 comments on commit b747303

Please sign in to comment.