feat: refactor available balance

This commit is contained in:
alter-eggo
2023-05-03 14:14:23 +04:00
committed by Anastasios
parent 4d31163d9b
commit 155e80973c
24 changed files with 200 additions and 89 deletions

View File

@@ -17,7 +17,7 @@ export function useTrackSwitchAccount() {
if (!accountBalanceCache) return;
try {
const balances = parseBalanceResponse(accountBalanceCache as any);
const hasStxBalance = !!balances?.stx.availableStx.amount.isGreaterThan(0);
const hasStxBalance = !!balances?.stx.unlockedStx.amount.isGreaterThan(0);
void analytics.track('switch_account', { index, hasStxBalance });
} finally {
}

View File

@@ -0,0 +1,47 @@
import { useMemo } from 'react';
import { createMoney } from '@shared/models/money.model';
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 { createStacksCryptoCurrencyAssetTypeWrapper } from '@app/query/stacks/balance/stacks-ft-balances.utils';
import { useCurrentStacksAccountAnchoredBalances } from '@app/query/stacks/balance/stx-balance.hooks';
import { useStxOutboundValue } from './use-stx-outbound-value';
export function useStxBalance() {
const anchoredBalanceQuery = useCurrentStacksAccountAnchoredBalances();
const totalBalance = anchoredBalanceQuery.data?.stx.balance;
const unlockedStxBalance = anchoredBalanceQuery.data?.stx.unlockedStx;
const stxMarketData = useCryptoCurrencyMarketData('STX');
const stxOutboundQuery = useStxOutboundValue();
const stxEffectiveBalance = isDefined(totalBalance)
? subtractMoney(totalBalance, stxOutboundQuery.data)
: createMoney(0, 'STX');
const stxEffectiveUsdBalance = isDefined(totalBalance)
? i18nFormatCurrency(baseCurrencyAmountInQuote(stxEffectiveBalance, stxMarketData))
: undefined;
return useMemo(() => {
return {
anchoredBalanceQuery,
stxOutboundQuery,
isLoading: anchoredBalanceQuery.isLoading || stxOutboundQuery.isLoading,
availableBalance: isDefined(unlockedStxBalance)
? subtractMoney(unlockedStxBalance, stxOutboundQuery.data)
: createMoney(0, 'STX'),
stxEffectiveBalance: createStacksCryptoCurrencyAssetTypeWrapper(stxEffectiveBalance.amount),
stxEffectiveUsdBalance,
};
}, [
anchoredBalanceQuery,
stxOutboundQuery,
unlockedStxBalance,
stxEffectiveBalance,
stxEffectiveUsdBalance,
]);
}

View File

@@ -0,0 +1,19 @@
import { sumMoney } from '@app/common/money/calculate-money';
import { useCurrentAccountMempoolTransactionsBalance } from '@app/query/stacks/mempool/mempool.hooks';
import { useCurrentAccountMicroblockBalanceQuery } from '@app/query/stacks/microblock/microblock.query';
import { useCurrentAccountStxAddressState } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
export function useStxOutboundValue() {
const stxAddress = useCurrentAccountStxAddressState();
const pendingTxsBalance = useCurrentAccountMempoolTransactionsBalance();
const microblockBalanceQuery = useCurrentAccountMicroblockBalanceQuery(stxAddress);
if (!microblockBalanceQuery.data) {
return { ...microblockBalanceQuery, data: pendingTxsBalance };
}
return {
...microblockBalanceQuery,
data: sumMoney([pendingTxsBalance, microblockBalanceQuery.data]),
};
}

View File

@@ -1,22 +0,0 @@
import { useMemo } from 'react';
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 { useStacksAnchoredCryptoCurrencyAssetBalance } from '@app/query/stacks/balance/stacks-ft-balances.hooks';
export function useStxAssetBalance(address: string) {
const stxMarketData = useCryptoCurrencyMarketData('STX');
const { data: balance, isLoading } = useStacksAnchoredCryptoCurrencyAssetBalance(address);
return useMemo(
() => ({
isLoading,
stxAssetBalance: balance,
stxUsdBalance: balance
? i18nFormatCurrency(baseCurrencyAmountInQuote(balance.balance, stxMarketData))
: undefined,
}),
[balance, isLoading, stxMarketData]
);
}

View File

@@ -26,7 +26,7 @@ export function useTotalBalance({ btcAddress, stxAddress }: UseTotalBalanceArgs)
if (!balances || !btcBalance) return null;
// calculate total balance
const stxUsdAmount = baseCurrencyAmountInQuote(balances.stx.availableStx, stxMarketData);
const stxUsdAmount = baseCurrencyAmountInQuote(balances.stx.balance, stxMarketData);
const btcUsdAmount = baseCurrencyAmountInQuote(btcBalance.balance, btcMarketData);
const totalBalance = { ...stxUsdAmount, amount: stxUsdAmount.amount.plus(btcUsdAmount.amount) };

View File

@@ -3,26 +3,27 @@ import { useMemo } from 'react';
import type { AllTransferableCryptoAssetBalances } from '@shared/models/crypto-asset-balance.model';
import { useNativeSegwitBalance } from '@app/query/bitcoin/balance/bitcoin-balances.query';
import {
useStacksAnchoredCryptoCurrencyAssetBalance,
useTransferableStacksFungibleTokenAssetBalances,
} from '@app/query/stacks/balance/stacks-ft-balances.hooks';
import { useTransferableStacksFungibleTokenAssetBalances } from '@app/query/stacks/balance/stacks-ft-balances.hooks';
import { createStacksCryptoCurrencyAssetTypeWrapper } from '@app/query/stacks/balance/stacks-ft-balances.utils';
import { useCurrentBtcNativeSegwitAccountAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
import { useStxBalance } from './balance/stx/use-stx-balance';
export function useAllTransferableCryptoAssetBalances(): AllTransferableCryptoAssetBalances[] {
const account = useCurrentStacksAccount();
const currentBtcAddress = useCurrentBtcNativeSegwitAccountAddressIndexZero();
const btcCryptoCurrencyAssetBalance = useNativeSegwitBalance(currentBtcAddress);
const { data: stxCryptoCurrencyAssetBalance } = useStacksAnchoredCryptoCurrencyAssetBalance(
account?.address ?? ''
const { availableBalance: availableStxBalance } = useStxBalance();
const stxCryptoCurrencyAssetBalance = createStacksCryptoCurrencyAssetTypeWrapper(
availableStxBalance.amount
);
const stacksFtAssetBalances = useTransferableStacksFungibleTokenAssetBalances(
account?.address ?? ''
);
return useMemo(() => {
if (!stxCryptoCurrencyAssetBalance) return [];
return [btcCryptoCurrencyAssetBalance, stxCryptoCurrencyAssetBalance, ...stacksFtAssetBalances];
}, [btcCryptoCurrencyAssetBalance, stacksFtAssetBalances, stxCryptoCurrencyAssetBalance]);
}

View File

@@ -3,11 +3,18 @@ import BigNumber from 'bignumber.js';
import { MarketData, createMarketData, createMarketPair } from '@shared/models/market.model';
import { createMoney, createMoneyFromDecimal } from '@shared/models/money.model';
import { baseCurrencyAmountInQuote, convertAmountToFractionalUnit } from './calculate-money';
import {
baseCurrencyAmountInQuote,
convertAmountToFractionalUnit,
subtractMoney,
sumMoney,
} from './calculate-money';
const tenMicroStx = createMoney(10, 'STX');
const tenStx = createMoneyFromDecimal(10, 'STX');
const tenBtc = createMoneyFromDecimal(10, 'BTC');
const mockWrongMarketData = {
pair: createMarketPair('BTC' as any, 'USD'),
price: createMoneyFromDecimal(1, 'EUR' as any, 2),
@@ -40,3 +47,25 @@ describe(convertAmountToFractionalUnit.name, () => {
test('it converts 99 as decimal amount to a fractional unit', () =>
expect(convertAmountToFractionalUnit(new BigNumber(99), 6).toNumber()).toEqual(99000000));
});
describe(sumMoney.name, () => {
test('it sums two money objects', () => {
const result = sumMoney([tenMicroStx, tenMicroStx]);
expect(result.amount.toString()).toEqual('20');
expect(result.symbol).toEqual(tenMicroStx.symbol);
});
test('it throws error when summing different currencies', () => {
expect(() => sumMoney([tenMicroStx, tenBtc])).toThrowError();
});
});
describe(subtractMoney.name, () => {
test('it subtracts two money objects', () => {
const result = subtractMoney(tenMicroStx, tenMicroStx);
expect(result.amount.toString()).toEqual('0');
expect(result.symbol).toEqual(tenMicroStx.symbol);
});
test('it throws error when subtracting different currencies', () => {
expect(() => subtractMoney(tenMicroStx, tenBtc)).toThrowError();
});
});

View File

@@ -4,6 +4,7 @@ import { MarketData, formatMarketPair } from '@shared/models/market.model';
import { Money, createMoney } from '@shared/models/money.model';
import { isNumber } from '@shared/utils';
import { sumNumbers } from '../math/helpers';
import { formatMoney } from './format-money';
import { isMoney } from './is-money';
@@ -36,3 +37,16 @@ export function convertAmountToBaseUnit(num: Money | BigNumber, decimals?: numbe
if (!isNumber(decimals)) throw new Error('Must define decimal of given currency');
return num.shiftedBy(-decimals);
}
export function subtractMoney(xAmount: Money, yAmount: Money) {
if (xAmount.symbol !== yAmount.symbol) throw new Error('Cannot subtract different currencies');
return createMoney(xAmount.amount.minus(yAmount.amount), xAmount.symbol, xAmount.decimals);
}
export function sumMoney(moneysArr: Money[]) {
if (moneysArr.some(item => item.symbol !== moneysArr[0].symbol))
throw new Error('Cannot sum different currencies');
const sum = sumNumbers(moneysArr.map(item => item.amount.toNumber()));
return createMoney(sum, moneysArr[0].symbol, moneysArr[0].decimals);
}

View File

@@ -1,4 +1,4 @@
import { Money, createMoney } from '@shared/models/money.model';
import { Money } from '@shared/models/money.model';
const thinSpace = '';
@@ -27,8 +27,3 @@ export function i18nFormatCurrency(quantity: Money, locale = 'en-US') {
export function formatDustUsdAmounts(value: string) {
return value.endsWith('0.00') ? '<' + thinSpace + value.replace('0.00', '0.01') : value;
}
export function subtractMoney(xAmount: Money, yAmount: Money) {
if (xAmount.symbol !== yAmount.symbol) throw new Error('Cannot subtract different currencies');
return createMoney(xAmount.amount.minus(yAmount.amount), xAmount.symbol, xAmount.decimals);
}

View File

@@ -5,7 +5,6 @@ import { Money } from '@shared/models/money.model';
import { isNumber } from '@shared/utils';
import { countDecimals } from '@app/common/math/helpers';
import { subtractMoney } from '@app/common/money/format-money';
import {
btcToSat,
microStxToStx,
@@ -80,22 +79,19 @@ export function stxAmountValidator() {
.concat(stxAmountPrecisionValidator(formatPrecisionError()));
}
export function stxAvailableBalanceValidator(availableBalance: Money, pendingTxsBalance: Money) {
export function stxAvailableBalanceValidator(availableBalance: Money) {
return yup
.number()
.typeError(formatErrorWithSymbol('STX', FormErrorMessages.MustBeNumber))
.test({
message: formatInsufficientBalanceError(availableBalance, sum =>
microStxToStx(subtractMoney(sum, pendingTxsBalance).amount).toString()
microStxToStx(sum.amount).toString()
),
test(value: unknown) {
const fee = stxToMicroStx(this.parent.fee);
if (!availableBalance || !isNumber(value)) return false;
const availableBalanceLessAll = subtractMoney(
availableBalance,
pendingTxsBalance
).amount.minus(fee);
return availableBalanceLessAll.isGreaterThanOrEqualTo(stxToMicroStx(value));
const availableBalanceLessFee = availableBalance.amount.minus(fee);
return availableBalanceLessFee.isGreaterThanOrEqualTo(stxToMicroStx(value));
},
});
}

View File

@@ -14,7 +14,7 @@ export function StxBalance(props: BalanceProps) {
const balance = useMemo(
() =>
stacksValue({
value: balances?.stx?.availableStx.amount ?? 0,
value: balances?.stx?.unlockedStx.amount ?? 0,
withTicker: true,
}),
[balances]

View File

@@ -48,7 +48,7 @@ export const CryptoCurrencyAssetItemLayout = forwardRefWithAs(
const [component, bind] = usePressable(isPressable);
const amount = balance.decimals
? ftDecimals(balance.amount, balance.decimals || 0)
? ftDecimals(balance.amount, balance.decimals)
: balance.amount.toString();
const dataTestId = CryptoAssetSelectors.CryptoAssetListItem.replace(
'{symbol}',
@@ -80,7 +80,7 @@ export const CryptoCurrencyAssetItemLayout = forwardRefWithAs(
</SpaceBetween>
<SpaceBetween height="1.25rem" width="100%">
<Caption>{caption}</Caption>
{Number(amount) > 0 && address ? <Caption>{usdBalance}</Caption> : null}
{balance.amount.toNumber() > 0 && address ? <Caption>{usdBalance}</Caption> : null}
{isUnanchored && subBalance ? <SubBalance balance={subBalance} /> : null}
</SpaceBetween>
</Flag>

View File

@@ -1,8 +1,8 @@
import { Box, Stack, StackProps } from '@stacks/ui';
import { HomePageSelectorsLegacy } from '@tests-legacy/page-objects/home.selectors';
import { useBtcAssetBalance } from '@app/common/hooks/balance/use-btc-balance';
import { useStxAssetBalance } from '@app/common/hooks/balance/use-stx-balance';
import { useBtcAssetBalance } from '@app/common/hooks/balance/btc/use-btc-balance';
import { useStxBalance } from '@app/common/hooks/balance/stx/use-stx-balance';
import { CryptoCurrencyAssetItem } from '@app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item';
import { StxAvatar } from '@app/components/crypto-assets/stacks/components/stx-avatar';
import { BtcIcon } from '@app/components/icons/btc-icon';
@@ -23,11 +23,11 @@ export function BalancesList({ address, ...props }: BalancesListProps) {
const { data: stxUnachoredAssetBalance } = useStacksUnanchoredCryptoCurrencyAssetBalance(address);
const stacksFtAssetBalances = useStacksFungibleTokenAssetBalancesAnchoredWithMetadata(address);
const isBitcoinEnabled = useConfigBitcoinEnabled();
const { stxUsdBalance, stxAssetBalance } = useStxAssetBalance(address);
const { stxEffectiveBalance, stxEffectiveUsdBalance } = useStxBalance();
const { btcAddress, btcAssetBalance, btcUsdBalance } = useBtcAssetBalance();
// Better handle loading state
if (!stxAssetBalance || !stxUnachoredAssetBalance) return <LoadingSpinner />;
if (!stxUnachoredAssetBalance) return <LoadingSpinner />;
return (
<Stack
@@ -46,9 +46,8 @@ export function BalancesList({ address, ...props }: BalancesListProps) {
)}
<CryptoCurrencyAssetItem
assetBalance={stxAssetBalance}
usdBalance={stxUsdBalance}
assetSubBalance={stxUnachoredAssetBalance}
assetBalance={stxEffectiveBalance}
usdBalance={stxEffectiveUsdBalance}
address={address}
icon={<StxAvatar {...props} />}
/>

View File

@@ -81,17 +81,17 @@ export function IncreaseFeeForm() {
validateOnChange={false}
validateOnBlur={false}
validateOnMount={false}
validationSchema={yup.object({ fee: stxFeeValidator(balances?.stx.availableStx) })}
validationSchema={yup.object({ fee: stxFeeValidator(balances?.stx.unlockedStx) })}
>
{() => (
<Stack spacing="extra-loose">
{tx && <StacksTransactionItem position="relative" transaction={tx} zIndex={99} />}
<Stack spacing="base">
<IncreaseFeeField currentFee={fee} />
{balances?.stx.availableStx.amount && (
{balances?.stx.unlockedStx.amount && (
<Caption>
Balance:{' '}
{stacksValue({ value: balances?.stx.availableStx.amount, fixedDecimals: true })}
{stacksValue({ value: balances?.stx.unlockedStx.amount, fixedDecimals: true })}
</Caption>
)}
</Stack>

View File

@@ -105,7 +105,7 @@ const ChooseAccountItem = memo((props: ChooseAccountItemProps) => {
<AccountBalanceLoading />
) : balances ? (
<AccountBalanceCaption
availableBalance={balances.stx.availableStx}
availableBalance={balances.stx.unlockedStx}
marketData={stxMarketData}
/>
) : null

View File

@@ -1,4 +1,6 @@
import { Box, color } from '@stacks/ui';
import { FiInfo } from 'react-icons/fi';
import { Box, Flex, Stack, Tooltip, color } from '@stacks/ui';
import { Money } from '@shared/models/money.model';
@@ -11,7 +13,8 @@ import { PreviewButton } from './preview-button';
export function FormFooter(props: { balance: Money }) {
const { balance } = props;
const balanceTooltipLabel =
'Amount that is immediately available for use after taking into account any pending transactions or holds placed on your account by the protocol.';
return (
<Box
bg={color('bg')}
@@ -28,7 +31,20 @@ export function FormFooter(props: { balance: Money }) {
<Box mt="loose" px="loose">
<PreviewButton />
<SpaceBetween>
<Caption>Balance</Caption>
<Flex alignItems="center">
<Caption mr="tight">Available balance</Caption>
<Tooltip placement="top" label={balanceTooltipLabel}>
<Stack>
<Box
_hover={{ cursor: 'pointer' }}
as={FiInfo}
color={color('text-caption')}
size="14px"
/>
</Stack>
</Tooltip>
</Flex>
<Caption>{formatMoney(balance)}</Caption>
</SpaceBetween>
</Box>

View File

@@ -6,8 +6,8 @@ import * as yup from 'yup';
import { STX_DECIMALS } from '@shared/constants';
import { logger } from '@shared/logger';
import { StacksSendFormValues } from '@shared/models/form.model';
import { createMoney } from '@shared/models/money.model';
import { useStxBalance } from '@app/common/hooks/balance/stx/use-stx-balance';
import { convertAmountToBaseUnit } from '@app/common/money/calculate-money';
import { useWalletType } from '@app/common/use-wallet-type';
import {
@@ -17,9 +17,7 @@ import {
import { stxFeeValidator } from '@app/common/validation/forms/fee-validators';
import { useLedgerNavigate } from '@app/features/ledger/hooks/use-ledger-navigate';
import { useUpdatePersistedSendFormValues } from '@app/features/popup-send-form-restoration/use-update-persisted-send-form-values';
import { useCurrentStacksAccountAnchoredBalances } from '@app/query/stacks/balance/stx-balance.hooks';
import { useCalculateStacksTxFees } from '@app/query/stacks/fees/fees.hooks';
import { useCurrentAccountMempoolTransactionsBalance } from '@app/query/stacks/mempool/mempool.hooks';
import {
useGenerateStxTokenTransferUnsignedTx,
useStxTokenTransferUnsignedTxState,
@@ -29,29 +27,25 @@ import { useSendFormNavigate } from '../../hooks/use-send-form-navigate';
import { useStacksCommonSendForm } from '../stacks/use-stacks-common-send-form';
export function useStxSendForm() {
const { data: balances } = useCurrentStacksAccountAnchoredBalances();
const unsignedTx = useStxTokenTransferUnsignedTxState();
const { data: stxFees } = useCalculateStacksTxFees(unsignedTx);
const generateTx = useGenerateStxTokenTransferUnsignedTx();
const pendingTxsBalance = useCurrentAccountMempoolTransactionsBalance();
const { onFormStateChange } = useUpdatePersistedSendFormValues();
const { whenWallet } = useWalletType();
const ledgerNavigate = useLedgerNavigate();
const sendFormNavigate = useSendFormNavigate();
const availableStxBalance = balances?.stx.availableStx ?? createMoney(0, 'STX');
const { availableBalance: availableStxBalance } = useStxBalance();
const sendMaxBalance = useMemo(
() =>
convertAmountToBaseUnit(
availableStxBalance.amount
.minus(pendingTxsBalance.amount)
.minus(stxFees?.estimates[1].fee.amount || 0),
availableStxBalance.amount.minus(stxFees?.estimates[1].fee.amount || 0),
STX_DECIMALS
),
[availableStxBalance, pendingTxsBalance, stxFees]
[availableStxBalance, stxFees]
);
const { initialValues, checkFormValidation, recipient, memo, nonce } = useStacksCommonSendForm({
@@ -67,9 +61,7 @@ export function useStxSendForm() {
stxFees,
validationSchema: yup.object({
amount: stxAmountValidator().concat(
stxAvailableBalanceValidator(availableStxBalance, pendingTxsBalance)
),
amount: stxAmountValidator().concat(stxAvailableBalanceValidator(availableStxBalance)),
fee: stxFeeValidator(availableStxBalance),
recipient,
memo,

View File

@@ -73,7 +73,7 @@ export const StxTransferInsufficientFundsErrorMessage = memo(props => {
<Caption>
{balance
? stacksValue({
value: balance.stx.availableStx.amount,
value: balance.stx.unlockedStx.amount,
withTicker: true,
})
: '--'}

View File

@@ -34,13 +34,13 @@ export function useTransactionError() {
}
if (balances) {
const zeroBalance = balances?.stx.availableStx.amount.toNumber() === 0;
const zeroBalance = balances?.stx.unlockedStx.amount.toNumber() === 0;
if (transactionRequest.txType === TransactionTypes.STXTransfer) {
if (zeroBalance) return TransactionErrorReason.StxTransferInsufficientFunds;
const transferAmount = new BigNumber(transactionRequest.amount);
if (transferAmount.gte(balances?.stx.availableStx.amount))
if (transferAmount.gte(balances?.stx.unlockedStx.amount))
return TransactionErrorReason.StxTransferInsufficientFunds;
}
@@ -49,7 +49,7 @@ export function useTransactionError() {
if (transactionRequest.fee) {
const feeValue = microStxToStx(transactionRequest.fee);
if (feeValue.gte(balances?.stx.availableStx.amount))
if (feeValue.gte(balances?.stx.unlockedStx.amount))
return TransactionErrorReason.FeeInsufficientFunds;
}
}

View File

@@ -93,7 +93,7 @@ function TransactionRequestBase() {
const validationSchema = !transactionRequest.sponsored
? yup.object({
fee: stxFeeValidator(stacksBalances?.stx.availableStx),
fee: stxFeeValidator(stacksBalances?.stx.unlockedStx),
nonce: nonceValidator,
})
: null;

View File

@@ -24,17 +24,13 @@ import {
export function useStacksAnchoredCryptoCurrencyAssetBalance(address: string) {
return useAnchoredStacksAccountBalanceQuery(address, {
select: resp =>
createStacksCryptoCurrencyAssetTypeWrapper(
parseBalanceResponse(resp).stx.availableStx.amount
),
createStacksCryptoCurrencyAssetTypeWrapper(parseBalanceResponse(resp).stx.unlockedStx.amount),
});
}
export function useStacksUnanchoredCryptoCurrencyAssetBalance(address: string) {
return useUnanchoredStacksAccountBalanceQuery(address, {
select: resp =>
createStacksCryptoCurrencyAssetTypeWrapper(
parseBalanceResponse(resp).stx.availableStx.amount
),
createStacksCryptoCurrencyAssetTypeWrapper(parseBalanceResponse(resp).stx.unlockedStx.amount),
});
}

View File

@@ -23,10 +23,11 @@ export function parseBalanceResponse(balances: AddressBalanceResponse) {
])
) as Record<AccountBalanceStxKeys, Money>;
const stx: AccountStxBalanceBigNumber & { availableStx: Money } = {
const stx: AccountStxBalanceBigNumber & { unlockedStx: Money } = {
...balances.stx,
...stxMoney,
availableStx: createMoney(stxMoney.balance.amount.minus(stxMoney.locked.amount), 'STX'),
balance: createMoney(stxMoney.balance.amount, 'STX'),
unlockedStx: createMoney(stxMoney.balance.amount.minus(stxMoney.locked.amount), 'STX'),
};
return { ...balances, stx };
}

View File

@@ -0,0 +1,28 @@
import { TokenTransferTransaction } from '@stacks/stacks-blockchain-api-types';
import { useQuery } from '@tanstack/react-query';
import { createMoney } from '@shared/models/money.model';
import { sumNumbers } from '@app/common/math/helpers';
import { useStacksClientUnanchored } from '@app/store/common/api-clients.hooks';
export function useCurrentAccountMicroblockBalanceQuery(address: string) {
const client = useStacksClientUnanchored();
async function accountMicroblockFetcher() {
const txs = await client.microblocksApi.getUnanchoredTxs({});
return txs.results as TokenTransferTransaction[];
}
return useQuery({
enabled: !!address,
queryKey: ['account-microblock', address],
queryFn: accountMicroblockFetcher,
select: resp => {
const senderMicroblockTxs = resp.filter(tx => tx.sender_address === address);
return createMoney(
sumNumbers(senderMicroblockTxs.map(tx => Number(tx.token_transfer.amount))),
'STX'
);
},
});
}