diff --git a/packages/blockchain-link-types/src/common.ts b/packages/blockchain-link-types/src/common.ts index 75004777e2cf..68254bde47fa 100644 --- a/packages/blockchain-link-types/src/common.ts +++ b/packages/blockchain-link-types/src/common.ts @@ -5,6 +5,7 @@ import type { AddressAlias, TokenTransfer as BlockbookTokenTransfer, ContractInfo, + MultiTokenValue, StakingPool, } from './blockbook-api'; @@ -188,6 +189,10 @@ export interface TokenInfo { accounts?: TokenAccount[]; // token accounts for solana policyId?: string; // Cardano policy id fingerprint?: string; // Cardano starting with "asset" + multiTokenValues?: MultiTokenValue[]; + ids?: string[]; + totalReceived?: string; + totalSent?: string; // transfers: number, // total transactions? } diff --git a/packages/suite/src/utils/wallet/nftUtils.ts b/packages/suite/src/utils/wallet/nftUtils.ts index d5a56325d1e4..0edc81292b4c 100644 --- a/packages/suite/src/utils/wallet/nftUtils.ts +++ b/packages/suite/src/utils/wallet/nftUtils.ts @@ -1,60 +1 @@ -import { isTokenDefinitionKnown, TokenDefinition } from '@suite-common/token-definitions'; -import { isNftMatchesSearch, filterNftTokens } from '@suite-common/wallet-utils'; -import { NetworkSymbol, getNetworkFeatures } from '@suite-common/wallet-config'; -import { Token } from '@trezor/blockchain-link-types/src/blockbook-api'; - -type GetNfts = { - tokens: Token[]; - symbol: NetworkSymbol; - nftDefinitions?: TokenDefinition; - searchQuery?: string; -}; - export type NftType = 'ERC721' | 'ERC1155'; - -export const getNfts = ({ tokens, symbol, nftDefinitions, searchQuery }: GetNfts) => { - // filter out NFT tokens until we implement them - const nfts = filterNftTokens(tokens); - - const hasNftDefinitions = getNetworkFeatures(symbol).includes('nft-definitions'); - - const shownVerified: Token[] = []; - const shownUnverified: Token[] = []; - const hiddenVerified: Token[] = []; - const hiddenUnverified: Token[] = []; - - nfts.forEach(token => { - const isKnown = isTokenDefinitionKnown(nftDefinitions?.data, symbol, token.contract || ''); - const isHidden = nftDefinitions?.hide.includes(token.contract || ''); - const isShown = nftDefinitions?.show.includes(token.contract || ''); - - const query = searchQuery ? searchQuery.trim().toLowerCase() : ''; - - if (searchQuery && !isNftMatchesSearch(token, query)) return; - - const pushToArray = (arrayVerified: Token[], arrayUnverified: Token[]) => { - if (isKnown) { - arrayVerified.push(token); - } else { - arrayUnverified.push(token); - } - }; - - if (isShown) { - pushToArray(shownVerified, shownUnverified); - } else if (hasNftDefinitions && !isKnown) { - pushToArray(hiddenVerified, hiddenUnverified); - } else if (isHidden) { - pushToArray(hiddenVerified, hiddenUnverified); - } else { - pushToArray(shownVerified, shownUnverified); - } - }); - - return { - shownVerified, - shownUnverified, - hiddenVerified, - hiddenUnverified, - }; -}; diff --git a/packages/suite/src/utils/wallet/tokenUtils.ts b/packages/suite/src/utils/wallet/tokenUtils.ts index 137411668bff..ce70f1d990c5 100644 --- a/packages/suite/src/utils/wallet/tokenUtils.ts +++ b/packages/suite/src/utils/wallet/tokenUtils.ts @@ -1,7 +1,12 @@ import { BigNumber } from '@trezor/utils/src/bigNumber'; import { Account, Rate, TokenAddress, RatesByKey } from '@suite-common/wallet-types'; import { TokenInfo } from '@trezor/connect'; -import { getFiatRateKey, isNftToken, isTokenMatchesSearch } from '@suite-common/wallet-utils'; +import { + getFiatRateKey, + isNftMatchesSearch, + isNftToken, + isTokenMatchesSearch, +} from '@suite-common/wallet-utils'; import { NetworkSymbol, getNetworkFeatures } from '@suite-common/wallet-config'; import { FiatCurrencyCode } from '@suite-common/suite-config'; import { @@ -10,6 +15,8 @@ import { isTokenDefinitionKnown, } from '@suite-common/token-definitions'; +import { Token } from '@trezor/blockchain-link-types/src/blockbook-api'; + export interface TokensWithRates extends TokenInfo { fiatValue: BigNumber; fiatRate?: Rate; @@ -65,32 +72,64 @@ export const formatTokenSymbol = (symbol: string) => { return isTokenSymbolLong ? `${upperCasedSymbol.slice(0, 7)}...` : upperCasedSymbol; }; -export const getTokens = ( - tokens: EnhancedTokenInfo[] | TokenInfo[], - symbol: NetworkSymbol, - coinDefinitions?: TokenDefinition, - searchQuery?: string, -) => { +type GetTokens = { + tokens: EnhancedTokenInfo[] | TokenInfo[]; + symbol: NetworkSymbol; + tokenDefinitions?: TokenDefinition; + searchQuery?: string; + isNft?: boolean; +}; + +export type TokensResult = { + shownWithBalance: EnhancedTokenInfo[]; + shownWithoutBalance: EnhancedTokenInfo[]; + hiddenWithBalance: EnhancedTokenInfo[]; + hiddenWithoutBalance: EnhancedTokenInfo[]; + unverifiedWithBalance: EnhancedTokenInfo[]; + unverifiedWithoutBalance: EnhancedTokenInfo[]; +}; + +export type NftTokensResult = { + shownVerified: EnhancedTokenInfo[]; + shownUnverified: EnhancedTokenInfo[]; + hiddenVerified: EnhancedTokenInfo[]; + hiddenUnverified: EnhancedTokenInfo[]; +}; + +export const getTokens = ({ + tokens, + symbol, + tokenDefinitions, + searchQuery, + isNft = false, +}: GetTokens): NftTokensResult | TokensResult => { // filter out NFT tokens until we implement them - const tokensWithoutNFTs = tokens.filter(token => !isNftToken(token)); + const filteredTokens = isNft + ? tokens.filter(token => isNftToken(token)) + : tokens.filter(token => !isNftToken(token)); - const hasCoinDefinitions = getNetworkFeatures(symbol).includes('coin-definitions'); + const hasDefinitions = getNetworkFeatures(symbol).includes( + isNft ? 'nft-definitions' : 'coin-definitions', + ); - const shownWithBalance: EnhancedTokenInfo[] = []; - const shownWithoutBalance: EnhancedTokenInfo[] = []; - const hiddenWithBalance: EnhancedTokenInfo[] = []; - const hiddenWithoutBalance: EnhancedTokenInfo[] = []; - const unverifiedWithBalance: EnhancedTokenInfo[] = []; - const unverifiedWithoutBalance: EnhancedTokenInfo[] = []; + const shownVerified: EnhancedTokenInfo[] = []; + const shownUnverified: EnhancedTokenInfo[] = []; + const hiddenVerified: EnhancedTokenInfo[] = []; + const hiddenUnverified: EnhancedTokenInfo[] = []; - tokensWithoutNFTs.forEach(token => { - const isKnown = isTokenDefinitionKnown(coinDefinitions?.data, symbol, token.contract); - const isHidden = coinDefinitions?.hide.includes(token.contract); - const isShown = coinDefinitions?.show.includes(token.contract); + filteredTokens.forEach(token => { + const isKnown = isTokenDefinitionKnown(tokenDefinitions?.data, symbol, token.contract); + const isHidden = tokenDefinitions?.hide.includes(token.contract); + const isShown = tokenDefinitions?.show.includes(token.contract); const query = searchQuery ? searchQuery.trim().toLowerCase() : ''; - if (searchQuery && !isTokenMatchesSearch(token, query)) return; + if ( + searchQuery && + (!isTokenMatchesSearch(token, query) || + (isNft && isNftMatchesSearch(token as Token, query))) + ) + return; const hasBalance = new BigNumber(token?.balance || '0').gt(0); @@ -106,22 +145,20 @@ export const getTokens = ( }; if (isShown) { - pushToArray(shownWithBalance, shownWithoutBalance); - } else if (hasCoinDefinitions && !isKnown) { - pushToArray(unverifiedWithBalance, unverifiedWithoutBalance); + pushToArray(shownVerified, shownUnverified); + } else if (hasDefinitions && !isKnown) { + pushToArray(hiddenVerified, hiddenUnverified); } else if (isHidden) { - pushToArray(hiddenWithBalance, hiddenWithoutBalance); + pushToArray(hiddenVerified, hiddenUnverified); } else { - pushToArray(shownWithBalance, shownWithoutBalance); + pushToArray(shownVerified, shownUnverified); } }); return { - shownWithBalance, - shownWithoutBalance, - hiddenWithBalance, - hiddenWithoutBalance, - unverifiedWithBalance, - unverifiedWithoutBalance, + shownVerified, + shownUnverified, + hiddenVerified, + hiddenUnverified, }; }; diff --git a/packages/suite/src/views/wallet/nfts/HiddenNfts.tsx b/packages/suite/src/views/wallet/nfts/HiddenNfts.tsx index 94fe60320529..66e68e668049 100644 --- a/packages/suite/src/views/wallet/nfts/HiddenNfts.tsx +++ b/packages/suite/src/views/wallet/nfts/HiddenNfts.tsx @@ -2,12 +2,10 @@ import { Banner, Column, H3 } from '@trezor/components'; import { SelectedAccountLoaded } from '@suite-common/wallet-types'; import NftsTable from './NftsTable/NftsTable'; import { Translation } from 'src/components/suite'; -import { Token } from '@trezor/blockchain-link-types/src/blockbook-api'; import { useSelector } from 'src/hooks/suite'; -import { getNfts } from 'src/utils/wallet/nftUtils'; import { selectNftDefinitions } from '@suite-common/token-definitions'; -import { filterNftTokens } from '@suite-common/wallet-utils'; import { NoTokens } from '../tokens/common/NoTokens'; +import { getTokens, NftTokensResult } from 'src/utils/wallet/tokenUtils'; type NftsTableProps = { selectedAccount: SelectedAccountLoaded; @@ -19,13 +17,14 @@ const HiddenNfts = ({ selectedAccount, searchQuery }: NftsTableProps) => { const nftDefinitions = useSelector(state => selectNftDefinitions(state, selectedAccount.account.symbol), ); - const nfts = getNfts({ - tokens: filteredTokens as Token[], + const nfts = getTokens({ + tokens: filteredTokens, symbol: selectedAccount.account.symbol, - nftDefinitions, - }); + tokenDefinitions: nftDefinitions, + isNft: true, + }) as NftTokensResult; - return nfts.hiddenVerified.length > 0 || nfts.hiddenUnverified.length > 0 ? ( + return (nfts && nfts.hiddenVerified.length > 0) || nfts.hiddenUnverified.length > 0 ? ( { const { translationString } = useTranslation(); const shouldShowCopyAddressModal = useSelector(selectIsCopyAddressModalShown); - const getNftContractExplorerUrl = (network: Network, nft: Token) => { + const getNftContractExplorerUrl = (network: Network, nft: EnhancedTokenInfo) => { const explorerUrl = network.explorer.account; const contractAddress = nft.contract; const queryString = network.explorer.queryString ?? ''; @@ -62,7 +62,7 @@ const NftsRow = ({ nft, type, network, shown }: NftsRowProps) => { return `${explorerUrl}${contractAddress}${queryString}`; }; - const getNftExplorerUrl = (network: Network, nft: Token, id?: string) => { + const getNftExplorerUrl = (network: Network, nft: EnhancedTokenInfo, id?: string) => { const explorerUrl = network.explorer.nft; const contractAddressWithId = nft.contract + `/${id}`; @@ -86,8 +86,6 @@ const NftsRow = ({ nft, type, network, shown }: NftsRowProps) => { } }; - const name = nft.name.length > 15 ? `${nft.name.slice(0, 15)}...` : nft.name; - return ( diff --git a/packages/suite/src/views/wallet/nfts/NftsTable/NftsTable.tsx b/packages/suite/src/views/wallet/nfts/NftsTable/NftsTable.tsx index caf926b8db6d..448d6bcfefdb 100644 --- a/packages/suite/src/views/wallet/nfts/NftsTable/NftsTable.tsx +++ b/packages/suite/src/views/wallet/nfts/NftsTable/NftsTable.tsx @@ -6,6 +6,7 @@ import { Translation } from 'src/components/suite/Translation'; import NftsRow from './NftsRow'; import { Token } from '@trezor/blockchain-link-types/src/blockbook-api'; import { getNetwork } from '@suite-common/wallet-config'; +import { NftTokensResult } from 'src/utils/wallet/tokenUtils'; type NftsTableProps = { selectedAccount: SelectedAccountLoaded; @@ -13,12 +14,7 @@ type NftsTableProps = { type: 'ERC721' | 'ERC1155'; shown?: boolean; verified?: boolean; - nfts: { - shownVerified: Token[]; - shownUnverified: Token[]; - hiddenVerified: Token[]; - hiddenUnverified: Token[]; - }; + nfts: NftTokensResult; }; const NftsTable = ({ diff --git a/suite-common/wallet-utils/src/accountUtils.ts b/suite-common/wallet-utils/src/accountUtils.ts index 721ac3182e71..4caa4de06be2 100644 --- a/suite-common/wallet-utils/src/accountUtils.ts +++ b/suite-common/wallet-utils/src/accountUtils.ts @@ -1187,6 +1187,7 @@ export const isNftMatchesSearch = (token: Token, search: string) => { token.ids ?.map(id => id.toString()) .join(', ') - .includes(search) + .includes(search) || + token.multiTokenValues?.some(value => value.id?.includes(search)) ); }; diff --git a/suite-common/wallet-utils/src/transactionUtils.ts b/suite-common/wallet-utils/src/transactionUtils.ts index 4e42e44231d9..266a3219024f 100644 --- a/suite-common/wallet-utils/src/transactionUtils.ts +++ b/suite-common/wallet-utils/src/transactionUtils.ts @@ -30,7 +30,6 @@ import { FiatCurrencyCode } from '@suite-common/suite-config'; import { formatAmount, formatNetworkAmount, isTokenMatchesSearch } from './accountUtils'; import { toFiatCurrency } from '../src/fiatConverterUtils'; import { getFiatRateKey, roundTimestampToNearestPastHour } from './fiatRatesUtils'; -import { Token } from '@trezor/blockchain-link-types/src/blockbook-api'; export const sortByBlockHeight = (a: { blockHeight?: number }, b: { blockHeight?: number }) => { // if both are missing the blockHeight don't change their order @@ -489,9 +488,6 @@ export const getNftTokenId = (transfer: TokenTransfer) => ? transfer.multiTokenValues[0].id : transfer.amount; -export const filterNftTokens = (tokens: Token[] | TokenInfo[]) => - tokens.filter(token => isNftToken(token as TokenInfo)) as TokenInfo[]; - export const getTxIcon = (txType: WalletAccountTransaction['type']) => { switch (txType) { case 'recv':