From bf8db7dd898685e7b5655e5b8f26f68c742c9b48 Mon Sep 17 00:00:00 2001 From: Josh Leonard <30185185+josheleonard@users.noreply.github.com> Date: Thu, 20 Apr 2023 15:15:04 -0400 Subject: [PATCH] feat: network selection during hardware import (#18152) --- .../common/slices/api.slice.ts | 58 +--- .../hardware-wallet-connect/accounts-list.tsx | 303 ++++++++++++------ .../hardware-wallet-connect/index.tsx | 22 +- .../hardware-wallet-connect/style.ts | 2 +- 4 files changed, 204 insertions(+), 181 deletions(-) diff --git a/components/brave_wallet_ui/common/slices/api.slice.ts b/components/brave_wallet_ui/common/slices/api.slice.ts index 230935b58ae2..6920eb7c311e 100644 --- a/components/brave_wallet_ui/common/slices/api.slice.ts +++ b/components/brave_wallet_ui/common/slices/api.slice.ts @@ -82,7 +82,6 @@ import { getTokenParam } from '../../utils/api-utils' import { getAccountType, getAddressLabelFromRegistry } from '../../utils/account-utils' import { getCoinFromTxDataUnion, - getFilecoinKeyringIdFromNetwork, hasEIP1559Support } from '../../utils/network-utils' import Amount from '../../utils/amount' @@ -150,7 +149,7 @@ export const setApiProxyFetcher = (fetcher: () => WalletApiProxy) => { const emptyBalance = '0x0' type GetAccountTokenCurrentBalanceArg = { - account: Pick + account: Pick token: GetBlockchainTokenIdArg & Pick } @@ -719,37 +718,6 @@ export function createWalletApi ( }, providesTags: [{ type: 'Network', id: NETWORK_TAG_IDS.SWAP_SUPPORTED }] }), - getDefaultNetworks: query({ - // We can probably remove this when all - // Transactions and Sign-Message Requests include a chainId - queryFn: async (arg, api, extraOptions, baseQuery) => { - try { - const { braveWalletService, jsonRpcService } - = baseQuery(undefined).data // apiProxy - const { originInfo } = await braveWalletService.getActiveOrigin() - - const defaultChains = await Promise.all( - SupportedCoinTypes.map(async (coinType) => { - const { network } - = await jsonRpcService.getNetwork(coinType, originInfo.origin) - return network - }) - ) - - return { - data: defaultChains - } - } catch (error) { - console.error(error) - return { - error: `Error occurred within "getDefaultNetworks": ${ - error.toString() // - }` - } - } - }, - providesTags: [{ type: 'Network', id: NETWORK_TAG_IDS.DEFAULTS }] - }), getSelectedChain: query({ queryFn: async (arg, api, extraOptions, baseQuery) => { try { @@ -1317,18 +1285,6 @@ export function createWalletApi ( case BraveWallet.CoinType.FIL: case BraveWallet.CoinType.ETH: default: { - if (BraveWallet.CoinType.FIL) { - // Get network keyring id - const filecoinKeyringIdFromNetwork = - getFilecoinKeyringIdFromNetwork({ - chainId: token.chainId, - coin: account.coin - }) - - if (account.keyringId !== filecoinKeyringIdFromNetwork) { - return { data: emptyBalanceResult } - } - } const { balance, error, errorMessage } = await jsonRpcService.getBalance( @@ -1441,7 +1397,6 @@ export function createWalletApi ( account: { address: account.address, coin: account.coin, - keyringId: account.keyringId }, token: { chainId: asset.chainId, @@ -2981,7 +2936,6 @@ export const { useGetCombinedTokenBalanceForAllAccountsQuery, useGetDefaultAccountAddressesQuery, useGetDefaultFiatCurrencyQuery, - useGetDefaultNetworksQuery, useGetERC721MetadataQuery, useGetGasEstimation1559Query, useGetNetworksRegistryQuery, @@ -3005,7 +2959,6 @@ export const { useLazyGetCombinedTokenBalanceForAllAccountsQuery, useLazyGetDefaultAccountAddressesQuery, useLazyGetDefaultFiatCurrencyQuery, - useLazyGetDefaultNetworksQuery, useLazyGetERC721MetadataQuery, useLazyGetGasEstimation1559Query, useLazyGetNetworksRegistryQuery, @@ -3402,8 +3355,7 @@ export const parseTransactionWithoutPricesAsync = async ({ { account: { address: account.address, - coin: account.coin, - keyringId: account.keyringId + coin: account.coin }, token: { chainId: nativeAsset.chainId, @@ -3425,8 +3377,7 @@ export const parseTransactionWithoutPricesAsync = async ({ { account: { address: account.address, - coin: account.coin, - keyringId: account.keyringId + coin: account.coin }, token: { chainId: token.chainId, @@ -3448,8 +3399,7 @@ export const parseTransactionWithoutPricesAsync = async ({ { account: { address: account.address, - coin: account.coin, - keyringId: account.keyringId + coin: account.coin }, token: { chainId: sellToken.chainId, diff --git a/components/brave_wallet_ui/components/desktop/popup-modals/add-account-modal/hardware-wallet-connect/accounts-list.tsx b/components/brave_wallet_ui/components/desktop/popup-modals/add-account-modal/hardware-wallet-connect/accounts-list.tsx index 45a6c0655fe9..0111bfc6a6a0 100644 --- a/components/brave_wallet_ui/components/desktop/popup-modals/add-account-modal/hardware-wallet-connect/accounts-list.tsx +++ b/components/brave_wallet_ui/components/desktop/popup-modals/add-account-modal/hardware-wallet-connect/accounts-list.tsx @@ -4,6 +4,7 @@ // you can obtain one at https://mozilla.org/MPL/2.0/. import * as React from 'react' import { create } from 'ethereum-blockies' +import { EntityId } from '@reduxjs/toolkit' import { Checkbox, Select } from 'brave-ui/components' import { ButtonsContainer, @@ -11,7 +12,7 @@ import { HardwareWalletAccountCircle, HardwareWalletAccountListItem, HardwareWalletAccountListItemRow, - HardwareWalletAccountsList, + HardwareWalletAccountsListContainer, SelectRow, SelectWrapper, LoadingWrapper, @@ -24,25 +25,30 @@ import { HardwareWalletDerivationPathsMapping, SolHardwareWalletDerivationPathLocaleMapping } from './types' -import { - FilecoinNetwork, - FilecoinNetworkTypes, - FilecoinNetworkLocaleMapping -} from '../../../../../common/hardware/types' +import { FilecoinNetwork } from '../../../../../common/hardware/types' import { BraveWallet, WalletAccountType, CreateAccountOptionsType } from '../../../../../constants/types' import { getLocale } from '../../../../../../common/locale' import { NavButton } from '../../../../extension' import { SearchBar } from '../../../../shared' +import { NetworkFilterSelector } from '../../../network-filter-selector' import { DisclaimerText } from '../style' +import { Skeleton } from '../../../../shared/loading-skeleton/styles' // Utils import { reduceAddress } from '../../../../../utils/reduce-address' import Amount from '../../../../../utils/amount' +import { + useGetAccountTokenCurrentBalanceQuery, + useGetNetworksRegistryQuery +} from '../../../../../common/slices/api.slice' +import { makeNetworkAsset } from '../../../../../options/asset-options' +import { + networkEntityAdapter +} from '../../../../../common/slices/entities/network.entity' interface Props { hardwareWallet: string accounts: BraveWallet.HardwareWalletAccount[] - selectedNetwork?: BraveWallet.NetworkInfo preAddedHardwareWalletAccounts: WalletAccountType[] onLoadMore: () => void selectedDerivationPaths: string[] @@ -50,40 +56,64 @@ interface Props { selectedDerivationScheme: string setSelectedDerivationScheme: (scheme: string) => void onAddAccounts: () => void - getBalance: (address: string, coin: BraveWallet.CoinType) => Promise filecoinNetwork: FilecoinNetwork onChangeFilecoinNetwork: (network: FilecoinNetwork) => void selectedAccountType: CreateAccountOptionsType } -export default function (props: Props) { - const { - accounts, - selectedNetwork, - preAddedHardwareWalletAccounts, - hardwareWallet, - selectedDerivationScheme, - setSelectedDerivationScheme, - setSelectedDerivationPaths, - selectedDerivationPaths, - onLoadMore, - onAddAccounts, - getBalance, - filecoinNetwork, - onChangeFilecoinNetwork, - selectedAccountType - } = props - const [filteredAccountList, setFilteredAccountList] = React.useState([]) +export const HardwareWalletAccountsList = ({ + accounts, + preAddedHardwareWalletAccounts, + hardwareWallet, + selectedDerivationScheme, + setSelectedDerivationScheme, + setSelectedDerivationPaths, + selectedDerivationPaths, + onLoadMore, + onAddAccounts, + filecoinNetwork, + onChangeFilecoinNetwork, + selectedAccountType +}: Props) => { + // queries + const { data: networksRegistry } = useGetNetworksRegistryQuery() + + // state + const [filteredAccountList, setFilteredAccountList] = React.useState< + BraveWallet.HardwareWalletAccount[] + >([]) const [isLoadingMore, setIsLoadingMore] = React.useState(false) + const [selectedNetworkId, setSelectedNetworkId] = React.useState( + selectedAccountType.coin === BraveWallet.CoinType.ETH + ? BraveWallet.MAINNET_CHAIN_ID + : selectedAccountType.coin === BraveWallet.CoinType.SOL + ? BraveWallet.SOLANA_MAINNET + : BraveWallet.FILECOIN_MAINNET + ) - React.useMemo(() => { - setFilteredAccountList(accounts) - setIsLoadingMore(false) - }, [accounts]) + // memos + const accountNativeAsset = React.useMemo(() => { + if (!networksRegistry) { + return undefined + } + return makeNetworkAsset(networksRegistry.entities[selectedNetworkId]) + }, [networksRegistry, selectedNetworkId]) + + const networksSubset = React.useMemo(() => { + if (!networksRegistry) { + return [] + } + return networksRegistry.idsByCoinType[selectedAccountType.coin].map( + (id) => networksRegistry.entities[id] as BraveWallet.NetworkInfo + ) + }, [networksRegistry, selectedAccountType]) - const ethDerivationPathsEnum = HardwareWalletDerivationPathsMapping[hardwareWallet] + // computed + const ethDerivationPathsEnum = + HardwareWalletDerivationPathsMapping[hardwareWallet] const solDerivationPathsEnum = SolHardwareWalletDerivationPathLocaleMapping + // methods const onSelectAccountCheckbox = (account: BraveWallet.HardwareWalletAccount) => () => { const { derivationPath } = account const isSelected = selectedDerivationPaths.includes(derivationPath) @@ -117,10 +147,46 @@ export default function (props: Props) { return preAddedHardwareWalletAccounts.some(e => e.address === account.address) }, [preAddedHardwareWalletAccounts]) + const onSelectNetwork = React.useCallback( + (n: BraveWallet.NetworkInfo): void => { + setSelectedNetworkId(networkEntityAdapter.selectId(n)) + if (selectedAccountType.coin === BraveWallet.CoinType.FIL) { + onChangeFilecoinNetwork(n.chainId as FilecoinNetwork) + } + }, + [selectedAccountType.coin, onChangeFilecoinNetwork] + ) + + // effects + React.useEffect(() => { + setFilteredAccountList(accounts) + setIsLoadingMore(false) + }, [accounts]) + + React.useEffect(() => { + if (selectedNetworkId) { + return + } + if (!networksRegistry) { + return + } + + // set network dropdown default value + setSelectedNetworkId( + networksRegistry.idsByCoinType[selectedAccountType.coin][0] + ) + }, [networksRegistry, selectedAccountType]) + + // render return ( <> + {selectedAccountType.coin === BraveWallet.CoinType.ETH ? ( ) : null} - {selectedAccountType.coin === BraveWallet.CoinType.FIL ? ( - - ) : null} {getLocale('braveWalletSwitchHDPathTextHardwareWallet')} - - - { - accounts.length === 0 && ( - - - - ) - } + + + {accounts.length === 0 && ( + + + + )} - { - accounts.length > 0 && filteredAccountList?.length === 0 && ( - - {getLocale('braveWalletConnectHardwareSearchNothingFound')} - - ) - } + {accounts.length > 0 && filteredAccountList?.length === 0 && ( + + {getLocale('braveWalletConnectHardwareSearchNothingFound')} + + )} - { - accounts.length > 0 && filteredAccountList.length > 0 && ( - <> - {filteredAccountList?.map((account) => { - return ( - - ) - })} - - ) - } - - + {accounts.length > 0 && filteredAccountList.length > 0 && ( + <> + {filteredAccountList?.map((account) => { + return ( + + ) + })} + + )} + void selected: boolean disabled: boolean - getBalance: (address: string, coin: BraveWallet.CoinType) => Promise + balanceAsset?: Pick< + BraveWallet.BlockchainToken, + | 'chainId' + | 'contractAddress' + | 'isErc721' + | 'isNft' + | 'symbol' + | 'tokenId' + | 'decimals' + > } -function AccountListItem (props: AccountListItemProps) { - const { account, onSelect, selected, disabled, getBalance, selectedNetwork } = props +function AccountListItem({ + account, + onSelect, + selected, + disabled, + balanceAsset +}: AccountListItemProps) { + // queries + const { data: balanceResult, isFetching: isLoadingBalance } = + useGetAccountTokenCurrentBalanceQuery( + { + account, + token: { + chainId: balanceAsset?.chainId || '', + contractAddress: balanceAsset?.contractAddress || '', + isErc721: balanceAsset?.isErc721 || false, + isNft: balanceAsset?.isNft || false, + symbol: balanceAsset?.symbol || '', + tokenId: balanceAsset?.tokenId || '' + } + }, + { skip: !balanceAsset } + ) + + // memos const orb = React.useMemo(() => { - return create({ seed: account.address.toLowerCase(), size: 8, scale: 16 }).toDataURL() + return create({ + seed: account.address.toLowerCase(), + size: 8, + scale: 16 + }).toDataURL() }, [account.address]) - const [balance, setBalance] = React.useState('') - React.useEffect(() => { - if (!selectedNetwork) { - return + const balance = React.useMemo(() => { + if ( + isLoadingBalance || + !balanceResult?.balance || + balanceAsset?.decimals === undefined + ) { + return undefined } - getBalance(account.address, account.coin).then((result) => { - const amount = new Amount(result) - .divideByDecimals(selectedNetwork.decimals) - setBalance(amount.format()) - }).catch() - }, [account, selectedNetwork, getBalance]) + return new Amount(balanceResult.balance) + .divideByDecimals(balanceAsset.decimals) + .formatAsAsset(undefined, balanceAsset.symbol) + }, [ + isLoadingBalance, + balanceResult?.balance, + balanceAsset?.decimals, + balanceAsset?.symbol + ]) + // render return ( @@ -259,15 +350,17 @@ function AccountListItem (props: AccountListItemProps) {
{reduceAddress(account.address)}
- {balance} - -
+ {isLoadingBalance ? ( + + ) : ( + {balance} + )} + +
) } + +export default HardwareWalletAccountsList \ No newline at end of file diff --git a/components/brave_wallet_ui/components/desktop/popup-modals/add-account-modal/hardware-wallet-connect/index.tsx b/components/brave_wallet_ui/components/desktop/popup-modals/add-account-modal/hardware-wallet-connect/index.tsx index 17c2fb3cddc6..c1fea2f25237 100644 --- a/components/brave_wallet_ui/components/desktop/popup-modals/add-account-modal/hardware-wallet-connect/index.tsx +++ b/components/brave_wallet_ui/components/desktop/popup-modals/add-account-modal/hardware-wallet-connect/index.tsx @@ -8,10 +8,9 @@ import { useDispatch, useSelector } from 'react-redux' // utils import { getLocale } from '../../../../../../common/locale' -import { getBalance } from '../../../../../common/async/lib' // components -import HardwareWalletAccountsList from './accounts-list' +import { HardwareWalletAccountsList } from './accounts-list' import { AuthorizeHardwareDeviceIFrame } from '../../../../shared/authorize-hardware-device/authorize-hardware-device' import { NavButton } from '../../../../extension' @@ -41,10 +40,6 @@ import { LedgerError } from '../../../../../common/hardware/ledgerjs/ledger-mess // hooks import { useLib } from '../../../../../common/hooks' -import { - useGetDefaultNetworksQuery, - useGetSelectedChainQuery -} from '../../../../../common/slices/api.slice' export interface Props { selectedAccountType: CreateAccountOptionsType @@ -84,10 +79,6 @@ export const HardwareWalletConnect = ({ onSuccess, selectedAccountType }: Props) const dispatch = useDispatch() const savedAccounts = useSelector(({ wallet }: { wallet: WalletState }) => wallet.accounts) - // queries - const { data: selectedNetwork } = useGetSelectedChainQuery() - const { data: defaultNetworks } = useGetDefaultNetworksQuery() - // state const [selectedHardwareWallet, setSelectedHardwareWallet] = React.useState(BraveWallet.LEDGER_HARDWARE_VENDOR) const [isConnecting, setIsConnecting] = React.useState(false) @@ -229,15 +220,6 @@ export const HardwareWalletConnect = ({ onSuccess, selectedAccountType }: Props) ) }, [savedAccounts]) - const selectedAccountTypesDefaultNetwork = React.useMemo(() => { - return ( - defaultNetworks?.find( - (network: BraveWallet.NetworkInfo) => - network.coin === selectedAccountType.coin - ) ?? selectedNetwork - ) - }, [defaultNetworks, selectedAccountType, selectedNetwork]) - // render if (showAuthorizeDevice) { return ( @@ -267,8 +249,6 @@ export const HardwareWalletConnect = ({ onSuccess, selectedAccountType }: Props) selectedDerivationScheme={selectedDerivationScheme} setSelectedDerivationScheme={onChangeDerivationScheme} onAddAccounts={onAddAccounts} - getBalance={getBalance} - selectedNetwork={selectedAccountTypesDefaultNetwork} filecoinNetwork={filecoinNetwork} onChangeFilecoinNetwork={onFilecoinNetworkChanged} selectedAccountType={selectedAccountType} diff --git a/components/brave_wallet_ui/components/desktop/popup-modals/add-account-modal/hardware-wallet-connect/style.ts b/components/brave_wallet_ui/components/desktop/popup-modals/add-account-modal/hardware-wallet-connect/style.ts index 662cb8af7167..4f21c2d81658 100644 --- a/components/brave_wallet_ui/components/desktop/popup-modals/add-account-modal/hardware-wallet-connect/style.ts +++ b/components/brave_wallet_ui/components/desktop/popup-modals/add-account-modal/hardware-wallet-connect/style.ts @@ -122,7 +122,7 @@ export const HardwareWalletAccountCircle = styled.div` background-size: cover; ` -export const HardwareWalletAccountsList = styled.div` +export const HardwareWalletAccountsListContainer = styled.div` display: flex; flex-direction: column; align-items: flex-start;