Skip to content

Commit

Permalink
refactor: price as market data
Browse files Browse the repository at this point in the history
  • Loading branch information
fbwoolf committed Apr 18, 2024
1 parent 90ace9d commit 2c3a093
Show file tree
Hide file tree
Showing 31 changed files with 263 additions and 290 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { StacksFungibleTokenAsset } from '@shared/models/crypto-asset.model';
import { createMoney } from '@shared/models/money.model';

import {
isFtNameLikeStx,
Expand Down Expand Up @@ -32,7 +31,7 @@ describe(isTransferableStacksFungibleTokenAsset.name, () => {
canTransfer: true,
hasMemo: true,
imageCanonicalUri: '',
price: createMoney(0, 'USD'),
price: null,
symbol: 'CAT',
};
expect(isTransferableStacksFungibleTokenAsset(asset)).toBeTruthy();
Expand All @@ -49,7 +48,7 @@ describe(isTransferableStacksFungibleTokenAsset.name, () => {
canTransfer: true,
hasMemo: true,
imageCanonicalUri: '',
price: createMoney(0, 'USD'),
price: null,
symbol: 'CAT',
};
expect(isTransferableStacksFungibleTokenAsset(asset)).toBeTruthy();
Expand Down
2 changes: 1 addition & 1 deletion src/app/common/money/calculate-money.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { isNumber } from '@shared/utils';

import { initBigNumber, sumNumbers } from '../math/helpers';
import { formatMoney } from './format-money';
import { isMoney } from './is-money';
import { isMoney } from './money.utils';

export function baseCurrencyAmountInQuote(quantity: Money, { pair, price }: MarketData) {
if (quantity.symbol.toLowerCase() !== pair.base.toLowerCase())
Expand Down
18 changes: 0 additions & 18 deletions src/app/common/money/fiat-conversion.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import BigNumber from 'bignumber.js';

import { Money } from '@shared/models/money.model';
import { isObject } from '@shared/utils';

export function isMoney(val: unknown): val is Money {
if (!isObject(val)) return false;
return 'amount' in val && 'symbol' in val && 'decimals' in val;
}

export function isMoneyGreaterThanZero(money: Money) {
if (!BigNumber.isBigNumber(money.amount)) return;
return !(money.amount.isNaN() || money.amount.isZero());
}
29 changes: 29 additions & 0 deletions src/app/common/utils/asset-order.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { Money } from '@shared/models/money.model';

export function sortAssetsByName<T extends { name: string }[]>(assets: T) {
return assets
.sort((a, b) => {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;

return 0;
})
.sort((a, b) => {
if (a.name === 'STX') return -1;
if (b.name !== 'STX') return 1;

return 0;
})
.sort((a, b) => {
if (a.name === 'BTC') return -1;
if (b.name !== 'BTC') return 1;

return 0;
});
}

export function migratePositiveAssetBalancesToTop<T extends { balance: Money }[]>(assets: T) {
const assetsWithPositiveBalance = assets.filter(asset => asset.balance.amount.isGreaterThan(0));
const assetsWithZeroBalance = assets.filter(asset => asset.balance.amount.isEqualTo(0));
return [...assetsWithPositiveBalance, ...assetsWithZeroBalance] as T;
}
23 changes: 0 additions & 23 deletions src/app/common/utils/sort-assets-by-symbol.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export function useBitcoinCustomFeeMultipleRecipients({
}: UseBitcoinCustomFeeArgsMultipleRecipients) {
const { balance } = useCurrentNativeSegwitAddressBalance();
const { data: utxos = [] } = useCurrentNativeSegwitUtxos();
const btcMarketData = useCryptoCurrencyMarketData('BTC');
const btcMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC');

return useCallback(
(feeRate: number) => {
Expand Down
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 { 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 @@ -41,7 +41,7 @@ export function useBitcoinFeesListMultipleRecipients({
recipients,
utxos,
}: UseBitcoinFeesListArgs) {
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,11 +3,9 @@ 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 { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money';
import { i18nFormatCurrency } from '@app/common/money/format-money';
import { isMoneyGreaterThanZero } from '@app/common/money/money.utils';
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 @@ -22,12 +20,9 @@ export function Brc20TokenAssetItemLayout({ onClick, token }: Brc20TokenAssetIte
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 showFiatBalance = isMoneyGreaterThanZero(token.price?.price);
const balanceAsFiat = showFiatBalance
? i18nFormatCurrency(
convertCryptoCurrencyMoneyToFiat(token.ticker, priceAsMoney, balanceAsMoney)
)
? i18nFormatCurrency(baseCurrencyAmountInQuote(balanceAsMoney, token.price))
: '';

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ 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 { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money';
import { i18nFormatCurrency } from '@app/common/money/format-money';
import { isMoneyGreaterThanZero } from '@app/common/money/money.utils';
import { ftDecimals } from '@app/common/stacks-utils';
import { formatContractId, getTicker } from '@app/common/utils';
import { spamFilter } from '@app/common/utils/spam-filter';
Expand All @@ -35,16 +33,10 @@ export function parseStacksFungibleTokenAssetBalance(
const title = spamFilter(friendlyName);

const showFiatBalance =
assetBalance.asset.price && checkIsMoneyAmountGreaterThanZero(assetBalance.asset.price);
assetBalance.asset.price && isMoneyGreaterThanZero(assetBalance.asset.price.price);
const balanceAsFiat = showFiatBalance
? assetBalance.asset.price &&
i18nFormatCurrency(
convertCryptoCurrencyMoneyToFiat(
assetBalance.balance.symbol,
assetBalance.asset.price,
assetBalance.balance
)
)
i18nFormatCurrency(baseCurrencyAmountInQuote(assetBalance.balance, assetBalance.asset.price))
: '';

return {
Expand Down
18 changes: 7 additions & 11 deletions src/app/pages/receive/components/receive-tokens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@ import { HomePageSelectors } from '@tests/selectors/home.selectors';
import { css } from 'leather-styles/css';
import { Stack } from 'leather-styles/jsx';

import { isDefined } from '@shared/utils';

import { copyToClipboard } from '@app/common/utils/copy-to-clipboard';
import { sortAssetsBySymbol } from '@app/common/utils/sort-assets-by-symbol';
import { useToast } from '@app/features/toasts/use-toast';
import { useAlexSdkSwappableCurrencyQuery } from '@app/query/common/alex-sdk/swappable-currency.query';
import { useAlexSwappableAssets } from '@app/query/common/alex-sdk/alex-sdk.hooks';
import { useConfigRunesEnabled } from '@app/query/common/remote-config/remote-config.query';
import { useCurrentNetwork } from '@app/store/networks/networks.selectors';
import { Avatar, defaultFallbackDelay, getAvatarFallback } from '@app/ui/components/avatar/avatar';
import { Avatar, defaultFallbackDelay } from '@app/ui/components/avatar/avatar';
import { Brc20AvatarIcon } from '@app/ui/components/avatar/brc20-avatar-icon';
import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon';
import { RunesAvatarIcon } from '@app/ui/components/avatar/runes-avatar-icon';
Expand All @@ -39,20 +36,19 @@ export function ReceiveTokens({
const toast = useToast();
const network = useCurrentNetwork();
const runesEnabled = useConfigRunesEnabled();
const { data: supportedCurrencies = [] } = useAlexSdkSwappableCurrencyQuery();
const { data: swapAssets = [] } = useAlexSwappableAssets();

const receivableAssets = useMemo(
() =>
sortAssetsBySymbol(supportedCurrencies.filter(isDefined))
swapAssets
.filter(asset => asset.name !== 'STX')
.map(asset => ({
...asset,
address: stxAddress,
fallback: getAvatarFallback(asset.name),
icon: asset.icon,
name: asset.name,
})),
[stxAddress, supportedCurrencies]
[stxAddress, swapAssets]
);

return (
<Stack className={css(receiveTabStyle)}>
<ReceiveItem
Expand Down
34 changes: 10 additions & 24 deletions src/app/pages/swap/alex-swap-container.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo, useState } from 'react';
import { useState } from 'react';
import { Outlet } from 'react-router-dom';

import { bytesToHex } from '@stacks/common';
Expand All @@ -18,7 +18,9 @@ import { alex } from '@shared/utils/alex-sdk';

import { LoadingKeys, useLoading } from '@app/common/hooks/use-loading';
import { useWalletType } from '@app/common/use-wallet-type';
import { migratePositiveAssetBalancesToTop } from '@app/common/utils/asset-order';
import { NonceSetter } from '@app/components/nonce-setter';
import { defaultSwapFee } from '@app/query/common/alex-sdk/alex-sdk.hooks';
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
import { useGenerateStacksContractCallUnsignedTx } from '@app/store/transactions/contract-call.hooks';
import { useSignStacksTransaction } from '@app/store/transactions/transaction.hooks';
Expand All @@ -29,14 +31,9 @@ import { generateSwapRoutes } from './generate-swap-routes';
import { useAlexBroadcastSwap } from './hooks/use-alex-broadcast-swap';
import { oneHundredMillion, useAlexSwap } from './hooks/use-alex-swap';
import { useStacksBroadcastSwap } from './hooks/use-stacks-broadcast-swap';
import { SwapAsset, SwapFormValues } from './hooks/use-swap-form';
import { SwapFormValues } from './hooks/use-swap-form';
import { useSwapNavigate } from './hooks/use-swap-navigate';
import { SwapContext, SwapProvider } from './swap.context';
import {
defaultSwapFee,
migratePositiveBalancesToTop,
sortSwappableAssetsBySymbol,
} from './swap.utils';

export const alexSwapRoutes = generateSwapRoutes(<AlexSwapContainer />);

Expand All @@ -57,27 +54,18 @@ function AlexSwapContainer() {
});

const {
fetchToAmount,
createSwapAssetFromAlexCurrency,
fetchQuoteAmount,
isFetchingExchangeRate,
onSetIsFetchingExchangeRate,
onSetSwapSubmissionData,
slippage,
supportedCurrencies,
swapAssets,
swapSubmissionData,
} = useAlexSwap();

const broadcastAlexSwap = useAlexBroadcastSwap();
const broadcastStacksSwap = useStacksBroadcastSwap();

const swappableAssets: SwapAsset[] = useMemo(
() =>
sortSwappableAssetsBySymbol(
supportedCurrencies.map(createSwapAssetFromAlexCurrency).filter(isDefined)
),
[createSwapAssetFromAlexCurrency, supportedCurrencies]
);

async function onSubmitSwapForReview(values: SwapFormValues) {
if (isUndefined(values.swapAssetBase) || isUndefined(values.swapAssetQuote)) {
logger.error('Error submitting swap for review');
Expand All @@ -96,9 +84,7 @@ function AlexSwapContainer() {
liquidityFee: new BigNumber(Number(lpFee)).dividedBy(oneHundredMillion).toNumber(),
nonce: values.nonce,
protocol: 'ALEX',
router: router
.map(x => createSwapAssetFromAlexCurrency(supportedCurrencies.find(y => y.id === x)))
.filter(isDefined),
router: router.map(x => swapAssets.find(asset => asset.currency === x)).filter(isDefined),
slippage,
sponsored: isSponsoredByAlex,
swapAmountBase: values.swapAmountBase,
Expand Down Expand Up @@ -191,15 +177,15 @@ function AlexSwapContainer() {
}

const swapContextValue: SwapContext = {
fetchToAmount,
fetchQuoteAmount,
isFetchingExchangeRate,
isSendingMax,
onSetIsFetchingExchangeRate,
onSetIsSendingMax: value => setIsSendingMax(value),
onSubmitSwapForReview,
onSubmitSwap,
swappableAssetsBase: migratePositiveBalancesToTop(swappableAssets),
swappableAssetsQuote: swappableAssets,
swappableAssetsBase: migratePositiveAssetBalancesToTop(swapAssets),
swappableAssetsQuote: swapAssets,
swapSubmissionData,
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { SwapSelectors } from '@tests/selectors/swap.selectors';

import {
checkIsMoneyAmountGreaterThanZero,
convertCryptoCurrencyMoneyToFiat,
} from '@app/common/money/fiat-conversion';
import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money';
import { formatMoneyWithoutSymbol, i18nFormatCurrency } from '@app/common/money/format-money';
import type { SwapAsset } from '@app/pages/swap/hooks/use-swap-form';
import { isMoneyGreaterThanZero } from '@app/common/money/money.utils';
import type { SwapAsset } from '@app/query/common/alex-sdk/alex-sdk.hooks';
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';
Expand All @@ -22,11 +20,9 @@ export function SwapAssetItem({ asset, onClick }: SwapAssetItemProps) {
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 showFiatBalance = isMoneyGreaterThanZero(asset.price.price);
const balanceAsFiat = showFiatBalance
? i18nFormatCurrency(
convertCryptoCurrencyMoneyToFiat(asset.balance.symbol, asset.price, asset.balance)
)
? i18nFormatCurrency(baseCurrencyAmountInQuote(asset.balance, asset.price))
: '';

return (
Expand Down
Loading

0 comments on commit 2c3a093

Please sign in to comment.