Skip to content

Commit

Permalink
feat: stacks ft fiat values from alex-sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
fbwoolf committed Apr 1, 2024
1 parent f32151d commit 2880d0a
Show file tree
Hide file tree
Showing 17 changed files with 116 additions and 86 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Money, createMoney } from '@shared/models/money.model';
import { type Money, createMoney } from '@shared/models/money.model';
import { isUndefined } from '@shared/utils';

import { useConvertAlexSdkCurrencyToFiatAmount } from '@app/common/hooks/use-convert-to-fiat-amount';
Expand All @@ -7,7 +7,6 @@ import { unitToFractionalUnit } from '@app/common/money/unit-conversion';

export function useAlexSdkAmountAsFiat(balance?: Money, price?: Money, value?: string) {
const convertAlexSdkCurrencyToUsd = useConvertAlexSdkCurrencyToFiatAmount(
// @ts-expect-error TODO: balance?.symbol should be of a Cryptocurrency type.
balance?.symbol ?? '',
price ?? createMoney(0, 'USD')
);
Expand All @@ -23,7 +22,6 @@ export function useAlexSdkAmountAsFiat(balance?: Money, price?: Money, value?: s

export function useAlexSdkBalanceAsFiat(balance?: Money, price?: Money) {
const convertAlexSdkCurrencyToUsd = useConvertAlexSdkCurrencyToFiatAmount(
// @ts-expect-error TODO: balance?.symbol should be of a Cryptocurrency type.
balance?.symbol ?? '',
price ?? createMoney(0, 'USD')
);
Expand All @@ -34,5 +32,7 @@ export function useAlexSdkBalanceAsFiat(balance?: Money, price?: Money) {
createMoney(balance.amount, balance.symbol, balance.decimals)
);

return convertedBalanceAsMoney.amount.isNaN() ? '' : i18nFormatCurrency(convertedBalanceAsMoney);
return convertedBalanceAsMoney.amount.isNaN() || convertedBalanceAsMoney.amount.isEqualTo(0)
? ''
: i18nFormatCurrency(convertedBalanceAsMoney);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { styled } from 'leather-styles/jsx';

import { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asset-balance.model';

import { useAlexSdkBalanceAsFiat } from '@app/common/hooks/use-alex-sdk';
import { StacksAssetAvatar } from '@app/components/crypto-assets/stacks/components/stacks-asset-avatar';
import { ItemLayout } from '@app/ui/components/item-layout/item-layout';
import { BasicTooltip } from '@app/ui/components/tooltip/basic-tooltip';
Expand All @@ -17,6 +18,7 @@ export function StacksFungibleTokenAssetItemLayout({
assetBalance,
onClick,
}: StacksFungibleTokenAssetItemLayoutProps) {
const balanceAsFiat = useAlexSdkBalanceAsFiat(assetBalance.balance, assetBalance.asset.price);
const { amount, avatar, caption, dataTestId, formattedBalance, imageCanonicalUri, title } =
parseStacksFungibleTokenAssetBalance(assetBalance);

Expand Down Expand Up @@ -45,6 +47,7 @@ export function StacksFungibleTokenAssetItemLayout({
</styled.span>
</BasicTooltip>
}
captionRight={balanceAsFiat}
/>
</Pressable>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ interface StacksBalanceListItemProps {
address: string;
}
export function StacksBalanceListItem({ address }: StacksBalanceListItemProps) {
const balaceDetails = useStxBalance();
return <StacksBalanceListItemLayout address={address} {...balaceDetails} />;
const balanceDetails = useStxBalance();
return <StacksBalanceListItemLayout address={address} {...balanceDetails} />;
}
25 changes: 7 additions & 18 deletions src/app/pages/fund/components/fund.layout.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,16 @@
import { Stack, styled } from 'leather-styles/jsx';

import type { Blockchains } from '@shared/models/blockchain.model';
import { CryptoCurrencies } from '@shared/models/currencies.model';

import { HasChildren } from '@app/common/has-children';

const nameMap: Record<CryptoCurrencies, { name: string; symbol: string }> = {
BTC: {
name: 'Bitcoin',
symbol: 'BTC',
},
STX: {
name: 'Stacks',
symbol: 'STX',
},
};
import { capitalize } from '@app/common/utils';

interface FundLayoutProps extends HasChildren {
blockchain: Blockchains;
symbol: CryptoCurrencies;
}

export function FundLayout({ symbol, children }: FundLayoutProps) {
const name = nameMap[symbol].name;
const nameAbbr = nameMap[symbol].symbol;
export function FundLayout({ blockchain, symbol, children }: FundLayoutProps) {
return (
<Stack
alignItems={{ base: 'left', md: 'center' }}
Expand All @@ -44,9 +33,9 @@ export function FundLayout({ symbol, children }: FundLayoutProps) {
maxWidth="544px"
textAlign={{ base: 'left', md: 'center' }}
>
Choose an exchange to fund your account with {name} ({nameAbbr}) or deposit from elsewhere.
Exchanges with “Fast checkout” make it easier to purchase {nameAbbr} for direct deposit into
your wallet with a credit card.
Choose an exchange to fund your account with {capitalize(blockchain)} ({symbol}) or deposit
from elsewhere. Exchanges with “Fast checkout” make it easier to purchase {symbol} for
direct deposit into your wallet with a credit card.
</styled.span>
{children}
</Stack>
Expand Down
45 changes: 16 additions & 29 deletions src/app/pages/fund/fund.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { Outlet, useParams } from 'react-router-dom';

import { isCryptoCurrency } from '@shared/models/currencies.model';

import { useBtcCryptoCurrencyAssetBalance } from '@app/common/hooks/balance/btc/use-btc-crypto-currency-asset-balance';
import { useStxCryptoCurrencyAssetBalance } from '@app/common/hooks/balance/stx/use-stx-crypto-currency-asset-balance';
import { FullPageLoadingSpinner } from '@app/components/loading-spinner';
Expand All @@ -18,37 +16,26 @@ export function FundPage() {
const stxCryptoCurrencyAssetBalance = useStxCryptoCurrencyAssetBalance();
const { currency } = useParams();

function getSymbol() {
if (isCryptoCurrency(currency)) {
return currency;
}
return 'STX';
}
function getAddress() {
switch (symbol) {
case 'BTC':
return bitcoinSigner?.address;
case 'STX':
return currentStxAccount?.address;
}
}
function getBalance() {
switch (symbol) {
case 'BTC':
return btcCryptoCurrencyAssetBalance;
case 'STX':
return stxCryptoCurrencyAssetBalance;
}
}

const symbol = getSymbol();
const address = getAddress();
const balance = getBalance();
const { address, balance, blockchain, symbol } =
currency === 'BTC'
? {
address: bitcoinSigner?.address,
balance: btcCryptoCurrencyAssetBalance,
blockchain: 'Bitcoin',
symbol: currency,
}
: {
address: currentStxAccount?.address,
balance: stxCryptoCurrencyAssetBalance,
blockchain: 'Stacks',
symbol: 'STX',
};

if (!address || !balance) return <FullPageLoadingSpinner />;

return (
<>
<FundLayout symbol={symbol}>
<FundLayout blockchain={blockchain} symbol={symbol}>
<FiatProvidersList address={address} symbol={symbol} />
</FundLayout>
<Outlet />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { SwapSelectors } from '@tests/selectors/swap.selectors';

import { useAlexSdkBalanceAsFiat } from '@app/common/hooks/use-alex-sdk';
import { formatMoneyWithoutSymbol } from '@app/common/money/format-money';
import type { SwapAsset } from '@app/pages/swap/hooks/use-swap-form';
import { useGetFungibleTokenMetadataQuery } from '@app/query/stacks/tokens/fungible-tokens/fungible-token-metadata.query';
import { isFtAsset } from '@app/query/stacks/tokens/token-metadata.utils';
import { Avatar, defaultFallbackDelay, getAvatarFallback } from '@app/ui/components/avatar/avatar';
import { ItemLayout } from '@app/ui/components/item-layout/item-layout';
import { Pressable } from '@app/ui/pressable/pressable';

import { useAlexSdkBalanceAsFiat } from '../../../hooks/use-alex-sdk-fiat-price';
import { SwapAsset } from '../../../hooks/use-swap-form';

interface SwapAssetItemProps {
asset: SwapAsset;
onClick(): void;
Expand Down
2 changes: 1 addition & 1 deletion src/app/pages/swap/components/swap-selected-asset-from.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { createMoney } from '@shared/models/money.model';
import { isUndefined } from '@shared/utils';

import { useShowFieldError } from '@app/common/form-utils';
import { useAlexSdkAmountAsFiat } from '@app/common/hooks/use-alex-sdk';
import { convertAmountToFractionalUnit } from '@app/common/money/calculate-money';
import { formatMoneyWithoutSymbol } from '@app/common/money/format-money';

import { useAlexSdkAmountAsFiat } from '../hooks/use-alex-sdk-fiat-price';
import { SwapFormValues } from '../hooks/use-swap-form';
import { useSwapContext } from '../swap.context';
import { SwapAmountField } from './swap-amount-field';
Expand Down
2 changes: 1 addition & 1 deletion src/app/pages/swap/components/swap-selected-asset-to.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useField } from 'formik';

import { useAlexSdkAmountAsFiat } from '@app/common/hooks/use-alex-sdk';
import { formatMoneyWithoutSymbol } from '@app/common/money/format-money';
import { LoadingSpinner } from '@app/components/loading-spinner';

import { useAlexSdkAmountAsFiat } from '../hooks/use-alex-sdk-fiat-price';
import { useSwapContext } from '../swap.context';
import { SwapAmountField } from './swap-amount-field';
import { SwapSelectedAssetLayout } from './swap-selected-asset.layout';
Expand Down
8 changes: 4 additions & 4 deletions src/app/pages/swap/hooks/use-alex-swap.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useCallback, useState } from 'react';
import { useAsync } from 'react-async-hook';

import { AlexSDK, Currency, TokenInfo } from 'alex-sdk';
import BigNumber from 'bignumber.js';
Expand All @@ -10,7 +9,8 @@ import { createMoney } from '@shared/models/money.model';
import { useStxBalance } from '@app/common/hooks/balance/stx/use-stx-balance';
import { convertAmountToFractionalUnit } from '@app/common/money/calculate-money';
import { pullContractIdFromIdentity } from '@app/common/utils';
import { useSwappableCurrencyQuery } from '@app/query/common/alex-swaps/swappable-currency.query';
import { useAlexSdkLatestPricesQuery } from '@app/query/common/alex-sdk/latest-prices.query';
import { useAlexSdkSwappableCurrencyQuery } from '@app/query/common/alex-sdk/swappable-currency.query';
import { useTransferableStacksFungibleTokenAssetBalances } from '@app/query/stacks/balance/stacks-ft-balances.hooks';
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';

Expand All @@ -24,8 +24,8 @@ export function useAlexSwap() {
const [swapSubmissionData, setSwapSubmissionData] = useState<SwapSubmissionData>();
const [slippage, _setSlippage] = useState(0.04);
const [isFetchingExchangeRate, setIsFetchingExchangeRate] = useState(false);
const { data: supportedCurrencies = [] } = useSwappableCurrencyQuery(alexSDK);
const { result: prices } = useAsync(async () => await alexSDK.getLatestPrices(), [alexSDK]);
const { data: supportedCurrencies = [] } = useAlexSdkSwappableCurrencyQuery(alexSDK);
const { data: prices } = useAlexSdkLatestPricesQuery(alexSDK);
const { availableBalance: availableStxBalance } = useStxBalance();
const account = useCurrentStacksAccount();
const stacksFtAssetBalances = useTransferableStacksFungibleTokenAssetBalances(
Expand Down
36 changes: 36 additions & 0 deletions src/app/query/common/alex-sdk/alex-sdk.hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useCallback, useState } from 'react';

import { AlexSDK, type Currency } from 'alex-sdk';
import BigNumber from 'bignumber.js';

import { logger } from '@shared/logger';
import { createMoney } from '@shared/models/money.model';
import { isDefined } from '@shared/utils';

import { convertAmountToFractionalUnit } from '@app/common/money/calculate-money';
import { pullContractIdFromIdentity } from '@app/common/utils';

import { useAlexSdkLatestPricesQuery } from './latest-prices.query';
import { useAlexSdkSwappableCurrencyQuery } from './swappable-currency.query';

export function useAlexSdKCurrencyPriceAsMoney() {
const alexSDK = useState(() => new AlexSDK())[0];
const { data: supportedCurrencies = [] } = useAlexSdkSwappableCurrencyQuery(alexSDK);
const { data: prices } = useAlexSdkLatestPricesQuery(alexSDK);

return useCallback(
(principal: string) => {
if (!prices) {
logger.error('Latest prices could not be found');
return createMoney(0, 'USD');
}
const tokenInfo = supportedCurrencies
.filter(isDefined)
.find(token => pullContractIdFromIdentity(token.contractAddress) === principal);
const currency = tokenInfo?.id as Currency;
const price = convertAmountToFractionalUnit(new BigNumber(prices[currency] ?? 0), 2);
return createMoney(price, 'USD');
},
[prices, supportedCurrencies]
);
}
12 changes: 12 additions & 0 deletions src/app/query/common/alex-sdk/latest-prices.query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useQuery } from '@tanstack/react-query';
import { AlexSDK } from 'alex-sdk';

export function useAlexSdkLatestPricesQuery(alexSDK: AlexSDK) {
return useQuery(['alex-sdk-latest-prices'], async () => alexSDK.getLatestPrices(), {
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
retryDelay: 1000 * 60,
staleTime: 1000 * 60 * 10,
});
}
12 changes: 12 additions & 0 deletions src/app/query/common/alex-sdk/swappable-currency.query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useQuery } from '@tanstack/react-query';
import { AlexSDK } from 'alex-sdk';

export function useAlexSdkSwappableCurrencyQuery(alexSDK: AlexSDK) {
return useQuery(['alex-sdk-swappable-currencies'], async () => alexSDK.fetchSwappableCurrency(), {
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
retryDelay: 1000 * 60,
staleTime: 1000 * 60 * 10,
});
}
16 changes: 0 additions & 16 deletions src/app/query/common/alex-swaps/swappable-currency.query.ts

This file was deleted.

7 changes: 6 additions & 1 deletion src/app/query/stacks/balance/stacks-ft-balances.hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asse

import { formatContractId } from '@app/common/utils';
import { useToast } from '@app/features/toasts/use-toast';
import { useAlexSdKCurrencyPriceAsMoney } from '@app/query/common/alex-sdk/alex-sdk.hooks';
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';

import { useGetFungibleTokenMetadataListQuery } from '../tokens/fungible-tokens/fungible-token-metadata.query';
Expand Down Expand Up @@ -35,6 +36,7 @@ function useStacksFungibleTokenAssetBalances(address: string) {

export function useStacksFungibleTokenAssetBalancesWithMetadata(address: string) {
const { data: initializedAssetBalances = [] } = useStacksFungibleTokenAssetBalances(address);
const priceAsMoney = useAlexSdKCurrencyPriceAsMoney();

const ftAssetsMetadata = useGetFungibleTokenMetadataListQuery(
initializedAssetBalances.map(assetBalance =>
Expand All @@ -49,7 +51,10 @@ export function useStacksFungibleTokenAssetBalancesWithMetadata(address: string)
if (!(metadata && isFtAsset(metadata))) return assetBalance;
return addQueriedMetadataToInitializedStacksFungibleTokenAssetBalance(
assetBalance,
metadata
metadata,
priceAsMoney(
formatContractId(assetBalance.asset.contractAddress, assetBalance.asset.contractName)
)
);
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
7 changes: 5 additions & 2 deletions src/app/query/stacks/balance/stacks-ft-balances.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type {
StacksCryptoCurrencyAssetBalance,
StacksFungibleTokenAssetBalance,
} from '@shared/models/crypto-asset-balance.model';
import { createMoney } from '@shared/models/money.model';
import { type Money, createMoney } from '@shared/models/money.model';

import { isTransferableStacksFungibleTokenAsset } from '@app/common/crypto-assets/stacks-crypto-asset.utils';
import { getAssetStringParts } from '@app/ui/utils/get-asset-string-parts';
Expand Down Expand Up @@ -47,6 +47,7 @@ export function createStacksFtCryptoAssetBalanceTypeWrapper(
hasMemo: false,
imageCanonicalUri: '',
name: '',
price: createMoney(0, 'USD'),
symbol: '',
},
};
Expand All @@ -68,7 +69,8 @@ export function convertFtBalancesToStacksFungibleTokenAssetBalanceType(

export function addQueriedMetadataToInitializedStacksFungibleTokenAssetBalance(
assetBalance: StacksFungibleTokenAssetBalance,
metadata: FtMetadataResponse
metadata: FtMetadataResponse,
price: Money
) {
return {
...assetBalance,
Expand All @@ -84,6 +86,7 @@ export function addQueriedMetadataToInitializedStacksFungibleTokenAssetBalance(
hasMemo: isTransferableStacksFungibleTokenAsset(assetBalance.asset),
imageCanonicalUri: metadata.image_canonical_uri ?? '',
name: metadata.name ?? '',
price,
symbol: metadata.symbol ?? '',
},
};
Expand Down
3 changes: 3 additions & 0 deletions src/shared/models/crypto-asset.model.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { Money } from './money.model';

export interface BitcoinCryptoCurrencyAsset {
decimals: number;
hasMemo: boolean;
Expand All @@ -22,6 +24,7 @@ export interface StacksFungibleTokenAsset {
hasMemo: boolean;
imageCanonicalUri: string;
name: string;
price: Money;
symbol: string;
}

Expand Down
Loading

0 comments on commit 2880d0a

Please sign in to comment.