[ENG-5185] change sip10 tokens to default on, if seen on swaps provider lists (#613)

This commit is contained in:
Tim Man
2024-10-11 20:28:48 +08:00
committed by GitHub
parent 9d36991953
commit 982ccbd776
28 changed files with 439 additions and 598 deletions

14
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -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') {

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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<typeof useGetBrc20FungibleTokens> & {
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);
};

View File

@@ -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<typeof useRuneFungibleTokensQuery> & {
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);
};

View File

@@ -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<FungibleToken[]>) =>
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<typeof useGetSip10FungibleTokens> & {
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);
};

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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',

View File

@@ -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 (
<StyledTokenTile
@@ -539,7 +567,7 @@ function Home() {
onClose={onSendModalClose}
onSelectCoin={onSendFtSelect}
visible={openSendModal}
coins={combinedFtList.filter((ft) => new BigNumber(ft.balance).gt(0))}
coins={combinedFtList}
title={t('SEND')}
loadingWalletData={loadingStxWalletData || loadingBtcWalletData}
/>

View File

@@ -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();

View File

@@ -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<FungibleTokenProtocol>(
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' && <Stacks />}
{coins.map((coin: FungibleToken) => (
{coins.map((coin) => (
<CoinItem
id={coin.principal}
key={coin.principal}
@@ -257,7 +235,7 @@ function ManageTokens() {
runeSymbol={coin.runeSymbol}
disabled={false}
toggled={toggled}
enabled={coin.visible}
enabled={coin.isEnabled}
/>
))}
{!coins.length && <ErrorsText>{t('NO_COINS')}</ErrorsText>}
@@ -316,15 +294,7 @@ function ManageTokens() {
RUNES
</Button>
</FtInfoContainer>
<TokenContainer>
{selectedProtocol === 'runes' && !showRunes ? (
<RunesContainer>
<RunesComingSoon src={runesComingSoon} />
</RunesContainer>
) : (
getCoinsList()
)}
</TokenContainer>
<TokenContainer>{getCoinsList()}</TokenContainer>
</ScrollableContainer>
</Container>
<BottomBar tab="dashboard" />

View File

@@ -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);

View File

@@ -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

View File

@@ -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,
});
};

View File

@@ -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('/');

View File

@@ -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);

View File

@@ -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;

View File

@@ -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';

View File

@@ -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
},
) => ({

View File

@@ -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),
);

View File

@@ -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<string> {
async getAddress(whichAddress: string): Promise<string> {
// 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<string> {
async selectLastToken(tokenType: 'BRC20' | 'SIP10'): Promise<string> {
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;
}

View File

@@ -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');

View File

@@ -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();
});
});
});

View File

@@ -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);