Skip to content

Commit

Permalink
test(suite): filterReceiveAccounts
Browse files Browse the repository at this point in the history
  • Loading branch information
enjojoy committed Oct 1, 2024
1 parent 5f6f057 commit 7b3e25e
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isDebugOnlyAccountType, Network, networksCollection } from '@suite-common/wallet-config';
import { Network, networksCollection } from '@suite-common/wallet-config';
import { selectDevice } from '@suite-common/wallet-core';
import { useEffect, useMemo, useState } from 'react';
import { useForm } from 'react-hook-form';
Expand All @@ -22,6 +22,7 @@ import {
} from 'src/types/coinmarket/coinmarketVerify';
import { useAccountAddressDictionary } from 'src/hooks/wallet/useAccounts';
import { TrezorDevice } from '@suite-common/suite-types';
import { filterReceiveAccounts } from '@suite-common/wallet-utils';

const getSelectAccountOptions = (
suiteReceiveAccounts: Account[] | undefined,
Expand Down Expand Up @@ -79,33 +80,13 @@ const getSuiteReceiveAccounts = ({
((n.isDebugOnlyNetwork && isDebug) || !n.isDebugOnlyNetwork),
);

const isSameDevice = (account: Account) => account.deviceState === device?.state;
const isSameNetwork = (account: Account) => account.symbol === receiveNetwork;
const isDebugAndIsAccountDebugOnly = (account: Account) =>
isDebugOnlyAccountType(account.accountType, account.symbol) && isDebug;
const isNotDebugOnlyAccount = (account: Account) =>
!isDebugOnlyAccountType(account.accountType, account.symbol);
// Check if the account is not empty
const isNotEmptyAccount = (account: Account) => !account.empty;
// Check if the account is marked as visible
const isVisibleAccount = (account: Account) => account.visible;
const isFirstNormalAccount = (account: Account) =>
account.accountType === 'normal' && account.index === 0;
const isCoinjoinAccount = (account: Account) => account.accountType === 'coinjoin';

if (receiveNetworks.length > 0) {
// Get accounts of the current symbol belonging to the current device.
return accounts.filter(
account =>
isSameDevice(account) &&
isSameNetwork(account) &&
!isCoinjoinAccount(account) &&
(isDebugAndIsAccountDebugOnly(account) || isNotDebugOnlyAccount(account)) &&
(isNotEmptyAccount(account) ||
isVisibleAccount(account) ||
isFirstNormalAccount(account)),
);
}
return filterReceiveAccounts({
accounts,
deviceState: device?.state,
receiveNetwork,
isDebug,
receiveNetworks,
});
}

return undefined;
Expand Down Expand Up @@ -142,6 +123,7 @@ const useCoinmarketVerifyAccount = ({
}),
[accounts, currency, device, isDebug, receiveNetwork],
);

const selectAccountOptions = useMemo(
() => getSelectAccountOptions(suiteReceiveAccounts, device),
[device, suiteReceiveAccounts],
Expand Down
16 changes: 0 additions & 16 deletions suite-common/wallet-config/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
AccountType,
Network,
NetworkFeature,
Networks,
NetworkSymbol,
NormalizedNetworkAccount,
} from './types';
Expand Down Expand Up @@ -45,21 +44,6 @@ export const normalizeNetworkAccounts = (network: Network): NormalizedNetworkAcc
export const isBlockbookBasedNetwork = (symbol: NetworkSymbol) =>
networks[symbol]?.customBackends.some(backend => backend === 'blockbook');

export const isDebugOnlyAccountType = (
accountType: AccountType,
symbol?: NetworkSymbol,
): boolean => {
if (!symbol) return false;

const network = (networks as Networks)?.[symbol];

if (!network) return false;

const accountTypeInfo = network.accountTypes[accountType];

return !!accountTypeInfo?.isDebugOnlyAccountType;
};

export const getNetworkType = (symbol: NetworkSymbol) => networks[symbol]?.networkType;

// Takes into account just network features, not features for specific accountTypes.
Expand Down
128 changes: 128 additions & 0 deletions suite-common/wallet-utils/src/__tests__/filterReceiveAccounts.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { testMocks } from '@suite-common/test-utils';
import { Network, networksCollection } from '@suite-common/wallet-config';
import { Account } from '@suite-common/wallet-types';

import { isDebugOnlyAccountType, filterReceiveAccounts } from '../filterReceiveAccounts';

const { getSuiteDevice, getWalletAccount } = testMocks;

const accountsList: Account[] = [
getWalletAccount({ symbol: 'eth', accountType: 'legacy' }),
getWalletAccount({ symbol: 'eth', accountType: 'normal' }),
getWalletAccount({ symbol: 'eth', accountType: 'ledger' }),
getWalletAccount({ symbol: 'btc', accountType: 'coinjoin' }),
getWalletAccount({ symbol: 'btc', accountType: 'taproot' }),
getWalletAccount({ symbol: 'btc', accountType: 'legacy' }),
getWalletAccount({ symbol: 'btc', accountType: 'segwit' }),
getWalletAccount({ symbol: 'btc', accountType: 'ledger' }),
getWalletAccount({ symbol: 'pol', accountType: 'legacy' }),
getWalletAccount({ symbol: 'pol', accountType: 'normal' }),
getWalletAccount({ symbol: 'pol', accountType: 'ledger' }),
getWalletAccount({ symbol: 'sol', accountType: 'normal', empty: true, visible: false }),
getWalletAccount({ symbol: 'sol', accountType: 'ledger' }),
getWalletAccount({
symbol: 'sol',
accountType: 'ledger',
empty: true,
visible: false,
}),
];

type RunFilterReceiveAccountsTestParams = {
isDebug?: boolean;
receiveNetwork?: string;
deviceState?: `${string}@${string}:${number}`;
accounts?: Account[];
};

const runFilterReceiveAccouns = ({
isDebug = true,
receiveNetwork = 'eth',
deviceState = '1stTestnetAddress@device_id:0',
accounts = accountsList,
}: RunFilterReceiveAccountsTestParams) => {
const device = getSuiteDevice({
unavailableCapabilities: {
dash: 'no-support',
},
state: deviceState,
});
const unavailableCapabilities = device?.unavailableCapabilities ?? {};

const receiveNetworks = networksCollection.filter(
(n: Network) =>
n.symbol === receiveNetwork &&
!unavailableCapabilities[n.symbol] &&
((n.isDebugOnlyNetwork && isDebug) || !n.isDebugOnlyNetwork),
);

return filterReceiveAccounts({
accounts,
deviceState: device.state,
receiveNetwork,
isDebug,
receiveNetworks,
});
};

describe('filter receive accounts', () => {
it('checks if account is debug only type', () => {
expect(isDebugOnlyAccountType('legacy', 'btc')).toBe(false);
expect(isDebugOnlyAccountType('segwit', 'btc')).toBe(false);
expect(isDebugOnlyAccountType('coinjoin', 'btc')).toBe(false);
expect(isDebugOnlyAccountType('taproot', 'btc')).toBe(false);
expect(isDebugOnlyAccountType('ledger', 'btc')).toBe(false);
expect(isDebugOnlyAccountType('legacy', 'eth')).toBe(true);
expect(isDebugOnlyAccountType('ledger', 'eth')).toBe(true);
expect(isDebugOnlyAccountType('normal', 'regtest')).toBe(false);
});

it('returns no results when given an empty accounts array', () => {
expect(runFilterReceiveAccouns({ accounts: [] })).toEqual([]);
});

it('returns no results when given a non-existing network in acccounts list', () => {
expect(runFilterReceiveAccouns({ receiveNetwork: 'bnb' })).toEqual([]);
});

it('returns all accounts when debug mode is on', () => {
const filteredAccounts = [
getWalletAccount({ symbol: 'eth', accountType: 'legacy' }),
getWalletAccount({ symbol: 'eth', accountType: 'normal' }),
getWalletAccount({ symbol: 'eth', accountType: 'ledger' }),
];
expect(runFilterReceiveAccouns({})).toEqual(filteredAccounts);
});

it('returns only non-debug accounts when debug mode is off', () => {
const filteredAccounts = [getWalletAccount({ symbol: 'eth', accountType: 'normal' })];

expect(runFilterReceiveAccouns({ isDebug: false })).toEqual(filteredAccounts);
});

it('returns no results when device is not the same', () => {
expect(runFilterReceiveAccouns({ deviceState: '2ndTestnetAddress@device_id:0' })).toEqual(
[],
);
});

it('excludes coinjoin accounts for BTC network (also tests isAnotherNetwork and isCoinjoinAccount methods)', () => {
const filteredAccounts = [
getWalletAccount({ symbol: 'btc', accountType: 'taproot' }),
getWalletAccount({ symbol: 'btc', accountType: 'legacy' }),
getWalletAccount({ symbol: 'btc', accountType: 'segwit' }),
getWalletAccount({ symbol: 'btc', accountType: 'ledger' }),
];

expect(runFilterReceiveAccouns({ receiveNetwork: 'btc' })).toEqual(filteredAccounts);
});

it('returns account when when its either first normal account (no matter is empty or not visible) or it is not empty and visible', () => {
const filteredAccounts = [
getWalletAccount({ symbol: 'sol', accountType: 'normal', empty: true, visible: false }),
getWalletAccount({ symbol: 'sol', accountType: 'ledger' }),
];

expect(runFilterReceiveAccouns({ receiveNetwork: 'sol' })).toEqual(filteredAccounts);
});
});
51 changes: 51 additions & 0 deletions suite-common/wallet-utils/src/filterReceiveAccounts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { AccountType, getNetwork, Network, NetworkSymbol } from '@suite-common/wallet-config';
import { Account } from '@suite-common/wallet-types';

export const isDebugOnlyAccountType = (
accountType: AccountType,
symbol?: NetworkSymbol,
): boolean => {
if (!symbol) return false;

const network = getNetwork(symbol);

const accountTypeInfo = network.accountTypes[accountType];

return !!accountTypeInfo?.isDebugOnlyAccountType;
};

type FilterReceiveAccountsProps = {
accounts: Account[];
deviceState: string | undefined;
receiveNetwork?: string;
isDebug: boolean;
receiveNetworks: Network[];
};

export const filterReceiveAccounts = ({
accounts,
deviceState,
receiveNetwork,
isDebug,
}: FilterReceiveAccountsProps): Account[] => {
const isSameDevice = (account: Account) => account.deviceState === deviceState;
const isSameNetwork = (account: Account) => account.symbol === receiveNetwork;
const shouldDisplayDebugOnly = (account: Account) =>
isDebug || !isDebugOnlyAccountType(account.accountType, account.symbol);
const isNotEmptyAccount = (account: Account) => !account.empty;
const isVisibleAccount = (account: Account) => account.visible;
const isFirstNormalAccount = (account: Account) =>
account.accountType === 'normal' && account.index === 0;
const isCoinjoinAccount = (account: Account) => account.accountType === 'coinjoin';

return accounts.filter(
account =>
isSameDevice(account) &&
isSameNetwork(account) &&
!isCoinjoinAccount(account) &&
shouldDisplayDebugOnly(account) &&
(isNotEmptyAccount(account) ||
isVisibleAccount(account) ||
isFirstNormalAccount(account)),
);
};
1 change: 1 addition & 0 deletions suite-common/wallet-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ export * from './validationUtils';
export * from './antiFraud';
export * from './stakingUtils';
export * from './reviewTransactionUtils';
export * from './filterReceiveAccounts';

export { analyzeTransactions as analyzeTransactionsFixtures } from './__fixtures__/transactionUtils';

0 comments on commit 7b3e25e

Please sign in to comment.