From 982ccbd776e69024b8da2fc06d61238ee405dd40 Mon Sep 17 00:00:00 2001 From: Tim Man Date: Fri, 11 Oct 2024 20:28:48 +0800 Subject: [PATCH] [ENG-5185] change sip10 tokens to default on, if seen on swaps provider lists (#613) --- package-lock.json | 14 +- package.json | 2 +- .../transactions/transactionAmount.tsx | 2 +- .../transactions/transactionTitle.tsx | 2 +- .../components/transactions/txTransfers.tsx | 2 +- .../ordinals/useGetBrc20FungibleTokens.ts | 58 ++- .../runes/useRuneFungibleTokensQuery.ts | 79 ++-- .../queries/stx/useGetSip10FungibleTokens.ts | 117 +++--- .../hooks/queries/stx/useGetSip10TokenInfo.ts | 3 +- src/app/hooks/queries/useAccountBalance.ts | 42 +- src/app/screens/coinDashboard/index.tsx | 6 +- src/app/screens/home/balanceCard/index.tsx | 6 +- src/app/screens/home/index.tsx | 68 +++- src/app/screens/listRune/index.tsx | 2 +- src/app/screens/manageTokens/index.tsx | 72 +--- src/app/screens/sendStx/index.tsx | 2 +- .../tokenFromBottomSheet/useFromTokens.ts | 4 +- .../tokenToBottomSheet/useToTokens.ts | 23 +- src/app/screens/swap/index.tsx | 6 +- src/app/screens/swap/useMasterCoinsList.tsx | 2 +- .../swap/useVisibleMasterCoinsList.tsx | 14 +- src/app/screens/unlistRune/index.tsx | 2 +- src/app/stores/index.ts | 4 +- src/app/utils/helper.ts | 14 +- tests/pages/wallet.ts | 103 +++-- tests/specs/managementAccount.spec.ts | 14 +- tests/specs/managementToken.spec.ts | 365 ++++++------------ tests/specs/transactionHistory.spec.ts | 9 +- 28 files changed, 439 insertions(+), 598 deletions(-) diff --git a/package-lock.json b/package-lock.json index a08062fb..7d7c77fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@playwright/test": "1.46.1", "@react-spring/web": "^9.6.1", "@sats-connect/core": "0.3.0", - "@secretkeylabs/xverse-core": "24.1.0", + "@secretkeylabs/xverse-core": "24.2.0", "@stacks/connect": "7.4.1", "@stacks/stacks-blockchain-api-types": "6.1.1", "@stacks/transactions": "6.16.1", @@ -1270,9 +1270,9 @@ } }, "node_modules/@secretkeylabs/xverse-core": { - "version": "24.1.0", - "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/24.1.0/0c57f062dd3c9444e284d101756658251fd8681d", - "integrity": "sha512-cc01cykJV5YQhAc09VHwD8Iw7voQ+lqBzZWMJ+B5EXasi97O+dEP80JQs2RCpV2MfdsakZO+9JCweP331UqUbw==", + "version": "24.2.0", + "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/24.2.0/67ae914442d4276dcfd0d1e3b8bdf623133557bb", + "integrity": "sha512-mBxIPL6jmGhRZLpUBL0ztTvBporCL/1CzTsz42/XZo7WFZIFfb1Arj2oQkeT+AngV+bf1CH60cV2vXnqH0mYkQ==", "license": "ISC", "dependencies": { "@bitcoinerlab/secp256k1": "^1.0.2", @@ -13635,9 +13635,9 @@ } }, "@secretkeylabs/xverse-core": { - "version": "24.1.0", - "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/24.1.0/0c57f062dd3c9444e284d101756658251fd8681d", - "integrity": "sha512-cc01cykJV5YQhAc09VHwD8Iw7voQ+lqBzZWMJ+B5EXasi97O+dEP80JQs2RCpV2MfdsakZO+9JCweP331UqUbw==", + "version": "24.2.0", + "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/24.2.0/67ae914442d4276dcfd0d1e3b8bdf623133557bb", + "integrity": "sha512-mBxIPL6jmGhRZLpUBL0ztTvBporCL/1CzTsz42/XZo7WFZIFfb1Arj2oQkeT+AngV+bf1CH60cV2vXnqH0mYkQ==", "requires": { "@bitcoinerlab/secp256k1": "^1.0.2", "@noble/curves": "^1.2.0", diff --git a/package.json b/package.json index 5de46c2c..a7ad4f33 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "@playwright/test": "1.46.1", "@react-spring/web": "^9.6.1", "@sats-connect/core": "0.3.0", - "@secretkeylabs/xverse-core": "24.1.0", + "@secretkeylabs/xverse-core": "24.2.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 f84201d0..7911eaa0 100644 --- a/src/app/components/transactions/transactionAmount.tsx +++ b/src/app/components/transactions/transactionAmount.tsx @@ -33,7 +33,7 @@ const TransactionValue = styled.p((props) => ({ export default function TransactionAmount(props: TransactionAmountProps): JSX.Element | null { const { transaction, currency, protocol, tokenSymbol } = props; - const { visible: sip10CoinsList } = useVisibleSip10FungibleTokens(); + const { data: sip10CoinsList } = useVisibleSip10FungibleTokens(); if (currency === 'STX' || (currency === 'FT' && protocol === 'stacks')) { const stxTransaction = transaction as StxTransactionData; if (stxTransaction.txType === 'token_transfer') { diff --git a/src/app/components/transactions/transactionTitle.tsx b/src/app/components/transactions/transactionTitle.tsx index cfd70607..2f757181 100644 --- a/src/app/components/transactions/transactionTitle.tsx +++ b/src/app/components/transactions/transactionTitle.tsx @@ -29,7 +29,7 @@ const TransactionTitleText = styled.p((props) => ({ export default function TransactionTitle(props: TransactionTitleProps) { const { transaction } = props; const { t } = useTranslation('translation', { keyPrefix: 'COIN_DASHBOARD_SCREEN' }); - const { visible: 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 18d1d79c..34cb6edd 100644 --- a/src/app/components/transactions/txTransfers.tsx +++ b/src/app/components/transactions/txTransfers.tsx @@ -60,7 +60,7 @@ interface TxTransfersProps { export default function TxTransfers(props: TxTransfersProps) { const { transaction, coin, txFilter } = props; const selectedAccount = useSelectedAccount(); - const { visible: 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 7d30bdaf..9fd844ab 100644 --- a/src/app/hooks/queries/ordinals/useGetBrc20FungibleTokens.ts +++ b/src/app/hooks/queries/ordinals/useGetBrc20FungibleTokens.ts @@ -2,13 +2,14 @@ import useSelectedAccount from '@hooks/useSelectedAccount'; import useWalletSelector from '@hooks/useWalletSelector'; import { getBrc20Tokens, + getFungibleTokenStates, getOrdinalsFtBalance, type Brc20Token, type FungibleToken, + type FungibleTokenWithStates, type SettingsNetwork, } from '@secretkeylabs/xverse-core'; import { useQuery } from '@tanstack/react-query'; -import BigNumber from 'bignumber.js'; const brc20TokenToFungibleToken = (coin: Brc20Token): FungibleToken => ({ name: coin.name, @@ -17,7 +18,6 @@ const brc20TokenToFungibleToken = (coin: Brc20Token): FungibleToken => ({ total_sent: '', total_received: '', assetName: coin.name ?? coin.ticker, - visible: true, ticker: coin.ticker, protocol: 'brc-20', supported: coin.supported, @@ -54,40 +54,38 @@ export const fetchBrc20FungibleTokens = ); }; -export const useGetBrc20FungibleTokens = () => { +export const useGetBrc20FungibleTokens = (select?: (data: FungibleTokenWithStates[]) => any) => { const { ordinalsAddress } = useSelectedAccount(); - const { fiatCurrency, network, spamTokens, showSpamTokens } = useWalletSelector(); - const queryFn = fetchBrc20FungibleTokens(ordinalsAddress, fiatCurrency, network); + const { brc20ManageTokens, fiatCurrency, network, spamTokens, showSpamTokens } = + useWalletSelector(); - const query = useQuery({ + 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), + select: selectWithDerivedState, }); - - return { - ...query, - unfilteredData: query.data, - data: query.data?.filter((ft) => { - let passedSpamCheck = true; - if (spamTokens?.length) { - passedSpamCheck = showSpamTokens || !spamTokens.includes(ft.principal); - } - return passedSpamCheck; - }), - }; }; -export const useVisibleBrc20FungibleTokens = (): ReturnType & { - visible: FungibleToken[]; -} => { - const { brc20ManageTokens } = useWalletSelector(); - const brc20Query = useGetBrc20FungibleTokens(); - return { - ...brc20Query, - visible: (brc20Query.data ?? []).filter((ft) => { - const userSetting = brc20ManageTokens[ft.principal]; - return userSetting === true || (userSetting === undefined && new BigNumber(ft.balance).gt(0)); - }), - }; +// convenience hook to get only enabled brc20 fungible tokens +export const useVisibleBrc20FungibleTokens = () => { + const selectEnabled = (data: FungibleTokenWithStates[]) => data.filter((ft) => ft.isEnabled); + return useGetBrc20FungibleTokens(selectEnabled); }; diff --git a/src/app/hooks/queries/runes/useRuneFungibleTokensQuery.ts b/src/app/hooks/queries/runes/useRuneFungibleTokensQuery.ts index e9526fe3..e11dc6dc 100644 --- a/src/app/hooks/queries/runes/useRuneFungibleTokensQuery.ts +++ b/src/app/hooks/queries/runes/useRuneFungibleTokensQuery.ts @@ -1,10 +1,12 @@ import useRunesApi from '@hooks/apiClients/useRunesApi'; -import useHasFeature from '@hooks/useHasFeature'; import useSelectedAccount from '@hooks/useSelectedAccount'; import useWalletSelector from '@hooks/useWalletSelector'; -import { FeatureId, type FungibleToken } from '@secretkeylabs/xverse-core'; +import { + getFungibleTokenStates, + type FungibleToken, + type FungibleTokenWithStates, +} from '@secretkeylabs/xverse-core'; import { useQuery } from '@tanstack/react-query'; -import BigNumber from 'bignumber.js'; export const fetchRuneBalances = ( @@ -31,47 +33,44 @@ export const fetchRuneBalances = } }; -export const useRuneFungibleTokensQuery = (backgroundRefetch = true) => { +export const useRuneFungibleTokensQuery = ( + select?: (data: FungibleTokenWithStates[]) => any, + backgroundRefetch = true, +) => { const { ordinalsAddress } = useSelectedAccount(); - const { network, fiatCurrency, spamTokens, showSpamTokens } = useWalletSelector(); - const showRunes = useHasFeature(FeatureId.RUNES_SUPPORT); + const { runesManageTokens, network, fiatCurrency, spamTokens, showSpamTokens } = + useWalletSelector(); const runesApi = useRunesApi(); + const queryFn = fetchRuneBalances(runesApi, ordinalsAddress, fiatCurrency); - const query = useQuery({ - queryKey: ['get-rune-fungible-tokens', network.type, ordinalsAddress, fiatCurrency], - enabled: Boolean(network && ordinalsAddress && showRunes), - refetchOnWindowFocus: backgroundRefetch, - refetchOnReconnect: backgroundRefetch, - queryFn, - }); - return { - ...query, - unfilteredData: query.data, - data: query.data?.filter((ft) => { - let passedSpamCheck = true; - if (spamTokens?.length) { - passedSpamCheck = showSpamTokens || !spamTokens.includes(ft.principal); - } - return passedSpamCheck; - }), + const selectWithDerivedState = (data: FungibleToken[]) => { + const withDerivedState = data.map( + (ft: FungibleToken) => + ({ + ...ft, + ...getFungibleTokenStates({ + fungibleToken: ft, + manageTokens: runesManageTokens, + spamTokens, + showSpamTokens, + }), + } as FungibleTokenWithStates), + ); + return select ? select(withDerivedState) : withDerivedState; }; + + return useQuery({ + queryKey: ['get-rune-fungible-tokens', network.type, ordinalsAddress, fiatCurrency], + queryFn, + enabled: Boolean(network && ordinalsAddress), + select: selectWithDerivedState, + refetchOnWindowFocus: !!backgroundRefetch, + refetchOnReconnect: !!backgroundRefetch, + }); }; -/* - * This hook is used to get the list of runes which the user has not hidden - */ -export const useVisibleRuneFungibleTokens = ( - backgroundRefetch = true, -): ReturnType & { - visible: FungibleToken[]; -} => { - const { runesManageTokens } = useWalletSelector(); - const runesQuery = useRuneFungibleTokensQuery(backgroundRefetch); - return { - ...runesQuery, - visible: (runesQuery.data ?? []).filter((ft) => { - const userSetting = runesManageTokens[ft.principal]; - return userSetting === true || (userSetting === undefined && new BigNumber(ft.balance).gt(0)); - }), - }; +// convenience hook to get only enabled rune fungible tokens +export const useVisibleRuneFungibleTokens = (backgroundRefetch = true) => { + const selectEnabled = (data: FungibleTokenWithStates[]) => data.filter((ft) => ft.isEnabled); + return useRuneFungibleTokensQuery(selectEnabled, backgroundRefetch); }; diff --git a/src/app/hooks/queries/stx/useGetSip10FungibleTokens.ts b/src/app/hooks/queries/stx/useGetSip10FungibleTokens.ts index 93203de7..88e810de 100644 --- a/src/app/hooks/queries/stx/useGetSip10FungibleTokens.ts +++ b/src/app/hooks/queries/stx/useGetSip10FungibleTokens.ts @@ -3,14 +3,14 @@ import useSelectedAccount from '@hooks/useSelectedAccount'; import useWalletSelector from '@hooks/useWalletSelector'; import { StacksNetwork, - // getCoinMetaData, - getCoinsInfo, getFtData, + getFungibleTokenStates, + getXverseApiClient, type FungibleToken, + type FungibleTokenWithStates, type SettingsNetwork, } from '@secretkeylabs/xverse-core'; import { useQuery } from '@tanstack/react-query'; -import BigNumber from 'bignumber.js'; export const fetchSip10FungibleTokens = ( @@ -18,47 +18,32 @@ export const fetchSip10FungibleTokens = fiatCurrency: string, network: SettingsNetwork, currentNetworkInstance: StacksNetwork, - ) => + ): (() => Promise) => async () => { - // get sip10 metadata and balances for the stxAddress const sip10Balances = await getFtData(stxAddress, currentNetworkInstance); + const sip10ContractInfos = + (await getXverseApiClient(network.type).getSip10Tokens( + sip10Balances.map((ft) => ft.principal), + fiatCurrency, + )) || []; - // getting contract ids of all fts - const contractids: string[] = sip10Balances.map((ft) => ft.principal); - const sip10ContractInfos = (await getCoinsInfo(network.type, contractids, fiatCurrency)) || []; - - // combine - return sip10Balances - .map((ft) => { - const found = (sip10ContractInfos || []).find((coin) => coin.contract === ft.principal); - if (!found) { - return ft; - } - return { - ...ft, - ...found, - visible: true, - name: found.name || ft.principal.split('.')[1], - }; - }) - .concat( - sip10ContractInfos - .filter((coin) => !sip10Balances.some((ft) => ft.principal === coin.contract)) - .map((coin) => ({ - ...coin, - principal: coin.contract, - assetName: coin.name || coin.contract.split('.')[1], - protocol: 'stacks', - balance: '0', - total_sent: '', - total_received: '', - })), - ); + return sip10Balances.map((ft) => { + const found = sip10ContractInfos.find((coin) => coin.contract === ft.principal); + if (!found) { + return ft; + } + return { + ...ft, + ...found, + name: found.name || ft.principal.split('.')[1], + }; + }); }; -export const useGetSip10FungibleTokens = () => { +export const useGetSip10FungibleTokens = (select?: (data: FungibleTokenWithStates[]) => any) => { const { stxAddress } = useSelectedAccount(); - const { fiatCurrency, network, spamTokens, showSpamTokens } = useWalletSelector(); + const { sip10ManageTokens, fiatCurrency, network, spamTokens, showSpamTokens } = + useWalletSelector(); const currentNetworkInstance = useNetworkSelector(); const queryFn = fetchSip10FungibleTokens( @@ -67,46 +52,32 @@ export const useGetSip10FungibleTokens = () => { 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; + }; - const query = useQuery({ + return useQuery({ queryKey: ['sip10-fungible-tokens', network.type, stxAddress, fiatCurrency], queryFn, enabled: Boolean(network && stxAddress), + select: selectWithDerivedState, }); - - return { - ...query, - unfilteredData: query.data, - data: query.data?.filter((ft) => { - let passedSpamCheck = true; - if (spamTokens?.length) { - passedSpamCheck = showSpamTokens || !spamTokens.includes(ft.principal); - } - return passedSpamCheck; - }), - }; }; -export const useVisibleSip10FungibleTokens = (): ReturnType & { - visible: FungibleToken[]; -} => { - const { sip10ManageTokens } = useWalletSelector(); - const sip10Query = useGetSip10FungibleTokens(); - // set visible false for unsupported tokens - const sip10FTList = sip10Query.data || []; - sip10FTList.forEach((ft) => { - ft.visible = !!ft.supported; - }); - - return { - ...sip10Query, - visible: sip10FTList.filter((ft) => { - const userSetting = sip10ManageTokens[ft.principal]; - - return ( - userSetting === true || - (userSetting === undefined && ft.supported && new BigNumber(ft.balance).gt(0)) - ); - }), - }; +// convenience hook to get only enabled sip10 fungible tokens +export const useVisibleSip10FungibleTokens = () => { + const selectEnabled = (data: FungibleTokenWithStates[]) => data.filter((ft) => ft.isEnabled); + return useGetSip10FungibleTokens(selectEnabled); }; diff --git a/src/app/hooks/queries/stx/useGetSip10TokenInfo.ts b/src/app/hooks/queries/stx/useGetSip10TokenInfo.ts index 3f621bbc..959c332c 100644 --- a/src/app/hooks/queries/stx/useGetSip10TokenInfo.ts +++ b/src/app/hooks/queries/stx/useGetSip10TokenInfo.ts @@ -15,11 +15,10 @@ const useGetSip10TokenInfo = ({ const fetchTokensInfo = async () => { if (principal) { - const coinsInfo = await xverseApiClient.getCoinsInfo( + const coinsInfo = await xverseApiClient.getSip10Tokens( [principal], fiatCurrency ?? defaultFiatCurrency, ); - const tokenInfo = coinsInfo.find((coin) => coin.contract === principal); return tokenInfo; } diff --git a/src/app/hooks/queries/useAccountBalance.ts b/src/app/hooks/queries/useAccountBalance.ts index 9baa4b6e..627bae3f 100644 --- a/src/app/hooks/queries/useAccountBalance.ts +++ b/src/app/hooks/queries/useAccountBalance.ts @@ -5,9 +5,11 @@ import useNetworkSelector from '@hooks/useNetwork'; import useWalletSelector from '@hooks/useWalletSelector'; import { API_TIMEOUT_MILLI, + getFungibleTokenStates, type Account, type BtcAddressData, type FungibleToken, + type FungibleTokenWithStates, type TokensResponse, } from '@secretkeylabs/xverse-core'; import { setAccountBalanceAction } from '@stores/wallet/actions/actionCreators'; @@ -30,6 +32,8 @@ const useAccountBalance = () => { brc20ManageTokens, sip10ManageTokens, runesManageTokens, + spamTokens, + showSpamTokens, } = useWalletSelector(); const { btcFiatRate, stxBtcRate } = useSupportedCoinRates(); const runesApi = useRunesApi(); @@ -38,6 +42,17 @@ const useAccountBalance = () => { const [isProcessingQueue, setIsProcessingQueue] = useState(false); const [queueLength, setQueueLength] = useState(0); + const withDerivedState = (ft: FungibleToken) => + ({ + ...ft, + ...getFungibleTokenStates({ + fungibleToken: ft, + manageTokens: sip10ManageTokens, + spamTokens, + showSpamTokens, + }), + } as FungibleTokenWithStates); + const fetchBalances = async (account: Account | null) => { if (!account) { return; @@ -45,9 +60,9 @@ const useAccountBalance = () => { let btcBalance = '0'; let stxBalance = '0'; - let finalSipCoinsList: FungibleToken[] = []; - let finalBrcCoinsList: FungibleToken[] = []; - let finalRunesCoinsList: FungibleToken[] = []; + let finalSipCoinsList: FungibleTokenWithStates[] = []; + let finalBrcCoinsList: FungibleTokenWithStates[] = []; + let finalRunesCoinsList: FungibleTokenWithStates[] = []; try { if (account.btcAddress) { @@ -61,15 +76,13 @@ const useAccountBalance = () => { fiatCurrency, network, ); - finalBrcCoinsList = (await fetchBrc20Balances()).filter((ft) => { - const setting = brc20ManageTokens[ft.principal]; - return setting === true || (setting === undefined && new BigNumber(ft.balance).gt(0)); - }); + finalBrcCoinsList = (await fetchBrc20Balances()) + .map(withDerivedState) + .filter((ft) => ft.isEnabled); const runeBalances = fetchRuneBalances(runesApi, account.ordinalsAddress, fiatCurrency); - finalRunesCoinsList = (await runeBalances()).filter((ft) => { - const setting = runesManageTokens[ft.principal]; - return setting === true || (setting === undefined && new BigNumber(ft.balance).gt(0)); - }); + finalRunesCoinsList = (await runeBalances()) + .map(withDerivedState) + .filter((ft) => ft.isEnabled); } if (account.stxAddress) { const apiUrl = `${stacksNetwork.coreApiUrl}/extended/v1/address/${account.stxAddress}/balances`; @@ -88,10 +101,9 @@ const useAccountBalance = () => { network, stacksNetwork, ); - finalSipCoinsList = (await fetchSip10Balances()).filter((ft) => { - const setting = sip10ManageTokens[ft.principal]; - return setting === true || (setting === undefined && new BigNumber(ft.balance).gt(0)); - }); + finalSipCoinsList = (await fetchSip10Balances()) + .map(withDerivedState) + .filter((ft) => ft.isEnabled); } } catch (error) { console.error('Failed to fetch balances:', error); diff --git a/src/app/screens/coinDashboard/index.tsx b/src/app/screens/coinDashboard/index.tsx index 13df8e35..490ba015 100644 --- a/src/app/screens/coinDashboard/index.tsx +++ b/src/app/screens/coinDashboard/index.tsx @@ -166,9 +166,9 @@ export default function CoinDashboard() { const { addToSpamTokens } = useSpamTokens(); const dispatch = useDispatch(); const { currency } = useParams(); - const { visible: runesCoinsList } = useVisibleRuneFungibleTokens(); - const { visible: sip10CoinsList } = useVisibleSip10FungibleTokens(); - const { visible: brc20CoinsList } = useVisibleBrc20FungibleTokens(); + const { data: runesCoinsList } = useVisibleRuneFungibleTokens(); + const { data: sip10CoinsList } = useVisibleSip10FungibleTokens(); + const { data: brc20CoinsList } = useVisibleBrc20FungibleTokens(); let selectedFt: FungibleToken | undefined; diff --git a/src/app/screens/home/balanceCard/index.tsx b/src/app/screens/home/balanceCard/index.tsx index 65f86d11..be2e94dd 100644 --- a/src/app/screens/home/balanceCard/index.tsx +++ b/src/app/screens/home/balanceCard/index.tsx @@ -80,9 +80,9 @@ function BalanceCard(props: BalanceCardProps) { const { setAccountBalance } = useAccountBalance(); const { isLoading, isRefetching } = props; const oldTotalBalance = accountBalances[selectedAccount.btcAddress]; - const { visible: sip10CoinsList } = useVisibleSip10FungibleTokens(); - const { visible: brc20CoinsList } = useVisibleBrc20FungibleTokens(); - const { visible: runesCoinList } = useVisibleRuneFungibleTokens(); + const { data: sip10CoinsList } = useVisibleSip10FungibleTokens(); + const { data: brc20CoinsList } = useVisibleBrc20FungibleTokens(); + const { data: runesCoinList } = useVisibleRuneFungibleTokens(); const balance = calculateTotalBalance({ stxBalance: stxData?.balance.toString() ?? '0', diff --git a/src/app/screens/home/index.tsx b/src/app/screens/home/index.tsx index 788b12f3..455a2f3f 100644 --- a/src/app/screens/home/index.tsx +++ b/src/app/screens/home/index.tsx @@ -10,9 +10,18 @@ import ReceiveCardComponent from '@components/receiveCardComponent'; import ShowBtcReceiveAlert from '@components/showBtcReceiveAlert'; import ShowOrdinalReceiveAlert from '@components/showOrdinalReceiveAlert'; import BottomBar from '@components/tabBar'; -import { useVisibleBrc20FungibleTokens } from '@hooks/queries/ordinals/useGetBrc20FungibleTokens'; -import { useVisibleRuneFungibleTokens } from '@hooks/queries/runes/useRuneFungibleTokensQuery'; -import { useVisibleSip10FungibleTokens } from '@hooks/queries/stx/useGetSip10FungibleTokens'; +import { + useGetBrc20FungibleTokens, + useVisibleBrc20FungibleTokens, +} from '@hooks/queries/ordinals/useGetBrc20FungibleTokens'; +import { + useRuneFungibleTokensQuery, + useVisibleRuneFungibleTokens, +} from '@hooks/queries/runes/useRuneFungibleTokensQuery'; +import { + useGetSip10FungibleTokens, + useVisibleSip10FungibleTokens, +} from '@hooks/queries/stx/useGetSip10FungibleTokens'; import useAppConfig from '@hooks/queries/useAppConfig'; import useBtcWalletData from '@hooks/queries/useBtcWalletData'; import useFeeMultipliers from '@hooks/queries/useFeeMultipliers'; @@ -28,7 +37,12 @@ import useWalletSelector from '@hooks/useWalletSelector'; import { ArrowDown, ArrowUp, Plus } from '@phosphor-icons/react'; import { animated, useTransition } from '@react-spring/web'; import CoinSelectModal from '@screens/home/coinSelectModal'; -import { AnalyticsEvents, FeatureId, type FungibleToken } from '@secretkeylabs/xverse-core'; +import { + AnalyticsEvents, + FeatureId, + type FungibleToken, + type FungibleTokenWithStates, +} from '@secretkeylabs/xverse-core'; import { changeShowDataCollectionAlertAction, setBrc20ManageTokensAction, @@ -43,7 +57,6 @@ import type { CurrencyTypes } from '@utils/constants'; import { isInOptions, isLedgerAccount } from '@utils/helper'; import { optInMixPanel, optOutMixPanel, trackMixPanel } from '@utils/mixpanel'; import { sortFtByFiatBalance } from '@utils/tokens'; -import BigNumber from 'bignumber.js'; import { useEffect, useState } from 'react'; import toast from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; @@ -113,21 +126,22 @@ function Home() { const { btcFiatRate, stxBtcRate } = useSupportedCoinRates(); const { data: notificationBannersArr, isFetching: isFetchingNotificationBannersArr } = useNotificationBanners(); + + const { data: fullSip10CoinsList } = useGetSip10FungibleTokens(); + const { data: fullBrc20CoinsList } = useGetBrc20FungibleTokens(); + const { data: fullRunesCoinsList } = useRuneFungibleTokensQuery(); const { - unfilteredData: fullSip10CoinsList, - visible: sip10CoinsList, + data: sip10CoinsList, isInitialLoading: loadingStxCoinData, isRefetching: refetchingStxCoinData, } = useVisibleSip10FungibleTokens(); const { - unfilteredData: fullBrc20CoinsList, - visible: brc20CoinsList, + data: brc20CoinsList, isInitialLoading: loadingBrcCoinData, isRefetching: refetchingBrcCoinData, } = useVisibleBrc20FungibleTokens(); const { - unfilteredData: fullRunesCoinsList, - visible: runesCoinsList, + data: runesCoinsList, isInitialLoading: loadingRunesData, isRefetching: refetchingRunesData, } = useVisibleRuneFungibleTokens(); @@ -163,11 +177,23 @@ function Home() { isEnabled: true, }; - if (fullRunesCoinsList?.find((ft) => ft.principal === spamToken.principal)) { + if ( + fullRunesCoinsList?.find( + (ft: FungibleTokenWithStates) => ft.principal === spamToken.principal, + ) + ) { dispatch(setRunesManageTokensAction(payload)); - } else if (fullSip10CoinsList?.find((ft) => ft.principal === spamToken.principal)) { + } else if ( + fullSip10CoinsList?.find( + (ft: FungibleTokenWithStates) => ft.principal === spamToken.principal, + ) + ) { dispatch(setSip10ManageTokensAction(payload)); - } else if (fullBrc20CoinsList?.find((ft) => ft.principal === spamToken.principal)) { + } else if ( + fullBrc20CoinsList?.find( + (ft: FungibleTokenWithStates) => ft.principal === spamToken.principal, + ) + ) { dispatch(setBrc20ManageTokensAction(payload)); } @@ -181,10 +207,12 @@ function Home() { } }, [spamToken]); - const combinedFtList = sip10CoinsList - .concat(brc20CoinsList) - .concat(runesCoinsList) - .sort((a, b) => sortFtByFiatBalance(a, b, stxBtcRate, btcFiatRate)); + const combinedFtList = (sip10CoinsList ?? []) + .concat(brc20CoinsList ?? []) + .concat(runesCoinsList ?? []) + .sort((a: FungibleTokenWithStates, b: FungibleTokenWithStates) => + sortFtByFiatBalance(a, b, stxBtcRate, btcFiatRate), + ); const filteredNotificationBannersArr = notificationBannersArr ? notificationBannersArr.filter((banner) => !notificationBanners[banner.id]) @@ -508,7 +536,7 @@ function Home() { onPress={handleTokenPressed} /> )} - {combinedFtList.map((coin) => { + {combinedFtList.map((coin: FungibleTokenWithStates) => { const isLoading = loadingStxCoinData || loadingBrcCoinData || loadingRunesData; return ( new BigNumber(ft.balance).gt(0))} + coins={combinedFtList} title={t('SEND')} loadingWalletData={loadingStxWalletData || loadingBtcWalletData} /> diff --git a/src/app/screens/listRune/index.tsx b/src/app/screens/listRune/index.tsx index 4589a802..910e3b3c 100644 --- a/src/app/screens/listRune/index.tsx +++ b/src/app/screens/listRune/index.tsx @@ -71,7 +71,7 @@ export default function ListRuneScreen() { const { t } = useTranslation('translation', { keyPrefix: 'LIST_RUNE_SCREEN' }); const navigate = useNavigate(); const { runeId } = useParams(); - const { visible: runesCoinsList } = useVisibleRuneFungibleTokens(false); + const { data: runesCoinsList } = useVisibleRuneFungibleTokens(false); const selectedRune = runesCoinsList.find((ft) => ft.principal === runeId); const { fiatCurrency } = useWalletSelector(); const { btcFiatRate } = useSupportedCoinRates(); diff --git a/src/app/screens/manageTokens/index.tsx b/src/app/screens/manageTokens/index.tsx index f9356edd..54072b2d 100644 --- a/src/app/screens/manageTokens/index.tsx +++ b/src/app/screens/manageTokens/index.tsx @@ -1,21 +1,18 @@ import stacksIcon from '@assets/img/dashboard/stx_icon.svg'; -import runesComingSoon from '@assets/img/manageTokens/runes_coming_soon.svg'; import OptionsDialog from '@components/optionsDialog/optionsDialog'; import BottomBar from '@components/tabBar'; import TopRow from '@components/topRow'; import { useGetBrc20FungibleTokens } from '@hooks/queries/ordinals/useGetBrc20FungibleTokens'; import { useRuneFungibleTokensQuery } from '@hooks/queries/runes/useRuneFungibleTokensQuery'; import { useGetSip10FungibleTokens } from '@hooks/queries/stx/useGetSip10FungibleTokens'; -import useHasFeature from '@hooks/useHasFeature'; import useSelectedAccount from '@hooks/useSelectedAccount'; import useWalletReducer from '@hooks/useWalletReducer'; import useWalletSelector from '@hooks/useWalletSelector'; import { Eye, EyeSlash } from '@phosphor-icons/react'; import CoinItem from '@screens/manageTokens/coinItem'; import { - FeatureId, - type FungibleToken, type FungibleTokenProtocol, + type FungibleTokenWithStates, } from '@secretkeylabs/xverse-core'; import { setBrc20ManageTokensAction, @@ -25,7 +22,6 @@ import { } from '@stores/wallet/actions/actionCreators'; import { StyledP } from '@ui-library/common.styled'; import { SPAM_OPTIONS_WIDTH } from '@utils/constants'; -import BigNumber from 'bignumber.js'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; @@ -97,14 +93,6 @@ const Description = styled.h1((props) => ({ marginBottom: props.theme.spacing(16), })); -const RunesContainer = styled.div((props) => ({ - marginTop: props.theme.spacing(24), - marginRight: props.theme.spacing(5), - display: 'flex', - flexDirection: 'row', - justifyContent: 'center', -})); - const ErrorsText = styled.p((props) => ({ ...props.theme.typography.body_bold_m, color: props.theme.colors.white_200, @@ -113,10 +101,6 @@ const ErrorsText = styled.p((props) => ({ textAlign: 'center', })); -const RunesComingSoon = styled.img({ - width: '70%', -}); - const ButtonRow = styled.button` display: flex; align-items: center; @@ -161,16 +145,20 @@ function ManageTokens() { const { t } = useTranslation('translation', { keyPrefix: 'TOKEN_SCREEN' }); const selectedAccount = useSelectedAccount(); - const { sip10ManageTokens, brc20ManageTokens, runesManageTokens, showSpamTokens } = - useWalletSelector(); - const { data: runesList, isError: runeError } = useRuneFungibleTokensQuery(); - const { data: sip10List, isError: sip10Error } = useGetSip10FungibleTokens(); - const { data: brc20List, isError: brc20Error } = useGetBrc20FungibleTokens(); + const { showSpamTokens } = useWalletSelector(); + const { data: runesList, isError: runeError } = useRuneFungibleTokensQuery((data) => + data.filter((ft) => ft.showToggle), + ); + const { data: sip10List, isError: sip10Error } = useGetSip10FungibleTokens((data) => + data.filter((ft) => ft.showToggle), + ); + const { data: brc20List, isError: brc20Error } = useGetBrc20FungibleTokens((data) => + data.filter((ft) => ft.showToggle), + ); const [selectedProtocol, setSelectedProtocol] = useState( selectedAccount?.stxAddress ? 'stacks' : 'brc-20', ); - const showRunes = useHasFeature(FeatureId.RUNES_SUPPORT); const [showOptionsDialog, setShowOptionsDialog] = useState(false); const [optionsDialogIndents, setOptionsDialogIndents] = useState< @@ -193,9 +181,9 @@ function ManageTokens() { }; const toggled = (isEnabled: boolean, _coinName: string, coinKey: string) => { - const runeFt = runesList?.find((ft) => ft.principal === coinKey); - const sip10Ft = sip10List?.find((ft) => ft.principal === coinKey); - const brc20Ft = brc20List?.find((ft) => ft.principal === coinKey); + const runeFt = runesList?.find((ft: FungibleTokenWithStates) => ft.principal === coinKey); + const sip10Ft = sip10List?.find((ft: FungibleTokenWithStates) => ft.principal === coinKey); + const brc20Ft = brc20List?.find((ft: FungibleTokenWithStates) => ft.principal === coinKey); const payload = { principal: coinKey, isEnabled }; @@ -211,29 +199,19 @@ function ManageTokens() { const handleBackButtonClick = () => navigate('/'); const getCoinsList = () => { - let coins: FungibleToken[]; + let coins: FungibleTokenWithStates[]; let error: boolean; switch (selectedProtocol) { case 'stacks': - coins = (sip10List ?? []).map((ft) => ({ - ...ft, - visible: - sip10ManageTokens[ft.principal] ?? (ft.supported && new BigNumber(ft.balance).gt(0)), - })); + coins = sip10List ?? []; error = sip10Error; break; case 'brc-20': - coins = (brc20List ?? []).map((ft) => ({ - ...ft, - visible: brc20ManageTokens[ft.principal] ?? new BigNumber(ft.balance).gt(0), - })); + coins = brc20List ?? []; error = brc20Error; break; case 'runes': - coins = (runesList ?? []).map((ft) => ({ - ...ft, - visible: runesManageTokens[ft.principal] ?? new BigNumber(ft.balance).gt(0), - })); + coins = runesList ?? []; error = runeError; break; default: @@ -246,7 +224,7 @@ function ManageTokens() { return ( <> {selectedProtocol === 'stacks' && } - {coins.map((coin: FungibleToken) => ( + {coins.map((coin) => ( ))} {!coins.length && {t('NO_COINS')}} @@ -316,15 +294,7 @@ function ManageTokens() { RUNES - - {selectedProtocol === 'runes' && !showRunes ? ( - - - - ) : ( - getCoinsList() - )} - + {getCoinsList()} diff --git a/src/app/screens/sendStx/index.tsx b/src/app/screens/sendStx/index.tsx index b0be2cb2..0d689ab5 100644 --- a/src/app/screens/sendStx/index.tsx +++ b/src/app/screens/sendStx/index.tsx @@ -60,7 +60,7 @@ function SendStxScreen() { // Will be used in the future when the summary screen is refactored const [, setRecipientDomain] = useState(''); const [memo, setMemo] = useState(stxMemo ?? ''); - const { visible: sip10CoinsList } = useVisibleSip10FungibleTokens(); + const { data: sip10CoinsList } = useVisibleSip10FungibleTokens(); const [searchParams] = useSearchParams(); const principal = searchParams.get('principal'); const fungibleToken = sip10CoinsList?.find((coin) => coin.principal === principal); diff --git a/src/app/screens/swap/components/tokenFromBottomSheet/useFromTokens.ts b/src/app/screens/swap/components/tokenFromBottomSheet/useFromTokens.ts index e886fc43..7091c029 100644 --- a/src/app/screens/swap/components/tokenFromBottomSheet/useFromTokens.ts +++ b/src/app/screens/swap/components/tokenFromBottomSheet/useFromTokens.ts @@ -1,11 +1,11 @@ import useSupportedCoinRates from '@hooks/queries/useSupportedCoinRates'; -import useMasterCoinsList from '@screens/swap/useMasterCoinsList'; +import useVisibleMasterCoinsList from '@screens/swap/useVisibleMasterCoinsList'; import { mapSwapProtocolToFTProtocol } from '@screens/swap/utils'; import { type Token } from '@secretkeylabs/xverse-core'; import { sortFtByFiatBalance } from '@utils/tokens'; const useFromTokens = (toToken?: Token) => { - const tokens = useMasterCoinsList(); + const tokens = useVisibleMasterCoinsList(); const { stxBtcRate, btcFiatRate } = useSupportedCoinRates(); // Sort tokens, keeping BTC as the first element, and STX (if enabled) as the second diff --git a/src/app/screens/swap/components/tokenToBottomSheet/useToTokens.ts b/src/app/screens/swap/components/tokenToBottomSheet/useToTokens.ts index c3085aea..122796ae 100644 --- a/src/app/screens/swap/components/tokenToBottomSheet/useToTokens.ts +++ b/src/app/screens/swap/components/tokenToBottomSheet/useToTokens.ts @@ -1,13 +1,18 @@ import useWalletSelector from '@hooks/useWalletSelector'; -import useVisibleMasterCoinsList from '@screens/swap/useVisibleMasterCoinsList'; +import useMasterCoinsList from '@screens/swap/useMasterCoinsList'; import { mapFTProtocolToSwapProtocol } from '@screens/swap/utils'; -import { getXverseApiClient, type Protocol, type TokenBasic } from '@secretkeylabs/xverse-core'; +import { + getXverseApiClient, + type Protocol, + type Token, + type TokenBasic, +} from '@secretkeylabs/xverse-core'; import { useQuery } from '@tanstack/react-query'; import { handleRetries } from '@utils/query'; const useToTokens = (protocol: Protocol, from?: TokenBasic, query?: string) => { - const coinsMasterList = useVisibleMasterCoinsList(); - const { network, spamTokens } = useWalletSelector(); + const coinsMasterList = useMasterCoinsList(); + const { network, spamTokens, runesManageTokens, sip10ManageTokens } = useWalletSelector(); const spamTokenSet = new Set(spamTokens); const userTokens = @@ -41,12 +46,20 @@ const useToTokens = (protocol: Protocol, from?: TokenBasic, query?: string) => { return filteredResponse; }; + const select = (data: Token[]) => + data.filter( + (token) => + !spamTokenSet.has(token.ticker) || + runesManageTokens[token.ticker] === true || // allow user to enable spam tokens + sip10ManageTokens[token.ticker] === true, + ); + return useQuery({ enabled: userTokens.length > 0, retry: handleRetries, queryKey: ['swap-from-tokens', network.type, userTokens, protocol, from, search], queryFn, - select: (data) => data.filter((rune) => !spamTokenSet.has(rune.ticker)), + select, }); }; diff --git a/src/app/screens/swap/index.tsx b/src/app/screens/swap/index.tsx index 120a0170..b045d98c 100644 --- a/src/app/screens/swap/index.tsx +++ b/src/app/screens/swap/index.tsx @@ -46,7 +46,7 @@ import trackSwapMixPanel from './mixpanel'; import QuoteSummary from './quoteSummary'; import QuotesModal from './quotesModal'; import type { OrderInfo, Side, StxOrderInfo } from './types'; -import useMasterCoinsList from './useMasterCoinsList'; +import useVisibleMasterCoinsList from './useVisibleMasterCoinsList'; import { getTrackingIdentifier, isStxTx, @@ -138,7 +138,7 @@ export default function SwapScreen() { const defaultFrom = params.get('from'); const { quotes, loading: quotesLoading, error: quotesError, fetchQuotes } = useGetQuotes(); const { data: runeFloorPrice } = useRuneFloorPriceQuery(toToken?.name ?? ''); - const coinsMasterList = useMasterCoinsList(); + const coinsMasterList = useVisibleMasterCoinsList(); const { tokenInfo: sip10FromTokenInfoUSD } = useGetSip10TokenInfo({ principal: toToken?.ticker, fiatCurrency: 'USD', @@ -149,7 +149,7 @@ export default function SwapScreen() { const token = coinsMasterList.find((coin) => coin.principal === defaultFrom); setFromToken(token); } - }, [defaultFrom, coinsMasterList.length]); + }, [defaultFrom, coinsMasterList]); const handleGoBack = () => { navigate('/'); diff --git a/src/app/screens/swap/useMasterCoinsList.tsx b/src/app/screens/swap/useMasterCoinsList.tsx index 2beb12cb..c0687ff7 100644 --- a/src/app/screens/swap/useMasterCoinsList.tsx +++ b/src/app/screens/swap/useMasterCoinsList.tsx @@ -31,7 +31,7 @@ export const stxFt: FungibleToken = { const useMasterCoinsList = () => { const { data: sip10FtList } = useGetSip10FungibleTokens(); - const { unfilteredData: runesFtList } = useRuneFungibleTokensQuery(); + const { data: runesFtList } = useRuneFungibleTokensQuery(); const { hideStx } = useWalletSelector(); const isStacksSwapsEnabled = useHasFeature(FeatureId.STACKS_SWAPS); diff --git a/src/app/screens/swap/useVisibleMasterCoinsList.tsx b/src/app/screens/swap/useVisibleMasterCoinsList.tsx index d8d0a026..10f86af0 100644 --- a/src/app/screens/swap/useVisibleMasterCoinsList.tsx +++ b/src/app/screens/swap/useVisibleMasterCoinsList.tsx @@ -7,20 +7,18 @@ import { useMemo } from 'react'; import { btcFt, stxFt } from './useMasterCoinsList'; const useVisibleMasterCoinsList = () => { - const { visible: sip10FtList } = useVisibleSip10FungibleTokens(); - const { visible: runesFtList } = useVisibleRuneFungibleTokens(); - const isStacksSwapsEnabled = useHasFeature(FeatureId.STACKS_SWAPS); - + const { data: sip10FtList } = useVisibleSip10FungibleTokens(); + const { data: runesFtList } = useVisibleRuneFungibleTokens(); const { hideStx } = useWalletSelector(); + const isStacksSwapsEnabled = useHasFeature(FeatureId.STACKS_SWAPS); const coinsMasterList = useMemo( () => [ - ...sip10FtList, - ...runesFtList, + ...(runesFtList || []), btcFt, - ...(!hideStx && isStacksSwapsEnabled ? [stxFt] : []), + ...(!hideStx && isStacksSwapsEnabled ? [stxFt, ...(sip10FtList ?? [])] : []), ], - [sip10FtList, runesFtList, hideStx, isStacksSwapsEnabled], + [runesFtList, hideStx, isStacksSwapsEnabled, sip10FtList], ); return coinsMasterList; diff --git a/src/app/screens/unlistRune/index.tsx b/src/app/screens/unlistRune/index.tsx index 22177702..861c6916 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 { visible: 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/stores/index.ts b/src/app/stores/index.ts index 61607e50..5eb2b25c 100644 --- a/src/app/stores/index.ts +++ b/src/app/stores/index.ts @@ -29,8 +29,8 @@ const migrations = { }, 3: ( state: WalletState & { - brcCoinsList: FungibleToken[] | null; // removed in v3 - coinsList: FungibleToken[] | null; // removed in v3 + brcCoinsList: (FungibleToken & { visible?: boolean })[] | null; // removed in v3 + coinsList: (FungibleToken & { visible?: boolean })[] | null; // removed in v3 coins: Coin[]; // removed in v3 }, ) => ({ diff --git a/src/app/utils/helper.ts b/src/app/utils/helper.ts index 2a767119..0550208d 100644 --- a/src/app/utils/helper.ts +++ b/src/app/utils/helper.ts @@ -4,7 +4,7 @@ import { microstacksToStx, satsToBtc, type Account, - type FungibleToken, + type FungibleTokenWithStates, type NetworkType, type NftData, type SettingsNetwork, @@ -252,9 +252,9 @@ export const calculateTotalBalance = ({ }: { stxBalance?: string; btcBalance?: string; - sipCoinsList: FungibleToken[]; - brcCoinsList: FungibleToken[]; - runesCoinList: FungibleToken[]; + sipCoinsList: FungibleTokenWithStates[]; + brcCoinsList: FungibleTokenWithStates[]; + runesCoinList: FungibleTokenWithStates[]; stxBtcRate: string; btcFiatRate: string; hideStx: boolean; @@ -277,7 +277,7 @@ export const calculateTotalBalance = ({ if (sipCoinsList) { totalBalance = sipCoinsList.reduce((acc, coin) => { - if (coin.visible && coin.tokenFiatRate && coin.decimals) { + if (coin.isEnabled && coin.tokenFiatRate && coin.decimals) { const tokenUnits = new BigNumber(10).exponentiatedBy(new BigNumber(coin.decimals)); const coinFiatValue = new BigNumber(coin.balance) .dividedBy(tokenUnits) @@ -291,7 +291,7 @@ export const calculateTotalBalance = ({ if (brcCoinsList) { totalBalance = brcCoinsList.reduce((acc, coin) => { - if (coin.visible && coin.tokenFiatRate) { + if (coin.isEnabled && coin.tokenFiatRate) { const coinFiatValue = new BigNumber(coin.balance).multipliedBy( new BigNumber(coin.tokenFiatRate), ); @@ -304,7 +304,7 @@ export const calculateTotalBalance = ({ if (runesCoinList) { totalBalance = runesCoinList.reduce((acc, coin) => { - if (coin.visible && coin.tokenFiatRate) { + if (coin.isEnabled && coin.tokenFiatRate) { const coinFiatValue = new BigNumber(getFtBalance(coin)).multipliedBy( new BigNumber(coin.tokenFiatRate), ); diff --git a/tests/pages/wallet.ts b/tests/pages/wallet.ts index 2fb0bfa9..2929860c 100644 --- a/tests/pages/wallet.ts +++ b/tests/pages/wallet.ts @@ -630,18 +630,18 @@ export default class Wallet { await expect(this.labelAccountName).toBeVisible(); await expect(this.buttonMenu).toBeVisible(); - await expect(await this.labelTokenSubtitle.count()).toBeGreaterThanOrEqual(2); + expect(await this.labelTokenSubtitle.count()).toBeGreaterThanOrEqual(2); await expect(this.navigationDashboard).toBeVisible(); await expect(this.navigationNFT).toBeVisible(); await expect(this.navigationStacking).toBeVisible(); await expect(this.navigationExplore).toBeVisible(); await expect(this.navigationSettings).toBeVisible(); - await expect(await this.divTokenRow.count()).toBeGreaterThan(1); + expect(await this.divTokenRow.count()).toBeGreaterThan(1); } async checkVisualsSendSTXPage3() { - await expect(this.page.url()).toContain('confirm-stx-tx'); + expect(this.page.url()).toContain('confirm-stx-tx'); await expect(this.buttonConfirm).toBeVisible(); await expect(this.buttonCancel).toBeVisible(); await expect(this.receiveAddress).toBeVisible(); @@ -655,10 +655,10 @@ export default class Wallet { * Checks the visibility and state of UI elements state on first page in Send Flow * * @param {string} url - The expected URL to validate the correct page navigation. - * @param {boolean} isSTX - Optional flag to apply STX-specific element checks (default: false). + * @param {boolean} moreInputFields - (default: false). */ async checkVisualsSendPage1(url: string, moreInputFields: boolean = false) { - await expect(this.page.url()).toContain(url); + expect(this.page.url()).toContain(url); await expect(this.buttonNext).toBeVisible(); await expect(this.buttonNext).toBeDisabled(); @@ -682,7 +682,7 @@ export default class Wallet { * @param {boolean} isSTX - Indicates if the page is STX-specific; adjusts element checks accordingly (default: false). */ async checkVisualsSendPage2(url: string, isSTX: boolean = false) { - await expect(this.page.url()).toContain(url); + expect(this.page.url()).toContain(url); await expect(this.buttonNext).toBeVisible(); await expect(this.buttonNext).toBeDisabled(); @@ -722,7 +722,7 @@ export default class Wallet { tokenImageShown: boolean = true, ordinalNumber?: string, ) { - await expect(this.page.url()).toContain(url); + expect(this.page.url()).toContain(url); await expect(this.buttonExpand).toBeVisible(); await expect(this.buttonCancel).toBeEnabled(); await expect(this.buttonConfirm).toBeEnabled(); @@ -749,29 +749,27 @@ export default class Wallet { // Execute these checks only if sendAddress is provided if (sendAddress) { await expect(this.sendAddress.first()).toBeVisible(); - await expect(await this.sendAddress.first().innerText()).toContain(sendAddress.slice(-4)); + expect(await this.sendAddress.first().innerText()).toContain(sendAddress.slice(-4)); } // Execute these checks only if recipientAddress is provided if (recipientAddress) { await expect(this.receiveAddress.first()).toBeVisible(); - await expect(await this.receiveAddress.first().innerText()).toContain( - recipientAddress.slice(-4), - ); + expect(await this.receiveAddress.first().innerText()).toContain(recipientAddress.slice(-4)); } // Collection Inscriptions don't have the ordinal number displayed in the Review // Check if the right ordinal number is shown if (ordinalNumber) { const reviewNumberOrdinal = await this.numberInscription.first().innerText(); - await expect(ordinalNumber).toMatch(reviewNumberOrdinal); + expect(ordinalNumber).toMatch(reviewNumberOrdinal); } } // Check Visuals of Rune Dashboard (without List button), return balance amount - async checkVisualsRunesDashboard(runeName) { + async checkVisualsRunesDashboard(runeName: string) { await expect(this.imageToken.first()).toBeVisible(); await expect(this.textCoinTitle).toBeVisible(); - await expect(await this.textCoinTitle).toContainText(runeName); + await expect(this.textCoinTitle).toContainText(runeName); await expect(this.coinBalance).toBeVisible(); await expect(this.buttonReceive).toBeVisible(); await expect(this.buttonSend).toBeVisible(); @@ -786,11 +784,11 @@ export default class Wallet { await expect(this.buttonSetPrice).toBeVisible(); await expect(this.buttonSetPrice).toBeDisabled(); await expect(this.runeItem.first()).toBeVisible(); - await expect(await this.runeItem.count()).toBeGreaterThanOrEqual(1); + expect(await this.runeItem.count()).toBeGreaterThanOrEqual(1); } async checkVisualsSwapPage() { - await expect(this.page.url()).toContain('swap'); + expect(this.page.url()).toContain('swap'); await expect(this.buttonDownArrow.first()).toBeVisible(); await expect(this.buttonGetQuotes.first()).toBeVisible(); await expect(this.buttonGetQuotes.first()).toBeDisabled(); @@ -798,8 +796,8 @@ export default class Wallet { await expect(this.swapTokenBalance).toContainText('--'); await expect(this.buttonBack).toBeVisible(); await expect(this.nameToken.first()).toContainText('Select asset'); - await expect(await this.nameToken).toHaveCount(2); - await expect(await this.buttonDownArrow).toHaveCount(2); + await expect(this.nameToken).toHaveCount(2); + await expect(this.buttonDownArrow).toHaveCount(2); await expect(this.buttonGetQuotes).toBeVisible(); await expect(this.textUSD).toBeVisible(); await expect(this.buttonSwapToken).toBeVisible(); @@ -813,7 +811,7 @@ export default class Wallet { const usdAmount = await this.textUSD.innerText(); const numericUSDValue = parseFloat(usdAmount.replace(/[^0-9.]/g, '')); - await expect(numericUSDValue).toBeGreaterThan(0); + expect(numericUSDValue).toBeGreaterThan(0); return numericUSDValue; } @@ -823,8 +821,8 @@ export default class Wallet { async checkVisualsQuotePage( tokenName: string, slippage: boolean, - numericQuoteValue, - numericUSDValue, + numericQuoteValue: number, + numericUSDValue: number, ) { await expect(this.buttonSwap).toBeVisible(); await expect(this.buttonEditFee).toBeVisible(); @@ -832,7 +830,7 @@ export default class Wallet { await expect(this.buttonSlippage).toBeVisible(); } // Only 2 token should be visible - await expect(await this.buttonSwapPlace.count()).toBe(2); + expect(await this.buttonSwapPlace.count()).toBe(2); // await expect(await this.imageToken.count()).toBe(2); // Check Rune token name @@ -841,18 +839,18 @@ export default class Wallet { // Check if USD amount from quote page is the same as from th swap start flow page const usdAmountQuote = await this.textUSD.first().innerText(); const numericUSDQuote = parseFloat(usdAmountQuote.replace(/[^0-9.]/g, '')); - await expect(numericUSDQuote).toEqual(numericUSDValue); + expect(numericUSDQuote).toEqual(numericUSDValue); // min-received-amount value should be the same as quoteAmount const minReceivedAmount = await this.minReceivedAmount.innerText(); const numericMinReceivedAmount = parseFloat(minReceivedAmount.replace(/[^0-9.]/g, '')); const formattedNumericMinReceivedAmount = parseFloat(numericMinReceivedAmount.toFixed(3)); - await expect(formattedNumericMinReceivedAmount).toEqual(numericQuoteValue); + expect(formattedNumericMinReceivedAmount).toEqual(numericQuoteValue); // check if quoteAmount is the same from the page before const quoteAmount2Page = await this.quoteAmount.last().innerText(); const numericQuote2Page = parseFloat(quoteAmount2Page.replace(/[^0-9.]/g, '')); - await expect(numericQuote2Page).toEqual(numericQuoteValue); + expect(numericQuote2Page).toEqual(numericQuoteValue); } async checkVisualsListOnMEPage() { @@ -879,7 +877,7 @@ export default class Wallet { // Save the current fee amount for comparison const originalFee = await this.feeAmount.innerText(); const numericOriginalFee = parseFloat(originalFee.replace(/[^0-9.]/g, '')); - await expect(numericOriginalFee).toBeGreaterThan(0); + expect(numericOriginalFee).toBeGreaterThan(0); let feePriority = 'Medium'; if (feePriorityShown) { feePriority = await this.labelFeePriority.innerText(); @@ -896,7 +894,7 @@ export default class Wallet { .locator(this.labelTotalFee) .innerText(); const numericFee = parseFloat(fee.replace(/[^0-9.]/g, '')); - await expect(numericFee).toBe(numericOriginalFee); + expect(numericFee).toBe(numericOriginalFee); // Save high fee rate for comparison const highFee = await this.labelTotalFee.first().innerText(); @@ -907,12 +905,12 @@ export default class Wallet { const newFee = await this.feeAmount.innerText(); const numericNewFee = parseFloat(newFee.replace(/[^0-9.]/g, '')); - await expect(numericNewFee).toBe(numericHighFee); + expect(numericNewFee).toBe(numericHighFee); } async navigateToCollectibles() { await this.navigationNFT.click(); - await expect(this.page.url()).toContain('nft-dashboard'); + expect(this.page.url()).toContain('nft-dashboard'); // If 'enable' rare sats pop up is appearing if (await this.buttonEnable.isVisible()) { await this.buttonEnable.click(); @@ -923,32 +921,30 @@ export default class Wallet { } // had to disable this rule as my first assertion was always changed to a wrong assertion - /* eslint-disable playwright/prefer-web-first-assertions */ async checkAmountsSendingSTX(amountSTXSend, STXTest, sendFee) { - await expect(await this.receiveAddress.first().innerText()).toContain(STXTest.slice(-4)); + expect(await this.receiveAddress.first().innerText()).toContain(STXTest.slice(-4)); // Sending amount without Fee const sendAmount = await this.confirmAmount.first().innerText(); const numericValueSendAmount = parseFloat(sendAmount.replace(/[^0-9.]/g, '')); - await expect(numericValueSendAmount).toEqual(amountSTXSend); + expect(numericValueSendAmount).toEqual(amountSTXSend); // Fees const fee = await this.feeAmount.innerText(); const numericValueFee = parseFloat(fee.replace(/[^0-9.]/g, '')); - await expect(numericValueFee).toEqual(sendFee); + expect(numericValueFee).toEqual(sendFee); } - /* eslint-disable playwright/prefer-web-first-assertions */ async checkAmountsSendingBTC(selfBTCTest, BTCTest, amountBTCSend) { // Sending amount without Fee const amountText = await this.confirmAmount.first().innerText(); const numericValueAmountText = parseFloat(amountText.replace(/[^0-9.]/g, '')); - await expect(numericValueAmountText).toEqual(amountBTCSend); + expect(numericValueAmountText).toEqual(amountBTCSend); // Address check sending and receiving - await expect(await this.sendAddress.innerText()).toContain(selfBTCTest.slice(-4)); - await expect(await this.receiveAddress.first().innerText()).toContain(BTCTest.slice(-4)); + expect(await this.sendAddress.innerText()).toContain(selfBTCTest.slice(-4)); + expect(await this.receiveAddress.first().innerText()).toContain(BTCTest.slice(-4)); const confirmAmountAfter = await this.confirmAmount.last().innerText(); const originalFee = await this.feeAmount.innerText(); @@ -963,7 +959,7 @@ export default class Wallet { // Balance - fees - sending amount const roundedResult = Number((num3 - num2 - amountBTCSend).toFixed(9)); // Check if Balance value after the transaction is the same as the calculated value - await expect(num1).toEqual(roundedResult); + expect(num1).toEqual(roundedResult); } async confirmSendTransaction(transactionIDShown: boolean = true) { @@ -976,7 +972,7 @@ export default class Wallet { await this.buttonClose.click(); } - async getAddress(whichAddress): Promise { + async getAddress(whichAddress: string): Promise { // click on 'Receive' button await this.allUpperButtons.nth(1).click(); @@ -994,7 +990,7 @@ export default class Wallet { return address; } - async getTokenBalance(tokenname) { + async getTokenBalance(tokenname: string) { const locator = this.page .getByRole('button') .filter({ has: this.labelTokenSubtitle.getByText(tokenname, { exact: true }) }) @@ -1005,14 +1001,14 @@ export default class Wallet { return numericValue; } - async clickOnSpecificToken(tokenname) { + async clickOnSpecificToken(tokenname: string) { const specificToken = this.page .getByRole('button') .filter({ has: this.labelTokenSubtitle.getByText(tokenname, { exact: true }) }); await specificToken.last().click(); } - async clickOnSpecificInscription(inscriptionName) { + async clickOnSpecificInscription(inscriptionName: string) { const specificToken = this.containersCollectibleItem .filter({ has: this.nameInscription.getByText(inscriptionName, { exact: true }), @@ -1022,7 +1018,7 @@ export default class Wallet { } // This function tries to click on a specific rune, if the rune is not enabled it will enable the test rune and then click on it - async checkAndClickOnSpecificRune(tokenname) { + async checkAndClickOnSpecificRune(tokenname: string) { // Check if test rune is enabled and if not enabled the test rune try { // click on the test rune @@ -1066,7 +1062,7 @@ export default class Wallet { await expect(this.inputFallbackBTCURL).toBeVisible(); } - async checkTestnetUrls(shouldContainTestnet) { + async checkTestnetUrls(shouldContainTestnet: boolean) { const inputsURL = [this.inputStacksURL, this.inputBTCURL, this.inputFallbackBTCURL]; const checks = inputsURL.map(async (input) => { const inputValue = await input.inputValue(); @@ -1097,6 +1093,7 @@ export default class Wallet { await this.checkTestnetUrls(true); + // TODO think of a better way to do this // Wait for the network to be switched so that API doesn't fail because of the rate limiting await this.page.waitForTimeout(15000); @@ -1119,6 +1116,7 @@ export default class Wallet { await this.checkTestnetUrls(false); + // TODO think of a better way to do this // Wait for the network to be switched so that API doesn't fail because of the rate limiting await this.page.waitForTimeout(15000); @@ -1156,30 +1154,21 @@ export default class Wallet { const balanceText = await this.labelCoinBalanceCurrency.innerText(); totalBalance = parseFloat(balanceText.replace(/[^\d.-]/g, '')); } - // Check if total balance of all tokens is the same as total wallet balance - const totalBalanceText = await this.balance.innerText(); - const totalBalanceWallet = parseFloat(totalBalanceText.replace(/[^\d.-]/g, '')); - await expect(totalBalanceWallet).toBe(totalBalance); return totalBalance; } - // The enableRandomToken function takes a parameter tokenType which can either be ‘BRC20’ or ‘SIP10’. This parameter determines additional actions specific to BRC20 tokens. - async enableRandomToken(tokenType: 'BRC20' | 'SIP10'): Promise { + async selectLastToken(tokenType: 'BRC20' | 'SIP10'): Promise { await this.manageTokenButton.click(); - await expect(this.page.url()).toContain('manage-tokens'); + expect(this.page.url()).toContain('manage-tokens'); // Click on the specific token type button if BRC20 is selected if (tokenType === 'BRC20') { await this.buttonBRC20.click(); } - // Enable a random token - const tokenName = await this.toggleRandomToken(true); - - // Navigate back and verify the token is visible + const chosenToken = this.divTokenRow.last(); + const tokenName = (await chosenToken.getAttribute('data-testid')) || 'default-value'; await this.buttonBack.click(); - await expect(this.labelTokenSubtitle.getByText(tokenName, { exact: true })).toBeVisible(); - return tokenName; } diff --git a/tests/specs/managementAccount.spec.ts b/tests/specs/managementAccount.spec.ts index 03afbfde..20d8a528 100644 --- a/tests/specs/managementAccount.spec.ts +++ b/tests/specs/managementAccount.spec.ts @@ -12,7 +12,7 @@ test.describe('Account Management', () => { await page.goto(`chrome-extension://${extensionId}/popup.html`); await wallet.checkVisualsStartpage(); await wallet.labelAccountName.click(); - await expect(page.url()).toContain('account-list'); + expect(page.url()).toContain('account-list'); await expect(wallet.labelAccountName).toHaveCount(1); await expect(wallet.buttonGenerateAccount).toBeVisible(); await expect(wallet.buttonConnectHardwareWallet).toBeVisible(); @@ -32,7 +32,7 @@ test.describe('Account Management', () => { await page.goto(`chrome-extension://${extensionId}/popup.html`); await wallet.checkVisualsStartpage(); await wallet.labelAccountName.click(); - await expect(page.url()).toContain('account-list'); + expect(page.url()).toContain('account-list'); await expect(wallet.labelAccountName).toHaveCount(1); await wallet.buttonAccountOptions.click(); await expect(wallet.buttonRenameAccount).toBeVisible(); @@ -65,7 +65,7 @@ test.describe('Account Management', () => { await page.goto(`chrome-extension://${extensionId}/popup.html`); await wallet.checkVisualsStartpage(); await wallet.labelAccountName.click(); - await expect(page.url()).toContain('account-list'); + expect(page.url()).toContain('account-list'); await expect(wallet.labelAccountName).toHaveCount(1); await wallet.buttonAccountOptions.click(); await expect(wallet.buttonRenameAccount).toBeVisible(); @@ -93,14 +93,14 @@ test.describe('Account Management', () => { await page.goto(`chrome-extension://${extensionId}/popup.html`); await wallet.checkVisualsStartpage(); await wallet.labelAccountName.click(); - await expect(page.url()).toContain('account-list'); + expect(page.url()).toContain('account-list'); await expect(wallet.labelAccountName).toHaveCount(1); await wallet.buttonGenerateAccount.click(); await expect(wallet.labelAccountName).toHaveCount(2); await expect(wallet.buttonAccountOptions).toHaveCount(2); await expect(wallet.accountBalance).toHaveCount(2); const balanceText = await wallet.getBalanceOfAllAccounts(); - await expect(balanceText).toBe(0); + expect(balanceText).toBe(0); }); test('Switch to another account and switch back', async ({ page, extensionId }) => { @@ -111,12 +111,12 @@ test.describe('Account Management', () => { await wallet.checkVisualsStartpage(); await expect(wallet.labelAccountName).toHaveText('Account 1'); await wallet.labelAccountName.click(); - await expect(page.url()).toContain('account-list'); + expect(page.url()).toContain('account-list'); await expect(wallet.labelAccountName).toHaveCount(1); await wallet.buttonGenerateAccount.click(); await expect(wallet.labelAccountName).toHaveCount(2); const balanceText = await wallet.getBalanceOfAllAccounts(); - await expect(balanceText).toBe(0); + expect(balanceText).toBe(0); await wallet.labelAccountName.last().click(); await wallet.checkVisualsStartpage(); await expect(wallet.labelAccountName).toHaveText('Account 2'); diff --git a/tests/specs/managementToken.spec.ts b/tests/specs/managementToken.spec.ts index 0b559955..69ded0ed 100644 --- a/tests/specs/managementToken.spec.ts +++ b/tests/specs/managementToken.spec.ts @@ -13,278 +13,143 @@ test.describe('Token Management', () => { await wallet.checkVisualsStartpage(); await expect(wallet.balance).toHaveText('$0.00'); await wallet.manageTokenButton.click(); - await expect(page.url()).toContain('manage-tokens'); + expect(page.url()).toContain('manage-tokens'); await expect(wallet.buttonBack).toBeVisible(); await expect(wallet.buttonSip10).toBeVisible(); await expect(wallet.buttonBRC20).toBeVisible(); await expect(wallet.buttonRunes).toBeVisible(); await expect(wallet.headingTokens).toBeVisible(); - // Check SIP10 token - await expect(wallet.checkboxTokenInactive.first()).toBeVisible(); - const amounttokenSIP = await wallet.labelCoinTitle.count(); - - await expect(amounttokenSIP).toBeGreaterThanOrEqual(15); - await expect(wallet.checkboxTokenInactive).toHaveCount(amounttokenSIP - 1); + // Check SIP10 token tab - only Stacks 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.checkboxToken).toHaveCount(amounttokenSIP); + await expect(wallet.checkboxTokenInactive).toHaveCount(0); - // Check BRC20 token + // Check BRC20 token tab - nothing shows when user has no brc20 balances await wallet.buttonBRC20.click(); - await expect(wallet.checkboxTokenInactive.first()).toBeVisible(); - const amounttokenBRC20 = await wallet.labelCoinTitle.count(); - await expect(amounttokenBRC20).toBeGreaterThanOrEqual(8); - await expect(wallet.checkboxTokenInactive).toHaveCount(amounttokenBRC20); - await expect(wallet.checkboxTokenActive).toHaveCount(0); - await expect(wallet.checkboxToken).toHaveCount(amounttokenBRC20); - - // Check rune token - await wallet.buttonRunes.click(); await expect(wallet.labelCoinTitle).toHaveCount(0); + await expect(wallet.checkboxToken).toHaveCount(0); await expect(wallet.checkboxTokenInactive).toHaveCount(0); await expect(wallet.checkboxTokenActive).toHaveCount(0); + + // Check rune token tab - nothing shows when user has no runes balances + await wallet.buttonRunes.click(); + await expect(wallet.labelCoinTitle).toHaveCount(0); await expect(wallet.checkboxToken).toHaveCount(0); + await expect(wallet.checkboxTokenInactive).toHaveCount(0); + await expect(wallet.checkboxTokenActive).toHaveCount(0); }); - test('Enable and disable some BRC-20 token', async ({ page, extensionId }) => { - const onboardingPage = new Onboarding(page); + test('Toggle a BRC-20 token', async ({ page, extensionId }) => { const wallet = new Wallet(page); - await onboardingPage.createWalletSkipBackup(strongPW); + await wallet.setupTest(extensionId, 'SEED_WORDS1', false); - await test.step('Enable a random token', async () => { - await page.goto(`chrome-extension://${extensionId}/popup.html`); - await wallet.checkVisualsStartpage(); - // Check balances - await expect(wallet.balance).toBeVisible(); - await expect(wallet.balance).toHaveText('$0.00'); - let balanceText = await wallet.getBalanceOfAllTokens(); - await expect(balanceText).toBe(0); - await wallet.manageTokenButton.click(); - await expect(page.url()).toContain('manage-tokens'); - await wallet.buttonBRC20.click(); - await expect(wallet.checkboxTokenInactive.first()).toBeVisible(); - const amounttokenBRC20 = await wallet.labelCoinTitle.count(); - // Enable random token - const tokenName = await wallet.toggleRandomToken(true); - // Check that amount of checkboxes changed - await expect(wallet.checkboxTokenActive).toHaveCount(1); - await expect(wallet.checkboxTokenInactive).toHaveCount(amounttokenBRC20 - 1); - await wallet.buttonBack.click(); - // new enabled token should be visible on dashboard - await expect(wallet.labelTokenSubtitle.getByText(tokenName, { exact: true })).toBeVisible(); - // Check balances - await expect(wallet.balance).toBeVisible(); - await expect(wallet.balance).toHaveText('$0.00'); - balanceText = await wallet.getBalanceOfAllTokens(); - await expect(balanceText).toBe(0); - }); - - await test.step('Enable some more token', async () => { + await test.step('Toggle a random token', async () => { await wallet.manageTokenButton.click(); await wallet.buttonBRC20.click(); - await expect(wallet.checkboxTokenInactive.first()).toBeVisible(); - const amounttokenBRC20 = await wallet.labelCoinTitle.count(); - const tokenName1 = await wallet.toggleRandomToken(true); - const tokenName2 = await wallet.toggleRandomToken(true); - const tokenName3 = await wallet.toggleRandomToken(true); - await expect(wallet.checkboxTokenActive).toHaveCount(4); - await expect(wallet.checkboxTokenInactive).toHaveCount(amounttokenBRC20 - 4); - await wallet.buttonBack.click(); - // new enabled tokens should be visible on dashboard - await expect(wallet.labelTokenSubtitle.getByText(tokenName1, { exact: true })).toBeVisible(); - await expect(wallet.labelTokenSubtitle.getByText(tokenName2, { exact: true })).toBeVisible(); - await expect(wallet.labelTokenSubtitle.getByText(tokenName3, { exact: true })).toBeVisible(); - }); - await test.step('Disable a random token', async () => { - await page.goto(`chrome-extension://${extensionId}/popup.html`); - await wallet.checkVisualsStartpage(); - await wallet.manageTokenButton.click(); - await expect(page.url()).toContain('manage-tokens'); - await wallet.buttonBRC20.click(); - await expect(wallet.checkboxTokenInactive.first()).toBeVisible(); - const amounttokenBRC20 = await wallet.labelCoinTitle.count(); - const tokenName = await wallet.toggleRandomToken(false); - await expect(wallet.checkboxTokenActive).toHaveCount(3); - await expect(wallet.checkboxTokenInactive).toHaveCount(amounttokenBRC20 - 3); - await wallet.buttonBack.click(); - // new enabled token should be visible on dashboard - await expect(wallet.labelTokenSubtitle.getByText(tokenName, { exact: true })).toBeHidden(); - // Check balances - await expect(wallet.balance).toBeVisible(); - await expect(wallet.balance).toHaveText('$0.00'); - const balanceText = await wallet.getBalanceOfAllTokens(); - await expect(balanceText).toBe(0); - }); - }); - - test('Enable and disable some SIP-10 token', async ({ page, extensionId }) => { - const onboardingPage = new Onboarding(page); - const wallet = new Wallet(page); - await onboardingPage.createWalletSkipBackup(strongPW); - - await test.step('Enable a random token', async () => { - await page.goto(`chrome-extension://${extensionId}/popup.html`); - await wallet.checkVisualsStartpage(); - // Check balances - await expect(wallet.balance).toBeVisible(); - await expect(wallet.balance).toHaveText('$0.00'); - let balanceText = await wallet.getBalanceOfAllTokens(); - await expect(balanceText).toBe(0); - await wallet.manageTokenButton.click(); - await expect(page.url()).toContain('manage-tokens'); - await expect(wallet.checkboxTokenInactive.first()).toBeVisible(); - const amounttokenSIP = await wallet.labelCoinTitle.count(); - // Enable random token - const tokenName = await wallet.toggleRandomToken(true); - // Check that amount of checkboxes changed - await expect(wallet.checkboxTokenActive).toHaveCount(2); - await expect(wallet.checkboxTokenInactive).toHaveCount(amounttokenSIP - 2); - await wallet.buttonBack.click(); - // new enabled token should be visible on dashboard - await expect(wallet.labelTokenSubtitle.getByText(tokenName, { exact: true })).toBeVisible(); - // Check balances - await expect(wallet.balance).toBeVisible(); - await expect(wallet.balance).toHaveText('$0.00'); - balanceText = await wallet.getBalanceOfAllTokens(); - await expect(balanceText).toBe(0); - }); - - await test.step('Enable some more token', async () => { - await wallet.manageTokenButton.click(); - await expect(wallet.checkboxTokenInactive.first()).toBeVisible(); - const amounttokenSIP = await wallet.labelCoinTitle.count(); - const tokenName1 = await wallet.toggleRandomToken(true); - const tokenName2 = await wallet.toggleRandomToken(true); - const tokenName3 = await wallet.toggleRandomToken(true); - await expect(wallet.checkboxTokenActive).toHaveCount(5); - await expect(wallet.checkboxTokenInactive).toHaveCount(amounttokenSIP - 5); - await wallet.buttonBack.click(); - // new enabled tokens should be visible on dashboard - await expect(wallet.labelTokenSubtitle.getByText(tokenName1, { exact: true })).toBeVisible(); - await expect(wallet.labelTokenSubtitle.getByText(tokenName2, { exact: true })).toBeVisible(); - await expect(wallet.labelTokenSubtitle.getByText(tokenName3, { exact: true })).toBeVisible(); - }); - - await test.step('Disable a random token', async () => { - await page.goto(`chrome-extension://${extensionId}/popup.html`); - await wallet.checkVisualsStartpage(); - await wallet.manageTokenButton.click(); - await expect(page.url()).toContain('manage-tokens'); - await expect(wallet.checkboxTokenInactive.first()).toBeVisible(); - const amounttokenSIP = await wallet.labelCoinTitle.count(); - const tokenName = await wallet.toggleRandomToken(false); - await expect(wallet.checkboxTokenActive).toHaveCount(4); - await expect(wallet.checkboxTokenInactive).toHaveCount(amounttokenSIP - 4); - await wallet.buttonBack.click(); - // new enabled token should be visible on dashboard - await expect(wallet.labelTokenSubtitle.getByText(tokenName, { exact: true })).toBeHidden(); - // Check balances - await expect(wallet.balance).toBeVisible(); - await expect(wallet.balance).toHaveText('$0.00'); - const balanceText = await wallet.getBalanceOfAllTokens(); - await expect(balanceText).toBe(0); - }); - }); - - test('Enable and disable all SIP-10 token', async ({ page, extensionId }) => { - const onboardingPage = new Onboarding(page); - const wallet = new Wallet(page); - await onboardingPage.createWalletSkipBackup(strongPW); - - await test.step('Enable a all tokens', async () => { - await page.goto(`chrome-extension://${extensionId}/popup.html`); - await wallet.checkVisualsStartpage(); - // Check balances - await expect(wallet.balance).toBeVisible(); - await expect(wallet.balance).toHaveText('$0.00'); - let balanceText = await wallet.getBalanceOfAllTokens(); - await expect(balanceText).toBe(0); - await expect(wallet.labelTokenSubtitle).toHaveCount(2); - await wallet.manageTokenButton.click(); - await expect(wallet.checkboxTokenInactive.first()).toBeVisible(); - const amounttokenSIP = await wallet.labelCoinTitle.count(); - await expect(page.url()).toContain('manage-tokens'); - await expect(wallet.checkboxToken).toHaveCount(amounttokenSIP); - await wallet.toggleAllTokens(true); - await expect(wallet.checkboxTokenActive).toHaveCount(amounttokenSIP); - await expect(wallet.checkboxTokenInactive).toHaveCount(0); - await wallet.buttonBack.click(); - await expect(wallet.labelTokenSubtitle).toHaveCount(amounttokenSIP + 1); - await expect(wallet.balance).toBeVisible(); - await expect(wallet.balance).toHaveText('$0.00'); - balanceText = await wallet.getBalanceOfAllTokens(); - await expect(balanceText).toBe(0); - }); - - await test.step('Disable all tokens', async () => { - await page.goto(`chrome-extension://${extensionId}/popup.html`); - await wallet.checkVisualsStartpage(); - await wallet.manageTokenButton.click(); - await expect(page.url()).toContain('manage-tokens'); - await wallet.toggleAllTokens(false); - await expect(wallet.checkboxTokenInactive.first()).toBeVisible(); - const amounttokenSIP = await wallet.labelCoinTitle.count(); - await expect(wallet.checkboxTokenActive).toHaveCount(0); - await expect(wallet.checkboxTokenInactive).toHaveCount(amounttokenSIP); - await wallet.buttonBack.click(); - await expect(wallet.labelTokenSubtitle).toHaveCount(1); - // Check balances - await expect(wallet.balance).toBeVisible(); - await expect(wallet.balance).toHaveText('$0.00'); - const balanceText = await wallet.getBalanceOfAllTokens(); - await expect(balanceText).toBe(0); - }); - }); - test('Enable and disable all BRC-20 token', async ({ page, extensionId }) => { - const onboardingPage = new Onboarding(page); - const wallet = new Wallet(page); - await onboardingPage.createWalletSkipBackup(strongPW); - - await test.step('Enable a all tokens', async () => { - await page.goto(`chrome-extension://${extensionId}/popup.html`); - await wallet.checkVisualsStartpage(); - // Check balances - await expect(wallet.balance).toBeVisible(); - await expect(wallet.balance).toHaveText('$0.00'); - let balanceText = await wallet.getBalanceOfAllTokens(); - await expect(balanceText).toBe(0); - await expect(wallet.labelTokenSubtitle).toHaveCount(2); - await wallet.manageTokenButton.click(); - await expect(page.url()).toContain('manage-tokens'); - await wallet.buttonBRC20.click(); - await expect(wallet.checkboxTokenInactive.first()).toBeVisible(); - const amounttokenBRC20 = await wallet.labelCoinTitle.count(); - await expect(wallet.checkboxToken).toHaveCount(amounttokenBRC20); - await wallet.toggleAllTokens(true); - await expect(wallet.checkboxTokenActive).toHaveCount(amounttokenBRC20); - await expect(wallet.checkboxTokenInactive).toHaveCount(0); - await wallet.buttonBack.click(); - await expect(wallet.labelTokenSubtitle).toHaveCount(amounttokenBRC20 + 2); - await expect(wallet.balance).toBeVisible(); - await expect(wallet.balance).toHaveText('$0.00'); - balanceText = await wallet.getBalanceOfAllTokens(); - await expect(balanceText).toBe(0); - }); - - await test.step('Disable all tokens', async () => { - await page.goto(`chrome-extension://${extensionId}/popup.html`); - await wallet.checkVisualsStartpage(); - await wallet.manageTokenButton.click(); - await expect(page.url()).toContain('manage-tokens'); - await wallet.buttonBRC20.click(); + // NOTE: requires an account with at least 1 brc20 token with balance await expect(wallet.checkboxTokenActive.first()).toBeVisible(); - const amounttokenBRC20 = await wallet.labelCoinTitle.count(); - await wallet.toggleAllTokens(false); - await expect(wallet.checkboxTokenActive).toHaveCount(0); - await expect(wallet.checkboxTokenInactive).toHaveCount(amounttokenBRC20); + + // disable a random token + const tokenName = await wallet.toggleRandomToken(false); + + // expect token to be hidden on dashboard + const fetchTokens = page.waitForResponse((response) => + response.url().includes('/brc20/tokens'), + ); await wallet.buttonBack.click(); - await expect(wallet.labelTokenSubtitle).toHaveCount(2); - // Check balances - await expect(wallet.balance).toBeVisible(); - await expect(wallet.balance).toHaveText('$0.00'); - const balanceText = await wallet.getBalanceOfAllTokens(); - await expect(balanceText).toBe(0); + await fetchTokens; + await expect(wallet.labelTokenSubtitle.getByText(tokenName, { exact: true })).toBeHidden(); + + // enable the token again + await wallet.manageTokenButton.click(); + await wallet.buttonBRC20.click(); + await page.getByTestId(tokenName).locator('label').click(); + + // expect to be visible again on dashboard + const fetchTokensAgain = page.waitForResponse((response) => + response.url().includes('/brc20/tokens'), + ); + await wallet.buttonBack.click(); + await fetchTokensAgain; + await expect(wallet.labelTokenSubtitle.getByText(tokenName, { exact: true })).toBeVisible(); + }); + }); + + test('Toggle a SIP-10 token', async ({ page, extensionId }) => { + const wallet = new Wallet(page); + await wallet.setupTest(extensionId, 'SEED_WORDS1', false); + + await test.step('Toggle a random token', async () => { + await wallet.manageTokenButton.click(); + await wallet.buttonSip10.click(); + + // NOTE: requires an account with at least 1 sip10 token with balance + await expect(wallet.checkboxTokenActive.first()).toBeVisible(); + + // disable a random token + const tokenName = await wallet.toggleRandomToken(false); + + // expect token to be hidden on dashboard + const fetchTokens = page.waitForResponse((response) => + response.url().includes('/sip10/tokens'), + ); + await wallet.buttonBack.click(); + await fetchTokens; + await expect(wallet.labelTokenSubtitle.getByText(tokenName, { exact: true })).toBeHidden(); + + // enable the token again + await wallet.manageTokenButton.click(); + await wallet.buttonSip10.click(); + await page.getByTestId(tokenName).locator('label').click(); + + // expect to be visible again on dashboard + const fetchTokensAgain = page.waitForResponse((response) => + response.url().includes('/sip10/tokens'), + ); + await wallet.buttonBack.click(); + await fetchTokensAgain; + await expect(wallet.labelTokenSubtitle.getByText(tokenName, { exact: true })).toBeVisible(); + }); + }); + + test('Toggle a Runes token', async ({ page, extensionId }) => { + const wallet = new Wallet(page); + await wallet.setupTest(extensionId, 'SEED_WORDS1', false); + + await test.step('Toggle a random token', async () => { + await wallet.manageTokenButton.click(); + await wallet.buttonRunes.click(); + + // NOTE: requires an account with at least 1 runes token with balance + await expect(wallet.checkboxTokenActive.first()).toBeVisible(); + + // disable a random token + const tokenName = await wallet.toggleRandomToken(false); + + // expect token to be hidden on dashboard + const fetchTokens = page.waitForResponse((response) => + response.url().includes('/runes/fiat-rates'), + ); + await wallet.buttonBack.click(); + await fetchTokens; + await expect(wallet.labelTokenSubtitle.getByText(tokenName, { exact: true })).toBeHidden(); + + // enable the token again + await wallet.manageTokenButton.click(); + await wallet.buttonRunes.click(); + await page.getByTestId(tokenName).locator('label').click(); + + // expect to be visible again on dashboard + const fetchTokensAgain = page.waitForResponse((response) => + response.url().includes('/runes/fiat-rates'), + ); + await wallet.buttonBack.click(); + await fetchTokensAgain; + await expect(wallet.labelTokenSubtitle.getByText(tokenName, { exact: true })).toBeVisible(); }); }); }); diff --git a/tests/specs/transactionHistory.spec.ts b/tests/specs/transactionHistory.spec.ts index 51b4918c..3acaccbe 100644 --- a/tests/specs/transactionHistory.spec.ts +++ b/tests/specs/transactionHistory.spec.ts @@ -5,12 +5,11 @@ test.describe('Transaction', () => { test('Visual Check SIP 10 Token Transaction history mainnet', async ({ page, extensionId }) => { const wallet = new Wallet(page); await wallet.setupTest(extensionId, 'SEED_WORDS1', false); - - const tokenName = await wallet.enableRandomToken('SIP10'); + const tokenName = await wallet.selectLastToken('SIP10'); await wallet.clickOnSpecificToken(tokenName); - await expect(page.url()).toContain('coinDashboard'); + expect(page.url()).toContain('coinDashboard'); // Check token detail page for token image and coin title await expect(wallet.imageToken).toBeVisible(); await expect(wallet.textCoinTitle).toBeVisible(); @@ -26,10 +25,10 @@ test.describe('Transaction', () => { test('Visual Check BRC 20 Token Transaction history mainnet', async ({ page, extensionId }) => { const wallet = new Wallet(page); await wallet.setupTest(extensionId, 'SEED_WORDS1', false); - const tokenName = await wallet.enableRandomToken('BRC20'); + const tokenName = await wallet.selectLastToken('BRC20'); await wallet.clickOnSpecificToken(tokenName); - await expect(page.url()).toContain('coinDashboard'); + expect(page.url()).toContain('coinDashboard'); // Check token detail page for coin title await expect(wallet.textCoinTitle).toBeVisible(); await expect(wallet.textCoinTitle).toContainText(tokenName);