From 2880d0a9e7a3d8455c28a517789fe69e286aa0d3 Mon Sep 17 00:00:00 2001 From: Fara Woolf Date: Tue, 26 Mar 2024 15:26:03 -0500 Subject: [PATCH] feat: stacks ft fiat values from alex-sdk --- .../hooks/use-alex-sdk.ts} | 8 ++-- ...tacks-fungible-token-asset-item.layout.tsx | 3 ++ .../components/stacks-balance-list-item.tsx | 4 +- src/app/pages/fund/components/fund.layout.tsx | 25 +++-------- src/app/pages/fund/fund.tsx | 45 +++++++------------ .../components/swap-asset-item.tsx | 5 +-- .../components/swap-selected-asset-from.tsx | 2 +- .../components/swap-selected-asset-to.tsx | 2 +- src/app/pages/swap/hooks/use-alex-swap.tsx | 8 ++-- .../query/common/alex-sdk/alex-sdk.hooks.ts | 36 +++++++++++++++ .../common/alex-sdk/latest-prices.query.ts | 12 +++++ .../alex-sdk/swappable-currency.query.ts | 12 +++++ .../alex-swaps/swappable-currency.query.ts | 16 ------- .../balance/stacks-ft-balances.hooks.ts | 7 ++- .../balance/stacks-ft-balances.utils.ts | 7 ++- src/shared/models/crypto-asset.model.ts | 3 ++ src/shared/models/currencies.model.ts | 7 +-- 17 files changed, 116 insertions(+), 86 deletions(-) rename src/app/{pages/swap/hooks/use-alex-sdk-fiat-price.tsx => common/hooks/use-alex-sdk.ts} (79%) create mode 100644 src/app/query/common/alex-sdk/alex-sdk.hooks.ts create mode 100644 src/app/query/common/alex-sdk/latest-prices.query.ts create mode 100644 src/app/query/common/alex-sdk/swappable-currency.query.ts delete mode 100644 src/app/query/common/alex-swaps/swappable-currency.query.ts diff --git a/src/app/pages/swap/hooks/use-alex-sdk-fiat-price.tsx b/src/app/common/hooks/use-alex-sdk.ts similarity index 79% rename from src/app/pages/swap/hooks/use-alex-sdk-fiat-price.tsx rename to src/app/common/hooks/use-alex-sdk.ts index 067538e9df7..f7ab94e4785 100644 --- a/src/app/pages/swap/hooks/use-alex-sdk-fiat-price.tsx +++ b/src/app/common/hooks/use-alex-sdk.ts @@ -1,4 +1,4 @@ -import { Money, createMoney } from '@shared/models/money.model'; +import { type Money, createMoney } from '@shared/models/money.model'; import { isUndefined } from '@shared/utils'; import { useConvertAlexSdkCurrencyToFiatAmount } from '@app/common/hooks/use-convert-to-fiat-amount'; @@ -7,7 +7,6 @@ import { unitToFractionalUnit } from '@app/common/money/unit-conversion'; export function useAlexSdkAmountAsFiat(balance?: Money, price?: Money, value?: string) { const convertAlexSdkCurrencyToUsd = useConvertAlexSdkCurrencyToFiatAmount( - // @ts-expect-error TODO: balance?.symbol should be of a Cryptocurrency type. balance?.symbol ?? '', price ?? createMoney(0, 'USD') ); @@ -23,7 +22,6 @@ export function useAlexSdkAmountAsFiat(balance?: Money, price?: Money, value?: s export function useAlexSdkBalanceAsFiat(balance?: Money, price?: Money) { const convertAlexSdkCurrencyToUsd = useConvertAlexSdkCurrencyToFiatAmount( - // @ts-expect-error TODO: balance?.symbol should be of a Cryptocurrency type. balance?.symbol ?? '', price ?? createMoney(0, 'USD') ); @@ -34,5 +32,7 @@ export function useAlexSdkBalanceAsFiat(balance?: Money, price?: Money) { createMoney(balance.amount, balance.symbol, balance.decimals) ); - return convertedBalanceAsMoney.amount.isNaN() ? '' : i18nFormatCurrency(convertedBalanceAsMoney); + return convertedBalanceAsMoney.amount.isNaN() || convertedBalanceAsMoney.amount.isEqualTo(0) + ? '' + : i18nFormatCurrency(convertedBalanceAsMoney); } diff --git a/src/app/components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item.layout.tsx b/src/app/components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item.layout.tsx index d3034ebd737..22da0ca62bd 100644 --- a/src/app/components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item.layout.tsx +++ b/src/app/components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item.layout.tsx @@ -2,6 +2,7 @@ import { styled } from 'leather-styles/jsx'; import { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asset-balance.model'; +import { useAlexSdkBalanceAsFiat } from '@app/common/hooks/use-alex-sdk'; import { StacksAssetAvatar } from '@app/components/crypto-assets/stacks/components/stacks-asset-avatar'; import { ItemLayout } from '@app/ui/components/item-layout/item-layout'; import { BasicTooltip } from '@app/ui/components/tooltip/basic-tooltip'; @@ -17,6 +18,7 @@ export function StacksFungibleTokenAssetItemLayout({ assetBalance, onClick, }: StacksFungibleTokenAssetItemLayoutProps) { + const balanceAsFiat = useAlexSdkBalanceAsFiat(assetBalance.balance, assetBalance.asset.price); const { amount, avatar, caption, dataTestId, formattedBalance, imageCanonicalUri, title } = parseStacksFungibleTokenAssetBalance(assetBalance); @@ -45,6 +47,7 @@ export function StacksFungibleTokenAssetItemLayout({ } + captionRight={balanceAsFiat} /> ); diff --git a/src/app/features/asset-list/components/stacks-balance-list-item.tsx b/src/app/features/asset-list/components/stacks-balance-list-item.tsx index a5f728565d6..3e804bf4e2a 100644 --- a/src/app/features/asset-list/components/stacks-balance-list-item.tsx +++ b/src/app/features/asset-list/components/stacks-balance-list-item.tsx @@ -6,6 +6,6 @@ interface StacksBalanceListItemProps { address: string; } export function StacksBalanceListItem({ address }: StacksBalanceListItemProps) { - const balaceDetails = useStxBalance(); - return ; + const balanceDetails = useStxBalance(); + return ; } diff --git a/src/app/pages/fund/components/fund.layout.tsx b/src/app/pages/fund/components/fund.layout.tsx index 8c5013f9b17..b6d20905ba6 100644 --- a/src/app/pages/fund/components/fund.layout.tsx +++ b/src/app/pages/fund/components/fund.layout.tsx @@ -1,27 +1,16 @@ import { Stack, styled } from 'leather-styles/jsx'; +import type { Blockchains } from '@shared/models/blockchain.model'; import { CryptoCurrencies } from '@shared/models/currencies.model'; import { HasChildren } from '@app/common/has-children'; - -const nameMap: Record = { - BTC: { - name: 'Bitcoin', - symbol: 'BTC', - }, - STX: { - name: 'Stacks', - symbol: 'STX', - }, -}; +import { capitalize } from '@app/common/utils'; interface FundLayoutProps extends HasChildren { + blockchain: Blockchains; symbol: CryptoCurrencies; } - -export function FundLayout({ symbol, children }: FundLayoutProps) { - const name = nameMap[symbol].name; - const nameAbbr = nameMap[symbol].symbol; +export function FundLayout({ blockchain, symbol, children }: FundLayoutProps) { return ( - Choose an exchange to fund your account with {name} ({nameAbbr}) or deposit from elsewhere. - Exchanges with “Fast checkout” make it easier to purchase {nameAbbr} for direct deposit into - your wallet with a credit card. + Choose an exchange to fund your account with {capitalize(blockchain)} ({symbol}) or deposit + from elsewhere. Exchanges with “Fast checkout” make it easier to purchase {symbol} for + direct deposit into your wallet with a credit card. {children} diff --git a/src/app/pages/fund/fund.tsx b/src/app/pages/fund/fund.tsx index 1dfdf95e892..a3dd5f32b78 100644 --- a/src/app/pages/fund/fund.tsx +++ b/src/app/pages/fund/fund.tsx @@ -1,7 +1,5 @@ import { Outlet, useParams } from 'react-router-dom'; -import { isCryptoCurrency } from '@shared/models/currencies.model'; - import { useBtcCryptoCurrencyAssetBalance } from '@app/common/hooks/balance/btc/use-btc-crypto-currency-asset-balance'; import { useStxCryptoCurrencyAssetBalance } from '@app/common/hooks/balance/stx/use-stx-crypto-currency-asset-balance'; import { FullPageLoadingSpinner } from '@app/components/loading-spinner'; @@ -18,37 +16,26 @@ export function FundPage() { const stxCryptoCurrencyAssetBalance = useStxCryptoCurrencyAssetBalance(); const { currency } = useParams(); - function getSymbol() { - if (isCryptoCurrency(currency)) { - return currency; - } - return 'STX'; - } - function getAddress() { - switch (symbol) { - case 'BTC': - return bitcoinSigner?.address; - case 'STX': - return currentStxAccount?.address; - } - } - function getBalance() { - switch (symbol) { - case 'BTC': - return btcCryptoCurrencyAssetBalance; - case 'STX': - return stxCryptoCurrencyAssetBalance; - } - } - - const symbol = getSymbol(); - const address = getAddress(); - const balance = getBalance(); + const { address, balance, blockchain, symbol } = + currency === 'BTC' + ? { + address: bitcoinSigner?.address, + balance: btcCryptoCurrencyAssetBalance, + blockchain: 'Bitcoin', + symbol: currency, + } + : { + address: currentStxAccount?.address, + balance: stxCryptoCurrencyAssetBalance, + blockchain: 'Stacks', + symbol: 'STX', + }; if (!address || !balance) return ; + return ( <> - + diff --git a/src/app/pages/swap/components/swap-choose-asset/components/swap-asset-item.tsx b/src/app/pages/swap/components/swap-choose-asset/components/swap-asset-item.tsx index 7fabdbcd131..8e56372da2b 100644 --- a/src/app/pages/swap/components/swap-choose-asset/components/swap-asset-item.tsx +++ b/src/app/pages/swap/components/swap-choose-asset/components/swap-asset-item.tsx @@ -1,15 +1,14 @@ import { SwapSelectors } from '@tests/selectors/swap.selectors'; +import { useAlexSdkBalanceAsFiat } from '@app/common/hooks/use-alex-sdk'; import { formatMoneyWithoutSymbol } from '@app/common/money/format-money'; +import type { SwapAsset } from '@app/pages/swap/hooks/use-swap-form'; import { useGetFungibleTokenMetadataQuery } from '@app/query/stacks/tokens/fungible-tokens/fungible-token-metadata.query'; import { isFtAsset } from '@app/query/stacks/tokens/token-metadata.utils'; import { Avatar, defaultFallbackDelay, getAvatarFallback } from '@app/ui/components/avatar/avatar'; import { ItemLayout } from '@app/ui/components/item-layout/item-layout'; import { Pressable } from '@app/ui/pressable/pressable'; -import { useAlexSdkBalanceAsFiat } from '../../../hooks/use-alex-sdk-fiat-price'; -import { SwapAsset } from '../../../hooks/use-swap-form'; - interface SwapAssetItemProps { asset: SwapAsset; onClick(): void; diff --git a/src/app/pages/swap/components/swap-selected-asset-from.tsx b/src/app/pages/swap/components/swap-selected-asset-from.tsx index 3a493dadef4..7dac171bcff 100644 --- a/src/app/pages/swap/components/swap-selected-asset-from.tsx +++ b/src/app/pages/swap/components/swap-selected-asset-from.tsx @@ -5,10 +5,10 @@ import { createMoney } from '@shared/models/money.model'; import { isUndefined } from '@shared/utils'; import { useShowFieldError } from '@app/common/form-utils'; +import { useAlexSdkAmountAsFiat } from '@app/common/hooks/use-alex-sdk'; import { convertAmountToFractionalUnit } from '@app/common/money/calculate-money'; import { formatMoneyWithoutSymbol } from '@app/common/money/format-money'; -import { useAlexSdkAmountAsFiat } from '../hooks/use-alex-sdk-fiat-price'; import { SwapFormValues } from '../hooks/use-swap-form'; import { useSwapContext } from '../swap.context'; import { SwapAmountField } from './swap-amount-field'; diff --git a/src/app/pages/swap/components/swap-selected-asset-to.tsx b/src/app/pages/swap/components/swap-selected-asset-to.tsx index 7e2c94f7049..284f9e0b823 100644 --- a/src/app/pages/swap/components/swap-selected-asset-to.tsx +++ b/src/app/pages/swap/components/swap-selected-asset-to.tsx @@ -1,9 +1,9 @@ import { useField } from 'formik'; +import { useAlexSdkAmountAsFiat } from '@app/common/hooks/use-alex-sdk'; import { formatMoneyWithoutSymbol } from '@app/common/money/format-money'; import { LoadingSpinner } from '@app/components/loading-spinner'; -import { useAlexSdkAmountAsFiat } from '../hooks/use-alex-sdk-fiat-price'; import { useSwapContext } from '../swap.context'; import { SwapAmountField } from './swap-amount-field'; import { SwapSelectedAssetLayout } from './swap-selected-asset.layout'; diff --git a/src/app/pages/swap/hooks/use-alex-swap.tsx b/src/app/pages/swap/hooks/use-alex-swap.tsx index 21f95a8bd08..e7aec796460 100644 --- a/src/app/pages/swap/hooks/use-alex-swap.tsx +++ b/src/app/pages/swap/hooks/use-alex-swap.tsx @@ -1,5 +1,4 @@ import { useCallback, useState } from 'react'; -import { useAsync } from 'react-async-hook'; import { AlexSDK, Currency, TokenInfo } from 'alex-sdk'; import BigNumber from 'bignumber.js'; @@ -10,7 +9,8 @@ import { createMoney } from '@shared/models/money.model'; import { useStxBalance } from '@app/common/hooks/balance/stx/use-stx-balance'; import { convertAmountToFractionalUnit } from '@app/common/money/calculate-money'; import { pullContractIdFromIdentity } from '@app/common/utils'; -import { useSwappableCurrencyQuery } from '@app/query/common/alex-swaps/swappable-currency.query'; +import { useAlexSdkLatestPricesQuery } from '@app/query/common/alex-sdk/latest-prices.query'; +import { useAlexSdkSwappableCurrencyQuery } from '@app/query/common/alex-sdk/swappable-currency.query'; import { useTransferableStacksFungibleTokenAssetBalances } from '@app/query/stacks/balance/stacks-ft-balances.hooks'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; @@ -24,8 +24,8 @@ export function useAlexSwap() { const [swapSubmissionData, setSwapSubmissionData] = useState(); const [slippage, _setSlippage] = useState(0.04); const [isFetchingExchangeRate, setIsFetchingExchangeRate] = useState(false); - const { data: supportedCurrencies = [] } = useSwappableCurrencyQuery(alexSDK); - const { result: prices } = useAsync(async () => await alexSDK.getLatestPrices(), [alexSDK]); + const { data: supportedCurrencies = [] } = useAlexSdkSwappableCurrencyQuery(alexSDK); + const { data: prices } = useAlexSdkLatestPricesQuery(alexSDK); const { availableBalance: availableStxBalance } = useStxBalance(); const account = useCurrentStacksAccount(); const stacksFtAssetBalances = useTransferableStacksFungibleTokenAssetBalances( diff --git a/src/app/query/common/alex-sdk/alex-sdk.hooks.ts b/src/app/query/common/alex-sdk/alex-sdk.hooks.ts new file mode 100644 index 00000000000..3e0fe7e4953 --- /dev/null +++ b/src/app/query/common/alex-sdk/alex-sdk.hooks.ts @@ -0,0 +1,36 @@ +import { useCallback, useState } from 'react'; + +import { AlexSDK, type Currency } from 'alex-sdk'; +import BigNumber from 'bignumber.js'; + +import { logger } from '@shared/logger'; +import { createMoney } from '@shared/models/money.model'; +import { isDefined } from '@shared/utils'; + +import { convertAmountToFractionalUnit } from '@app/common/money/calculate-money'; +import { pullContractIdFromIdentity } from '@app/common/utils'; + +import { useAlexSdkLatestPricesQuery } from './latest-prices.query'; +import { useAlexSdkSwappableCurrencyQuery } from './swappable-currency.query'; + +export function useAlexSdKCurrencyPriceAsMoney() { + const alexSDK = useState(() => new AlexSDK())[0]; + const { data: supportedCurrencies = [] } = useAlexSdkSwappableCurrencyQuery(alexSDK); + const { data: prices } = useAlexSdkLatestPricesQuery(alexSDK); + + return useCallback( + (principal: string) => { + if (!prices) { + logger.error('Latest prices could not be found'); + return createMoney(0, 'USD'); + } + const tokenInfo = supportedCurrencies + .filter(isDefined) + .find(token => pullContractIdFromIdentity(token.contractAddress) === principal); + const currency = tokenInfo?.id as Currency; + const price = convertAmountToFractionalUnit(new BigNumber(prices[currency] ?? 0), 2); + return createMoney(price, 'USD'); + }, + [prices, supportedCurrencies] + ); +} diff --git a/src/app/query/common/alex-sdk/latest-prices.query.ts b/src/app/query/common/alex-sdk/latest-prices.query.ts new file mode 100644 index 00000000000..992a3dbe624 --- /dev/null +++ b/src/app/query/common/alex-sdk/latest-prices.query.ts @@ -0,0 +1,12 @@ +import { useQuery } from '@tanstack/react-query'; +import { AlexSDK } from 'alex-sdk'; + +export function useAlexSdkLatestPricesQuery(alexSDK: AlexSDK) { + return useQuery(['alex-sdk-latest-prices'], async () => alexSDK.getLatestPrices(), { + refetchOnMount: false, + refetchOnReconnect: false, + refetchOnWindowFocus: false, + retryDelay: 1000 * 60, + staleTime: 1000 * 60 * 10, + }); +} diff --git a/src/app/query/common/alex-sdk/swappable-currency.query.ts b/src/app/query/common/alex-sdk/swappable-currency.query.ts new file mode 100644 index 00000000000..3b7f5149fdb --- /dev/null +++ b/src/app/query/common/alex-sdk/swappable-currency.query.ts @@ -0,0 +1,12 @@ +import { useQuery } from '@tanstack/react-query'; +import { AlexSDK } from 'alex-sdk'; + +export function useAlexSdkSwappableCurrencyQuery(alexSDK: AlexSDK) { + return useQuery(['alex-sdk-swappable-currencies'], async () => alexSDK.fetchSwappableCurrency(), { + refetchOnMount: false, + refetchOnReconnect: false, + refetchOnWindowFocus: false, + retryDelay: 1000 * 60, + staleTime: 1000 * 60 * 10, + }); +} diff --git a/src/app/query/common/alex-swaps/swappable-currency.query.ts b/src/app/query/common/alex-swaps/swappable-currency.query.ts deleted file mode 100644 index 415bbcc09df..00000000000 --- a/src/app/query/common/alex-swaps/swappable-currency.query.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { AlexSDK } from 'alex-sdk'; - -export function useSwappableCurrencyQuery(alexSDK: AlexSDK) { - return useQuery( - ['alex-supported-swap-currencies'], - async () => alexSDK.fetchSwappableCurrency(), - { - refetchOnMount: false, - refetchOnReconnect: false, - refetchOnWindowFocus: false, - retryDelay: 1000 * 60, - staleTime: 1000 * 60 * 10, - } - ); -} diff --git a/src/app/query/stacks/balance/stacks-ft-balances.hooks.ts b/src/app/query/stacks/balance/stacks-ft-balances.hooks.ts index f7059f30df3..1dcd2a36d37 100644 --- a/src/app/query/stacks/balance/stacks-ft-balances.hooks.ts +++ b/src/app/query/stacks/balance/stacks-ft-balances.hooks.ts @@ -7,6 +7,7 @@ import type { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asse import { formatContractId } from '@app/common/utils'; import { useToast } from '@app/features/toasts/use-toast'; +import { useAlexSdKCurrencyPriceAsMoney } from '@app/query/common/alex-sdk/alex-sdk.hooks'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useGetFungibleTokenMetadataListQuery } from '../tokens/fungible-tokens/fungible-token-metadata.query'; @@ -35,6 +36,7 @@ function useStacksFungibleTokenAssetBalances(address: string) { export function useStacksFungibleTokenAssetBalancesWithMetadata(address: string) { const { data: initializedAssetBalances = [] } = useStacksFungibleTokenAssetBalances(address); + const priceAsMoney = useAlexSdKCurrencyPriceAsMoney(); const ftAssetsMetadata = useGetFungibleTokenMetadataListQuery( initializedAssetBalances.map(assetBalance => @@ -49,7 +51,10 @@ export function useStacksFungibleTokenAssetBalancesWithMetadata(address: string) if (!(metadata && isFtAsset(metadata))) return assetBalance; return addQueriedMetadataToInitializedStacksFungibleTokenAssetBalance( assetBalance, - metadata + metadata, + priceAsMoney( + formatContractId(assetBalance.asset.contractAddress, assetBalance.asset.contractName) + ) ); }), // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/src/app/query/stacks/balance/stacks-ft-balances.utils.ts b/src/app/query/stacks/balance/stacks-ft-balances.utils.ts index b93f7bd0211..de6d494fb23 100644 --- a/src/app/query/stacks/balance/stacks-ft-balances.utils.ts +++ b/src/app/query/stacks/balance/stacks-ft-balances.utils.ts @@ -7,7 +7,7 @@ import type { StacksCryptoCurrencyAssetBalance, StacksFungibleTokenAssetBalance, } from '@shared/models/crypto-asset-balance.model'; -import { createMoney } from '@shared/models/money.model'; +import { type Money, createMoney } from '@shared/models/money.model'; import { isTransferableStacksFungibleTokenAsset } from '@app/common/crypto-assets/stacks-crypto-asset.utils'; import { getAssetStringParts } from '@app/ui/utils/get-asset-string-parts'; @@ -47,6 +47,7 @@ export function createStacksFtCryptoAssetBalanceTypeWrapper( hasMemo: false, imageCanonicalUri: '', name: '', + price: createMoney(0, 'USD'), symbol: '', }, }; @@ -68,7 +69,8 @@ export function convertFtBalancesToStacksFungibleTokenAssetBalanceType( export function addQueriedMetadataToInitializedStacksFungibleTokenAssetBalance( assetBalance: StacksFungibleTokenAssetBalance, - metadata: FtMetadataResponse + metadata: FtMetadataResponse, + price: Money ) { return { ...assetBalance, @@ -84,6 +86,7 @@ export function addQueriedMetadataToInitializedStacksFungibleTokenAssetBalance( hasMemo: isTransferableStacksFungibleTokenAsset(assetBalance.asset), imageCanonicalUri: metadata.image_canonical_uri ?? '', name: metadata.name ?? '', + price, symbol: metadata.symbol ?? '', }, }; diff --git a/src/shared/models/crypto-asset.model.ts b/src/shared/models/crypto-asset.model.ts index 057282fc6c0..a2d8f9b9a33 100644 --- a/src/shared/models/crypto-asset.model.ts +++ b/src/shared/models/crypto-asset.model.ts @@ -1,3 +1,5 @@ +import type { Money } from './money.model'; + export interface BitcoinCryptoCurrencyAsset { decimals: number; hasMemo: boolean; @@ -22,6 +24,7 @@ export interface StacksFungibleTokenAsset { hasMemo: boolean; imageCanonicalUri: string; name: string; + price: Money; symbol: string; } diff --git a/src/shared/models/currencies.model.ts b/src/shared/models/currencies.model.ts index 115968881a5..ffbdfdf0045 100644 --- a/src/shared/models/currencies.model.ts +++ b/src/shared/models/currencies.model.ts @@ -1,9 +1,6 @@ -const CRYPTO_CURRENCIES_ARRAY = ['BTC', 'STX'] as const; +import type { LiteralUnion } from 'leather-styles/types'; -export type CryptoCurrencies = (typeof CRYPTO_CURRENCIES_ARRAY)[number]; - -export const isCryptoCurrency = (value: unknown): value is CryptoCurrencies => - CRYPTO_CURRENCIES_ARRAY.some(cryptocurrency => cryptocurrency === value); +export type CryptoCurrencies = LiteralUnion<'BTC' | 'STX', string>; export type FiatCurrencies = 'USD' | string;