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 10, 2024
1 parent d927ec0 commit 52f1a7a
Show file tree
Hide file tree
Showing 27 changed files with 224 additions and 120 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
96 changes: 96 additions & 0 deletions src/app/common/money/fiat-conversion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import type { CryptoCurrencies } from '@shared/models/currencies.model';
import { createMarketData, createMarketPair } from '@shared/models/market.model';
import { type Money, createMoney } from '@shared/models/money.model';
import { isUndefined } from '@shared/utils';

import { baseCurrencyAmountInQuote } from './calculate-money';
import { unitToFractionalUnit } from './unit-conversion';

export function checkIsMoneyAmountValidToDisplay(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);
}

export function convertCryptoCurrencyValueToFiat(balance: Money, price: Money, value: string) {
if (isUndefined(balance) || isUndefined(price) || isUndefined(value)) return;

const cryptoCurrencyMarketData = createMarketData(createMarketPair(balance.symbol, 'USD'), price);

const valueAsMoney = createMoney(
unitToFractionalUnit(balance.decimals)(value),
balance.symbol,
balance.decimals
);
return baseCurrencyAmountInQuote(valueAsMoney, cryptoCurrencyMarketData);
}

// Used in swaps for amount input fiat conversion while typing
// export function useFormattedFractionalUnitAsFiat(balance?: Money, price?: Money, value?: string) {
// const convertCurrencyToUsd = useConvertCryptoCurrencyToUsdAmount(
// balance?.symbol ?? '',
// price ?? createMoney(0, 'USD')
// );

// if (isUndefined(balance) || isUndefined(price) || isUndefined(value)) return;

// const convertedAmountAsMoney = convertCurrencyToUsd(
// createMoney(unitToFractionalUnit(balance.decimals)(value), balance.symbol, balance.decimals)
// );

// if (convertedAmountAsMoney.amount.isNaN()) return;
// return i18nFormatCurrency(convertedAmountAsMoney);
// }

// export function convertCryptoCurrencyToFractionalUnitAsFiat(
// balance: Money,
// price: Money,
// value: string
// ) {
// const valueAsMoney = createMoney(
// unitToFractionalUnit(balance.decimals)(value),
// balance.symbol,
// balance.decimals
// );
// const convertCurrencyToFiatAmount = convertCryptoCurrencyMoneyToFiat(
// balance.symbol,
// price,
// valueAsMoney
// );

// return convertCurrencyToFiatAmount;
// }

// Used in asset list and swaps to show ft fiat balances
// export function useFormattedBaseUnitAsFiat(balance: Money, price?: Money | null) {
// const convertCurrencyToUsd = useConvertCryptoCurrencyToUsdAmount(
// balance.symbol,
// price ?? createMoney(0, 'USD')
// );

// if (isUndefined(balance) || isUndefined(price)) return;

// const convertedBalanceAsMoney = convertCurrencyToUsd(
// createMoney(balance.amount, balance.symbol, balance.decimals)
// );

// if (convertedBalanceAsMoney.amount.isNaN() || convertedBalanceAsMoney.amount.isEqualTo(0)) return;
// return i18nFormatCurrency(convertedBalanceAsMoney);
// }

// export function convertCryptoCurrencyBalanceToFiatAmount(balance: Money, price: Money) {
// const convertCurrencyToFiatAmount = convertCryptoCurrencyMoneyToFiat(
// balance.symbol,
// price,
// valueAsMoney
// );

// return convertCurrencyToFiatAmount;
// }
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,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 @@ -22,7 +22,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 {
checkIsMoneyAmountValidToDisplay,
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 = checkIsMoneyAmountValidToDisplay(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 {
checkIsMoneyAmountValidToDisplay,
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 && checkIsMoneyAmountValidToDisplay(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
Loading

0 comments on commit 52f1a7a

Please sign in to comment.