From e80e888dad27faadb0309100d11ecc8d4d9f7741 Mon Sep 17 00:00:00 2001 From: fede erbes Date: Wed, 18 Dec 2024 12:35:19 +0100 Subject: [PATCH] feat: enable sip10 and brc20 top tokens to display sBTC by default (#831) * feat: enable sip10 and brc20 top tokens * test: fix token management smoketest * chore: remove unused export * chore: use core release v34.0.0 and update test comment --- package-lock.json | 96 +++++++++++++------ package.json | 2 +- .../transactions/transactionAmount.tsx | 2 +- .../transactions/transactionTitle.tsx | 2 +- .../components/transactions/txTransfers.tsx | 2 +- .../ordinals/useGetBrc20FungibleTokens.ts | 27 ++---- .../runes/useRuneFungibleTokensQuery.ts | 48 ++-------- .../queries/stx/useGetSip10FungibleTokens.ts | 27 ++---- src/app/screens/home/balanceCard/index.tsx | 6 +- src/app/screens/unlistRune/index.tsx | 2 +- src/app/utils/tokens.ts | 52 ++++++++++ tests/specs/managementToken.spec.ts | 8 +- 12 files changed, 160 insertions(+), 114 deletions(-) diff --git a/package-lock.json b/package-lock.json index bf2e8cba5..994e46d65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@sats-connect/core": "0.4.3", "@scure/base": "^1.1.9", "@scure/btc-signer": "1.2.1", - "@secretkeylabs/xverse-core": "^33.0.1", + "@secretkeylabs/xverse-core": "34.0.0", "@stacks/connect": "7.4.1", "@stacks/stacks-blockchain-api-types": "6.1.1", "@stacks/transactions": "6.16.1", @@ -591,12 +591,36 @@ } }, "node_modules/@bitcoinerlab/secp256k1": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@bitcoinerlab/secp256k1/-/secp256k1-1.1.1.tgz", - "integrity": "sha512-uhjW51WfVLpnHN7+G0saDcM/k9IqcyTbZ+bDgLF3AX8V/a3KXSE9vn7UPBrcdU72tp0J4YPR7BHp2m7MLAZ/1Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@bitcoinerlab/secp256k1/-/secp256k1-1.2.0.tgz", + "integrity": "sha512-jeujZSzb3JOZfmJYI0ph1PVpCRV5oaexCgy+RvCXV8XlY+XFB/2n3WOcvBsKLsOw78KYgnQrQWb2HrKE4be88Q==", + "dependencies": { + "@noble/curves": "^1.7.0" + } + }, + "node_modules/@bitcoinerlab/secp256k1/node_modules/@noble/curves": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz", + "integrity": "sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==", "dependencies": { - "@noble/hashes": "^1.1.5", - "@noble/secp256k1": "^1.7.1" + "@noble/hashes": "1.6.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@bitcoinerlab/secp256k1/node_modules/@noble/hashes": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.0.tgz", + "integrity": "sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/@emotion/is-prop-valid": { @@ -1413,9 +1437,9 @@ } }, "node_modules/@secretkeylabs/xverse-core": { - "version": "33.0.1", - "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/33.0.1/47b49ab7d26560370d77d0716d958a27e860b5f5", - "integrity": "sha512-Tn0ka5dM3MDiwV9i7J1peATtU7nTtBgDLGUp6keSp/z2VO0/3vBld06ME2UqUueBGJrF7iBG+NYVSz/FLfiYog==", + "version": "34.0.0", + "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/34.0.0/844ddf23abf2bbf39a145b4e6a6582e7c0c93419", + "integrity": "sha512-7SAfWP2NmGYdNqMVrHCcVXw9ZYClDFZPEhZO2UrssHpB/hzyEdE8iBXCyugwIR4i/sTkYVf5INO1p1MVLLP+ug==", "dependencies": { "@bitcoinerlab/secp256k1": "^1.0.2", "@noble/curves": "^1.2.0", @@ -2867,9 +2891,9 @@ } }, "node_modules/@zondax/ledger-stacks/node_modules/@types/node": { - "version": "18.19.65", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.65.tgz", - "integrity": "sha512-Ay5BZuO1UkTmVHzZJNvZKw/E+iB3GQABb6kijEz89w2JrfhNA+M/ebp18pfz9Gqe9ywhMC8AA8yC01lZq48J+Q==", + "version": "18.19.68", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.68.tgz", + "integrity": "sha512-QGtpFH1vB99ZmTa63K4/FU8twThj4fuVSBkGddTp7uIL/cuoLWIUSL2RcOaigBhfR+hg5pgGkBnkoOxrTVBMKw==", "dependencies": { "undici-types": "~5.26.4" } @@ -3596,9 +3620,9 @@ } }, "node_modules/bitcoinjs-lib": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.6.tgz", - "integrity": "sha512-Fk8+Vc+e2rMoDU5gXkW9tD+313rhkm5h6N9HfZxXvYU9LedttVvmXKTgd9k5rsQJjkSfsv6XRM8uhJv94SrvcA==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.7.tgz", + "integrity": "sha512-tlf/r2DGMbF7ky1MgUqXHzypYHakkEnm0SZP23CJKIqNY/5uNAnMbFhMJdhjrL/7anfb/U8+AlpdjPWjPnAalg==", "dependencies": { "@noble/hashes": "^1.2.0", "bech32": "^2.0.0", @@ -13413,12 +13437,26 @@ } }, "@bitcoinerlab/secp256k1": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@bitcoinerlab/secp256k1/-/secp256k1-1.1.1.tgz", - "integrity": "sha512-uhjW51WfVLpnHN7+G0saDcM/k9IqcyTbZ+bDgLF3AX8V/a3KXSE9vn7UPBrcdU72tp0J4YPR7BHp2m7MLAZ/1Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@bitcoinerlab/secp256k1/-/secp256k1-1.2.0.tgz", + "integrity": "sha512-jeujZSzb3JOZfmJYI0ph1PVpCRV5oaexCgy+RvCXV8XlY+XFB/2n3WOcvBsKLsOw78KYgnQrQWb2HrKE4be88Q==", "requires": { - "@noble/hashes": "^1.1.5", - "@noble/secp256k1": "^1.7.1" + "@noble/curves": "^1.7.0" + }, + "dependencies": { + "@noble/curves": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz", + "integrity": "sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==", + "requires": { + "@noble/hashes": "1.6.0" + } + }, + "@noble/hashes": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.0.tgz", + "integrity": "sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==" + } } }, "@emotion/is-prop-valid": { @@ -13992,9 +14030,9 @@ } }, "@secretkeylabs/xverse-core": { - "version": "33.0.1", - "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/33.0.1/47b49ab7d26560370d77d0716d958a27e860b5f5", - "integrity": "sha512-Tn0ka5dM3MDiwV9i7J1peATtU7nTtBgDLGUp6keSp/z2VO0/3vBld06ME2UqUueBGJrF7iBG+NYVSz/FLfiYog==", + "version": "34.0.0", + "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/34.0.0/844ddf23abf2bbf39a145b4e6a6582e7c0c93419", + "integrity": "sha512-7SAfWP2NmGYdNqMVrHCcVXw9ZYClDFZPEhZO2UrssHpB/hzyEdE8iBXCyugwIR4i/sTkYVf5INO1p1MVLLP+ug==", "requires": { "@bitcoinerlab/secp256k1": "^1.0.2", "@noble/curves": "^1.2.0", @@ -15199,9 +15237,9 @@ } }, "@types/node": { - "version": "18.19.65", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.65.tgz", - "integrity": "sha512-Ay5BZuO1UkTmVHzZJNvZKw/E+iB3GQABb6kijEz89w2JrfhNA+M/ebp18pfz9Gqe9ywhMC8AA8yC01lZq48J+Q==", + "version": "18.19.68", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.68.tgz", + "integrity": "sha512-QGtpFH1vB99ZmTa63K4/FU8twThj4fuVSBkGddTp7uIL/cuoLWIUSL2RcOaigBhfR+hg5pgGkBnkoOxrTVBMKw==", "requires": { "undici-types": "~5.26.4" } @@ -15763,9 +15801,9 @@ } }, "bitcoinjs-lib": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.6.tgz", - "integrity": "sha512-Fk8+Vc+e2rMoDU5gXkW9tD+313rhkm5h6N9HfZxXvYU9LedttVvmXKTgd9k5rsQJjkSfsv6XRM8uhJv94SrvcA==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.7.tgz", + "integrity": "sha512-tlf/r2DGMbF7ky1MgUqXHzypYHakkEnm0SZP23CJKIqNY/5uNAnMbFhMJdhjrL/7anfb/U8+AlpdjPWjPnAalg==", "requires": { "@noble/hashes": "^1.2.0", "bech32": "^2.0.0", diff --git a/package.json b/package.json index 30806a0f3..d4b5a8dd3 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "@sats-connect/core": "0.4.3", "@scure/base": "^1.1.9", "@scure/btc-signer": "1.2.1", - "@secretkeylabs/xverse-core": "^33.0.1", + "@secretkeylabs/xverse-core": "34.0.0", "@stacks/connect": "7.4.1", "@stacks/stacks-blockchain-api-types": "6.1.1", "@stacks/transactions": "6.16.1", diff --git a/src/app/components/transactions/transactionAmount.tsx b/src/app/components/transactions/transactionAmount.tsx index da4523232..4d8d2a5ce 100644 --- a/src/app/components/transactions/transactionAmount.tsx +++ b/src/app/components/transactions/transactionAmount.tsx @@ -39,7 +39,7 @@ export default function TransactionAmount({ protocol, tokenSymbol, }: Props): JSX.Element | null { - const { data: sip10CoinsList } = useVisibleSip10FungibleTokens(); + const { data: sip10CoinsList = [] } = useVisibleSip10FungibleTokens(); const { balanceHidden } = useWalletSelector(); if (currency === 'STX' || (currency === 'FT' && protocol === 'stacks')) { diff --git a/src/app/components/transactions/transactionTitle.tsx b/src/app/components/transactions/transactionTitle.tsx index 7e1b14586..063f68231 100644 --- a/src/app/components/transactions/transactionTitle.tsx +++ b/src/app/components/transactions/transactionTitle.tsx @@ -28,7 +28,7 @@ type Props = { export default function TransactionTitle({ transaction }: Props) { const { t } = useTranslation('translation', { keyPrefix: 'COIN_DASHBOARD_SCREEN' }); - const { data: sip10CoinsList } = useVisibleSip10FungibleTokens(); + const { data: sip10CoinsList = [] } = useVisibleSip10FungibleTokens(); const getTokenTransferTitle = ( tx: StxTransactionData | BtcTransactionData | Brc20HistoryTransactionData, diff --git a/src/app/components/transactions/txTransfers.tsx b/src/app/components/transactions/txTransfers.tsx index b93efc43b..d6d13ba6f 100644 --- a/src/app/components/transactions/txTransfers.tsx +++ b/src/app/components/transactions/txTransfers.tsx @@ -63,7 +63,7 @@ export default function TxTransfers(props: TxTransfersProps) { const { transaction, coin, txFilter } = props; const { balanceHidden } = useWalletSelector(); const selectedAccount = useSelectedAccount(); - const { data: sip10CoinsList } = useVisibleSip10FungibleTokens(); + const { data: sip10CoinsList = [] } = useVisibleSip10FungibleTokens(); const { t } = useTranslation('translation', { keyPrefix: 'COIN_DASHBOARD_SCREEN' }); function formatAddress(addr: string): string { diff --git a/src/app/hooks/queries/ordinals/useGetBrc20FungibleTokens.ts b/src/app/hooks/queries/ordinals/useGetBrc20FungibleTokens.ts index d29cf6a3b..50584960e 100644 --- a/src/app/hooks/queries/ordinals/useGetBrc20FungibleTokens.ts +++ b/src/app/hooks/queries/ordinals/useGetBrc20FungibleTokens.ts @@ -2,7 +2,6 @@ import useSelectedAccount from '@hooks/useSelectedAccount'; import useWalletSelector from '@hooks/useWalletSelector'; import { getBrc20Tokens, - getFungibleTokenStates, getOrdinalsFtBalance, type Brc20Token, type FungibleToken, @@ -10,6 +9,8 @@ import { type SettingsNetwork, } from '@secretkeylabs/xverse-core'; import { useQuery } from '@tanstack/react-query'; +import { selectWithDerivedState } from '@utils/tokens'; +import useGetTopTokens from '../useGetTopTokens'; const brc20TokenToFungibleToken = (coin: Brc20Token): FungibleToken => ({ name: coin.name, @@ -60,30 +61,22 @@ export const useGetBrc20FungibleTokens = (select?: (data: FungibleTokenWithState const { ordinalsAddress } = useSelectedAccount(); const { brc20ManageTokens, fiatCurrency, network, spamTokens, showSpamTokens } = useWalletSelector(); + const { data: topTokensData } = useGetTopTokens(); const queryFn = fetchBrc20FungibleTokens(ordinalsAddress, fiatCurrency, network); - const selectWithDerivedState = (data: FungibleToken[]) => { - const withDerivedState = data.map( - (ft: FungibleToken) => - ({ - ...ft, - ...getFungibleTokenStates({ - fungibleToken: ft, - manageTokens: brc20ManageTokens, - spamTokens, - showSpamTokens, - }), - } as FungibleTokenWithStates), - ); - return select ? select(withDerivedState) : withDerivedState; - }; return useQuery({ queryKey: ['brc20-fungible-tokens', ordinalsAddress, network.type, fiatCurrency], queryFn, enabled: Boolean(network && ordinalsAddress), keepPreviousData: true, - select: selectWithDerivedState, + select: selectWithDerivedState({ + manageTokens: brc20ManageTokens, + spamTokens, + showSpamTokens, + topTokensData: topTokensData?.['brc-20'], + select, + }), }); }; diff --git a/src/app/hooks/queries/runes/useRuneFungibleTokensQuery.ts b/src/app/hooks/queries/runes/useRuneFungibleTokensQuery.ts index 4a71b2df0..4f9ec86f1 100644 --- a/src/app/hooks/queries/runes/useRuneFungibleTokensQuery.ts +++ b/src/app/hooks/queries/runes/useRuneFungibleTokensQuery.ts @@ -1,12 +1,9 @@ import useRunesApi from '@hooks/apiClients/useRunesApi'; import useSelectedAccount from '@hooks/useSelectedAccount'; import useWalletSelector from '@hooks/useWalletSelector'; -import { - getFungibleTokenStates, - type FungibleToken, - type FungibleTokenWithStates, -} from '@secretkeylabs/xverse-core'; +import { type FungibleToken, type FungibleTokenWithStates } from '@secretkeylabs/xverse-core'; import { useQuery } from '@tanstack/react-query'; +import { selectWithDerivedState } from '@utils/tokens'; import useGetTopTokens from '../useGetTopTokens'; export const fetchRuneBalances = @@ -48,45 +45,18 @@ export const useRuneFungibleTokensQuery = ( const { data: topTokensData } = useGetTopTokens(); const queryFn = fetchRuneBalances(runesApi, ordinalsAddress, fiatCurrency); - const selectWithDerivedState = (data: FungibleToken[]) => { - const topTokens = { ...topTokensData?.runes }; - const tokensWithDerivedState = data.map((ft: FungibleToken) => { - let token = ft; - if (topTokens[token.principal]) { - delete topTokens[token.principal]; - token = { ...token, isTopToken: true }; - } - return { - ...token, - ...getFungibleTokenStates({ - fungibleToken: token, - manageTokens: runesManageTokens, - spamTokens, - showSpamTokens, - }), - } as FungibleTokenWithStates; - }); - const topTokensWithDerivedState = Object.values(topTokens).map((ft) => { - const token = { ...ft, isTopToken: true }; - return { - ...token, - ...getFungibleTokenStates({ - fungibleToken: token, - manageTokens: runesManageTokens, - spamTokens, - showSpamTokens, - }), - }; - }); - const withDerivedState = tokensWithDerivedState.concat(topTokensWithDerivedState); - return select ? select(withDerivedState) : withDerivedState; - }; return useQuery({ queryKey: ['get-rune-fungible-tokens', network.type, ordinalsAddress, fiatCurrency], queryFn, enabled: Boolean(network && ordinalsAddress), - select: selectWithDerivedState, + select: selectWithDerivedState({ + manageTokens: runesManageTokens, + spamTokens, + showSpamTokens, + topTokensData: topTokensData?.runes, + select, + }), refetchOnWindowFocus: !!backgroundRefetch, refetchOnReconnect: !!backgroundRefetch, keepPreviousData: true, diff --git a/src/app/hooks/queries/stx/useGetSip10FungibleTokens.ts b/src/app/hooks/queries/stx/useGetSip10FungibleTokens.ts index 289bc8a77..66d4dba44 100644 --- a/src/app/hooks/queries/stx/useGetSip10FungibleTokens.ts +++ b/src/app/hooks/queries/stx/useGetSip10FungibleTokens.ts @@ -4,13 +4,14 @@ import useWalletSelector from '@hooks/useWalletSelector'; import { StacksNetwork, getFtData, - getFungibleTokenStates, getXverseApiClient, type FungibleToken, type FungibleTokenWithStates, type SettingsNetwork, } from '@secretkeylabs/xverse-core'; import { useQuery } from '@tanstack/react-query'; +import { selectWithDerivedState } from '@utils/tokens'; +import useGetTopTokens from '../useGetTopTokens'; export const fetchSip10FungibleTokens = ( @@ -45,6 +46,7 @@ export const useGetSip10FungibleTokens = (select?: (data: FungibleTokenWithState const { sip10ManageTokens, fiatCurrency, network, spamTokens, showSpamTokens } = useWalletSelector(); const currentNetworkInstance = useNetworkSelector(); + const { data: topTokensData } = useGetTopTokens(); const queryFn = fetchSip10FungibleTokens( stxAddress, @@ -52,28 +54,19 @@ export const useGetSip10FungibleTokens = (select?: (data: FungibleTokenWithState network, currentNetworkInstance, ); - const selectWithDerivedState = (data: FungibleToken[]) => { - const withDerivedState = data.map( - (ft: FungibleToken) => - ({ - ...ft, - ...getFungibleTokenStates({ - fungibleToken: ft, - manageTokens: sip10ManageTokens, - spamTokens, - showSpamTokens, - }), - } as FungibleTokenWithStates), - ); - return select ? select(withDerivedState) : withDerivedState; - }; return useQuery({ queryKey: ['sip10-fungible-tokens', network.type, stxAddress, fiatCurrency], queryFn, enabled: Boolean(network && stxAddress), keepPreviousData: true, - select: selectWithDerivedState, + select: selectWithDerivedState({ + manageTokens: sip10ManageTokens, + spamTokens, + showSpamTokens, + topTokensData: topTokensData?.stacks, + select, + }), }); }; diff --git a/src/app/screens/home/balanceCard/index.tsx b/src/app/screens/home/balanceCard/index.tsx index 85dd1b24d..2022bc3de 100644 --- a/src/app/screens/home/balanceCard/index.tsx +++ b/src/app/screens/home/balanceCard/index.tsx @@ -106,9 +106,9 @@ function BalanceCard({ isLoading, isRefetching }: Props) { const { setAccountBalance } = useAccountBalance(); // TODO: refactor this into a hook const oldTotalBalance = accountBalances[getAccountBalanceKey(selectedAccount)]; - const { data: sip10CoinsList } = useVisibleSip10FungibleTokens(); - const { data: brc20CoinsList } = useVisibleBrc20FungibleTokens(); - const { data: runesCoinList } = useVisibleRuneFungibleTokens(); + const { data: sip10CoinsList = [] } = useVisibleSip10FungibleTokens(); + const { data: brc20CoinsList = [] } = useVisibleBrc20FungibleTokens(); + const { data: runesCoinList = [] } = useVisibleRuneFungibleTokens(); const { toggleBalanceView, balanceDisplayState } = useToggleBalanceView(); const balance = calculateTotalBalance({ diff --git a/src/app/screens/unlistRune/index.tsx b/src/app/screens/unlistRune/index.tsx index 861c6916a..b69a0a874 100644 --- a/src/app/screens/unlistRune/index.tsx +++ b/src/app/screens/unlistRune/index.tsx @@ -27,7 +27,7 @@ export default function UnlistRuneScreen() { const { t } = useTranslation('translation', { keyPrefix: 'LIST_RUNE_SCREEN' }); const navigate = useNavigate(); const { runeId } = useParams(); - const { data: runesCoinsList } = useVisibleRuneFungibleTokens(); + const { data: runesCoinsList = [] } = useVisibleRuneFungibleTokens(); const selectedRune = runesCoinsList.find((ft) => ft.principal === runeId); const showRunesListing = useHasFeature(FeatureId.RUNES_LISTING) || process.env.NODE_ENV === 'development'; diff --git a/src/app/utils/tokens.ts b/src/app/utils/tokens.ts index e02ed01d9..f25ff728f 100644 --- a/src/app/utils/tokens.ts +++ b/src/app/utils/tokens.ts @@ -1,8 +1,10 @@ import { getFiatEquivalent, + getFungibleTokenStates, microstacksToStx, satsToBtc, type FungibleToken, + type FungibleTokenWithStates, type StxAddressData, } from '@secretkeylabs/xverse-core'; import type { CurrencyTypes } from '@utils/constants'; @@ -75,3 +77,53 @@ export const sortFtByFiatBalance = ( const bAmount = BigNumber(bFiatAmount || 0); return aAmount.isLessThan(bAmount) ? 1 : aAmount.isGreaterThan(bAmount) ? -1 : 0; }; + +type FTTokenVisibilityObject = Record; + +export const selectWithDerivedState = + ({ + manageTokens, + spamTokens, + showSpamTokens, + topTokensData, + select, + }: { + manageTokens: FTTokenVisibilityObject; + spamTokens?: string[]; + showSpamTokens: boolean; + topTokensData?: Record; + select?: (data: FungibleTokenWithStates[]) => FungibleTokenWithStates[]; + }) => + (data: FungibleToken[]) => { + const topTokens = { ...topTokensData }; + const tokensWithDerivedState = data.map((ft: FungibleToken) => { + let token = ft; + if (topTokens[token.principal]) { + delete topTokens[token.principal]; + token = { ...token, isTopToken: true }; + } + return { + ...token, + ...getFungibleTokenStates({ + fungibleToken: token, + manageTokens, + spamTokens, + showSpamTokens, + }), + } as FungibleTokenWithStates; + }); + const topTokensWithDerivedState = Object.values(topTokens).map((ft) => { + const token = { ...ft, isTopToken: true }; + return { + ...token, + ...getFungibleTokenStates({ + fungibleToken: token, + manageTokens, + spamTokens, + showSpamTokens, + }), + }; + }); + const withDerivedState = tokensWithDerivedState.concat(topTokensWithDerivedState); + return select ? select(withDerivedState) : withDerivedState; + }; diff --git a/tests/specs/managementToken.spec.ts b/tests/specs/managementToken.spec.ts index 056dc7554..12900019f 100644 --- a/tests/specs/managementToken.spec.ts +++ b/tests/specs/managementToken.spec.ts @@ -20,11 +20,11 @@ test.describe('Token Management', () => { await expect(wallet.buttonRunes).toBeVisible(); await expect(wallet.headingTokens).toBeVisible(); - // Check SIP10 token tab - only Stacks should be showing when user has no sip10 balances + // Check SIP10 token tab - only Stacks and sBTC should be showing when user has no sip10 balances await wallet.buttonSip10.click(); - await expect(wallet.labelCoinTitle).toHaveCount(1); - await expect(wallet.checkboxToken).toHaveCount(1); - await expect(wallet.checkboxTokenActive).toHaveCount(1); + await expect(wallet.labelCoinTitle).toHaveCount(2); + await expect(wallet.checkboxToken).toHaveCount(2); + await expect(wallet.checkboxTokenActive).toHaveCount(2); await expect(wallet.checkboxTokenInactive).toHaveCount(0); // Check BRC20 token tab - nothing shows when user has no brc20 balances