diff --git a/src/app/components/balance/btc-balance.tsx b/src/app/components/balance/btc-balance.tsx index a35c5af4ec3..09c5cc75be1 100644 --- a/src/app/components/balance/btc-balance.tsx +++ b/src/app/components/balance/btc-balance.tsx @@ -9,7 +9,7 @@ export function BtcBalance() { {signer => ( - {asset => {formatMoney(asset.balance.availableBalance)}} + {token => {formatMoney(token.balance.availableBalance)}} )} diff --git a/src/app/components/crypto-asset-item/crypto-asset-item.layout.tsx b/src/app/components/crypto-asset-item/crypto-asset-item.layout.tsx index efe8e0bd245..aa78c4df860 100644 --- a/src/app/components/crypto-asset-item/crypto-asset-item.layout.tsx +++ b/src/app/components/crypto-asset-item/crypto-asset-item.layout.tsx @@ -1,9 +1,9 @@ import { ReactNode } from 'react'; +import type { CryptoAssetBalance } from '@leather-wallet/models'; import { Box, Flex, styled } from 'leather-styles/jsx'; import { spamFilter } from '@app/common/utils/spam-filter'; -import type { AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; import { BulletSeparator } from '@app/ui/components/bullet-separator/bullet-separator'; import { ItemLayout } from '@app/ui/components/item-layout/item-layout'; import { SkeletonLoader } from '@app/ui/components/skeleton-loader/skeleton-loader'; @@ -16,29 +16,33 @@ import { parseCryptoAssetBalance } from './crypto-asset-item.layout.utils'; interface CryptoAssetItemLayoutProps { additionalBalanceInfo?: ReactNode; additionalBalanceInfoAsFiat?: ReactNode; - asset: AccountCryptoAssetWithDetails; + balance: CryptoAssetBalance; caption?: string; + contractId?: string; fiatBalance?: string; icon: React.ReactNode; isLoading?: boolean; name: string; - onClick?(asset: AccountCryptoAssetWithDetails): void; + onClick?(symbol: string, contractId?: string): void; rightElement?: React.ReactNode; + symbol: string; } export function CryptoAssetItemLayout({ additionalBalanceInfo, additionalBalanceInfoAsFiat, - asset, + balance, caption, + contractId, fiatBalance, icon, isLoading = false, name, onClick, rightElement, + symbol, }: CryptoAssetItemLayoutProps) { - const { dataTestId, formattedBalance } = parseCryptoAssetBalance(asset.balance); - const { availableBalance } = asset.balance; + const { dataTestId, formattedBalance } = parseCryptoAssetBalance(balance); + const { availableBalance } = balance; const title = spamFilter(name); const titleRight = ( @@ -88,7 +92,7 @@ export function CryptoAssetItemLayout({ if (isInteractive) return ( - onClick(asset)} my="space.02"> + onClick(symbol, contractId)} my="space.02"> {content} ); diff --git a/src/app/components/loaders/brc20-tokens-loader.tsx b/src/app/components/loaders/brc20-tokens-loader.tsx index b7f152d2e61..df59ccce764 100644 --- a/src/app/components/loaders/brc20-tokens-loader.tsx +++ b/src/app/components/loaders/brc20-tokens-loader.tsx @@ -1,10 +1,18 @@ -import { useBrc20AccountCryptoAssetsWithDetails } from '@app/query/bitcoin/ordinals/brc20/brc20-tokens.hooks'; -import type { Brc20AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; +import type { Brc20CryptoAssetInfo, CryptoAssetBalance, MarketData } from '@leather-wallet/models'; + +import { useBrc20Tokens } from '@app/query/bitcoin/ordinals/brc20/brc20-tokens.hooks'; interface Brc20TokensLoaderProps { - children(tokens: Brc20AccountCryptoAssetWithDetails[]): React.ReactNode; + children( + tokens: { + assetInfo: Brc20CryptoAssetInfo; + balance: CryptoAssetBalance; + holderAddress: string; + marketData: MarketData; + }[] + ): React.ReactNode; } export function Brc20TokensLoader({ children }: Brc20TokensLoaderProps) { - const tokens = useBrc20AccountCryptoAssetsWithDetails(); + const tokens = useBrc20Tokens(); return children(tokens); } diff --git a/src/app/components/loaders/btc-crypto-asset-loader.tsx b/src/app/components/loaders/btc-crypto-asset-loader.tsx index 3ed06ddb8bf..37536b7f795 100644 --- a/src/app/components/loaders/btc-crypto-asset-loader.tsx +++ b/src/app/components/loaders/btc-crypto-asset-loader.tsx @@ -1,11 +1,30 @@ -import { useBtcAccountCryptoAssetWithDetails } from '@app/query/bitcoin/btc/btc-crypto-asset.hooks'; -import type { BtcAccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; +import type { BtcCryptoAssetInfo, CryptoAssetBalance, MarketData } from '@leather-wallet/models'; + +import { BTC_DECIMALS } from '@shared/constants'; + +import { useGetBitcoinBalanceByAddress } from '@app/query/bitcoin/balance/btc-balance.hooks'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; +import { createCryptoAssetBalance } from '@app/query/common/models'; + +const btcCryptoAssetInfo: BtcCryptoAssetInfo = { + decimals: BTC_DECIMALS, + hasMemo: false, + name: 'bitcoin', + symbol: 'BTC', +}; interface BtcCryptoAssetLoaderProps { address: string; - children(asset: BtcAccountCryptoAssetWithDetails, isInitialLoading: boolean): React.ReactNode; + children( + token: { assetInfo: BtcCryptoAssetInfo; balance: CryptoAssetBalance; marketData: MarketData }, + isInitialLoading: boolean + ): React.ReactNode; } export function BtcCryptoAssetLoader({ address, children }: BtcCryptoAssetLoaderProps) { - const { asset, isInitialLoading } = useBtcAccountCryptoAssetWithDetails(address); - return children(asset, isInitialLoading); + const marketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); + const { balance, isInitialLoading } = useGetBitcoinBalanceByAddress(address); + return children( + { assetInfo: btcCryptoAssetInfo, balance: createCryptoAssetBalance(balance), marketData }, + isInitialLoading + ); } diff --git a/src/app/components/loaders/sip10-tokens-loader.tsx b/src/app/components/loaders/sip10-tokens-loader.tsx new file mode 100644 index 00000000000..ea1baa87035 --- /dev/null +++ b/src/app/components/loaders/sip10-tokens-loader.tsx @@ -0,0 +1,20 @@ +import type { CryptoAssetBalance, MarketData, Sip10CryptoAssetInfo } from '@leather-wallet/models'; + +import { useFilteredSip10Tokens } from '@app/query/stacks/sip10/sip10-tokens.hooks'; +import type { Sip10CryptoAssetFilter } from '@app/query/stacks/sip10/sip10-tokens.utils'; + +interface Sip10TokensLoaderProps { + address: string; + filter: Sip10CryptoAssetFilter; + children( + tokens: { + assetInfo: Sip10CryptoAssetInfo; + balance: CryptoAssetBalance; + marketData: MarketData; + }[] + ): React.ReactNode; +} +export function Sip10TokensLoader({ address, filter, children }: Sip10TokensLoaderProps) { + const tokens = useFilteredSip10Tokens({ address, filter }); + return children(tokens); +} diff --git a/src/app/components/loaders/stx-crypto-asset-loader.tsx b/src/app/components/loaders/stx-crypto-asset-loader.tsx index 72824eb4770..3692601755e 100644 --- a/src/app/components/loaders/stx-crypto-asset-loader.tsx +++ b/src/app/components/loaders/stx-crypto-asset-loader.tsx @@ -1,11 +1,35 @@ -import type { StxAccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; -import { useStxAccountCryptoAssetWithDetails } from '@app/query/stacks/stx/stx-crypto-asset.hooks'; +import type { MarketData, StxCryptoAssetBalance, StxCryptoAssetInfo } from '@leather-wallet/models'; + +import { STX_DECIMALS } from '@shared/constants'; + +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; +import { isFetchedWithSuccess } from '@app/query/query-config'; +import { useStxCryptoAssetBalance } from '@app/query/stacks/balance/account-balance.hooks'; + +const stxCryptoAssetInfo: StxCryptoAssetInfo = { + decimals: STX_DECIMALS, + hasMemo: true, + name: 'stacks', + symbol: 'STX', +}; interface StxCryptoAssetLoaderProps { address: string; - children(asset: StxAccountCryptoAssetWithDetails, isInitialLoading: boolean): React.ReactNode; + children( + token: { + assetInfo: StxCryptoAssetInfo; + balance: StxCryptoAssetBalance; + marketData: MarketData; + }, + isInitialLoading: boolean + ): React.ReactNode; } export function StxCryptoAssetLoader({ address, children }: StxCryptoAssetLoaderProps) { - const { asset, isInitialLoading } = useStxAccountCryptoAssetWithDetails(address); - return children(asset, isInitialLoading); + const marketData = useCryptoCurrencyMarketDataMeanAverage('STX'); + const query = useStxCryptoAssetBalance(address); + if (!isFetchedWithSuccess(query)) return; + return children( + { assetInfo: stxCryptoAssetInfo, balance: query.data, marketData }, + query.isInitialLoading + ); } diff --git a/src/app/features/asset-list/asset-list.tsx b/src/app/features/asset-list/asset-list.tsx index 570f48e8161..9d99313b6b9 100644 --- a/src/app/features/asset-list/asset-list.tsx +++ b/src/app/features/asset-list/asset-list.tsx @@ -1,3 +1,4 @@ +import type { CryptoAssetBalance, MarketData } from '@leather-wallet/models'; import { Stack } from 'leather-styles/jsx'; import { useWalletType } from '@app/common/use-wallet-type'; @@ -9,6 +10,7 @@ import { import { Brc20TokensLoader } from '@app/components/loaders/brc20-tokens-loader'; import { BtcCryptoAssetLoader } from '@app/components/loaders/btc-crypto-asset-loader'; import { RunesLoader } from '@app/components/loaders/runes-loader'; +import { Sip10TokensLoader } from '@app/components/loaders/sip10-tokens-loader'; import { Src20TokensLoader } from '@app/components/loaders/src20-tokens-loader'; import { CurrentStacksAccountLoader } from '@app/components/loaders/stacks-account-loader'; import { Stx20TokensLoader } from '@app/components/loaders/stx20-tokens-loader'; @@ -19,7 +21,7 @@ import { Src20TokenAssetList } from '@app/features/asset-list/bitcoin/src20-toke import { Stx20TokenAssetList } from '@app/features/asset-list/stacks/stx20-token-asset-list/stx20-token-asset-list'; import { StxCryptoAssetItem } from '@app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item'; import { StxCryptoAssetItemFallback } from '@app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item-fallback'; -import type { AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; +import type { CryptoAssetInfo } from '@app/query/common/models'; import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; import { BtcCryptoAssetItem } from './bitcoin/btc-crypto-asset-item/btc-crypto-asset-item'; @@ -28,9 +30,14 @@ import { Sip10TokenAssetList } from './stacks/sip10-token-asset-list/sip10-token import { Sip10TokenAssetListUnsupported } from './stacks/sip10-token-asset-list/sip10-token-asset-list-unsupported'; export type AssetListVariant = 'interactive' | 'read-only'; +export interface AssetItem { + assetInfo: CryptoAssetInfo; + balance: CryptoAssetBalance; + marketData: MarketData; +} interface AssetListProps { - onClick?(asset: AccountCryptoAssetWithDetails): void; + onClick?(symbol: string, contractId?: string): void; variant?: AssetListVariant; } export function AssetList({ onClick, variant = 'read-only' }: AssetListProps) { @@ -46,11 +53,11 @@ export function AssetList({ onClick, variant = 'read-only' }: AssetListProps) { {nativeSegwitAccount => ( - {(asset, isInitialLoading) => ( + {(token, isInitialLoading) => ( )} @@ -64,11 +71,11 @@ export function AssetList({ onClick, variant = 'read-only' }: AssetListProps) { > {nativeSegwitAccount => ( - {(asset, isInitialLoading) => ( + {(token, isInitialLoading) => ( )} @@ -89,11 +96,16 @@ export function AssetList({ onClick, variant = 'read-only' }: AssetListProps) { {account => ( <> - {(asset, isInitialLoading) => ( - + {(token, isInitialLoading) => ( + )} - + + {tokens => } + {isReadOnly && ( {tokens => } @@ -109,7 +121,7 @@ export function AssetList({ onClick, variant = 'read-only' }: AssetListProps) { {taprootAccount => ( <> - {tokens => } + {tokens => } {isReadOnly && ( <> @@ -129,7 +141,11 @@ export function AssetList({ onClick, variant = 'read-only' }: AssetListProps) { {isReadOnly && ( - {account => } + {account => ( + + {tokens => } + + )} )} diff --git a/src/app/features/asset-list/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx b/src/app/features/asset-list/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx index 875048d35af..83b46ae205a 100644 --- a/src/app/features/asset-list/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx +++ b/src/app/features/asset-list/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx @@ -7,39 +7,48 @@ import { RouteUrls } from '@shared/route-urls'; import { capitalize } from '@app/common/utils'; import { CryptoAssetItemLayout } from '@app/components/crypto-asset-item/crypto-asset-item.layout'; -import type { AssetListVariant } from '@app/features/asset-list/asset-list'; +import type { AssetItem, AssetListVariant } from '@app/features/asset-list/asset-list'; import { useCurrentBtcAvailableBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; -import type { Brc20AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; import { Brc20AvatarIcon } from '@app/ui/components/avatar/brc20-avatar-icon'; +interface Brc20AssetItem extends AssetItem { + holderAddress: string; +} + interface Brc20TokenAssetListProps { - assets: Brc20AccountCryptoAssetWithDetails[]; + tokens: Brc20AssetItem[]; variant?: AssetListVariant; } -export function Brc20TokenAssetList({ assets, variant }: Brc20TokenAssetListProps) { +export function Brc20TokenAssetList({ tokens, variant }: Brc20TokenAssetListProps) { const navigate = useNavigate(); const { balance, isInitialLoading } = useCurrentBtcAvailableBalanceNativeSegwit(); const hasPositiveBtcBalanceForFees = variant === 'interactive' && balance.amount.isGreaterThan(0); - function navigateToBrc20SendForm(asset: Brc20AccountCryptoAssetWithDetails) { - const { balance, holderAddress, info, marketData } = asset; - navigate(RouteUrls.SendBrc20SendForm.replace(':ticker', info.symbol), { - state: { balance: balance.availableBalance, holderAddress, marketData, ticker: info.symbol }, + function navigateToBrc20SendForm(token: Brc20AssetItem) { + const { balance, holderAddress, assetInfo, marketData } = token; + navigate(RouteUrls.SendBrc20SendForm.replace(':ticker', assetInfo.symbol), { + state: { + balance: balance.availableBalance, + holderAddress, + marketData, + ticker: assetInfo.symbol, + }, }); } return ( - {assets.map(asset => ( + {tokens.map(token => ( } isLoading={isInitialLoading} - key={asset.info.symbol} - name={asset.info.symbol} - onClick={hasPositiveBtcBalanceForFees ? () => navigateToBrc20SendForm(asset) : undefined} + key={token.assetInfo.symbol} + name={token.assetInfo.name} + onClick={hasPositiveBtcBalanceForFees ? () => navigateToBrc20SendForm(token) : undefined} + symbol={token.assetInfo.symbol} /> ))} diff --git a/src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item-fallback.tsx b/src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item-fallback.tsx index f7e7c71e797..ea7f59c5b87 100644 --- a/src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item-fallback.tsx +++ b/src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item-fallback.tsx @@ -1,5 +1,6 @@ +import { createMoney } from '@shared/models/money.model'; + import { CryptoAssetItemLayout } from '@app/components/crypto-asset-item/crypto-asset-item.layout'; -import { btcCryptoAssetPlaceholder } from '@app/query/bitcoin/btc/btc-crypto-asset.hooks'; import { useCheckLedgerBlockchainAvailable } from '@app/store/accounts/blockchain/utils'; import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; @@ -14,10 +15,11 @@ export function BtcCryptoAssetItemFallback({ variant }: StxCryptoAssetItemFallba if (variant === 'interactive' && !checkBlockchainAvailable('bitcoin')) return null; return ( } - name={btcCryptoAssetPlaceholder.info.name} - rightElement={} + name="bitcoin" + rightElement={} + symbol="BTC" /> ); } diff --git a/src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item.tsx b/src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item.tsx index 1128f53945e..df248fbbb26 100644 --- a/src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item.tsx +++ b/src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item.tsx @@ -2,39 +2,37 @@ import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money'; import { i18nFormatCurrency } from '@app/common/money/format-money'; import { capitalize } from '@app/common/utils'; import { CryptoAssetItemLayout } from '@app/components/crypto-asset-item/crypto-asset-item.layout'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; -import type { - AccountCryptoAssetWithDetails, - BtcAccountCryptoAssetWithDetails, -} from '@app/query/models/crypto-asset.model'; import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; +import type { AssetItem } from '../../asset-list'; + interface BtcCryptoAssetItemProps { - asset: BtcAccountCryptoAssetWithDetails; + token: AssetItem; isLoading: boolean; - onClick?(asset: AccountCryptoAssetWithDetails): void; + onClick?(symbol: string): void; rightElement?: React.ReactNode; } export function BtcCryptoAssetItem({ - asset, isLoading, + token, onClick, rightElement, }: BtcCryptoAssetItemProps) { - const marketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); + const { assetInfo, balance, marketData } = token; const availableBalanceAsFiat = i18nFormatCurrency( - baseCurrencyAmountInQuote(asset.balance.availableBalance, marketData) + baseCurrencyAmountInQuote(balance.availableBalance, marketData) ); return ( } isLoading={isLoading} - name={capitalize(asset.info.name)} + name={capitalize(assetInfo.name)} onClick={onClick} rightElement={rightElement} + symbol={assetInfo.symbol} /> ); } diff --git a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.tsx b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.tsx index e7b421e56ac..710ab0f567d 100644 --- a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.tsx +++ b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.tsx @@ -1,36 +1,47 @@ +import type { CryptoAssetBalance, MarketData, Sip10CryptoAssetInfo } from '@leather-wallet/models'; + +import { convertAssetBalanceToFiat } from '@app/common/asset-utils'; +import { getSafeImageCanonicalUri } from '@app/common/stacks-utils'; +import { spamFilter } from '@app/common/utils/spam-filter'; import { CryptoAssetItemLayout } from '@app/components/crypto-asset-item/crypto-asset-item.layout'; import { StacksAssetAvatar } from '@app/components/stacks-asset-avatar'; -import type { - AccountCryptoAssetWithDetails, - Sip10AccountCryptoAssetWithDetails, -} from '@app/query/models/crypto-asset.model'; - -import { parseSip10TokenCryptoAssetBalance } from './sip10-token-asset-item.utils'; interface Sip10TokenAssetItemProps { - asset: Sip10AccountCryptoAssetWithDetails; - onClick?(asset: AccountCryptoAssetWithDetails): void; + assetInfo: Sip10CryptoAssetInfo; + balance: CryptoAssetBalance; + marketData: MarketData; + onClick?(symbol: string, contractId?: string): void; } -export function Sip10TokenAssetItem({ asset, onClick }: Sip10TokenAssetItemProps) { - const { avatar, fiatBalance, imageCanonicalUri, title } = - parseSip10TokenCryptoAssetBalance(asset); +export function Sip10TokenAssetItem({ + assetInfo, + balance, + marketData, + onClick, +}: Sip10TokenAssetItemProps) { + const name = spamFilter(assetInfo.name); + const fiatBalance = convertAssetBalanceToFiat({ + balance: balance.availableBalance, + marketData, + }); return ( - {title[0]} + {name[0]} } - name={asset.info.name} + name={name} onClick={onClick} + symbol={assetInfo.symbol} /> ); } diff --git a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.utils.ts b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.utils.ts deleted file mode 100644 index e1b09175109..00000000000 --- a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.utils.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { CryptoAssetSelectors } from '@tests/selectors/crypto-asset.selectors'; - -import { convertAssetBalanceToFiat } from '@app/common/asset-utils'; -import { formatBalance } from '@app/common/format-balance'; -import { ftDecimals, getSafeImageCanonicalUri } from '@app/common/stacks-utils'; -import { spamFilter } from '@app/common/utils/spam-filter'; -import type { Sip10AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; - -export function parseSip10TokenCryptoAssetBalance(asset: Sip10AccountCryptoAssetWithDetails) { - const { balance, info } = asset; - const { contractId, decimals, imageCanonicalUri, name, symbol } = info; - - const amount = ftDecimals(balance.availableBalance.amount, decimals); - const avatar = contractId; - const dataTestId = - symbol && CryptoAssetSelectors.CryptoAssetListItem.replace('{symbol}', symbol.toLowerCase()); - const formattedBalance = formatBalance(amount); - const safeImageCanonicalUri = getSafeImageCanonicalUri(imageCanonicalUri, name); - const title = spamFilter(name); - const fiatBalance = convertAssetBalanceToFiat({ - ...asset, - balance: asset.balance.availableBalance, - }); - - return { - amount, - avatar, - fiatBalance, - dataTestId, - formattedBalance, - imageCanonicalUri: safeImageCanonicalUri, - title, - }; -} diff --git a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list-unsupported.tsx b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list-unsupported.tsx index 72b52332198..d8c25c5ebd2 100644 --- a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list-unsupported.tsx +++ b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list-unsupported.tsx @@ -2,25 +2,24 @@ import { useState } from 'react'; import { Stack, styled } from 'leather-styles/jsx'; -import { useFilteredSip10AccountCryptoAssetsWithDetails } from '@app/query/stacks/sip10/sip10-tokens.hooks'; import { Accordion } from '@app/ui/components/accordion/accordion'; import { Sip10TokenAssetItem } from './sip10-token-asset-item'; +import type { Sip10AssetItem } from './sip10-token-asset-list'; const accordionValue = 'accordion-unsupported-token-asset-list'; -export function Sip10TokenAssetListUnsupported({ address }: { address: string }) { +interface Sip10TokenAssetListUnsupportedProps { + tokens: Sip10AssetItem[]; +} +export function Sip10TokenAssetListUnsupported({ tokens }: Sip10TokenAssetListUnsupportedProps) { const [isOpen, setIsOpen] = useState(false); - const assets = useFilteredSip10AccountCryptoAssetsWithDetails({ - address, - filter: 'unsupported', - }); function onValueChange(value: string) { setIsOpen(value === accordionValue); } - if (!assets.length) return null; + if (!tokens.length) return null; return ( @@ -30,8 +29,13 @@ export function Sip10TokenAssetListUnsupported({ address }: { address: string }) - {assets.map(asset => ( - + {tokens.map(token => ( + ))} diff --git a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list.tsx b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list.tsx index 28e2b49c71e..34ebce50741 100644 --- a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list.tsx +++ b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list.tsx @@ -1,28 +1,30 @@ +import type { Sip10CryptoAssetInfo } from '@leather-wallet/models'; import { Stack } from 'leather-styles/jsx'; -import { isDefined } from '@shared/utils'; - -import type { AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; -import { useFilteredSip10AccountCryptoAssetsWithDetails } from '@app/query/stacks/sip10/sip10-tokens.hooks'; - +import type { AssetItem } from '../../asset-list'; import { Sip10TokenAssetItem } from './sip10-token-asset-item'; -interface Sip10TokenAssetListProps { - address: string; - onClick?(asset: AccountCryptoAssetWithDetails): void; +export interface Sip10AssetItem extends AssetItem { + assetInfo: Sip10CryptoAssetInfo; } -export function Sip10TokenAssetList({ address, onClick }: Sip10TokenAssetListProps) { - const assets = useFilteredSip10AccountCryptoAssetsWithDetails({ - address, - filter: isDefined(onClick) ? 'all' : 'supported', - }); - if (!assets.length) return null; +interface Sip10TokenAssetListProps { + tokens: Sip10AssetItem[]; + onClick?(symbol: string, contractId?: string): void; +} +export function Sip10TokenAssetList({ tokens, onClick }: Sip10TokenAssetListProps) { + if (!tokens.length) return null; return ( - {assets.map(asset => ( - + {tokens.map(token => ( + ))} ); diff --git a/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item-fallback.tsx b/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item-fallback.tsx index 1eb4c256dcd..e3113593253 100644 --- a/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item-fallback.tsx +++ b/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item-fallback.tsx @@ -1,5 +1,6 @@ +import { createMoney } from '@shared/models/money.model'; + import { CryptoAssetItemLayout } from '@app/components/crypto-asset-item/crypto-asset-item.layout'; -import { stxCryptoAssetPlaceholder } from '@app/query/stacks/stx/stx-crypto-asset.hooks'; import { useCheckLedgerBlockchainAvailable } from '@app/store/accounts/blockchain/utils'; import { StxAvatarIcon } from '@app/ui/components/avatar/stx-avatar-icon'; @@ -14,10 +15,11 @@ export function StxCryptoAssetItemFallback({ variant }: StxCryptoAssetItemFallba if (variant === 'interactive' && !checkBlockchainAvailable('stacks')) return null; return ( } - name={stxCryptoAssetPlaceholder.info.name} - rightElement={} + name="stacks" + rightElement={} + symbol="STX" /> ); } diff --git a/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.stories.tsx b/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.stories.tsx index 7fe04fcfecb..312ef059e7d 100644 --- a/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.stories.tsx +++ b/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.stories.tsx @@ -42,8 +42,8 @@ const stxCryptoAssetBalance = { export const StxCryptoAssetItem: Story = { args: { - asset: { - info: { + token: { + assetInfo: { decimals: 6, hasMemo: true, name: 'stacks', @@ -53,17 +53,18 @@ export const StxCryptoAssetItem: Story = { ...stxCryptoAssetBalance, lockedBalance: { amount: new BigNumber(0), decimals: 6, symbol }, }, - chain: 'stacks', - marketData: null, - type: 'stx', + marketData: { + pair: { base: 'STX', quote: 'USD' }, + price: { amount: new BigNumber(0), decimals: 6, symbol }, + }, }, }, }; export const StxCryptoAssetItemWithLockedBalance: Story = { args: { - asset: { - info: { + token: { + assetInfo: { decimals: 6, hasMemo: true, name: 'stacks', @@ -73,9 +74,10 @@ export const StxCryptoAssetItemWithLockedBalance: Story = { ...stxCryptoAssetBalance, lockedBalance: { amount: new BigNumber(1000000000), decimals: 6, symbol }, }, - chain: 'stacks', - marketData: null, - type: 'stx', + marketData: { + pair: { base: 'STX', quote: 'USD' }, + price: { amount: new BigNumber(0), decimals: 6, symbol }, + }, }, }, }; diff --git a/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.tsx b/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.tsx index 55092bab5f1..adaf2c5d88e 100644 --- a/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.tsx +++ b/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.tsx @@ -1,3 +1,4 @@ +import type { StxCryptoAssetBalance } from '@leather-wallet/models'; import { styled } from 'leather-styles/jsx'; import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money'; @@ -5,24 +6,24 @@ import { i18nFormatCurrency } from '@app/common/money/format-money'; import { ftDecimals } from '@app/common/stacks-utils'; import { capitalize } from '@app/common/utils'; import { CryptoAssetItemLayout } from '@app/components/crypto-asset-item/crypto-asset-item.layout'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; -import type { - AccountCryptoAssetWithDetails, - StxAccountCryptoAssetWithDetails, -} from '@app/query/models/crypto-asset.model'; import { StxAvatarIcon } from '@app/ui/components/avatar/stx-avatar-icon'; import { BulletOperator } from '@app/ui/components/bullet-separator/bullet-separator'; import { Caption } from '@app/ui/components/typography/caption'; +import type { AssetItem } from '../../asset-list'; + +interface StxAssetItem extends AssetItem { + balance: StxCryptoAssetBalance; +} + interface StxCryptoAssetItemProps { - asset: StxAccountCryptoAssetWithDetails; + token: StxAssetItem; isLoading: boolean; - onClick?(asset: AccountCryptoAssetWithDetails): void; + onClick?(symbol: string): void; } -export function StxCryptoAssetItem({ asset, isLoading, onClick }: StxCryptoAssetItemProps) { - const marketData = useCryptoCurrencyMarketDataMeanAverage('STX'); - - const { availableBalance, lockedBalance } = asset.balance; +export function StxCryptoAssetItem({ token, isLoading, onClick }: StxCryptoAssetItemProps) { + const { assetInfo, balance, marketData } = token; + const { availableBalance, lockedBalance } = balance; const showAdditionalInfo = lockedBalance.amount.isGreaterThan(0); const lockedBalanceAsFiat = i18nFormatCurrency( @@ -43,12 +44,13 @@ export function StxCryptoAssetItem({ asset, isLoading, onClick }: StxCryptoAsset } isLoading={isLoading} - name={capitalize(asset.info.name)} + name={capitalize(assetInfo.name)} onClick={onClick} + symbol={assetInfo.symbol} /> ); } diff --git a/src/app/pages/fund/choose-asset-to-fund/choose-asset-to-fund.tsx b/src/app/pages/fund/choose-asset-to-fund/choose-asset-to-fund.tsx index 58eb7a9cb2b..d3a2da54c0d 100644 --- a/src/app/pages/fund/choose-asset-to-fund/choose-asset-to-fund.tsx +++ b/src/app/pages/fund/choose-asset-to-fund/choose-asset-to-fund.tsx @@ -12,7 +12,6 @@ import { BtcCryptoAssetLoader } from '@app/components/loaders/btc-crypto-asset-l import { CurrentStacksAccountLoader } from '@app/components/loaders/stacks-account-loader'; import { StxCryptoAssetLoader } from '@app/components/loaders/stx-crypto-asset-loader'; import { StxCryptoAssetItem } from '@app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item'; -import type { AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; import { Card } from '@app/ui/layout/card/card'; import { Page } from '@app/ui/layout/page/page.layout'; @@ -21,8 +20,7 @@ export function ChooseCryptoAssetToFund() { const navigate = useNavigate(); const navigateToFund = useCallback( - (asset: AccountCryptoAssetWithDetails) => - navigate(RouteUrls.Fund.replace(':currency', asset.info.symbol)), + (symbol: string) => navigate(RouteUrls.Fund.replace(':currency', symbol)), [navigate] ); @@ -40,13 +38,14 @@ export function ChooseCryptoAssetToFund() { {signer => ( - {(asset, isLoading) => ( + {(token, isLoading) => ( } isLoading={isLoading} - name={capitalize(asset.info.name)} - onClick={() => navigateToFund(asset)} + name={capitalize(token.assetInfo.name)} + onClick={() => navigateToFund(token.assetInfo.symbol)} + symbol={token.assetInfo.symbol} /> )} @@ -56,11 +55,11 @@ export function ChooseCryptoAssetToFund() { {account => ( - {(asset, isInitialLoading) => ( + {(token, isInitialLoading) => ( navigateToFund(asset)} + onClick={() => navigateToFund(token.assetInfo.symbol)} /> )} diff --git a/src/app/pages/home/components/send-button.tsx b/src/app/pages/home/components/send-button.tsx index 33da083cc91..ee6b1050afa 100644 --- a/src/app/pages/home/components/send-button.tsx +++ b/src/app/pages/home/components/send-button.tsx @@ -10,7 +10,7 @@ import { whenPageMode } from '@app/common/utils'; import { openIndexPageInNewTab } from '@app/common/utils/open-in-new-tab'; import { useBtcCryptoAssetBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; import { useStxCryptoAssetBalance } from '@app/query/stacks/balance/account-balance.hooks'; -import { useTransferableSip10CryptoAssetsWithDetails } from '@app/query/stacks/sip10/sip10-tokens.hooks'; +import { useTransferableSip10Tokens } from '@app/query/stacks/sip10/sip10-tokens.hooks'; import { useCurrentAccountNativeSegwitIndexZeroSignerNullable } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { IconButton } from '@app/ui/components/icon-button/icon-button'; @@ -23,7 +23,7 @@ function SendButtonSuspense() { const btcAddress = useCurrentAccountNativeSegwitIndexZeroSignerNullable()?.address; const { btcCryptoAssetBalance } = useBtcCryptoAssetBalanceNativeSegwit(btcAddress ?? ''); const { data: stxCryptoAssetBalance } = useStxCryptoAssetBalance(address); - const stacksFtAssets = useTransferableSip10CryptoAssetsWithDetails(address); + const stacksFtAssets = useTransferableSip10Tokens(address); const isDisabled = !btcCryptoAssetBalance && !stxCryptoAssetBalance && stacksFtAssets?.length === 0; diff --git a/src/app/pages/send/choose-crypto-asset/choose-crypto-asset.tsx b/src/app/pages/send/choose-crypto-asset/choose-crypto-asset.tsx index 2cb7f107d19..009b08993f0 100644 --- a/src/app/pages/send/choose-crypto-asset/choose-crypto-asset.tsx +++ b/src/app/pages/send/choose-crypto-asset/choose-crypto-asset.tsx @@ -5,35 +5,19 @@ import { Box, styled } from 'leather-styles/jsx'; import { RouteUrls } from '@shared/route-urls'; import { AssetList } from '@app/features/asset-list/asset-list'; -import { useToast } from '@app/features/toasts/use-toast'; import { useConfigBitcoinSendEnabled } from '@app/query/common/remote-config/remote-config.query'; -import type { AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; -import { getSip10InfoFromAsset } from '@app/query/stacks/sip10/sip10-tokens.hooks'; import { Card } from '@app/ui/layout/card/card'; -import { getAssetStringParts } from '@app/ui/utils/get-asset-string-parts'; export function ChooseCryptoAsset() { const navigate = useNavigate(); const isBitcoinSendEnabled = useConfigBitcoinSendEnabled(); - const toast = useToast(); - function navigateToSendForm(asset: AccountCryptoAssetWithDetails) { - switch (asset.type) { - case 'btc': - if (!isBitcoinSendEnabled) return navigate(RouteUrls.SendBtcDisabled); - return navigate(`${RouteUrls.SendCryptoAsset}/${asset.info.symbol.toLowerCase()}`); - case 'sip-10': - const info = getSip10InfoFromAsset(asset); - if (info) { - const { assetName } = getAssetStringParts(info.contractId); - const symbol = !info.symbol ? assetName : info.symbol.toLowerCase(); - return navigate(`${RouteUrls.SendCryptoAsset}/${symbol}/${info.contractId}`); - } - toast.error('No contract id'); - return navigate('..'); - default: - return navigate(`${RouteUrls.SendCryptoAsset}/${asset.info.symbol.toLowerCase()}`); + function navigateToSendForm(symbol: string, contractId?: string) { + if (!isBitcoinSendEnabled) return navigate(RouteUrls.SendBtcDisabled); + if (contractId) { + return navigate(`${RouteUrls.SendCryptoAsset}/${symbol}/${contractId}`); } + return navigate(`${RouteUrls.SendCryptoAsset}/${symbol.toLowerCase()}`); } return ( diff --git a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form-container.tsx b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form-container.tsx new file mode 100644 index 00000000000..9d70cade2e4 --- /dev/null +++ b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form-container.tsx @@ -0,0 +1,140 @@ +import { Outlet } from 'react-router-dom'; + +import type { BtcCryptoAssetInfo, CryptoAssetBalance, MarketData } from '@leather-wallet/models'; +import { SendCryptoAssetSelectors } from '@tests/selectors/send.selectors'; +import { Form, Formik } from 'formik'; +import { Box } from 'leather-styles/jsx'; + +import { HIGH_FEE_WARNING_LEARN_MORE_URL_BTC } from '@shared/constants'; + +import { formatMoney } from '@app/common/money/format-money'; +import { HighFeeDialog } from '@app/features/dialogs/high-fee-dialog/high-fee-dialog'; +import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; +import { Button } from '@app/ui/components/button/button'; +import { Callout } from '@app/ui/components/callout/callout'; +import { AvailableBalance } from '@app/ui/components/containers/footers/available-balance'; +import { Footer } from '@app/ui/components/containers/footers/footer'; +import { Link } from '@app/ui/components/link/link'; +import { Card } from '@app/ui/layout/card/card'; +import { CardContent } from '@app/ui/layout/card/card-content'; + +import { AmountField } from '../../components/amount-field'; +import { SelectedAssetField } from '../../components/selected-asset-field'; +import { SendFiatValue } from '../../components/send-fiat-value'; +import { TransferRecipientField } from '../../family/bitcoin/components/bitcoin-recipient-field'; +import { BitcoinSendMaxButton } from '../../family/bitcoin/components/bitcoin-send-max-button'; +import { useSendFormRouteState } from '../../hooks/use-send-form-route-state'; +import { createDefaultInitialFormValues, defaultSendFormFormikProps } from '../../send-form.utils'; +import { useBtcSendForm } from './use-btc-send-form'; + +interface BtcSendFormContainerProps { + assetInfo: BtcCryptoAssetInfo; + balance: CryptoAssetBalance; + marketData: MarketData; +} +export function BtcSendFormContainer({ + assetInfo, + balance, + marketData, +}: BtcSendFormContainerProps) { + const routeState = useSendFormRouteState(); + const symbol = assetInfo.symbol; + + const { + calcMaxSpend, + chooseTransactionFee, + currentNetwork, + formRef, + isSendingMax, + onFormStateChange, + onSetIsSendingMax, + utxos, + validationSchema, + } = useBtcSendForm(); + + return ( + + + {props => { + onFormStateChange(props.values); + const sendMaxCalculation = calcMaxSpend(props.values.recipient, utxos); + + return ( +
+ + + + + } + > + + + } + onSetIsSendingMax={onSetIsSendingMax} + isSendingMax={isSendingMax} + switchableAmount={ + + } + /> + } + name={assetInfo.name} + symbol={symbol} + /> + + {currentNetwork.chain.bitcoin.bitcoinNetwork === 'testnet' && ( + + This is a Bitcoin testnet transaction. + + Get testnet BTC here ↗ + + + )} + + + + + + {/* This is for testing purposes only, to make sure the form is ready to be submitted. */} + {calcMaxSpend(props.values.recipient, utxos).spendableBitcoin.toNumber() > 0 ? ( + + ) : null} + + ); + }} +
+
+ ); +} diff --git a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form.tsx index 57eec8dabd2..9298ba2dc84 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form.tsx @@ -1,136 +1,22 @@ -import { Outlet } from 'react-router-dom'; +import { BitcoinNativeSegwitAccountLoader } from '@app/components/loaders/bitcoin-account-loader'; +import { BtcCryptoAssetLoader } from '@app/components/loaders/btc-crypto-asset-loader'; -import { SendCryptoAssetSelectors } from '@tests/selectors/send.selectors'; -import { Form, Formik } from 'formik'; -import { Box } from 'leather-styles/jsx'; - -import { HIGH_FEE_WARNING_LEARN_MORE_URL_BTC } from '@shared/constants'; -import { CryptoCurrencies } from '@shared/models/currencies.model'; - -import { formatMoney } from '@app/common/money/format-money'; -import { HighFeeDialog } from '@app/features/dialogs/high-fee-dialog/high-fee-dialog'; -import { useBtcAccountCryptoAssetWithDetails } from '@app/query/bitcoin/btc/btc-crypto-asset.hooks'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; -import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; -import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; -import { Button } from '@app/ui/components/button/button'; -import { Callout } from '@app/ui/components/callout/callout'; -import { AvailableBalance } from '@app/ui/components/containers/footers/available-balance'; -import { Footer } from '@app/ui/components/containers/footers/footer'; -import { Link } from '@app/ui/components/link/link'; -import { Card } from '@app/ui/layout/card/card'; -import { CardContent } from '@app/ui/layout/card/card-content'; - -import { AmountField } from '../../components/amount-field'; -import { SelectedAssetField } from '../../components/selected-asset-field'; -import { SendFiatValue } from '../../components/send-fiat-value'; -import { TransferRecipientField } from '../../family/bitcoin/components/bitcoin-recipient-field'; -import { BitcoinSendMaxButton } from '../../family/bitcoin/components/bitcoin-send-max-button'; -import { useSendFormRouteState } from '../../hooks/use-send-form-route-state'; -import { createDefaultInitialFormValues, defaultSendFormFormikProps } from '../../send-form.utils'; -import { useBtcSendForm } from './use-btc-send-form'; - -const symbol: CryptoCurrencies = 'BTC'; +import { BtcSendFormContainer } from './btc-send-form-container'; export function BtcSendForm() { - const routeState = useSendFormRouteState(); - const btcMarketData = useCryptoCurrencyMarketDataMeanAverage(symbol); - - const { address } = useCurrentAccountNativeSegwitIndexZeroSigner(); - const { asset } = useBtcAccountCryptoAssetWithDetails(address); - const { balance, info } = asset; - - const { - calcMaxSpend, - chooseTransactionFee, - currentNetwork, - formRef, - isSendingMax, - onFormStateChange, - onSetIsSendingMax, - utxos, - validationSchema, - } = useBtcSendForm(); - return ( - - - {props => { - onFormStateChange(props.values); - const sendMaxCalculation = calcMaxSpend(props.values.recipient, utxos); - - return ( -
- - - - - } - > - - - } - onSetIsSendingMax={onSetIsSendingMax} - isSendingMax={isSendingMax} - switchableAmount={ - - } - /> - } name={info.name} symbol={symbol} /> - - {currentNetwork.chain.bitcoin.bitcoinNetwork === 'testnet' && ( - - This is a Bitcoin testnet transaction. - - Get testnet BTC here ↗ - - - )} - - - - - - {/* This is for testing purposes only, to make sure the form is ready to be submitted. */} - {calcMaxSpend(props.values.recipient, utxos).spendableBitcoin.toNumber() > 0 ? ( - - ) : null} - - ); - }} -
-
+ + {signer => ( + + {token => ( + + )} + + )} + ); } diff --git a/src/app/pages/send/send-crypto-asset-form/form/sip10/sip10-token-send-form-container.tsx b/src/app/pages/send/send-crypto-asset-form/form/sip10/sip10-token-send-form-container.tsx index fbee8ba9b37..d89feab19b0 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/sip10/sip10-token-send-form-container.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/sip10/sip10-token-send-form-container.tsx @@ -1,5 +1,6 @@ +import type { CryptoAssetBalance, MarketData, Sip10CryptoAssetInfo } from '@leather-wallet/models'; + import { StacksAssetAvatar } from '@app/components/stacks-asset-avatar'; -import type { Sip10AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; import { StxAvatarIcon } from '@app/ui/components/avatar/stx-avatar-icon'; import { AmountField } from '../../components/amount-field'; @@ -10,9 +11,15 @@ import { StacksCommonSendForm } from '../stacks/stacks-common-send-form'; import { useSip10SendForm } from './use-sip10-send-form'; interface Sip10TokenSendFormContainerProps { - asset: Sip10AccountCryptoAssetWithDetails; + assetInfo: Sip10CryptoAssetInfo; + balance: CryptoAssetBalance; + marketData: MarketData; } -export function Sip10TokenSendFormContainer({ asset }: Sip10TokenSendFormContainerProps) { +export function Sip10TokenSendFormContainer({ + assetInfo, + balance, + marketData, +}: Sip10TokenSendFormContainerProps) { const { availableTokenBalance, initialValues, @@ -21,10 +28,9 @@ export function Sip10TokenSendFormContainer({ asset }: Sip10TokenSendFormContain stacksFtFees: fees, validationSchema, avatar, - marketData, decimals, symbol, - } = useSip10SendForm({ asset }); + } = useSip10SendForm({ assetInfo, balance }); const amountField = ( ; } - return children({ asset }); + return children(token); } export function Sip10TokenSendForm() { return ( - {({ asset }) => } + {token => ( + + )} ); } diff --git a/src/app/pages/send/send-crypto-asset-form/form/sip10/use-sip10-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/sip10/use-sip10-send-form.tsx index 41c3b66c600..af77f8edd1d 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/sip10/use-sip10-send-form.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/sip10/use-sip10-send-form.tsx @@ -1,5 +1,6 @@ import { useMemo } from 'react'; +import type { CryptoAssetBalance, Sip10CryptoAssetInfo } from '@leather-wallet/models'; import { FormikHelpers } from 'formik'; import * as yup from 'yup'; @@ -9,7 +10,6 @@ import { StacksSendFormValues } from '@shared/models/form.model'; import { convertAmountToBaseUnit } from '@app/common/money/calculate-money'; import { getSafeImageCanonicalUri } from '@app/common/stacks-utils'; import { stacksFungibleTokenAmountValidator } from '@app/common/validation/forms/amount-validators'; -import type { Sip10AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; import { useCalculateStacksTxFees } from '@app/query/stacks/fees/fees.hooks'; import { useFtTokenTransferUnsignedTx, @@ -20,31 +20,32 @@ import { useSendFormNavigate } from '../../hooks/use-send-form-navigate'; import { useStacksCommonSendForm } from '../stacks/use-stacks-common-send-form'; interface UseSip10SendFormArgs { - asset: Sip10AccountCryptoAssetWithDetails; + assetInfo: Sip10CryptoAssetInfo; + balance: CryptoAssetBalance; } -export function useSip10SendForm({ asset }: UseSip10SendFormArgs) { - const generateTx = useGenerateFtTokenTransferUnsignedTx(asset.info); +export function useSip10SendForm({ assetInfo, balance }: UseSip10SendFormArgs) { + const generateTx = useGenerateFtTokenTransferUnsignedTx(assetInfo); const sendFormNavigate = useSendFormNavigate(); - const unsignedTx = useFtTokenTransferUnsignedTx(asset.info); + const unsignedTx = useFtTokenTransferUnsignedTx(assetInfo); const { data: stacksFtFees } = useCalculateStacksTxFees(unsignedTx); - const availableTokenBalance = asset.balance.availableBalance; + const availableTokenBalance = balance.availableBalance; const sendMaxBalance = useMemo( () => convertAmountToBaseUnit(availableTokenBalance), [availableTokenBalance] ); const { initialValues, checkFormValidation, recipient, memo, nonce } = useStacksCommonSendForm({ - symbol: asset.info.symbol, + symbol: assetInfo.symbol, availableTokenBalance, }); function createFtAvatar() { return { - avatar: asset.info.contractId, - imageCanonicalUri: getSafeImageCanonicalUri(asset.info.imageCanonicalUri, asset.info.name), + avatar: assetInfo.contractId, + imageCanonicalUri: getSafeImageCanonicalUri(assetInfo.imageCanonicalUri, assetInfo.name), }; } @@ -53,9 +54,8 @@ export function useSip10SendForm({ asset }: UseSip10SendFormArgs) { initialValues, sendMaxBalance, stacksFtFees, - symbol: asset.info.symbol, - decimals: asset.info.decimals, - marketData: asset.marketData, + symbol: assetInfo.symbol, + decimals: assetInfo.decimals, avatar: createFtAvatar(), validationSchema: yup.object({ amount: stacksFungibleTokenAmountValidator(availableTokenBalance), @@ -75,8 +75,8 @@ export function useSip10SendForm({ asset }: UseSip10SendFormArgs) { if (!tx) return logger.error('Attempted to generate unsigned tx, but tx is undefined'); sendFormNavigate.toConfirmAndSignStacksSip10Transaction({ - decimals: asset.info.decimals, - name: asset.info.name, + decimals: assetInfo.decimals, + name: assetInfo.name, tx, }); }, diff --git a/src/app/query/bitcoin/balance/btc-balance-native-segwit.hooks.ts b/src/app/query/bitcoin/balance/btc-balance-native-segwit.hooks.ts index 84f43815ec3..ad5325c6c08 100644 --- a/src/app/query/bitcoin/balance/btc-balance-native-segwit.hooks.ts +++ b/src/app/query/bitcoin/balance/btc-balance-native-segwit.hooks.ts @@ -11,7 +11,7 @@ import { useGetBitcoinBalanceByAddress } from './btc-balance.hooks'; function createBtcCryptoAssetBalance(balance: Money): BtcCryptoAssetBalance { return { availableBalance: balance, - // TODO: Asset refactor: can we determine these here or are they nec? + // TODO: Can we determine these here or are they nec? protectedBalance: createMoney(0, 'BTC'), uneconomicalBalance: createMoney(0, 'BTC'), }; diff --git a/src/app/query/bitcoin/btc/btc-crypto-asset.hooks.ts b/src/app/query/bitcoin/btc/btc-crypto-asset.hooks.ts deleted file mode 100644 index 52cc752989d..00000000000 --- a/src/app/query/bitcoin/btc/btc-crypto-asset.hooks.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { BtcCryptoAssetInfo } from '@leather-wallet/models'; - -import { BTC_DECIMALS } from '@shared/constants'; -import { createMoney } from '@shared/models/money.model'; - -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; -import { createAccountCryptoAssetWithDetailsFactory } from '@app/query/models/crypto-asset.model'; - -import type { BtcAccountCryptoAssetWithDetails } from '../../models/crypto-asset.model'; -import { useBtcCryptoAssetBalanceNativeSegwit } from '../balance/btc-balance-native-segwit.hooks'; - -const btcCryptoAssetInfo: BtcCryptoAssetInfo = { - decimals: BTC_DECIMALS, - hasMemo: false, - name: 'bitcoin', - symbol: 'BTC', -}; - -const btcCryptoAssetBalancePlaceholder = { - availableBalance: createMoney(0, 'BTC'), - protectedBalance: createMoney(0, 'BTC'), - uneconomicalBalance: createMoney(0, 'BTC'), -}; - -export const btcCryptoAssetPlaceholder = - createAccountCryptoAssetWithDetailsFactory({ - balance: btcCryptoAssetBalancePlaceholder, - chain: 'bitcoin', - info: btcCryptoAssetInfo, - marketData: null, - type: 'btc', - }); - -export function useBtcAccountCryptoAssetWithDetails(address: string) { - const { btcCryptoAssetBalance, isInitialLoading } = useBtcCryptoAssetBalanceNativeSegwit(address); - const marketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); - - return { - asset: createAccountCryptoAssetWithDetailsFactory({ - balance: btcCryptoAssetBalance, - chain: 'bitcoin', - info: btcCryptoAssetInfo, - marketData, - type: 'btc', - }), - isInitialLoading, - }; -} diff --git a/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.hooks.ts b/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.hooks.ts index d0dff8e26b9..e836ec9bcd8 100644 --- a/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.hooks.ts +++ b/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.hooks.ts @@ -1,3 +1,4 @@ +import type { Brc20CryptoAssetInfo } from '@leather-wallet/models'; import BigNumber from 'bignumber.js'; import { createMarketData, createMarketPair } from '@shared/models/market.model'; @@ -6,11 +7,9 @@ import { createMoney } from '@shared/models/money.model'; import { unitToFractionalUnit } from '@app/common/money/unit-conversion'; import { useGetBrc20TokensQuery } from '@app/query/bitcoin/ordinals/brc20/brc20-tokens.query'; import { useCalculateBitcoinFiatValue } from '@app/query/common/market-data/market-data.hooks'; +import { createCryptoAssetBalance } from '@app/query/common/models'; import { useConfigOrdinalsbot } from '@app/query/common/remote-config/remote-config.query'; -import { - type Brc20AccountCryptoAssetWithDetails, - createAccountCryptoAssetWithDetailsFactory, -} from '@app/query/models/crypto-asset.model'; +import { isFetchedWithSuccess } from '@app/query/query-config'; import { useAppDispatch } from '@app/store'; import { useCurrentAccountIndex } from '@app/store/accounts/account'; import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; @@ -88,44 +87,44 @@ export function useBrc20Transfers(holderAddress: string) { }; } -export function useBrc20AccountCryptoAssetsWithDetails() { +function createBrc20CryptoAssetInfo(decimals: number, ticker: string): Brc20CryptoAssetInfo { + return { + decimals, + hasMemo: false, + name: 'brc-20', + symbol: ticker, + }; +} + +export function useBrc20Tokens() { const calculateBitcoinFiatValue = useCalculateBitcoinFiatValue(); - const { data: allBrc20TokensResponse } = useGetBrc20TokensQuery(); + const query = useGetBrc20TokensQuery(); + + if (!isFetchedWithSuccess(query)) return []; - const tokens = allBrc20TokensResponse?.pages + const tokens = query.data.pages .flatMap(page => page.brc20Tokens) .filter(token => token.length > 0) .flatMap(token => token); - return ( - tokens?.map(token => { - const priceAsFiat = calculateBitcoinFiatValue( - createMoney(new BigNumber(token.balance.min_listed_unit_price ?? 0), 'BTC') - ); - return createAccountCryptoAssetWithDetailsFactory({ - balance: { - availableBalance: createMoney( - unitToFractionalUnit(token.info.decimals)(new BigNumber(token.balance.overall_balance)), - token.balance.ticker, - token.info.decimals - ), - }, - chain: 'bitcoin', - holderAddress: token.holderAddress, - info: { - decimals: token.info.decimals, - hasMemo: false, - name: 'brc-20', - symbol: token.info.ticker, - }, - marketData: token.balance.min_listed_unit_price - ? createMarketData( - createMarketPair(token.balance.ticker, 'USD'), - createMoney(priceAsFiat.amount, 'USD') - ) - : null, - type: 'brc-20', - }); - }) ?? [] - ); + return tokens.map(token => { + const fiatPrice = calculateBitcoinFiatValue( + createMoney(new BigNumber(token.balance.min_listed_unit_price ?? 0), 'BTC') + ); + return { + assetInfo: createBrc20CryptoAssetInfo(token.info.decimals, token.balance.ticker), + balance: createCryptoAssetBalance( + createMoney( + unitToFractionalUnit(token.info.decimals)(new BigNumber(token.balance.overall_balance)), + token.balance.ticker, + token.info.decimals + ) + ), + holderAddress: token.holderAddress, + marketData: createMarketData( + createMarketPair(token.balance.ticker, 'USD'), + createMoney(fiatPrice.amount, 'USD') + ), + }; + }); } diff --git a/src/app/query/common/alex-sdk/alex-sdk.hooks.ts b/src/app/query/common/alex-sdk/alex-sdk.hooks.ts index 6d3b95fcb92..e798511448d 100644 --- a/src/app/query/common/alex-sdk/alex-sdk.hooks.ts +++ b/src/app/query/common/alex-sdk/alex-sdk.hooks.ts @@ -12,7 +12,7 @@ import { sortAssetsByName } from '@app/common/asset-utils'; import { convertAmountToFractionalUnit } from '@app/common/money/calculate-money'; import { pullContractIdFromIdentity } from '@app/common/utils'; import { useCurrentStxAvailableUnlockedBalance } from '@app/query/stacks/balance/account-balance.hooks'; -import { useTransferableSip10CryptoAssetsWithDetails } from '@app/query/stacks/sip10/sip10-tokens.hooks'; +import { useTransferableSip10Tokens } from '@app/query/stacks/sip10/sip10-tokens.hooks'; import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { getAvatarFallback } from '@app/ui/components/avatar/avatar'; @@ -38,11 +38,12 @@ export function useAlexCurrencyPriceAsMarketData() { const { data: prices } = useAlexSdkLatestPricesQuery(); return useCallback( - (principal: string, symbol?: string) => { + (principal: string, symbol: string) => { const tokenInfo = supportedCurrencies .filter(isDefined) .find(token => pullContractIdFromIdentity(token.contractAddress) === principal); - if (!symbol || !prices || !tokenInfo) return null; + if (!prices || !tokenInfo) + return createMarketData(createMarketPair(symbol, 'USD'), createMoney(0, 'USD')); const currency = tokenInfo.id as Currency; const price = convertAmountToFractionalUnit(new BigNumber(prices[currency] ?? 0), 2); return createMarketData(createMarketPair(symbol, 'USD'), createMoney(price, 'USD')); @@ -56,7 +57,7 @@ function useCreateSwapAsset() { const { data: prices } = useAlexSdkLatestPricesQuery(); const priceAsMarketData = useAlexCurrencyPriceAsMarketData(); const availableUnlockedBalance = useCurrentStxAvailableUnlockedBalance(); - const assets = useTransferableSip10CryptoAssetsWithDetails(address); + const sip10Tokens = useTransferableSip10Tokens(address); return useCallback( (tokenInfo?: TokenInfo): SwapAsset | undefined => { @@ -68,8 +69,8 @@ function useCreateSwapAsset() { const currency = tokenInfo.id as Currency; const principal = pullContractIdFromIdentity(tokenInfo.contractAddress); - const availableBalance = assets.find(a => a.info.contractId === principal)?.balance - .availableBalance; + const availableBalance = sip10Tokens.find(token => token.assetInfo.contractId === principal) + ?.balance.availableBalance; const swapAsset = { currency, @@ -96,7 +97,7 @@ function useCreateSwapAsset() { : priceAsMarketData(principal, tokenInfo.name), }; }, - [assets, availableUnlockedBalance, priceAsMarketData, prices] + [availableUnlockedBalance, priceAsMarketData, prices, sip10Tokens] ); } diff --git a/src/app/query/common/models.ts b/src/app/query/common/models.ts new file mode 100644 index 00000000000..a27b269fdc3 --- /dev/null +++ b/src/app/query/common/models.ts @@ -0,0 +1,25 @@ +import type { + Brc20CryptoAssetInfo, + BtcCryptoAssetInfo, + CryptoAssetBalance, + Money, + RuneCryptoAssetInfo, + Sip10CryptoAssetInfo, + Src20CryptoAssetInfo, + Stx20CryptoAssetInfo, + StxCryptoAssetInfo, +} from '@leather-wallet/models'; + +// TODO: Move to the models pkg +export type CryptoAssetInfo = + | BtcCryptoAssetInfo + | StxCryptoAssetInfo + | Brc20CryptoAssetInfo + | RuneCryptoAssetInfo + | Src20CryptoAssetInfo + | Sip10CryptoAssetInfo + | Stx20CryptoAssetInfo; + +export function createCryptoAssetBalance(balance: Money): CryptoAssetBalance { + return { availableBalance: balance }; +} diff --git a/src/app/query/models/crypto-asset.model.ts b/src/app/query/models/crypto-asset.model.ts deleted file mode 100644 index ac2ecaa1389..00000000000 --- a/src/app/query/models/crypto-asset.model.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { - BaseCryptoAssetInfo, - Blockchains, - Brc20CryptoAssetInfo, - BtcCryptoAssetBalance, - BtcCryptoAssetInfo, - CryptoAssetBalance, - CryptoAssetType, - MarketData, - Sip10CryptoAssetInfo, - StxCryptoAssetBalance, - StxCryptoAssetInfo, -} from '@leather-wallet/models'; - -interface AccountCryptoAssetDetails { - balance: CryptoAssetBalance; - chain: Blockchains; - info: BaseCryptoAssetInfo; - marketData: MarketData | null; - type: CryptoAssetType; -} - -export function createAccountCryptoAssetWithDetailsFactory( - args: T -): T { - const { balance, chain, info, marketData, type } = args; - return { - balance, - chain, - info, - marketData, - type, - } as T; -} - -export interface BtcAccountCryptoAssetWithDetails extends AccountCryptoAssetDetails { - balance: BtcCryptoAssetBalance; - info: BtcCryptoAssetInfo; -} - -export interface StxAccountCryptoAssetWithDetails extends AccountCryptoAssetDetails { - balance: StxCryptoAssetBalance; - info: StxCryptoAssetInfo; -} - -export interface Brc20AccountCryptoAssetWithDetails extends AccountCryptoAssetDetails { - holderAddress: string; - info: Brc20CryptoAssetInfo; -} - -export interface Sip10AccountCryptoAssetWithDetails extends AccountCryptoAssetDetails { - info: Sip10CryptoAssetInfo; -} - -export type AccountCryptoAssetWithDetails = - | BtcAccountCryptoAssetWithDetails - | StxAccountCryptoAssetWithDetails - | Brc20AccountCryptoAssetWithDetails - | Sip10AccountCryptoAssetWithDetails; diff --git a/src/app/query/query-config.ts b/src/app/query/query-config.ts index a5ede1495ab..ec1998617d7 100644 --- a/src/app/query/query-config.ts +++ b/src/app/query/query-config.ts @@ -1,4 +1,8 @@ -import { UseQueryOptions } from '@tanstack/react-query'; +import { + type QueryObserverSuccessResult, + UseQueryOptions, + type UseQueryResult, +} from '@tanstack/react-query'; type AllowedReactQueryConfigOptions = keyof Pick< UseQueryOptions, @@ -9,3 +13,9 @@ export type AppUseQueryConfig = Pick< UseQueryOptions, AllowedReactQueryConfigOptions >; + +export function isFetchedWithSuccess( + query: UseQueryResult +): query is QueryObserverSuccessResult { + return !query.isError && !query.isLoading && query.data !== undefined; +} diff --git a/src/app/query/stacks/sip10/sip10-tokens.hooks.ts b/src/app/query/stacks/sip10/sip10-tokens.hooks.ts index d50030f12e1..69f56b1bdd9 100644 --- a/src/app/query/stacks/sip10/sip10-tokens.hooks.ts +++ b/src/app/query/stacks/sip10/sip10-tokens.hooks.ts @@ -12,31 +12,19 @@ import { useAlexCurrencyPriceAsMarketData, useAlexSwappableAssets, } from '@app/query/common/alex-sdk/alex-sdk.hooks'; -import { - type AccountCryptoAssetWithDetails, - type Sip10AccountCryptoAssetWithDetails, - createAccountCryptoAssetWithDetailsFactory, -} from '@app/query/models/crypto-asset.model'; +import { createCryptoAssetBalance } from '@app/query/common/models'; import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { getAssetStringParts } from '@app/ui/utils/get-asset-string-parts'; import { useStacksAccountBalanceFungibleTokens } from '../balance/account-balance.hooks'; import { useStacksAccountFungibleTokenMetadata } from '../token-metadata/fungible-tokens/fungible-token-metadata.hooks'; import { isFtAsset } from '../token-metadata/token-metadata.utils'; -import { - type Sip10CryptoAssetFilter, - filterSip10AccountCryptoAssetsWithDetails, -} from './sip10-tokens.utils'; +import { type Sip10CryptoAssetFilter, filterSip10Tokens } from './sip10-tokens.utils'; export function isTransferableSip10Token(asset: Partial) { return !isUndefined(asset.decimals) && !isUndefined(asset.name) && !isUndefined(asset.symbol); } -export function getSip10InfoFromAsset(asset: AccountCryptoAssetWithDetails) { - if ('contractId' in asset.info) return asset.info; - return; -} - function createSip10CryptoAssetInfo( contractId: string, key: string, @@ -57,7 +45,7 @@ function createSip10CryptoAssetInfo( }; } -function useSip10AccountCryptoAssetsWithDetails(address: string) { +function useSip10Tokens(address: string) { const { data: tokens = {} } = useStacksAccountBalanceFungibleTokens(address); const tokenMetadata = useStacksAccountFungibleTokenMetadata(tokens); const priceAsMarketData = useAlexCurrencyPriceAsMarketData(); @@ -69,20 +57,15 @@ function useSip10AccountCryptoAssetsWithDetails(address: string) { const token = tokenMetadata[i].data; if (!(token && isFtAsset(token))) return; const contractId = pullContractIdFromIdentity(key); + const symbol = token.symbol ?? getTicker(token.name ?? ''); - return createAccountCryptoAssetWithDetailsFactory({ - balance: { - availableBalance: createMoney( - new BigNumber(value.balance), - token.symbol ?? getTicker(token.name ?? ''), - token.decimals ?? 0 - ), - }, - chain: 'stacks', - info: createSip10CryptoAssetInfo(contractId, key, token), - marketData: priceAsMarketData(contractId, token.symbol), - type: 'sip-10', - }); + return { + assetInfo: createSip10CryptoAssetInfo(contractId, key, token), + balance: createCryptoAssetBalance( + createMoney(new BigNumber(value.balance), symbol, token.decimals ?? 0) + ), + marketData: priceAsMarketData(contractId, symbol), + }; }) .filter(isDefined) .filter(asset => asset.balance.availableBalance.amount.isGreaterThan(0)), @@ -90,35 +73,27 @@ function useSip10AccountCryptoAssetsWithDetails(address: string) { ); } -export function useSip10CryptoAssetWithDetails(contractId: string) { +export function useSip10Token(contractId: string) { const address = useCurrentStacksAccountAddress(); - const assets = useSip10AccountCryptoAssetsWithDetails(address); + const tokens = useSip10Tokens(address); return useMemo( - () => assets.find(asset => asset.info.contractId === contractId), - [assets, contractId] + () => tokens.find(token => token.assetInfo.contractId === contractId), + [contractId, tokens] ); } -interface UseFilteredSip10AccountCryptoAssetsWithDetailsArgs { +interface UseFilteredSip10TokensArgs { address: string; filter?: Sip10CryptoAssetFilter; } -export function useFilteredSip10AccountCryptoAssetsWithDetails({ - address, - filter = 'all', -}: UseFilteredSip10AccountCryptoAssetsWithDetailsArgs) { - const assets = useSip10AccountCryptoAssetsWithDetails(address); +export function useFilteredSip10Tokens({ address, filter = 'all' }: UseFilteredSip10TokensArgs) { + const tokens = useSip10Tokens(address); const { data: swapAssets = [] } = useAlexSwappableAssets(); - return useMemo( - () => filterSip10AccountCryptoAssetsWithDetails(assets, swapAssets, filter), - [assets, swapAssets, filter] - ); + return useMemo(() => filterSip10Tokens(swapAssets, tokens, filter), [swapAssets, tokens, filter]); } -export function useTransferableSip10CryptoAssetsWithDetails( - address: string -): Sip10AccountCryptoAssetWithDetails[] { - const assets = useSip10AccountCryptoAssetsWithDetails(address); - return useMemo(() => assets.filter(asset => asset.info.canTransfer), [assets]); +export function useTransferableSip10Tokens(address: string) { + const tokens = useSip10Tokens(address); + return useMemo(() => tokens.filter(token => token.assetInfo.canTransfer), [tokens]); } diff --git a/src/app/query/stacks/sip10/sip10-tokens.utils.ts b/src/app/query/stacks/sip10/sip10-tokens.utils.ts index 015f5154f60..48922b69606 100644 --- a/src/app/query/stacks/sip10/sip10-tokens.utils.ts +++ b/src/app/query/stacks/sip10/sip10-tokens.utils.ts @@ -1,22 +1,27 @@ +import type { CryptoAssetBalance, MarketData, Sip10CryptoAssetInfo } from '@leather-wallet/models'; + import type { SwapAsset } from '@app/query/common/alex-sdk/alex-sdk.hooks'; -import type { Sip10AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; import { getAssetStringParts } from '@app/ui/utils/get-asset-string-parts'; export type Sip10CryptoAssetFilter = 'all' | 'supported' | 'unsupported'; -export function filterSip10AccountCryptoAssetsWithDetails( - assets: Sip10AccountCryptoAssetWithDetails[], +export function filterSip10Tokens( swapAssets: SwapAsset[], + tokens: { + assetInfo: Sip10CryptoAssetInfo; + balance: CryptoAssetBalance; + marketData: MarketData; + }[], filter: Sip10CryptoAssetFilter ) { - return assets.filter(asset => { - const { address: contractAddress } = getAssetStringParts(asset.info.contractId); + return tokens.filter(token => { + const { address: contractAddress } = getAssetStringParts(token.assetInfo.contractId); if (filter === 'supported') { return swapAssets.some(swapAsset => swapAsset.principal.includes(contractAddress)); } if (filter === 'unsupported') { return !swapAssets.some(swapAsset => swapAsset.principal.includes(contractAddress)); } - return assets; + return tokens; }); } diff --git a/src/app/query/stacks/stx/stx-crypto-asset.hooks.ts b/src/app/query/stacks/stx/stx-crypto-asset.hooks.ts deleted file mode 100644 index 6c89c853988..00000000000 --- a/src/app/query/stacks/stx/stx-crypto-asset.hooks.ts +++ /dev/null @@ -1,58 +0,0 @@ -import type { StxCryptoAssetInfo } from '@leather-wallet/models'; - -import { STX_DECIMALS } from '@shared/constants'; -import { createMoney } from '@shared/models/money.model'; -import { isUndefined } from '@shared/utils'; - -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; -import { - type StxAccountCryptoAssetWithDetails, - createAccountCryptoAssetWithDetailsFactory, -} from '@app/query/models/crypto-asset.model'; - -import { useStxCryptoAssetBalance } from '../balance/account-balance.hooks'; - -const stxCryptoAssetInfo: StxCryptoAssetInfo = { - decimals: STX_DECIMALS, - hasMemo: true, - name: 'stacks', - symbol: 'STX', -}; - -const stxCryptoAssetBalancePlaceholder = { - availableBalance: createMoney(0, 'STX'), - availableUnlockedBalance: createMoney(0, 'STX'), - inboundBalance: createMoney(0, 'STX'), - lockedBalance: createMoney(0, 'STX'), - outboundBalance: createMoney(0, 'STX'), - pendingBalance: createMoney(0, 'STX'), - totalBalance: createMoney(0, 'STX'), - unlockedBalance: createMoney(0, 'STX'), -}; - -export const stxCryptoAssetPlaceholder = - createAccountCryptoAssetWithDetailsFactory({ - balance: stxCryptoAssetBalancePlaceholder, - chain: 'stacks', - info: stxCryptoAssetInfo, - marketData: null, - type: 'stx', - }); - -export function useStxAccountCryptoAssetWithDetails(address: string) { - const { data: balance, isInitialLoading } = useStxCryptoAssetBalance(address); - const marketData = useCryptoCurrencyMarketDataMeanAverage('STX'); - - if (isUndefined(balance)) return { asset: stxCryptoAssetPlaceholder, isInitialLoading }; - - return { - asset: createAccountCryptoAssetWithDetailsFactory({ - balance, - chain: 'stacks', - info: stxCryptoAssetInfo, - marketData, - type: 'stx', - }), - isInitialLoading, - }; -}