Skip to content

Commit

Permalink
feat: brc-20 token balances as fiat, #4408
Browse files Browse the repository at this point in the history
  • Loading branch information
fbwoolf committed Apr 20, 2024
1 parent 6a53bff commit d59662f
Show file tree
Hide file tree
Showing 28 changed files with 161 additions and 121 deletions.
4 changes: 2 additions & 2 deletions src/app/common/hooks/balance/btc/use-btc-balance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions src/app/common/hooks/balance/stx/use-stx-balance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions src/app/common/hooks/balance/use-total-balance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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);
Expand Down
38 changes: 0 additions & 38 deletions src/app/common/hooks/use-alex-sdk.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/app/common/hooks/use-bitcoin-contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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();
Expand Down
21 changes: 4 additions & 17 deletions src/app/common/hooks/use-convert-to-fiat-amount.ts
Original file line number Diff line number Diff line change
@@ -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]
);
}
3 changes: 1 addition & 2 deletions src/app/common/money/calculate-money.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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');
Expand Down
18 changes: 18 additions & 0 deletions src/app/common/money/fiat-conversion.ts
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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) => {
Expand Down
4 changes: 2 additions & 2 deletions src/app/components/bitcoin-fees-list/use-bitcoin-fees-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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 (
<Pressable onClick={onClick} my="space.02">
Expand All @@ -26,14 +39,15 @@ export function Brc20TokenAssetItemLayout({ onClick, token }: Brc20TokenAssetIte
titleRight={
<BasicTooltip
asChild
label={formattedBalance.isAbbreviated ? balance : undefined}
label={formattedBalance.isAbbreviated ? balanceAsString : undefined}
side="left"
>
<styled.span data-testid={token.ticker} textStyle="label.02">
{formattedBalance.value}
</styled.span>
</BasicTooltip>
}
captionRight={balanceAsFiat}
/>
</Pressable>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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 (
<Pressable data-testid={dataTestId} onClick={onClick} my="space.02">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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));
Expand Down
4 changes: 2 additions & 2 deletions src/app/pages/rpc-sign-psbt/use-rpc-sign-psbt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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();

Expand Down
Loading

0 comments on commit d59662f

Please sign in to comment.