UNPKG

sui-explorer-local

Version:
121 lines (100 loc) 3.36 kB
// Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 import { useSuiClient } from '@mysten/dapp-kit'; import { CoinMetadata } from '@mysten/sui.js/client'; import { SUI_TYPE_ARG } from '@mysten/sui.js/utils'; import { useQuery, type UseQueryResult } from '@tanstack/react-query'; import BigNumber from 'bignumber.js'; import { useMemo } from 'react'; import { formatAmount } from '../utils/formatAmount'; type FormattedCoin = [ formattedBalance: string, coinSymbol: string, queryResult: UseQueryResult<CoinMetadata | null>, ]; export enum CoinFormat { ROUNDED = 'ROUNDED', FULL = 'FULL', } /** * Formats a coin balance based on our standard coin display logic. * If the balance is less than 1, it will be displayed in its full decimal form. * For values greater than 1, it will be truncated to 3 decimal places. */ export function formatBalance( balance: bigint | number | string, decimals: number, format: CoinFormat = CoinFormat.ROUNDED, ) { const bn = new BigNumber(balance.toString()).shiftedBy(-1 * decimals); if (format === CoinFormat.FULL) { return bn.toFormat(); } return formatAmount(bn); } const ELLIPSIS = '\u{2026}'; const SYMBOL_TRUNCATE_LENGTH = 5; const NAME_TRUNCATE_LENGTH = 10; export function useCoinMetadata(coinType?: string | null) { const client = useSuiClient(); return useQuery({ queryKey: ['coin-metadata', coinType], queryFn: async () => { if (!coinType) { throw new Error('Fetching coin metadata should be disabled when coin type is disabled.'); } // Optimize the known case of SUI to avoid a network call: if (coinType === SUI_TYPE_ARG) { const metadata: CoinMetadata = { id: null, decimals: 9, description: '', iconUrl: null, name: 'Sui', symbol: 'SUI', }; return metadata; } return client.getCoinMetadata({ coinType }); }, select(data) { if (!data) return null; return { ...data, symbol: data.symbol.length > SYMBOL_TRUNCATE_LENGTH ? data.symbol.slice(0, SYMBOL_TRUNCATE_LENGTH) + ELLIPSIS : data.symbol, name: data.name.length > NAME_TRUNCATE_LENGTH ? data.name.slice(0, NAME_TRUNCATE_LENGTH) + ELLIPSIS : data.name, }; }, retry: false, enabled: !!coinType, staleTime: Infinity, gcTime: 24 * 60 * 60 * 1000, }); } // TODO #1: This handles undefined values to make it easier to integrate with // the reset of the app as it is today, but it really shouldn't in a perfect world. export function useFormatCoin( balance?: bigint | number | string | null, coinType?: string | null, format: CoinFormat = CoinFormat.ROUNDED, ): FormattedCoin { const fallbackSymbol = useMemo(() => (coinType ? getCoinSymbol(coinType) ?? '' : ''), [coinType]); const queryResult = useCoinMetadata(coinType); const { isFetched, data } = queryResult; const formatted = useMemo(() => { if (typeof balance === 'undefined' || balance === null) return ''; if (!isFetched) return '...'; return formatBalance(balance, data?.decimals ?? 0, format); }, [data?.decimals, isFetched, balance, format]); return [formatted, isFetched ? data?.symbol || fallbackSymbol : '', queryResult]; } /** @deprecated use coin metadata instead */ export function getCoinSymbol(coinTypeArg: string) { return coinTypeArg.substring(coinTypeArg.lastIndexOf(':') + 1); }