From d59662ffc241cda6408f56789eccea1445364fc5 Mon Sep 17 00:00:00 2001 From: Fara Woolf Date: Mon, 8 Apr 2024 13:49:54 -0500 Subject: [PATCH] feat: brc-20 token balances as fiat, #4408 --- .../hooks/balance/btc/use-btc-balance.ts | 4 +- .../hooks/balance/stx/use-stx-balance.ts | 4 +- .../hooks/balance/use-total-balance.tsx | 6 +-- src/app/common/hooks/use-alex-sdk.ts | 38 ------------------- src/app/common/hooks/use-bitcoin-contracts.ts | 4 +- .../hooks/use-convert-to-fiat-amount.ts | 21 ++-------- src/app/common/money/calculate-money.ts | 3 +- src/app/common/money/fiat-conversion.ts | 18 +++++++++ .../hooks/use-bitcoin-custom-fee.tsx | 4 +- .../use-bitcoin-fees-list.ts | 4 +- .../brc20-token-asset-item.layout.tsx | 20 ++++++++-- .../fungible-token-asset.utils.ts | 19 ++++++++++ ...tacks-fungible-token-asset-item.layout.tsx | 14 +++++-- .../hooks/use-stacks-transaction-summary.ts | 4 +- .../bitcoin-contract-list-item-layout.tsx | 4 +- .../rpc-send-transfer-confirmation.tsx | 4 +- .../pages/rpc-sign-psbt/use-rpc-sign-psbt.tsx | 4 +- .../hooks/use-send-inscription-fees-list.ts | 4 +- .../form/btc/btc-send-form-confirmation.tsx | 4 +- .../form/btc/btc-send-form.tsx | 4 +- .../form/stx/stx-send-form.tsx | 4 +- .../components/swap-asset-item.tsx | 14 +++++-- .../swap-asset-select-base.tsx | 20 +++++----- .../swap-asset-select-quote.tsx | 23 +++++++---- src/app/pages/swap/swap.utils.ts | 19 +++++++++- src/app/query/bitcoin/bitcoin-client.ts | 1 + .../ordinals/brc20/brc20-tokens.query.ts | 10 ++--- .../common/market-data/market-data.hooks.ts | 4 +- 28 files changed, 161 insertions(+), 121 deletions(-) delete mode 100644 src/app/common/hooks/use-alex-sdk.ts create mode 100644 src/app/common/money/fiat-conversion.ts diff --git a/src/app/common/hooks/balance/btc/use-btc-balance.ts b/src/app/common/hooks/balance/btc/use-btc-balance.ts index 688d39d5852..3012db712a8 100644 --- a/src/app/common/hooks/balance/btc/use-btc-balance.ts +++ b/src/app/common/hooks/balance/btc/use-btc-balance.ts @@ -4,10 +4,10 @@ import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money'; import { i18nFormatCurrency } from '@app/common/money/format-money'; import { createBitcoinCryptoCurrencyAssetTypeWrapper } from '@app/query/bitcoin/address/address.utils'; import { useNativeSegwitBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; -import { useCryptoCurrencyMarketData } from '@app/query/common/market-data/market-data.hooks'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; export function useBtcAssetBalance(btcAddress: string) { - const btcMarketData = useCryptoCurrencyMarketData('BTC'); + const btcMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); const { btcBalance: btcAssetBalance, isLoading, diff --git a/src/app/common/hooks/balance/stx/use-stx-balance.ts b/src/app/common/hooks/balance/stx/use-stx-balance.ts index 5a7312902f7..76789b68c61 100644 --- a/src/app/common/hooks/balance/stx/use-stx-balance.ts +++ b/src/app/common/hooks/balance/stx/use-stx-balance.ts @@ -5,7 +5,7 @@ import { isDefined } from '@shared/utils'; import { baseCurrencyAmountInQuote, subtractMoney } from '@app/common/money/calculate-money'; import { i18nFormatCurrency } from '@app/common/money/format-money'; -import { useCryptoCurrencyMarketData } from '@app/query/common/market-data/market-data.hooks'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { createStacksCryptoCurrencyAssetTypeWrapper } from '@app/query/stacks/balance/stacks-ft-balances.utils'; import { useCurrentStacksAccountBalances } from '@app/query/stacks/balance/stx-balance.hooks'; import { useCurrentAccountMempoolTransactionsBalance } from '@app/query/stacks/mempool/mempool.hooks'; @@ -15,7 +15,7 @@ export function useStxBalance() { const totalBalance = stxBalanceQuery.data?.stx.balance; const unlockedStxBalance = stxBalanceQuery.data?.stx.unlockedStx; - const stxMarketData = useCryptoCurrencyMarketData('STX'); + const stxMarketData = useCryptoCurrencyMarketDataMeanAverage('STX'); const pendingTxsBalance = useCurrentAccountMempoolTransactionsBalance(); const stxEffectiveBalance = isDefined(totalBalance) diff --git a/src/app/common/hooks/balance/use-total-balance.tsx b/src/app/common/hooks/balance/use-total-balance.tsx index ac6c01904cc..91b54228fc3 100644 --- a/src/app/common/hooks/balance/use-total-balance.tsx +++ b/src/app/common/hooks/balance/use-total-balance.tsx @@ -4,7 +4,7 @@ import { createMoney } from '@shared/models/money.model'; import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money'; import { i18nFormatCurrency } from '@app/common/money/format-money'; -import { useCryptoCurrencyMarketData } from '@app/query/common/market-data/market-data.hooks'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { useStacksAccountBalances } from '@app/query/stacks/balance/stx-balance.hooks'; import { useBtcAssetBalance } from './btc/use-btc-balance'; @@ -16,8 +16,8 @@ interface UseTotalBalanceArgs { export function useTotalBalance({ btcAddress, stxAddress }: UseTotalBalanceArgs) { // get market data - const btcMarketData = useCryptoCurrencyMarketData('BTC'); - const stxMarketData = useCryptoCurrencyMarketData('STX'); + const btcMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); + const stxMarketData = useCryptoCurrencyMarketDataMeanAverage('STX'); // get stx balance const { data: balances, isLoading, isInitialLoading } = useStacksAccountBalances(stxAddress); diff --git a/src/app/common/hooks/use-alex-sdk.ts b/src/app/common/hooks/use-alex-sdk.ts deleted file mode 100644 index bb69ed7386f..00000000000 --- a/src/app/common/hooks/use-alex-sdk.ts +++ /dev/null @@ -1,38 +0,0 @@ -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'; -import { i18nFormatCurrency } from '@app/common/money/format-money'; -import { unitToFractionalUnit } from '@app/common/money/unit-conversion'; - -export function useAlexSdkAmountAsFiat(balance?: Money, price?: Money, value?: string) { - const convertAlexSdkCurrencyToUsd = useConvertAlexSdkCurrencyToFiatAmount( - balance?.symbol ?? '', - price ?? createMoney(0, 'USD') - ); - - if (isUndefined(balance) || isUndefined(price) || isUndefined(value)) return; - - const convertedAmountAsMoney = convertAlexSdkCurrencyToUsd( - createMoney(unitToFractionalUnit(balance.decimals)(value), balance.symbol, balance.decimals) - ); - - if (convertedAmountAsMoney.amount.isNaN()) return; - return i18nFormatCurrency(convertedAmountAsMoney); -} - -export function useAlexSdkBalanceAsFiat(balance: Money, price?: Money | null) { - const convertAlexSdkCurrencyToUsd = useConvertAlexSdkCurrencyToFiatAmount( - balance.symbol, - price ?? createMoney(0, 'USD') - ); - - if (isUndefined(balance) || isUndefined(price)) return; - - const convertedBalanceAsMoney = convertAlexSdkCurrencyToUsd( - createMoney(balance.amount, balance.symbol, balance.decimals) - ); - - if (convertedBalanceAsMoney.amount.isNaN() || convertedBalanceAsMoney.amount.isEqualTo(0)) return; - return i18nFormatCurrency(convertedBalanceAsMoney); -} diff --git a/src/app/common/hooks/use-bitcoin-contracts.ts b/src/app/common/hooks/use-bitcoin-contracts.ts index 8bec3e13ca6..5a75f6bd570 100644 --- a/src/app/common/hooks/use-bitcoin-contracts.ts +++ b/src/app/common/hooks/use-bitcoin-contracts.ts @@ -16,7 +16,7 @@ import { makeRpcErrorResponse, makeRpcSuccessResponse } from '@shared/rpc/rpc-me import { sendAcceptedBitcoinContractOfferToProtocolWallet } from '@app/query/bitcoin/contract/send-accepted-bitcoin-contract-offer'; import { useCalculateBitcoinFiatValue, - useCryptoCurrencyMarketData, + useCryptoCurrencyMarketDataMeanAverage, } from '@app/query/common/market-data/market-data.hooks'; import { useCurrentAccountIndex } from '@app/store/accounts/account'; import { @@ -57,7 +57,7 @@ export interface BitcoinContractOfferDetails { export function useBitcoinContracts() { const navigate = useNavigate(); const defaultParams = useDefaultRequestParams(); - const bitcoinMarketData = useCryptoCurrencyMarketData('BTC'); + const bitcoinMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); const calculateFiatValue = useCalculateBitcoinFiatValue(); const bitcoinAccountDetails = useCurrentAccountNativeSegwitIndexZeroSigner(); diff --git a/src/app/common/hooks/use-convert-to-fiat-amount.ts b/src/app/common/hooks/use-convert-to-fiat-amount.ts index f38c9dae623..5d3cc82e6f3 100644 --- a/src/app/common/hooks/use-convert-to-fiat-amount.ts +++ b/src/app/common/hooks/use-convert-to-fiat-amount.ts @@ -1,30 +1,17 @@ -import { useCallback, useMemo } from 'react'; +import { useCallback } from 'react'; import { CryptoCurrencies } from '@shared/models/currencies.model'; -import { createMarketData, createMarketPair } from '@shared/models/market.model'; -import type { Money } from '@shared/models/money.model'; +import { type Money } from '@shared/models/money.model'; -import { useCryptoCurrencyMarketData } from '@app/query/common/market-data/market-data.hooks'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { baseCurrencyAmountInQuote } from '../money/calculate-money'; export function useConvertCryptoCurrencyToFiatAmount(currency: CryptoCurrencies) { - const cryptoCurrencyMarketData = useCryptoCurrencyMarketData(currency); + const cryptoCurrencyMarketData = useCryptoCurrencyMarketDataMeanAverage(currency); return useCallback( (value: Money) => baseCurrencyAmountInQuote(value, cryptoCurrencyMarketData), [cryptoCurrencyMarketData] ); } - -export function useConvertAlexSdkCurrencyToFiatAmount(currency: CryptoCurrencies, price: Money) { - const alexCurrencyMarketData = useMemo( - () => createMarketData(createMarketPair(currency, 'USD'), price), - [currency, price] - ); - - return useCallback( - (value: Money) => baseCurrencyAmountInQuote(value, alexCurrencyMarketData), - [alexCurrencyMarketData] - ); -} diff --git a/src/app/common/money/calculate-money.ts b/src/app/common/money/calculate-money.ts index 8063e2d0ce1..bc332c82d70 100644 --- a/src/app/common/money/calculate-money.ts +++ b/src/app/common/money/calculate-money.ts @@ -9,7 +9,7 @@ import { formatMoney } from './format-money'; import { isMoney } from './is-money'; export function baseCurrencyAmountInQuote(quantity: Money, { pair, price }: MarketData) { - if (quantity.symbol !== pair.base) + if (quantity.symbol.toLowerCase() !== pair.base.toLowerCase()) throw new Error( `Cannot calculate value of ${formatMoney(quantity)} with market pair of ${formatMarketPair( pair @@ -39,7 +39,6 @@ export function convertToMoneyTypeWithDefaultOfZero( return createMoney(initBigNumber(num ?? 0), symbol.toUpperCase(), decimals); } -// ts-unused-exports:disable-next-line export function convertAmountToBaseUnit(num: Money | BigNumber, decimals?: number) { if (isMoney(num)) return num.amount.shiftedBy(-num.decimals); if (!isNumber(decimals)) throw new Error('Must define decimal of given currency'); diff --git a/src/app/common/money/fiat-conversion.ts b/src/app/common/money/fiat-conversion.ts new file mode 100644 index 00000000000..0159e7aeffe --- /dev/null +++ b/src/app/common/money/fiat-conversion.ts @@ -0,0 +1,18 @@ +import type { CryptoCurrencies } from '@shared/models/currencies.model'; +import { createMarketData, createMarketPair } from '@shared/models/market.model'; +import { type Money } from '@shared/models/money.model'; + +import { baseCurrencyAmountInQuote } from './calculate-money'; + +export function checkIsMoneyAmountGreaterThanZero(money: Money) { + return !(money.amount.isNaN() || money.amount.isZero()); +} + +export function convertCryptoCurrencyMoneyToFiat( + currency: CryptoCurrencies, + price: Money, + money: Money +) { + const cryptoCurrencyMarketData = createMarketData(createMarketPair(currency, 'USD'), price); + return baseCurrencyAmountInQuote(money, cryptoCurrencyMarketData); +} diff --git a/src/app/components/bitcoin-custom-fee/hooks/use-bitcoin-custom-fee.tsx b/src/app/components/bitcoin-custom-fee/hooks/use-bitcoin-custom-fee.tsx index 7982b0dccd9..2d4c757c11e 100644 --- a/src/app/components/bitcoin-custom-fee/hooks/use-bitcoin-custom-fee.tsx +++ b/src/app/components/bitcoin-custom-fee/hooks/use-bitcoin-custom-fee.tsx @@ -13,7 +13,7 @@ import { } from '@app/common/transactions/bitcoin/coinselect/local-coin-selection'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; import { useCurrentNativeSegwitAddressBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; -import { useCryptoCurrencyMarketData } from '@app/query/common/market-data/market-data.hooks'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; export const MAX_FEE_RATE_MULTIPLIER = 50; @@ -25,7 +25,7 @@ interface UseBitcoinCustomFeeArgs { export function useBitcoinCustomFee({ amount, isSendingMax, recipient }: UseBitcoinCustomFeeArgs) { const { balance } = useCurrentNativeSegwitAddressBalance(); const { data: utxos = [] } = useCurrentNativeSegwitUtxos(); - const btcMarketData = useCryptoCurrencyMarketData('BTC'); + const btcMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); return useCallback( (feeRate: number) => { diff --git a/src/app/components/bitcoin-fees-list/use-bitcoin-fees-list.ts b/src/app/components/bitcoin-fees-list/use-bitcoin-fees-list.ts index 14fe826a0c8..b7e90374b4b 100644 --- a/src/app/components/bitcoin-fees-list/use-bitcoin-fees-list.ts +++ b/src/app/components/bitcoin-fees-list/use-bitcoin-fees-list.ts @@ -13,7 +13,7 @@ import { import { useCurrentNativeSegwitAddressBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; import { UtxoResponseItem } from '@app/query/bitcoin/bitcoin-client'; import { useAverageBitcoinFeeRates } from '@app/query/bitcoin/fees/fee-estimates.hooks'; -import { useCryptoCurrencyMarketData } from '@app/query/common/market-data/market-data.hooks'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { FeesListItem } from './bitcoin-fees-list'; @@ -44,7 +44,7 @@ export function useBitcoinFeesList({ utxos, }: UseBitcoinFeesListArgs) { const { balance } = useCurrentNativeSegwitAddressBalance(); - const btcMarketData = useCryptoCurrencyMarketData('BTC'); + const btcMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); const { data: feeRates, isLoading } = useAverageBitcoinFeeRates(); const feesList: FeesListItem[] = useMemo(() => { diff --git a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-item.layout.tsx b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-item.layout.tsx index 84924791aaa..0ce58463fb0 100644 --- a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-item.layout.tsx +++ b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-item.layout.tsx @@ -3,6 +3,11 @@ import { styled } from 'leather-styles/jsx'; import { createMoney } from '@shared/models/money.model'; import { formatBalance } from '@app/common/format-balance'; +import { + checkIsMoneyAmountGreaterThanZero, + convertCryptoCurrencyMoneyToFiat, +} from '@app/common/money/fiat-conversion'; +import { i18nFormatCurrency } from '@app/common/money/format-money'; import { Brc20Token } from '@app/query/bitcoin/bitcoin-client'; import { Brc20AvatarIcon } from '@app/ui/components/avatar/brc20-avatar-icon'; import { ItemLayout } from '@app/ui/components/item-layout/item-layout'; @@ -14,8 +19,16 @@ interface Brc20TokenAssetItemLayoutProps { onClick?(): void; } export function Brc20TokenAssetItemLayout({ onClick, token }: Brc20TokenAssetItemLayoutProps) { - const balance = createMoney(Number(token.overall_balance), token.ticker, 0).amount.toString(); - const formattedBalance = formatBalance(balance); + const balanceAsMoney = createMoney(Number(token.overall_balance), token.ticker, token.decimals); + const balanceAsString = balanceAsMoney.amount.toString(); + const formattedBalance = formatBalance(balanceAsString); + const priceAsMoney = createMoney(token.min_listed_unit_price, 'USD'); + const showFiatBalance = checkIsMoneyAmountGreaterThanZero(priceAsMoney); + const balanceAsFiat = showFiatBalance + ? i18nFormatCurrency( + convertCryptoCurrencyMoneyToFiat(token.ticker, priceAsMoney, balanceAsMoney) + ) + : ''; return ( @@ -26,7 +39,7 @@ export function Brc20TokenAssetItemLayout({ onClick, token }: Brc20TokenAssetIte titleRight={ @@ -34,6 +47,7 @@ export function Brc20TokenAssetItemLayout({ onClick, token }: Brc20TokenAssetIte } + captionRight={balanceAsFiat} /> ); diff --git a/src/app/components/crypto-assets/stacks/fungible-token-asset/fungible-token-asset.utils.ts b/src/app/components/crypto-assets/stacks/fungible-token-asset/fungible-token-asset.utils.ts index 06336273364..ada3faf3059 100644 --- a/src/app/components/crypto-assets/stacks/fungible-token-asset/fungible-token-asset.utils.ts +++ b/src/app/components/crypto-assets/stacks/fungible-token-asset/fungible-token-asset.utils.ts @@ -4,6 +4,11 @@ import type { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asse import { getImageCanonicalUri } from '@app/common/crypto-assets/stacks-crypto-asset.utils'; import { formatBalance } from '@app/common/format-balance'; +import { + checkIsMoneyAmountGreaterThanZero, + convertCryptoCurrencyMoneyToFiat, +} from '@app/common/money/fiat-conversion'; +import { i18nFormatCurrency } from '@app/common/money/format-money'; import { ftDecimals } from '@app/common/stacks-utils'; import { formatContractId, getTicker } from '@app/common/utils'; import { spamFilter } from '@app/common/utils/spam-filter'; @@ -29,9 +34,23 @@ export function parseStacksFungibleTokenAssetBalance( const caption = symbol || getTicker(friendlyName); const title = spamFilter(friendlyName); + const showFiatBalance = + assetBalance.asset.price && checkIsMoneyAmountGreaterThanZero(assetBalance.asset.price); + const balanceAsFiat = showFiatBalance + ? assetBalance.asset.price && + i18nFormatCurrency( + convertCryptoCurrencyMoneyToFiat( + assetBalance.balance.symbol, + assetBalance.asset.price, + assetBalance.balance + ) + ) + : ''; + return { amount, avatar, + balanceAsFiat, caption, dataTestId, formattedBalance, 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 3106f3e1d68..cf66b4cced8 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,7 +2,6 @@ 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'; @@ -18,9 +17,16 @@ export function StacksFungibleTokenAssetItemLayout({ assetBalance, onClick, }: StacksFungibleTokenAssetItemLayoutProps) { - const balanceAsFiat = useAlexSdkBalanceAsFiat(assetBalance.balance, assetBalance.asset.price); - const { amount, avatar, caption, dataTestId, formattedBalance, imageCanonicalUri, title } = - parseStacksFungibleTokenAssetBalance(assetBalance); + const { + amount, + avatar, + balanceAsFiat, + caption, + dataTestId, + formattedBalance, + imageCanonicalUri, + title, + } = parseStacksFungibleTokenAssetBalance(assetBalance); return ( diff --git a/src/app/features/stacks-transaction-request/hooks/use-stacks-transaction-summary.ts b/src/app/features/stacks-transaction-request/hooks/use-stacks-transaction-summary.ts index 309f3b9acca..16fbd3c7af1 100644 --- a/src/app/features/stacks-transaction-request/hooks/use-stacks-transaction-summary.ts +++ b/src/app/features/stacks-transaction-request/hooks/use-stacks-transaction-summary.ts @@ -21,13 +21,13 @@ import { import { formatMoney, i18nFormatCurrency } from '@app/common/money/format-money'; import { getEstimatedConfirmationTime } from '@app/common/transactions/stacks/transaction.utils'; import { removeTrailingNullCharacters } from '@app/common/utils'; -import { useCryptoCurrencyMarketData } from '@app/query/common/market-data/market-data.hooks'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { useStacksBlockTime } from '@app/query/stacks/info/info.hooks'; import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; import { microStxToStx } from '@app/ui/utils/micro-stx-to-stx'; export function useStacksTransactionSummary(token: CryptoCurrencies) { - const tokenMarketData = useCryptoCurrencyMarketData(token); + const tokenMarketData = useCryptoCurrencyMarketDataMeanAverage(token); const { isTestnet } = useCurrentNetworkState(); const { data: blockTime } = useStacksBlockTime(); diff --git a/src/app/pages/bitcoin-contract-list/components/bitcoin-contract-list-item-layout.tsx b/src/app/pages/bitcoin-contract-list/components/bitcoin-contract-list-item-layout.tsx index 3d5d9161ed9..b7549ec79cd 100644 --- a/src/app/pages/bitcoin-contract-list/components/bitcoin-contract-list-item-layout.tsx +++ b/src/app/pages/bitcoin-contract-list/components/bitcoin-contract-list-item-layout.tsx @@ -8,7 +8,7 @@ import { useBitcoinExplorerLink } from '@app/common/hooks/use-bitcoin-explorer-l import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money'; import { i18nFormatCurrency } from '@app/common/money/format-money'; import { satToBtc } from '@app/common/money/unit-conversion'; -import { useCryptoCurrencyMarketData } from '@app/query/common/market-data/market-data.hooks'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { Flag } from '@app/ui/components/flag/flag'; import { Caption } from '@app/ui/components/typography/caption'; import { BitcoinContractIcon } from '@app/ui/icons/bitcoin-contract-icon'; @@ -26,7 +26,7 @@ export function BitcoinContractListItemLayout({ txid, }: BitcoinContractListItemLayoutProps) { const { handleOpenBitcoinTxLink: handleOpenTxLink } = useBitcoinExplorerLink(); - const bitcoinMarketData = useCryptoCurrencyMarketData('BTC'); + const bitcoinMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); const getFiatValue = useCallback( (value: string) => diff --git a/src/app/pages/rpc-send-transfer/rpc-send-transfer-confirmation.tsx b/src/app/pages/rpc-send-transfer/rpc-send-transfer-confirmation.tsx index ea506cdf7fe..c67f39b2c2e 100644 --- a/src/app/pages/rpc-send-transfer/rpc-send-transfer-confirmation.tsx +++ b/src/app/pages/rpc-send-transfer/rpc-send-transfer-confirmation.tsx @@ -22,7 +22,7 @@ import { import { InfoCardFooter } from '@app/components/info-card/info-card'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; import { useBitcoinBroadcastTransaction } from '@app/query/bitcoin/transaction/use-bitcoin-broadcast-transaction'; -import { useCryptoCurrencyMarketData } from '@app/query/common/market-data/market-data.hooks'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { Button } from '@app/ui/components/button/button'; import { truncateMiddle } from '@app/ui/utils/truncate-middle'; @@ -51,7 +51,7 @@ export function RpcSendTransferConfirmation() { const bitcoinAddress = useCurrentAccountNativeSegwitAddressIndexZero(); const { broadcastTx, isBroadcasting } = useBitcoinBroadcastTransaction(); const { refetch } = useCurrentNativeSegwitUtxos(); - const btcMarketData = useCryptoCurrencyMarketData('BTC'); + const btcMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); const psbt = decodeBitcoinTx(tx); const transferAmount = sumMoney(recipients.map(r => r.amount)); diff --git a/src/app/pages/rpc-sign-psbt/use-rpc-sign-psbt.tsx b/src/app/pages/rpc-sign-psbt/use-rpc-sign-psbt.tsx index 0ed40f404d2..79963033b10 100644 --- a/src/app/pages/rpc-sign-psbt/use-rpc-sign-psbt.tsx +++ b/src/app/pages/rpc-sign-psbt/use-rpc-sign-psbt.tsx @@ -19,7 +19,7 @@ import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by import { useBitcoinBroadcastTransaction } from '@app/query/bitcoin/transaction/use-bitcoin-broadcast-transaction'; import { useCalculateBitcoinFiatValue, - useCryptoCurrencyMarketData, + useCryptoCurrencyMarketDataMeanAverage, } from '@app/query/common/market-data/market-data.hooks'; import { useGetAssumedZeroIndexSigningConfig } from '@app/store/accounts/blockchain/bitcoin/bitcoin.hooks'; @@ -36,7 +36,7 @@ export function useRpcSignPsbt() { const { signPsbt, getPsbtAsTransaction } = usePsbtSigner(); const { broadcastTx, isBroadcasting } = useBitcoinBroadcastTransaction(); const { refetch } = useCurrentNativeSegwitUtxos(); - const btcMarketData = useCryptoCurrencyMarketData('BTC'); + const btcMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); const calculateBitcoinFiatValue = useCalculateBitcoinFiatValue(); const getDefaultSigningConfig = useGetAssumedZeroIndexSigningConfig(); diff --git a/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-fees-list.ts b/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-fees-list.ts index 52435aa4d3f..4f077a65ddc 100644 --- a/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-fees-list.ts +++ b/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-fees-list.ts @@ -10,7 +10,7 @@ import { FeesListItem } from '@app/components/bitcoin-fees-list/bitcoin-fees-lis import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; import { UtxoWithDerivationPath } from '@app/query/bitcoin/bitcoin-client'; import { useAverageBitcoinFeeRates } from '@app/query/bitcoin/fees/fee-estimates.hooks'; -import { useCryptoCurrencyMarketData } from '@app/query/common/market-data/market-data.hooks'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { useCurrentAccountNativeSegwitSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { useGenerateUnsignedOrdinalTx } from './use-generate-ordinal-tx'; @@ -29,7 +29,7 @@ export function useSendInscriptionFeesList({ const createNativeSegwitSigner = useCurrentAccountNativeSegwitSigner(); const { data: nativeSegwitUtxos } = useCurrentNativeSegwitUtxos(); - const btcMarketData = useCryptoCurrencyMarketData('BTC'); + const btcMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); const { data: feeRates, isLoading } = useAverageBitcoinFeeRates(); const { coverFeeFromAdditionalUtxos } = useGenerateUnsignedOrdinalTx(utxo); diff --git a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form-confirmation.tsx b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form-confirmation.tsx index d061ecf82b8..7d0f68256fe 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form-confirmation.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form-confirmation.tsx @@ -25,7 +25,7 @@ import { } from '@app/components/info-card/info-card'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; import { useBitcoinBroadcastTransaction } from '@app/query/bitcoin/transaction/use-bitcoin-broadcast-transaction'; -import { useCryptoCurrencyMarketData } from '@app/query/common/market-data/market-data.hooks'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { Button } from '@app/ui/components/button/button'; import { Footer } from '@app/ui/components/containers/footers/footer'; import { Card } from '@app/ui/layout/card/card'; @@ -53,7 +53,7 @@ export function BtcSendFormConfirmation() { const { refetch } = useCurrentNativeSegwitUtxos(); const analytics = useAnalytics(); - const btcMarketData = useCryptoCurrencyMarketData('BTC'); + const btcMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); const { broadcastTx, isBroadcasting } = useBitcoinBroadcastTransaction(); const transaction = btc.Transaction.fromRaw(hexToBytes(tx)); diff --git a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form.tsx index e7b99a86efc..13731614514 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form.tsx @@ -10,7 +10,7 @@ import { CryptoCurrencies } from '@shared/models/currencies.model'; import { formatMoney } from '@app/common/money/format-money'; import { HighFeeDialog } from '@app/features/dialogs/high-fee-dialog/high-fee-dialog'; import { useNativeSegwitBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; -import { useCryptoCurrencyMarketData } from '@app/query/common/market-data/market-data.hooks'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; import { Button } from '@app/ui/components/button/button'; @@ -34,7 +34,7 @@ const symbol: CryptoCurrencies = 'BTC'; export function BtcSendForm() { const routeState = useSendFormRouteState(); - const btcMarketData = useCryptoCurrencyMarketData(symbol); + const btcMarketData = useCryptoCurrencyMarketDataMeanAverage(symbol); const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner(); const { btcBalance } = useNativeSegwitBalance(nativeSegwitSigner.address); diff --git a/src/app/pages/send/send-crypto-asset-form/form/stx/stx-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/stx/stx-send-form.tsx index 0e95742f203..f663c70878a 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/stx/stx-send-form.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/stx/stx-send-form.tsx @@ -1,6 +1,6 @@ import { CryptoCurrencies } from '@shared/models/currencies.model'; -import { useCryptoCurrencyMarketData } from '@app/query/common/market-data/market-data.hooks'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { StxAvatarIcon } from '@app/ui/components/avatar/stx-avatar-icon'; import { AmountField } from '../../components/amount-field'; @@ -13,7 +13,7 @@ import { useStxSendForm } from './use-stx-send-form'; const symbol: CryptoCurrencies = 'STX'; export function StxSendForm() { - const stxMarketData = useCryptoCurrencyMarketData(symbol); + const stxMarketData = useCryptoCurrencyMarketDataMeanAverage(symbol); const { availableStxBalance, diff --git a/src/app/pages/swap/components/swap-asset-dialog/components/swap-asset-item.tsx b/src/app/pages/swap/components/swap-asset-dialog/components/swap-asset-item.tsx index 951e4b7c7ab..2b191fd32bf 100644 --- a/src/app/pages/swap/components/swap-asset-dialog/components/swap-asset-item.tsx +++ b/src/app/pages/swap/components/swap-asset-dialog/components/swap-asset-item.tsx @@ -1,7 +1,10 @@ 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 { + checkIsMoneyAmountGreaterThanZero, + convertCryptoCurrencyMoneyToFiat, +} from '@app/common/money/fiat-conversion'; +import { formatMoneyWithoutSymbol, i18nFormatCurrency } 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'; @@ -14,12 +17,17 @@ interface SwapAssetItemProps { onClick(): void; } export function SwapAssetItem({ asset, onClick }: SwapAssetItemProps) { - const balanceAsFiat = useAlexSdkBalanceAsFiat(asset.balance, asset.price); const { data: ftMetadata } = useGetFungibleTokenMetadataQuery(asset.principal); const ftMetadataName = ftMetadata && isFtAsset(ftMetadata) ? ftMetadata.name : asset.name; const displayName = asset.displayName ?? ftMetadataName; const fallback = getAvatarFallback(asset.name); + const showFiatBalance = checkIsMoneyAmountGreaterThanZero(asset.price); + const balanceAsFiat = showFiatBalance + ? i18nFormatCurrency( + convertCryptoCurrencyMoneyToFiat(asset.balance.symbol, asset.price, asset.balance) + ) + : ''; return ( diff --git a/src/app/pages/swap/components/swap-asset-select/swap-asset-select-base.tsx b/src/app/pages/swap/components/swap-asset-select/swap-asset-select-base.tsx index 154674fae2a..94221abaa7b 100644 --- a/src/app/pages/swap/components/swap-asset-select/swap-asset-select-base.tsx +++ b/src/app/pages/swap/components/swap-asset-select/swap-asset-select-base.tsx @@ -3,16 +3,16 @@ import { useField, useFormikContext } from 'formik'; import { createMoney } from '@shared/models/money.model'; import { RouteUrls } from '@shared/route-urls'; -import { isUndefined } from '@shared/utils'; +import { isDefined, 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 { formatMoneyWithoutSymbol, i18nFormatCurrency } from '@app/common/money/format-money'; import { SwapFormValues } from '../../hooks/use-swap-form'; import { useSwapNavigate } from '../../hooks/use-swap-navigate'; import { useSwapContext } from '../../swap.context'; +import { convertInputAmountValueToFiat } from '../../swap.utils'; import { SwapAmountField } from './components/swap-amount-field'; import { SwapAssetSelectLayout } from './components/swap-asset-select.layout'; @@ -30,11 +30,13 @@ export function SwapAssetSelectBase() { const [assetField] = useField('swapAssetBase'); const navigate = useSwapNavigate(); - const amountAsFiat = useAlexSdkAmountAsFiat( - assetField.value.balance, - assetField.value.price, - amountField.value - ); + const amountAsFiat = + isDefined(assetField.value && amountField.value) && + convertInputAmountValueToFiat( + assetField.value.balance, + assetField.value.price, + amountField.value + ); const formattedBalance = formatMoneyWithoutSymbol(assetField.value.balance); const isSwapAssetBaseBalanceGreaterThanZero = values.swapAssetBase?.balance.amount.isGreaterThan(0); @@ -74,7 +76,7 @@ export function SwapAssetSelectBase() { showError={!!(showError && values.swapAssetQuote)} swapAmountInput={ diff --git a/src/app/pages/swap/components/swap-asset-select/swap-asset-select-quote.tsx b/src/app/pages/swap/components/swap-asset-select/swap-asset-select-quote.tsx index 5a74b2e3a20..16ff6822f9c 100644 --- a/src/app/pages/swap/components/swap-asset-select/swap-asset-select-quote.tsx +++ b/src/app/pages/swap/components/swap-asset-select/swap-asset-select-quote.tsx @@ -1,13 +1,14 @@ import { useField } from 'formik'; import { RouteUrls } from '@shared/route-urls'; +import { isDefined } from '@shared/utils'; -import { useAlexSdkAmountAsFiat } from '@app/common/hooks/use-alex-sdk'; -import { formatMoneyWithoutSymbol } from '@app/common/money/format-money'; +import { formatMoneyWithoutSymbol, i18nFormatCurrency } from '@app/common/money/format-money'; import { LoadingSpinner } from '@app/components/loading-spinner'; import { useSwapNavigate } from '../../hooks/use-swap-navigate'; import { useSwapContext } from '../../swap.context'; +import { convertInputAmountValueToFiat } from '../../swap.utils'; import { SwapAmountField } from './components/swap-amount-field'; import { SwapAssetSelectLayout } from './components/swap-asset-select.layout'; @@ -17,11 +18,13 @@ export function SwapAssetSelectQuote() { const [assetField] = useField('swapAssetQuote'); const navigate = useSwapNavigate(); - const amountAsFiat = useAlexSdkAmountAsFiat( - assetField.value?.balance, - assetField.value?.price, - amountField.value - ); + const amountAsFiat = + isDefined(assetField.value && amountField.value) && + convertInputAmountValueToFiat( + assetField.value.balance, + assetField.value.price, + amountField.value + ); return ( ) : ( - + ) } symbol={assetField.value?.name ?? 'Select asset'} diff --git a/src/app/pages/swap/swap.utils.ts b/src/app/pages/swap/swap.utils.ts index 00ec3bf69c5..e05a135802d 100644 --- a/src/app/pages/swap/swap.utils.ts +++ b/src/app/pages/swap/swap.utils.ts @@ -1,4 +1,10 @@ -import { createMoney } from '@shared/models/money.model'; +import { type Money, createMoney } from '@shared/models/money.model'; + +import { + checkIsMoneyAmountGreaterThanZero, + convertCryptoCurrencyMoneyToFiat, +} from '@app/common/money/fiat-conversion'; +import { unitToFractionalUnit } from '@app/common/money/unit-conversion'; import { SwapAsset } from './hooks/use-swap-form'; @@ -30,3 +36,14 @@ export function migratePositiveBalancesToTop(swappableAssets: SwapAsset[]) { const assetsWithZeroBalance = swappableAssets.filter(asset => asset.balance.amount.isEqualTo(0)); return [...assetsWithPositiveBalance, ...assetsWithZeroBalance]; } + +export function convertInputAmountValueToFiat(balance: Money, price: Money, value: string) { + const valueAsMoney = createMoney( + unitToFractionalUnit(balance.decimals)(value), + balance.symbol, + balance.decimals + ); + + if (!checkIsMoneyAmountGreaterThanZero(valueAsMoney)) return; + return convertCryptoCurrencyMoneyToFiat(balance.symbol, price, valueAsMoney); +} diff --git a/src/app/query/bitcoin/bitcoin-client.ts b/src/app/query/bitcoin/bitcoin-client.ts index 44796947f62..1629779f3ba 100644 --- a/src/app/query/bitcoin/bitcoin-client.ts +++ b/src/app/query/bitcoin/bitcoin-client.ts @@ -70,6 +70,7 @@ interface Brc20TokenResponse { available_balance: string; transferrable_balance: string; image_url: string | null; + min_listed_unit_price: number; } export interface Brc20Token extends Brc20TokenResponse { diff --git a/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.query.ts b/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.query.ts index bb01f0a222b..4df6782d3ca 100644 --- a/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.query.ts +++ b/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.query.ts @@ -51,18 +51,18 @@ export function useGetBrc20TokensQuery() { } const brc20TokensPromises = addressesData.map(async address => { - const brc20Tokens = await client.HiroApi.getBrc20Balance(address); + const brc20Tokens = await client.BestinslotApi.getBrc20Balance(address); const tickerPromises = await Promise.all( - brc20Tokens.results.map(token => { - return client.HiroApi.getBrc20TickerData(token.ticker); + brc20Tokens.data.map(token => { + return client.BestinslotApi.getBrc20TickerData(token.ticker); }) ); - return brc20Tokens.results.map((token, index) => { + return brc20Tokens.data.map((token, index) => { return { ...token, - decimals: tickerPromises[index].results[0].decimals, + decimals: tickerPromises[index].data.decimals, holderAddress: address, }; }); diff --git a/src/app/query/common/market-data/market-data.hooks.ts b/src/app/query/common/market-data/market-data.hooks.ts index 499fd612837..1c4d63a9ca3 100644 --- a/src/app/query/common/market-data/market-data.hooks.ts +++ b/src/app/query/common/market-data/market-data.hooks.ts @@ -37,7 +37,7 @@ function pullPriceDataFromAvailableResponses(responses: MarketDataVendorWithPric .map(val => convertAmountToFractionalUnit(val, currencyDecimalsMap.USD)); } -export function useCryptoCurrencyMarketData(currency: CryptoCurrencies): MarketData { +export function useCryptoCurrencyMarketDataMeanAverage(currency: CryptoCurrencies): MarketData { const { data: coingecko } = useCoinGeckoMarketDataQuery(currency); const { data: coincap } = useCoincapMarketDataQuery(currency); const { data: binance } = useBinanceMarketDataQuery(currency); @@ -55,7 +55,7 @@ export function useCryptoCurrencyMarketData(currency: CryptoCurrencies): MarketD } export function useCalculateBitcoinFiatValue() { - const btcMarketData = useCryptoCurrencyMarketData('BTC'); + const btcMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); return useCallback( (value: Money) => baseCurrencyAmountInQuote(value, btcMarketData),