From ac2416706c2ae2ba8bfbceb6795f1ec7a7f136b3 Mon Sep 17 00:00:00 2001 From: tomasklim Date: Tue, 5 Nov 2024 19:48:28 +0100 Subject: [PATCH] fix(suite): fix issues with TokenSelect --- .../src/components/form/Select/Select.tsx | 17 +- .../components/SelectAssetModal/AssetItem.tsx | 6 +- .../SelectAssetModal/AssetItemNotFound.tsx | 3 +- .../SelectAssetModal.stories.tsx | 3 +- .../SelectAssetModal.storiesData.ts | 466 +++++++ .../SelectAssetModal/SelectAssetModal.tsx | 89 +- .../SelectAssetModal/SendTokenTabs.tsx | 31 +- .../components/SelectAssetModal/mockData.ts | 1138 ----------------- .../components/TokenIconSet/TokenIconSet.tsx | 6 +- .../src/actions/suite/copyAddressActions.ts | 28 + .../TxDetailModal => copy}/IOAddress.tsx | 0 .../suite/copy/TokenAddressRow.tsx} | 87 +- .../AdvancedTxDetails/IODetails/IODetails.tsx | 2 +- .../TxDetailModal/BasicTxDetails.tsx | 3 +- .../CoinmarketFormInputCryptoSelect.tsx | 25 +- .../send/Outputs/Amount/TokenSelect.tsx | 211 --- .../src/views/wallet/send/Outputs/Outputs.tsx | 5 +- .../send/Outputs/TokenSelect/TokenSelect.tsx | 455 ++++--- .../Outputs/TokenSelect/tokenSelectUtils.ts | 140 -- .../tokens/common/TokensTable/TokenRow.tsx | 57 +- suite-common/wallet-utils/src/tokenUtils.ts | 16 +- 21 files changed, 955 insertions(+), 1833 deletions(-) create mode 100644 packages/product-components/src/components/SelectAssetModal/SelectAssetModal.storiesData.ts delete mode 100644 packages/product-components/src/components/SelectAssetModal/mockData.ts create mode 100644 packages/suite/src/actions/suite/copyAddressActions.ts rename packages/suite/src/components/suite/{modals/ReduxModal/UserContextModal/TxDetailModal => copy}/IOAddress.tsx (100%) rename packages/suite/src/{views/wallet/send/Outputs/TokenSelect/TokenAddress.tsx => components/suite/copy/TokenAddressRow.tsx} (53%) delete mode 100644 packages/suite/src/views/wallet/send/Outputs/Amount/TokenSelect.tsx delete mode 100644 packages/suite/src/views/wallet/send/Outputs/TokenSelect/tokenSelectUtils.ts diff --git a/packages/components/src/components/form/Select/Select.tsx b/packages/components/src/components/form/Select/Select.tsx index 8ad697c3289..d934f76ca8a 100644 --- a/packages/components/src/components/form/Select/Select.tsx +++ b/packages/components/src/components/form/Select/Select.tsx @@ -121,7 +121,6 @@ type WrapperProps = TransientProps< $isWithPlaceholder: boolean; $hasBottomPadding: boolean; $elevation: Elevation; - $focusEnabled: boolean; }; const Wrapper = styled.div` @@ -165,16 +164,9 @@ const Wrapper = styled.div` } &:focus-within { - ${({ $focusEnabled }) => - $focusEnabled - ? css` - .${reactSelectClassNamePrefix}__dropdown-indicator { - transform: rotate(180deg); - } - ` - : css` - border-color: transparent; - `} + .${reactSelectClassNamePrefix}__dropdown-indicator { + transform: rotate(180deg); + } } } @@ -280,7 +272,6 @@ interface CommonProps extends Omit, 'onChange' | 'menuI * @description pass `null` if bottom text can be `undefined` */ bottomText?: ReactNode; - focusEnabled?: boolean; hasBottomPadding?: boolean; minValueWidth?: string; // TODO: should be probably removed inputState?: InputState; @@ -307,7 +298,6 @@ export const Select = ({ useKeyPressScroll, isSearchable = false, minValueWidth = 'initial', - focusEnabled = true, isMenuOpen, inputState, components, @@ -370,7 +360,6 @@ export const Select = ({ $minValueWidth={minValueWidth} $isDisabled={isDisabled} $isMenuOpen={isMenuOpen} - $focusEnabled={focusEnabled} $isWithLabel={!!label} $isWithPlaceholder={!!placeholder} $hasBottomPadding={hasBottomPadding === true && bottomText === null} diff --git a/packages/product-components/src/components/SelectAssetModal/AssetItem.tsx b/packages/product-components/src/components/SelectAssetModal/AssetItem.tsx index 7ce1ecf25c6..0a53ee04c84 100644 --- a/packages/product-components/src/components/SelectAssetModal/AssetItem.tsx +++ b/packages/product-components/src/components/SelectAssetModal/AssetItem.tsx @@ -2,8 +2,8 @@ import styled, { useTheme } from 'styled-components'; import { spacings, spacingsPx } from '@trezor/theme'; import { Badge, Column, Icon, Row, Text } from '@trezor/components'; -import styled, { useTheme } from 'styled-components'; import { NetworkSymbol } from '@suite-common/wallet-config'; + import { SelectAssetOptionCurrencyProps } from './SelectAssetModal'; const ClickableContainer = styled.div` @@ -31,9 +31,9 @@ const BadgeWrapper = styled.div` type AssetItemProps = { name?: string; - symbol: NetworkSymbol; + symbol: string; badge: string | undefined; - networkSymbol: NetworkSymbol; + networkSymbol: NetworkSymbol | ({} & string); coingeckoId: string; logo: JSX.Element; isFavorite?: boolean; diff --git a/packages/product-components/src/components/SelectAssetModal/AssetItemNotFound.tsx b/packages/product-components/src/components/SelectAssetModal/AssetItemNotFound.tsx index 1dc91d89436..10488a27a9a 100644 --- a/packages/product-components/src/components/SelectAssetModal/AssetItemNotFound.tsx +++ b/packages/product-components/src/components/SelectAssetModal/AssetItemNotFound.tsx @@ -8,8 +8,7 @@ import { NetworkFilterCategories, TokenFilterCategories, NetworkFilterCategory, - SelectAssetNetworkProps, SelectAssetSearchCategoryType } from './SelectAssetModal'; - +} from './SelectAssetModal'; interface AssetItemNotFoundProps { searchCategory: SelectAssetSearchCategory; diff --git a/packages/product-components/src/components/SelectAssetModal/SelectAssetModal.stories.tsx b/packages/product-components/src/components/SelectAssetModal/SelectAssetModal.stories.tsx index 9eb22a0b8dc..78b6198f947 100644 --- a/packages/product-components/src/components/SelectAssetModal/SelectAssetModal.stories.tsx +++ b/packages/product-components/src/components/SelectAssetModal/SelectAssetModal.stories.tsx @@ -6,8 +6,7 @@ import { action } from '@storybook/addon-actions'; import { intermediaryTheme, NewModal } from '@trezor/components'; -import { SelectAssetModal as SelectAssetModalComponent, SelectAssetModalProps } from '../../index'; -import { selectAssetModalOptions, selectAssetModalNetworks } from './mockData'; +import { selectAssetModalOptions, selectAssetModalNetworks } from './SelectAssetModal.storiesData'; import { NetworkFilterCategory } from './SelectAssetModal'; const meta: Meta = { diff --git a/packages/product-components/src/components/SelectAssetModal/SelectAssetModal.storiesData.ts b/packages/product-components/src/components/SelectAssetModal/SelectAssetModal.storiesData.ts new file mode 100644 index 00000000000..81972003dc5 --- /dev/null +++ b/packages/product-components/src/components/SelectAssetModal/SelectAssetModal.storiesData.ts @@ -0,0 +1,466 @@ +import type { SelectAssetOptionCurrencyProps, SelectAssetOptionGroupProps } from '../../index'; +import { NetworkFilterCategory } from './SelectAssetModal'; + +export const selectAssetModalOptions: ( + | (SelectAssetOptionCurrencyProps & { value: string; label: string }) + | SelectAssetOptionGroupProps +)[] = [ + { + type: 'group', + label: 'TR_COINMARKET_POPULAR_CURRENCIES', + }, + { + type: 'currency', + value: 'bitcoin', + label: 'BTC', + cryptoName: 'Bitcoin', + coingeckoId: 'bitcoin', + contractAddress: null, + symbol: 'BTC', + networkSymbol: 'btc', + }, + { + type: 'currency', + value: 'litecoin', + label: 'LTC', + cryptoName: 'Litecoin', + coingeckoId: 'litecoin', + contractAddress: null, + symbol: 'LTC', + networkSymbol: 'ltc', + }, + { + type: 'currency', + value: 'cardano', + label: 'ADA', + cryptoName: 'Cardano', + coingeckoId: 'cardano', + contractAddress: null, + symbol: 'ADA', + networkSymbol: 'ada', + }, + { + type: 'currency', + value: 'solana', + label: 'SOL', + cryptoName: 'Solana', + coingeckoId: 'solana', + contractAddress: null, + symbol: 'SOL', + networkSymbol: 'sol', + }, + { + type: 'currency', + value: 'bitcoin-gold', + label: 'BTG', + cryptoName: 'Bitcoin Gold', + coingeckoId: 'bitcoin-gold', + contractAddress: null, + symbol: 'BTG', + networkSymbol: 'btg', + }, + { + type: 'currency', + value: 'zcash', + label: 'ZEC', + cryptoName: 'Zcash', + coingeckoId: 'zcash', + contractAddress: null, + symbol: 'ZEC', + networkSymbol: 'zec', + }, + { + type: 'currency', + value: 'ethereum-classic', + label: 'ETC', + cryptoName: 'Ethereum Classic', + coingeckoId: 'ethereum-classic', + contractAddress: null, + symbol: 'ETC', + networkSymbol: 'etc', + }, + { + type: 'currency', + value: 'bitcoin-cash', + label: 'BCH', + cryptoName: 'Bitcoin Cash', + coingeckoId: 'bitcoin-cash', + contractAddress: null, + symbol: 'BCH', + networkSymbol: 'bch', + }, + { + type: 'currency', + value: 'dash', + label: 'DASH', + cryptoName: 'Dash', + coingeckoId: 'dash', + contractAddress: null, + symbol: 'DASH', + networkSymbol: 'dash', + }, + { + type: 'currency', + value: 'ripple', + label: 'XRP', + cryptoName: 'XRP', + coingeckoId: 'ripple', + contractAddress: null, + symbol: 'XRP', + networkSymbol: 'xrp', + }, + { + type: 'currency', + value: 'digibyte', + label: 'DGB', + cryptoName: 'DigiByte', + coingeckoId: 'digibyte', + contractAddress: null, + symbol: 'DGB', + networkSymbol: 'dgb', + }, + { + type: 'currency', + value: 'dogecoin', + label: 'DOGE', + cryptoName: 'Dogecoin', + coingeckoId: 'dogecoin', + contractAddress: null, + symbol: 'DOGE', + networkSymbol: 'doge', + }, + { + type: 'currency', + value: 'polygon-ecosystem-token', + label: 'POL', + cryptoName: 'POL (ex-MATIC)', + coingeckoId: 'polygon-ecosystem-token', + contractAddress: null, + symbol: 'POL', + networkSymbol: 'pol', + }, + { + type: 'currency', + value: 'binancecoin', + label: 'BNB', + cryptoName: 'BNB', + coingeckoId: 'binancecoin', + contractAddress: null, + symbol: 'BNB', + networkSymbol: 'bnb', + }, + { + type: 'group', + label: 'TR_COINMARKET_OTHER_CURRENCIES', + }, + { + type: 'currency', + value: 'monero', + label: 'XMR', + cryptoName: 'Monero', + coingeckoId: 'monero', + contractAddress: null, + symbol: 'XMR', + networkSymbol: 'monero', + }, + { + type: 'currency', + value: 'verge', + label: 'XVG', + cryptoName: 'Verge', + coingeckoId: 'verge', + contractAddress: null, + symbol: 'XVG', + networkSymbol: 'verge', + }, + { + type: 'currency', + value: 'qtum', + label: 'QTUM', + cryptoName: 'Qtum', + coingeckoId: 'qtum', + contractAddress: null, + symbol: 'QTUM', + networkSymbol: 'qtum', + }, + { + type: 'currency', + value: 'stellar', + label: 'XLM', + cryptoName: 'Stellar', + coingeckoId: 'stellar', + contractAddress: null, + symbol: 'XLM', + networkSymbol: 'stellar', + }, + { + type: 'currency', + value: 'nem', + label: 'XEM', + cryptoName: 'NEM', + coingeckoId: 'nem', + contractAddress: null, + symbol: 'XEM', + networkSymbol: 'nem', + }, + { + type: 'group', + label: 'TR_COINMARKET_NETWORK_TOKENS', + coingeckoId: 'ethereum', + networkName: 'Ethereum', + }, + { + type: 'currency', + value: 'ethereum--0xd26114cd6ee289accf82350c8d8487fedb8a0c07', + label: 'OMG', + cryptoName: 'OMG Network', + coingeckoId: 'ethereum', + contractAddress: '0xd26114cd6ee289accf82350c8d8487fedb8a0c07', + networkName: 'Ethereum', + symbol: 'OMG', + networkSymbol: 'eth', + }, + { + type: 'currency', + value: 'ethereum--0x595832f8fc6bf59c85c527fec3740a1b7a361269', + label: 'POWR', + cryptoName: 'Powerledger', + coingeckoId: 'ethereum', + contractAddress: '0x595832f8fc6bf59c85c527fec3740a1b7a361269', + networkName: 'Ethereum', + symbol: 'POWR', + networkSymbol: 'eth', + }, + { + type: 'currency', + value: 'ethereum--0x41e5560054824ea6b0732e656e3ad64e20e94e45', + label: 'CVC', + cryptoName: 'Civic', + coingeckoId: 'ethereum', + contractAddress: '0x41e5560054824ea6b0732e656e3ad64e20e94e45', + networkName: 'Ethereum', + symbol: 'CVC', + networkSymbol: 'eth', + }, + { + type: 'currency', + value: 'ethereum--0xb64ef51c888972c908cfacf59b47c1afbc0ab8ac', + label: 'STORJ', + cryptoName: 'Storj', + coingeckoId: 'ethereum', + contractAddress: '0xb64ef51c888972c908cfacf59b47c1afbc0ab8ac', + networkName: 'Ethereum', + symbol: 'STORJ', + networkSymbol: 'eth', + }, + { + type: 'currency', + value: 'ethereum--0x744d70fdbe2ba4cf95131626614a1763df805b9e', + label: 'SNT', + cryptoName: 'Status', + coingeckoId: 'ethereum', + contractAddress: '0x744d70fdbe2ba4cf95131626614a1763df805b9e', + networkName: 'Ethereum', + symbol: 'SNT', + networkSymbol: 'eth', + }, + { + type: 'currency', + value: 'ethereum--0xe41d2489571d322189246dafa5ebde1f4699f498', + label: 'ZRX', + cryptoName: '0x Protocol', + coingeckoId: 'ethereum', + contractAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498', + networkName: 'Ethereum', + symbol: 'ZRX', + networkSymbol: 'eth', + }, + { + type: 'currency', + value: 'ethereum--0xbbbbca6a901c926f240b89eacb641d8aec7aeafd', + label: 'LRC', + cryptoName: 'Loopring', + coingeckoId: 'ethereum', + contractAddress: '0xbbbbca6a901c926f240b89eacb641d8aec7aeafd', + networkName: 'Ethereum', + symbol: 'LRC', + networkSymbol: 'eth', + }, + { + type: 'currency', + value: 'ethereum--0xade00c28244d5ce17d72e40330b1c318cd12b7c3', + label: 'ADX', + cryptoName: 'AdEx', + coingeckoId: 'ethereum', + contractAddress: '0xade00c28244d5ce17d72e40330b1c318cd12b7c3', + networkName: 'Ethereum', + symbol: 'ADX', + networkSymbol: 'eth', + }, + { + type: 'currency', + value: 'ethereum--0x1f573d6fb3f13d689ff844b4ce37794d79a7ff1c', + label: 'BNT', + cryptoName: 'Bancor Network', + coingeckoId: 'ethereum', + contractAddress: '0x1f573d6fb3f13d689ff844b4ce37794d79a7ff1c', + networkName: 'Ethereum', + symbol: 'BNT', + networkSymbol: 'eth', + }, + { + type: 'group', + label: 'TR_COINMARKET_NETWORK_TOKENS', + coingeckoId: 'solana', + networkName: 'Solana', + }, + { + type: 'currency', + value: 'solana--Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', + label: 'USDT', + cryptoName: 'Tether', + coingeckoId: 'solana', + contractAddress: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', + networkName: 'Solana', + symbol: 'USDT', + networkSymbol: 'sol', + }, + { + type: 'currency', + value: 'solana--EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', + label: 'USDC', + cryptoName: 'USDC', + coingeckoId: 'solana', + contractAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', + networkName: 'Solana', + symbol: 'USDC', + networkSymbol: 'sol', + }, + { + type: 'currency', + value: 'solana--4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R', + label: 'RAY', + cryptoName: 'Raydium', + coingeckoId: 'solana', + contractAddress: '4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R', + networkName: 'Solana', + symbol: 'RAY', + networkSymbol: 'sol', + }, + { + type: 'currency', + value: 'solana--7i5KKsX2weiTkry7jA4ZwSuXGhs5eJBEjY8vVxR4pfRx', + label: 'GMT', + cryptoName: 'GMT', + coingeckoId: 'solana', + contractAddress: '7i5KKsX2weiTkry7jA4ZwSuXGhs5eJBEjY8vVxR4pfRx', + networkName: 'Solana', + symbol: 'GMT', + networkSymbol: 'sol', + }, + { + type: 'group', + label: 'TR_COINMARKET_NETWORK_TOKENS', + coingeckoId: 'binance-smart-chain', + networkName: 'BNB Smart Chain', + }, + { + type: 'currency', + value: 'binance-smart-chain--0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82', + label: 'CAKE', + cryptoName: 'PancakeSwap', + coingeckoId: 'binance-smart-chain', + contractAddress: '0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82', + networkName: 'BNB Smart Chain', + symbol: 'CAKE', + networkSymbol: 'binance-smart-chain', + }, + { + type: 'currency', + value: 'binance-smart-chain--0xe02df9e3e622debdd69fb838bb799e3f168902c5', + label: 'BAKE', + cryptoName: 'BakerySwap', + coingeckoId: 'binance-smart-chain', + contractAddress: '0xe02df9e3e622debdd69fb838bb799e3f168902c5', + networkName: 'BNB Smart Chain', + symbol: 'BAKE', + networkSymbol: 'binance-smart-chain', + }, + { + type: 'currency', + value: 'binance-smart-chain--0xfd7b3a77848f1c2d67e05e54d78d174a0c850335', + label: 'ONT', + cryptoName: 'Binance-Peg Ontology', + coingeckoId: 'binance-smart-chain', + contractAddress: '0xfd7b3a77848f1c2d67e05e54d78d174a0c850335', + networkName: 'BNB Smart Chain', + symbol: 'ONT', + networkSymbol: 'binance-smart-chain', + }, + { + type: 'currency', + value: 'binance-smart-chain--0xe9e7cea3dedca5984780bafc599bd69add087d56', + label: 'BUSD', + cryptoName: 'Binance-Peg BUSD', + coingeckoId: 'binance-smart-chain', + contractAddress: '0xe9e7cea3dedca5984780bafc599bd69add087d56', + networkName: 'BNB Smart Chain', + symbol: 'BUSD', + networkSymbol: 'binance-smart-chain', + }, + { + type: 'group', + label: 'TR_COINMARKET_NETWORK_TOKENS', + coingeckoId: 'base', + networkName: 'Base', + }, + { + type: 'currency', + value: 'base--0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', + label: 'USDC', + cryptoName: 'USDC', + coingeckoId: 'base', + contractAddress: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', + networkName: 'Base', + symbol: 'USDC', + networkSymbol: 'base', + }, + { + type: 'currency', + value: 'base--0x532f27101965dd16442e59d40670faf5ebb142e4', + label: 'BRETT', + cryptoName: 'Brett', + coingeckoId: 'base', + contractAddress: '0x532f27101965dd16442e59d40670faf5ebb142e4', + networkName: 'Base', + symbol: 'BRETT', + networkSymbol: 'base', + }, +]; + +export const selectAssetModalNetworks: NetworkFilterCategory[] = [ + { + name: 'Ethereum', + symbol: 'eth', + coingeckoId: 'ethereum', + coingeckoNativeId: 'ethereum', + }, + { + name: 'Polygon PoS', + symbol: 'pol', + coingeckoId: 'polygon-pos', + coingeckoNativeId: 'polygon-ecosystem-token', + }, + { + name: 'Solana', + symbol: 'sol', + coingeckoId: 'solana', + coingeckoNativeId: 'solana', + }, + { + name: 'BNB Smart Chain', + symbol: 'bnb', + coingeckoId: 'binance-smart-chain', + coingeckoNativeId: 'binancecoin', + }, +]; diff --git a/packages/product-components/src/components/SelectAssetModal/SelectAssetModal.tsx b/packages/product-components/src/components/SelectAssetModal/SelectAssetModal.tsx index d3f20890f28..c94334740bd 100644 --- a/packages/product-components/src/components/SelectAssetModal/SelectAssetModal.tsx +++ b/packages/product-components/src/components/SelectAssetModal/SelectAssetModal.tsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from 'react'; +import { ReactNode, useMemo, useState } from 'react'; import { useIntl } from 'react-intl'; import { @@ -11,12 +11,12 @@ import { VirtualizedList, } from '@trezor/components'; import { mapElevationToBackgroundToken, spacings } from '@trezor/theme'; -import { getNetworkByCoingeckoId, Network } from '@suite-common/wallet-config'; +import { getNetworkByCoingeckoId, Network, NetworkSymbol } from '@suite-common/wallet-config'; +import { getContractAddressForNetwork } from '@suite-common/wallet-utils'; import { AssetItem } from './AssetItem'; import { NetworkTabs } from './NetworkTabs'; import { AssetItemNotFound } from './AssetItemNotFound'; -import { getNetworkByCoingeckoId, Network, NetworkSymbol } from '@suite-common/wallet-config'; import { SendTokenTabs } from './SendTokenTabs'; export interface SelectAssetOptionCurrencyProps { @@ -60,8 +60,8 @@ export type SelectAssetSearchCategory = { } | null; export type TokenFilterCategory = { - type: 'visibleWithBalance' | 'hiddenWithBalance'; - label: string; + category: 'tokens' | 'hidden'; + label: ReactNode; }; export type TokenFilterCategories = { @@ -73,13 +73,10 @@ export interface SelectAssetModalProps { options: SelectAssetOptionProps[]; filterCategories: NetworkFilterCategories | TokenFilterCategories; searchPlaceholderText?: string; - setPickedSendTokenCategory?: (category: TokenFilterCategory['type']) => void; - pickedSendTokenCategory?: TokenFilterCategory['type']; onSelectAssetModal: (selectedAsset: SelectAssetOptionCurrencyProps) => void; onFavoriteClick?: (isFavorite: boolean) => void; onClose: () => void; disableSearchByNetwork?: boolean; - selectAssetModalHeight?: SelectAssetModalHeight; } interface AssetProps @@ -93,8 +90,6 @@ interface AssetProps unverified?: boolean; } -export type SelectAssetModalHeight = 'short' | 'tall'; - const ITEM_HEIGHT = 60; const LIST_MIN_HEIGHT = ITEM_HEIGHT * 3; @@ -139,48 +134,58 @@ export const SelectAssetModal = ({ options, filterCategories, searchPlaceholderText, - setPickedSendTokenCategory, - pickedSendTokenCategory = 'visibleWithBalance', onSelectAssetModal, onFavoriteClick, disableSearchByNetwork = false, onClose, - selectAssetModalHeight = 'tall', }: SelectAssetModalProps) => { const intl = useIntl(); const [search, setSearch] = useState(''); const [searchCategory, setSearchCategory] = useState(null); // coingeckoNativeId as fallback for ex. polygon - const [end, setEnd] = useState(options.length); - const data = useMemo(() => getData(options), [options]); + const [pickedSendTokenCategory, setPickedSendTokenCategory] = + useState('tokens'); + + const filteredOptions: SelectAssetOptionProps[] = + pickedSendTokenCategory === 'tokens' + ? options.filter( + option => option.type === 'currency' && !option.hidden && !option.unverified, + ) + : options.filter( + option => option.type === 'currency' && (option.hidden || option.unverified), + ); + + const [end, setEnd] = useState(filteredOptions.length); + + const data = useMemo(() => getData(filteredOptions), [filteredOptions]); const { scrollElementRef, onScroll, ShadowTop, ShadowBottom, ShadowContainer } = useScrollShadow(); - const networkCount = getNetworkCount(options); + const networkCount = getNetworkCount(filteredOptions); - const searchPlaceholder = searchPlaceholderText - ? searchPlaceholderText - : intl.formatMessage({ - id: 'TR_SELECT_NAME_OR_ADDRESS', - defaultMessage: 'Search by name, symbol, network or contract address', - }); + const searchPlaceholder = + searchPlaceholderText ?? + intl.formatMessage({ + id: 'TR_SELECT_NAME_OR_ADDRESS', + defaultMessage: 'Search by name, symbol, network or contract address', + }); const filteredData = data.filter(item => { - const categoryFilter = searchCategory - ? item.coingeckoId === searchCategory.coingeckoId || - item.coingeckoId === searchCategory.coingeckoNativeId - : true; - const networkContractAddress = `${item.networkSymbol}${item.contractAddress ? `--${item.contractAddress}` : ''}`; + if ( + searchCategory && + item.coingeckoId !== searchCategory.coingeckoId && + item.coingeckoId !== searchCategory.coingeckoNativeId + ) { + return false; + } + const contractAddress = item.contractAddress || undefined; const searchFor = (property: string | undefined) => property?.toLocaleLowerCase().includes(search.toLocaleLowerCase()); return ( - (searchFor(item.name) || - searchFor(item.badge) || - (!disableSearchByNetwork && searchFor(item.symbol)) || - (disableSearchByNetwork - ? searchFor(contractAddress) - : searchFor(networkContractAddress))) && - categoryFilter + searchFor(item.name) || + searchFor(item.badge) || + (!disableSearchByNetwork && searchFor(item.symbol)) || + (disableSearchByNetwork ? searchFor(contractAddress) : searchFor(item.networkSymbol)) ); }); @@ -189,7 +194,7 @@ export const SelectAssetModal = ({ }); const HEADER_HEIGHT = 267; - const LIST_HEIGHT = `calc(${selectAssetModalHeight === 'short' ? '50' : '80'}vh - ${HEADER_HEIGHT}px)`; + const LIST_HEIGHT = `calc(80vh - ${HEADER_HEIGHT}px)`; return ( } handleClick={onSelectAssetModal} diff --git a/packages/product-components/src/components/SelectAssetModal/SendTokenTabs.tsx b/packages/product-components/src/components/SelectAssetModal/SendTokenTabs.tsx index 5d47cc9cd4d..01a52f3b0ff 100644 --- a/packages/product-components/src/components/SelectAssetModal/SendTokenTabs.tsx +++ b/packages/product-components/src/components/SelectAssetModal/SendTokenTabs.tsx @@ -1,7 +1,9 @@ +import styled from 'styled-components'; + import { Icon, Row, useElevation } from '@trezor/components'; import { borders, Elevation, mapElevationToBorder, spacings, spacingsPx } from '@trezor/theme'; + import { TokenFilterCategory } from './SelectAssetModal'; -import styled from 'styled-components'; import { CheckableTag } from './CheckableTag'; interface NetworkTabsWrapperProps { @@ -9,8 +11,7 @@ interface NetworkTabsWrapperProps { } const NetworkTabsWrapper = styled.div` - margin-left: -${spacingsPx.md}; - width: calc(100% + ${spacings.md * 2}px); + margin: 0 -${spacingsPx.md}; padding: ${spacings.zero} ${spacingsPx.md} ${spacingsPx.lg}; border-bottom: ${borders.widths.small} solid ${({ theme, $elevation }) => mapElevationToBorder({ $elevation, theme })}; @@ -18,8 +19,8 @@ const NetworkTabsWrapper = styled.div` interface SendTokenTabsProps { sendTokenCategories: TokenFilterCategory[]; - pickedCategory: TokenFilterCategory['type']; - setPickedCategory: (value: TokenFilterCategory['type']) => void; + pickedCategory: TokenFilterCategory['category']; + setPickedCategory: (value: TokenFilterCategory['category']) => void; } export const SendTokenTabs = ({ @@ -29,25 +30,27 @@ export const SendTokenTabs = ({ }: SendTokenTabsProps) => { const { elevation } = useElevation(); + const handleCategoryClick = (type: TokenFilterCategory['category']) => { + setPickedCategory(type); + }; + return ( - {sendTokenCategories.map(category => ( + {sendTokenCategories.map(({ category, label }) => ( { - setPickedCategory(category.type); - }} - key={category.type} + $variant={pickedCategory === category ? 'primary' : 'tertiary'} + onClick={() => handleCategoryClick(category)} > - {category.label} + {label} ))} diff --git a/packages/product-components/src/components/SelectAssetModal/mockData.ts b/packages/product-components/src/components/SelectAssetModal/mockData.ts deleted file mode 100644 index 57cbde24666..00000000000 --- a/packages/product-components/src/components/SelectAssetModal/mockData.ts +++ /dev/null @@ -1,1138 +0,0 @@ -import type { SelectAssetOptionProps } from '../../index'; -import { NetworkFilterCategory } from './SelectAssetModal'; - -export const selectAssetModalOptions: SelectAssetOptionProps[] = [ - { - type: 'group', - label: 'TR_COINMARKET_POPULAR_CURRENCIES', - }, - { - type: 'currency', - networkSymbol: 'bitcoin', - symbol: 'BTC', - cryptoName: 'Bitcoin', - coingeckoId: 'bitcoin', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'ethereum', - symbol: 'ETH', - cryptoName: 'Ethereum', - coingeckoId: 'ethereum', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'litecoin', - symbol: 'LTC', - cryptoName: 'Litecoin', - coingeckoId: 'litecoin', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'solana', - symbol: 'SOL', - cryptoName: 'Solana', - coingeckoId: 'solana', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'cardano', - symbol: 'ADA', - cryptoName: 'Cardano', - coingeckoId: 'cardano', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'bitcoin-cash', - symbol: 'BCH', - cryptoName: 'Bitcoin Cash', - coingeckoId: 'bitcoin-cash', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'binancecoin', - symbol: 'BNB', - cryptoName: 'BNB', - coingeckoId: 'binancecoin', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'dogecoin', - symbol: 'DOGE', - cryptoName: 'Dogecoin', - coingeckoId: 'dogecoin', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'ripple', - symbol: 'XRP', - cryptoName: 'XRP', - coingeckoId: 'ripple', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'digibyte', - symbol: 'DGB', - cryptoName: 'DigiByte', - coingeckoId: 'digibyte', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'polygon-pos', - symbol: 'POL', - cryptoName: 'Polygon PoS', - coingeckoId: 'polygon-pos', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'ethereum-classic', - symbol: 'ETC', - cryptoName: 'Ethereum Classic', - coingeckoId: 'ethereum-classic', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'bitcoin-gold', - symbol: 'BTG', - cryptoName: 'Bitcoin Gold', - coingeckoId: 'bitcoin-gold', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'dash', - symbol: 'DASH', - cryptoName: 'Dash', - coingeckoId: 'dash', - contractAddress: null, - }, - { - type: 'group', - label: 'TR_COINMARKET_OTHER_CURRENCIES', - }, - { - type: 'currency', - networkSymbol: 'bitcoin-cash-sv', - symbol: 'BSV', - cryptoName: 'Bitcoin SV', - coingeckoId: 'bitcoin-cash-sv', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'eos', - symbol: 'EOS', - cryptoName: 'EOS', - coingeckoId: 'eos', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'neo', - symbol: 'NEO', - cryptoName: 'NEO', - coingeckoId: 'neo', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'tron', - symbol: 'TRX', - cryptoName: 'TRON', - coingeckoId: 'tron', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'stellar', - symbol: 'XLM', - cryptoName: 'Stellar', - coingeckoId: 'stellar', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'tezos', - symbol: 'XTZ', - cryptoName: 'Tezos', - coingeckoId: 'tezos', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'vechain', - symbol: 'VET', - cryptoName: 'VeChain', - coingeckoId: 'vechain', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'ravencoin', - symbol: 'RVN', - cryptoName: 'Ravencoin', - coingeckoId: 'ravencoin', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'zilliqa', - symbol: 'ZIL', - cryptoName: 'Zilliqa', - coingeckoId: 'zilliqa', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'cosmos', - symbol: 'ATOM', - cryptoName: 'Cosmos Hub', - coingeckoId: 'cosmos', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'harmony', - symbol: 'ONE', - cryptoName: 'Harmony', - coingeckoId: 'harmony', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'nanomatic', - symbol: 'NANO', - cryptoName: 'Nanomatic', - coingeckoId: 'nanomatic', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'qtum', - symbol: 'QTUM', - cryptoName: 'Qtum', - coingeckoId: 'qtum', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'syscoin', - symbol: 'SYS', - cryptoName: 'Syscoin', - coingeckoId: 'syscoin', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'fantom', - symbol: 'FTM', - cryptoName: 'Fantom', - coingeckoId: 'fantom', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'icon', - symbol: 'ICX', - cryptoName: 'ICON', - coingeckoId: 'icon', - contractAddress: null, - }, - { - type: 'currency', - networkSymbol: 'siacoin', - symbol: 'SC', - cryptoName: 'Siacoin', - coingeckoId: 'siacoin', - contractAddress: null, - }, - { - type: 'group', - label: 'TR_COINMARKET_NETWORK_TOKENS', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x111111111117dc0aa78b770fa6a738034120c302', - symbol: '1INCH', - cryptoName: '1inch', - coingeckoId: 'ethereum', - contractAddress: '0x111111111117dc0aa78b770fa6a738034120c302', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9', - symbol: 'AAVE', - cryptoName: 'Aave', - coingeckoId: 'ethereum', - contractAddress: '0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xa0b73e1ff0b80914ab6fe0444e65848c4c34450b', - symbol: 'CRO', - cryptoName: 'Cronos', - coingeckoId: 'ethereum', - contractAddress: '0xa0b73e1ff0b80914ab6fe0444e65848c4c34450b', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x761d38e5ddf6ccf6cf7c55759d5210750b5d60f3', - symbol: 'ELON', - cryptoName: 'Dogelon Mars', - coingeckoId: 'ethereum', - contractAddress: '0x761d38e5ddf6ccf6cf7c55759d5210750b5d60f3', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x728f30fa2f100742c7949d1961804fa8e0b1387d', - symbol: 'GHX', - cryptoName: 'GamerCoin', - coingeckoId: 'ethereum', - contractAddress: '0x728f30fa2f100742c7949d1961804fa8e0b1387d', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x0f5d2fb29fb7d3cfee444a200298f468908cc942', - symbol: 'MANA', - cryptoName: 'Decentraland', - coingeckoId: 'ethereum', - contractAddress: '0x0f5d2fb29fb7d3cfee444a200298f468908cc942', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x4a220e6096b25eadb88358cb44068a3248254675', - symbol: 'QNT', - cryptoName: 'Quant', - coingeckoId: 'ethereum', - contractAddress: '0x4a220e6096b25eadb88358cb44068a3248254675', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x3845badade8e6dff049820680d1f14bd3903a5d0', - symbol: 'SAND', - cryptoName: 'The Sandbox', - coingeckoId: 'ethereum', - contractAddress: '0x3845badade8e6dff049820680d1f14bd3903a5d0', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce', - symbol: 'SHIB', - cryptoName: 'Shiba Inu', - coingeckoId: 'ethereum', - contractAddress: '0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x7825e833d495f3d1c28872415a4aee339d26ac88', - symbol: 'WTLOS', - cryptoName: 'Wrapped Telos', - coingeckoId: 'ethereum', - contractAddress: '0x7825e833d495f3d1c28872415a4aee339d26ac88', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x0000000000085d4780b73119b644ae5ecd22b376', - symbol: 'TUSD', - cryptoName: 'TrueUSD', - coingeckoId: 'ethereum', - contractAddress: '0x0000000000085d4780b73119b644ae5ecd22b376', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', - symbol: 'UNI', - cryptoName: 'Uniswap', - coingeckoId: 'ethereum', - contractAddress: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x55296f69f40ea6d20e478533c15a6b08b654e758', - symbol: 'XYO', - cryptoName: 'XYO Network', - coingeckoId: 'ethereum', - contractAddress: '0x55296f69f40ea6d20e478533c15a6b08b654e758', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xdac17f958d2ee523a2206206994597c13d831ec7', - symbol: 'USDT', - cryptoName: 'Tether', - coingeckoId: 'ethereum', - contractAddress: '0xdac17f958d2ee523a2206206994597c13d831ec7', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xbd3de9a069648c84d27d74d701c9fa3253098b15', - symbol: 'EQX', - cryptoName: 'EQIFi', - coingeckoId: 'ethereum', - contractAddress: '0xbd3de9a069648c84d27d74d701c9fa3253098b15', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xc944e90c64b2c07662a292be6244bdf05cda44a7', - symbol: 'GRT', - cryptoName: 'The Graph', - coingeckoId: 'ethereum', - contractAddress: '0xc944e90c64b2c07662a292be6244bdf05cda44a7', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x4d224452801aced8b2f0aebe155379bb5d594381', - symbol: 'APE', - cryptoName: 'ApeCoin', - coingeckoId: 'ethereum', - contractAddress: '0x4d224452801aced8b2f0aebe155379bb5d594381', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xc00e94cb662c3520282e6f5717214004a7f26888', - symbol: 'COMP', - cryptoName: 'Compound', - coingeckoId: 'ethereum', - contractAddress: '0xc00e94cb662c3520282e6f5717214004a7f26888', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xddb3422497e61e13543bea06989c0789117555c5', - symbol: 'COTI', - cryptoName: 'COTI', - coingeckoId: 'ethereum', - contractAddress: '0xddb3422497e61e13543bea06989c0789117555c5', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x6b175474e89094c44da98b954eedeac495271d0f', - symbol: 'DAI', - cryptoName: 'Dai', - coingeckoId: 'ethereum', - contractAddress: '0x6b175474e89094c44da98b954eedeac495271d0f', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x4c19596f5aaff459fa38b0f7ed92f11ae6543784', - symbol: 'TRU', - cryptoName: 'TrueFi', - coingeckoId: 'ethereum', - contractAddress: '0x4c19596f5aaff459fa38b0f7ed92f11ae6543784', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xd13c7342e1ef687c5ad21b27c2b65d772cab5c8c', - symbol: 'UOS', - cryptoName: 'Ultra', - coingeckoId: 'ethereum', - contractAddress: '0xd13c7342e1ef687c5ad21b27c2b65d772cab5c8c', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x6982508145454ce325ddbe47a25d4ec3d2311933', - symbol: 'PEPE', - cryptoName: 'Pepe', - coingeckoId: 'ethereum', - contractAddress: '0x6982508145454ce325ddbe47a25d4ec3d2311933', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xbb0e17ef65f82ab018d8edd776e8dd940327b28b', - symbol: 'AXS', - cryptoName: 'Axie Infinity', - coingeckoId: 'ethereum', - contractAddress: '0xbb0e17ef65f82ab018d8edd776e8dd940327b28b', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x514910771af9ca656af840dff83e8264ecf986ca', - symbol: 'LINK', - cryptoName: 'Chainlink', - coingeckoId: 'ethereum', - contractAddress: '0x514910771af9ca656af840dff83e8264ecf986ca', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x0d8775f648430679a709e98d2b0cb6250d2887ef', - symbol: 'BAT', - cryptoName: 'Basic Attention', - coingeckoId: 'ethereum', - contractAddress: '0x0d8775f648430679a709e98d2b0cb6250d2887ef', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x3506424f91fd33084466f402d5d97f05f8e3b4af', - symbol: 'CHZ', - cryptoName: 'Chiliz', - coingeckoId: 'ethereum', - contractAddress: '0x3506424f91fd33084466f402d5d97f05f8e3b4af', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', - symbol: 'MKR', - cryptoName: 'Maker', - coingeckoId: 'ethereum', - contractAddress: '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x6b3595068778dd592e39a122f4f5a5cf09c90fe2', - symbol: 'SUSHI', - cryptoName: 'Sushi', - coingeckoId: 'ethereum', - contractAddress: '0x6b3595068778dd592e39a122f4f5a5cf09c90fe2', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', - symbol: 'USDC', - cryptoName: 'USDC', - coingeckoId: 'ethereum', - contractAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', - symbol: 'WBTC', - cryptoName: 'Wrapped Bitcoin', - coingeckoId: 'ethereum', - contractAddress: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x41e5560054824ea6b0732e656e3ad64e20e94e45', - symbol: 'CVC', - cryptoName: 'Civic', - coingeckoId: 'ethereum', - contractAddress: '0x41e5560054824ea6b0732e656e3ad64e20e94e45', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xd26114cd6ee289accf82350c8d8487fedb8a0c07', - symbol: 'OMG', - cryptoName: 'OMG Network', - coingeckoId: 'ethereum', - contractAddress: '0xd26114cd6ee289accf82350c8d8487fedb8a0c07', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f', - symbol: 'SNX', - cryptoName: 'Synthetix Network', - coingeckoId: 'ethereum', - contractAddress: '0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e', - symbol: 'YFI', - cryptoName: 'yearn.finance', - coingeckoId: 'ethereum', - contractAddress: '0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xf57e7e7c23978c3caec3c3548e3d615c346e79ff', - symbol: 'IMX', - cryptoName: 'Immutable', - coingeckoId: 'ethereum', - contractAddress: '0xf57e7e7c23978c3caec3c3548e3d615c346e79ff', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x8e870d67f660d95d5be530380d0ec0bd388289e1', - symbol: 'USDP', - cryptoName: 'Pax Dollar', - coingeckoId: 'ethereum', - contractAddress: '0x8e870d67f660d95d5be530380d0ec0bd388289e1', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x455e53cbb86018ac2b8092fdcd39d8444affc3f6', - symbol: 'POL', - cryptoName: 'POL (ex-MATIC)', - coingeckoId: 'ethereum', - contractAddress: '0x455e53cbb86018ac2b8092fdcd39d8444affc3f6', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x8290333cef9e6d528dd5618fb97a76f268f3edd4', - symbol: 'ANKR', - cryptoName: 'Ankr Network', - coingeckoId: 'ethereum', - contractAddress: '0x8290333cef9e6d528dd5618fb97a76f268f3edd4', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xd533a949740bb3306d119cc777fa900ba034cd52', - symbol: 'CRV', - cryptoName: 'Curve DAO', - coingeckoId: 'ethereum', - contractAddress: '0xd533a949740bb3306d119cc777fa900ba034cd52', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x92d6c1e31e14520e676a687f0a93788b716beff5', - symbol: 'ETHDYDX', - cryptoName: 'dYdX', - coingeckoId: 'ethereum', - contractAddress: '0x92d6c1e31e14520e676a687f0a93788b716beff5', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x75231f58b43240c9718dd58b4967c5114342a86c', - symbol: 'OKB', - cryptoName: 'OKB', - coingeckoId: 'ethereum', - contractAddress: '0x75231f58b43240c9718dd58b4967c5114342a86c', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x4cc19356f2d37338b9802aa8e8fc58b0373296e7', - symbol: 'KEY', - cryptoName: 'SelfKey', - coingeckoId: 'ethereum', - contractAddress: '0x4cc19356f2d37338b9802aa8e8fc58b0373296e7', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x3593d125a4f7849a1b059e64f4517a86dd60c95d', - symbol: 'OM', - cryptoName: 'MANTRA', - coingeckoId: 'ethereum', - contractAddress: '0x3593d125a4f7849a1b059e64f4517a86dd60c95d', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xa62cc35625b0c8dc1faea39d33625bb4c15bd71c', - symbol: 'STMX', - cryptoName: 'StormX', - coingeckoId: 'ethereum', - contractAddress: '0xa62cc35625b0c8dc1faea39d33625bb4c15bd71c', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xdc9ac3c20d1ed0b540df9b1fedc10039df13f99c', - symbol: 'UTK', - cryptoName: 'xMoney', - coingeckoId: 'ethereum', - contractAddress: '0xdc9ac3c20d1ed0b540df9b1fedc10039df13f99c', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - symbol: 'WETH', - cryptoName: 'WETH', - coingeckoId: 'ethereum', - contractAddress: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xe41d2489571d322189246dafa5ebde1f4699f498', - symbol: 'ZRX', - cryptoName: '0x Protocol', - coingeckoId: 'ethereum', - contractAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xba11d00c5f74255f56a5e366f4f77f5a186d7f55', - symbol: 'BAND', - cryptoName: 'Band Protocol', - coingeckoId: 'ethereum', - contractAddress: '0xba11d00c5f74255f56a5e366f4f77f5a186d7f55', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x1f573d6fb3f13d689ff844b4ce37794d79a7ff1c', - symbol: 'BNT', - cryptoName: 'Bancor Network', - coingeckoId: 'ethereum', - contractAddress: '0x1f573d6fb3f13d689ff844b4ce37794d79a7ff1c', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xbbbbca6a901c926f240b89eacb641d8aec7aeafd', - symbol: 'LRC', - cryptoName: 'Loopring', - coingeckoId: 'ethereum', - contractAddress: '0xbbbbca6a901c926f240b89eacb641d8aec7aeafd', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xf629cbd94d3791c9250152bd8dfbdf380e2a3b9c', - symbol: 'ENJ', - cryptoName: 'Enjin Coin', - coingeckoId: 'ethereum', - contractAddress: '0xf629cbd94d3791c9250152bd8dfbdf380e2a3b9c', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x6810e776880c02933d47db1b9fc05908e5386b96', - symbol: 'GNO', - cryptoName: 'Gnosis', - coingeckoId: 'ethereum', - contractAddress: '0x6810e776880c02933d47db1b9fc05908e5386b96', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xdefa4e8a7bcba345f687a2f1456f5edd9ce97202', - symbol: 'KNC', - cryptoName: 'Kyber Network Crystal', - coingeckoId: 'ethereum', - contractAddress: '0xdefa4e8a7bcba345f687a2f1456f5edd9ce97202', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0', - symbol: 'POL', - cryptoName: 'Polygon', - coingeckoId: 'ethereum', - contractAddress: '0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xec67005c4e498ec7f55e092bd1d35cbc47c91892', - symbol: 'MLN', - cryptoName: 'Enzyme', - coingeckoId: 'ethereum', - contractAddress: '0xec67005c4e498ec7f55e092bd1d35cbc47c91892', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x408e41876cccdc0f92210600ef50372656052a38', - symbol: 'REN', - cryptoName: 'Ren', - coingeckoId: 'ethereum', - contractAddress: '0x408e41876cccdc0f92210600ef50372656052a38', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x221657776846890989a759ba2973e427dff5c9bb', - symbol: 'REP', - cryptoName: 'Augur', - coingeckoId: 'ethereum', - contractAddress: '0x221657776846890989a759ba2973e427dff5c9bb', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xb64ef51c888972c908cfacf59b47c1afbc0ab8ac', - symbol: 'STORJ', - cryptoName: 'Storj', - coingeckoId: 'ethereum', - contractAddress: '0xb64ef51c888972c908cfacf59b47c1afbc0ab8ac', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x595832f8fc6bf59c85c527fec3740a1b7a361269', - symbol: 'POWR', - cryptoName: 'Powerledger', - coingeckoId: 'ethereum', - contractAddress: '0x595832f8fc6bf59c85c527fec3740a1b7a361269', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xade00c28244d5ce17d72e40330b1c318cd12b7c3', - symbol: 'ADX', - cryptoName: 'AdEx', - coingeckoId: 'ethereum', - contractAddress: '0xade00c28244d5ce17d72e40330b1c318cd12b7c3', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x5732046a883704404f284ce41ffadd5b007fd668', - symbol: 'BLZ', - cryptoName: 'Bluzelle', - coingeckoId: 'ethereum', - contractAddress: '0x5732046a883704404f284ce41ffadd5b007fd668', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x4f9254c83eb525f9fcf346490bbb3ed28a81c667', - symbol: 'CELR', - cryptoName: 'Celer Network', - coingeckoId: 'ethereum', - contractAddress: '0x4f9254c83eb525f9fcf346490bbb3ed28a81c667', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x3597bfd533a99c9aa083587b074434e61eb0a258', - symbol: 'DENT', - cryptoName: 'Dent', - coingeckoId: 'ethereum', - contractAddress: '0x3597bfd533a99c9aa083587b074434e61eb0a258', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x1776e1f26f98b1a5df9cd347953a26dd3cb46671', - symbol: 'NMR', - cryptoName: 'Numeraire', - coingeckoId: 'ethereum', - contractAddress: '0x1776e1f26f98b1a5df9cd347953a26dd3cb46671', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x8f8221afbb33998d8584a2b05749ba73c37a938a', - symbol: 'REQ', - cryptoName: 'Request', - coingeckoId: 'ethereum', - contractAddress: '0x8f8221afbb33998d8584a2b05749ba73c37a938a', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x607f4c5bb672230e8672085532f7e901544a7375', - symbol: 'RLC', - cryptoName: 'iExec RLC', - coingeckoId: 'ethereum', - contractAddress: '0x607f4c5bb672230e8672085532f7e901544a7375', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x6c6ee5e31d828de241282b9606c8e98ea48526e2', - symbol: 'HOT', - cryptoName: 'Holo', - coingeckoId: 'ethereum', - contractAddress: '0x6c6ee5e31d828de241282b9606c8e98ea48526e2', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x8ce9137d39326ad0cd6491fb5cc0cba0e089b6a9', - symbol: 'SXP', - cryptoName: 'Solar', - coingeckoId: 'ethereum', - contractAddress: '0x8ce9137d39326ad0cd6491fb5cc0cba0e089b6a9', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xbc396689893d065f41bc2c6ecbee5e0085233447', - symbol: 'PERP', - cryptoName: 'Perpetual Protocol', - coingeckoId: 'ethereum', - contractAddress: '0xbc396689893d065f41bc2c6ecbee5e0085233447', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0xbf2179859fc6d5bee9bf9158632dc51678a4100e', - symbol: 'ELF', - cryptoName: 'aelf', - coingeckoId: 'ethereum', - contractAddress: '0xbf2179859fc6d5bee9bf9158632dc51678a4100e', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x056fd409e1d7a124bd7017459dfea2f387b6d5cd', - symbol: 'GUSD', - cryptoName: 'Gemini Dollar', - coingeckoId: 'ethereum', - contractAddress: '0x056fd409e1d7a124bd7017459dfea2f387b6d5cd', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x42476f744292107e34519f9c357927074ea3f75d', - symbol: 'LOOM', - cryptoName: 'Loom Network (NEW)', - coingeckoId: 'ethereum', - contractAddress: '0x42476f744292107e34519f9c357927074ea3f75d', - networkName: 'Ethereum', - }, - { - type: 'currency', - networkSymbol: 'ethereum--0x814e0908b12a99fecf5bc101bb5d0b8b5cdf7d26', - symbol: 'MDT', - cryptoName: 'Measurable Data', - coingeckoId: 'ethereum', - contractAddress: '0x814e0908b12a99fecf5bc101bb5d0b8b5cdf7d26', - networkName: 'Ethereum', - }, - { - type: 'group', - label: 'TR_COINMARKET_NETWORK_TOKENS', - networkName: 'Solana', - }, - { - type: 'currency', - networkSymbol: 'solana--kinXdEcpDQeHPEuQnqmUgtYykqKGVFq6CeVX5iAHJq6', - symbol: 'KIN', - cryptoName: 'Kin', - coingeckoId: 'solana', - contractAddress: 'kinXdEcpDQeHPEuQnqmUgtYykqKGVFq6CeVX5iAHJq6', - networkName: 'Solana', - }, - { - type: 'currency', - networkSymbol: 'solana--EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', - symbol: 'USDC', - cryptoName: 'USDC', - coingeckoId: 'solana', - contractAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', - networkName: 'Solana', - }, - { - type: 'currency', - networkSymbol: 'solana--Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', - symbol: 'USDT', - cryptoName: 'Tether', - coingeckoId: 'solana', - contractAddress: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', - networkName: 'Solana', - }, - { - type: 'group', - label: 'TR_COINMARKET_NETWORK_TOKENS', - networkName: 'BNB Smart Chain', - }, - { - type: 'currency', - networkSymbol: 'binance-smart-chain--0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82', - symbol: 'CAKE', - cryptoName: 'PancakeSwap', - coingeckoId: 'binance-smart-chain', - contractAddress: '0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82', - networkName: 'BNB Smart Chain', - }, - { - type: 'currency', - networkSymbol: 'binance-smart-chain--0x55d398326f99059ff775485246999027b3197955', - symbol: 'BSC-USD', - cryptoName: 'Binance Bridged USDT (BNB Smart Chain)', - coingeckoId: 'binance-smart-chain', - contractAddress: '0x55d398326f99059ff775485246999027b3197955', - networkName: 'BNB Smart Chain', - }, - { - type: 'currency', - networkSymbol: 'binance-smart-chain--0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d', - symbol: 'USDC', - cryptoName: 'Binance Bridged USDC (BNB Smart Chain)', - coingeckoId: 'binance-smart-chain', - contractAddress: '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d', - networkName: 'BNB Smart Chain', - }, - { - type: 'currency', - networkSymbol: 'binance-smart-chain--0x111111111117dc0aa78b770fa6a738034120c302', - symbol: '1INCH', - cryptoName: '1inch', - coingeckoId: 'binance-smart-chain', - contractAddress: '0x111111111117dc0aa78b770fa6a738034120c302', - networkName: 'BNB Smart Chain', - }, - { - type: 'group', - label: 'TR_COINMARKET_NETWORK_TOKENS', - networkName: 'Fantom', - }, - { - type: 'currency', - networkSymbol: 'fantom--0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83', - symbol: 'WFTM', - cryptoName: 'Wrapped Fantom', - coingeckoId: 'fantom', - contractAddress: '0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83', - networkName: 'Fantom', - }, - { - type: 'group', - label: 'TR_COINMARKET_NETWORK_TOKENS', - networkName: 'TRON', - }, - { - type: 'currency', - networkSymbol: 'tron--TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', - symbol: 'USDT', - cryptoName: 'Tether', - coingeckoId: 'tron', - contractAddress: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', - networkName: 'TRON', - }, - { - type: 'currency', - networkSymbol: 'tron--TAFjULxiVgT4qWk6UZwjqwZXTSaGaqnVp4', - symbol: 'BTT', - cryptoName: 'BitTorrent', - coingeckoId: 'tron', - contractAddress: 'TAFjULxiVgT4qWk6UZwjqwZXTSaGaqnVp4', - networkName: 'TRON', - }, - { - type: 'group', - label: 'TR_COINMARKET_NETWORK_TOKENS', - networkName: 'Polygon POS', - }, - { - type: 'currency', - networkSymbol: 'polygon-pos--0xc2132d05d31c914a87c6611c10748aeb04b58e8f', - symbol: 'USDT', - cryptoName: 'Polygon Bridged USDT (Polygon)', - coingeckoId: 'polygon-pos', - contractAddress: '0xc2132d05d31c914a87c6611c10748aeb04b58e8f', - networkName: 'Polygon POS', - }, - { - type: 'currency', - networkSymbol: 'polygon-pos--0x3c499c542cef5e3811e1192ce70d8cc03d5c3359', - symbol: 'USDC', - cryptoName: 'USDC', - coingeckoId: 'polygon-pos', - contractAddress: '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359', - networkName: 'Polygon POS', - }, - { - type: 'currency', - networkSymbol: 'polygon-pos--0x7ceb23fd6bc0add59e62ac25578270cff1b9f619', - symbol: 'WETH', - cryptoName: 'Polygon PoS Bridged WETH (Polygon POS)', - coingeckoId: 'polygon-pos', - contractAddress: '0x7ceb23fd6bc0add59e62ac25578270cff1b9f619', - networkName: 'Polygon POS', - }, - { - type: 'group', - label: 'TR_COINMARKET_NETWORK_TOKENS', - networkName: 'Arbitrum One', - }, - { - type: 'currency', - networkSymbol: 'arbitrum-one--0xaf88d065e77c8cc2239327c5edb3a432268e5831', - symbol: 'USDC', - cryptoName: 'USDC', - coingeckoId: 'arbitrum-one', - contractAddress: '0xaf88d065e77c8cc2239327c5edb3a432268e5831', - networkName: 'Arbitrum One', - }, - { - type: 'currency', - networkSymbol: 'arbitrum-one--0x912ce59144191c1204e64559fe8253a0e49e6548', - symbol: 'ARB', - cryptoName: 'Arbitrum', - coingeckoId: 'arbitrum-one', - contractAddress: '0x912ce59144191c1204e64559fe8253a0e49e6548', - networkName: 'Arbitrum One', - }, - { - type: 'group', - label: 'TR_COINMARKET_NETWORK_TOKENS', - networkName: 'Base', - }, - { - type: 'currency', - networkSymbol: 'base--0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', - symbol: 'USDC', - cryptoName: 'USDC', - coingeckoId: 'base', - contractAddress: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', - networkName: 'Base', - }, -]; - -export const selectAssetModalNetworks: NetworkFilterCategory[] = [ - { - name: 'Ethereum', - symbol: 'eth', - coingeckoId: 'ethereum', - coingeckoNativeId: 'ethereum', - }, - { - name: 'Polygon PoS', - symbol: 'pol', - coingeckoId: 'polygon-pos', - coingeckoNativeId: 'polygon-ecosystem-token', - }, - { - name: 'Solana', - symbol: 'sol', - coingeckoId: 'solana', - coingeckoNativeId: 'solana', - }, - { - name: 'BNB Smart Chain', - symbol: 'bnb', - coingeckoId: 'binance-smart-chain', - coingeckoNativeId: 'binancecoin', - }, -]; diff --git a/packages/product-components/src/components/TokenIconSet/TokenIconSet.tsx b/packages/product-components/src/components/TokenIconSet/TokenIconSet.tsx index 1c4fd8f02c8..76bd5f2216c 100644 --- a/packages/product-components/src/components/TokenIconSet/TokenIconSet.tsx +++ b/packages/product-components/src/components/TokenIconSet/TokenIconSet.tsx @@ -3,8 +3,7 @@ import styled, { css } from 'styled-components'; import { AssetLogo, useElevation } from '@trezor/components'; import { getCoingeckoId, NetworkSymbol } from '@suite-common/wallet-config'; import { TokenInfo } from '@trezor/connect'; - -import styled, { css } from 'styled-components'; +import { getContractAddressForNetwork } from '@suite-common/wallet-utils'; import { borders, Elevation, mapElevationToBackground, mapElevationToBorder } from '@trezor/theme'; export type TokenIconSetProps = { @@ -58,9 +57,10 @@ export const TokenIconSet = ({ network, tokens }: TokenIconSetProps) => { key={token.contract} size={20} coingeckoId={coingeckoId ?? ''} - contractAddress={token.contract.toLowerCase()} + contractAddress={getContractAddressForNetwork(network, token.contract)} placeholder={token.symbol?.toUpperCase() ?? ''} placeholderWithTooltip={false} + // TODO: shouldFetch? /> ))} diff --git a/packages/suite/src/actions/suite/copyAddressActions.ts b/packages/suite/src/actions/suite/copyAddressActions.ts new file mode 100644 index 00000000000..c35a2dcb68d --- /dev/null +++ b/packages/suite/src/actions/suite/copyAddressActions.ts @@ -0,0 +1,28 @@ +import { Dispatch } from 'redux'; + +import { AddressType } from '@suite-common/wallet-types'; +import { copyToClipboard } from '@trezor/dom-utils'; +import { notificationsActions } from '@suite-common/toast-notifications'; + +import { openModal } from 'src/actions/suite/modalActions'; + +export const showCopyAddressModal = + (address: string, addressType: AddressType) => (dispatch: Dispatch) => { + dispatch( + openModal({ + type: 'copy-address', + addressType, + address, + }), + ); + }; + +export const copyAddressToClipboard = (address: string) => (dispatch: Dispatch) => { + const result = copyToClipboard(address); + + const isSuccess = result === true; + + if (isSuccess) { + dispatch(notificationsActions.addToast({ type: 'copy-to-clipboard' })); + } +}; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/IOAddress.tsx b/packages/suite/src/components/suite/copy/IOAddress.tsx similarity index 100% rename from packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/IOAddress.tsx rename to packages/suite/src/components/suite/copy/IOAddress.tsx diff --git a/packages/suite/src/views/wallet/send/Outputs/TokenSelect/TokenAddress.tsx b/packages/suite/src/components/suite/copy/TokenAddressRow.tsx similarity index 53% rename from packages/suite/src/views/wallet/send/Outputs/TokenSelect/TokenAddress.tsx rename to packages/suite/src/components/suite/copy/TokenAddressRow.tsx index 23d660c2059..f0a01ab6d4d 100644 --- a/packages/suite/src/views/wallet/send/Outputs/TokenSelect/TokenAddress.tsx +++ b/packages/suite/src/components/suite/copy/TokenAddressRow.tsx @@ -1,7 +1,8 @@ -import { HiddenPlaceholder } from 'src/components/suite/HiddenPlaceholder'; -import { Icon, Link, Text, TextProps } from '@trezor/components'; -import { useMemo, useState } from 'react'; +import { MouseEvent, useState } from 'react'; + import styled, { css, useTheme } from 'styled-components'; + +import { Icon, Link, Text, TextProps } from '@trezor/components'; import { borders, spacingsPx } from '@trezor/theme'; const IconWrapper = styled.div` @@ -53,7 +54,7 @@ const TextOverflowContainer = styled.div<{ $shouldAllowCopy?: boolean }>` `} `; -interface TokenAddressProps { +interface TokenAddressRowProps { tokenExplorerUrl?: string; tokenContractAddress: string; shouldAllowCopy?: boolean; @@ -63,68 +64,66 @@ interface TokenAddressProps { } // This is needed because icon interferes with pointer events of Select +// eslint-disable-next-line local-rules/no-override-ds-component const IconWithNoPointer = styled(Icon)` pointer-events: none; `; -export const TokenAddress = ({ + +export const TokenAddressRow = ({ tokenContractAddress, tokenExplorerUrl, shouldAllowCopy = true, typographyStyle = 'label', variant = 'default', onCopy, -}: TokenAddressProps) => { +}: TokenAddressRowProps) => { const [isClicked, setIsClicked] = useState(false); const theme = useTheme(); - const copy = (e: React.MouseEvent) => { - e.stopPropagation(); + const copy = (event: MouseEvent) => { + setIsClicked(true); + event.stopPropagation(); onCopy(); }; - const shortenAddress = useMemo(() => { - return `${tokenContractAddress.slice(0, 6)}...${tokenContractAddress.slice(-4)}`; - }, [tokenContractAddress]); + const shortenedTokenAddress = `${tokenContractAddress.slice(0, 6)}...${tokenContractAddress.slice(-4)}`; - // HiddenPlaceholder disableKeepingWidth: it isn't needed (no numbers to redact), but inline-block disrupts overflow behavior return ( - - setIsClicked(false)} - data-testid="@tx-detail/txid-value" - id={tokenContractAddress} - $shouldAllowCopy={shouldAllowCopy} - > - {shortenAddress} - {shouldAllowCopy ? ( - copy(e)}> + setIsClicked(false)} + data-testid="@tx-detail/txid-value" + id={tokenContractAddress} + $shouldAllowCopy={shouldAllowCopy} + > + {shortenedTokenAddress} + {shouldAllowCopy ? ( + + + + ) : null} + {tokenExplorerUrl ? ( + + e.stopPropagation()} + > - - ) : null} - {tokenExplorerUrl ? ( - - e.stopPropagation()} - > - - - - ) : null} - - + + + ) : null} + ); }; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AdvancedTxDetails/IODetails/IODetails.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AdvancedTxDetails/IODetails/IODetails.tsx index 749ff18c6ee..c71cbe1032d 100644 --- a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AdvancedTxDetails/IODetails/IODetails.tsx +++ b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AdvancedTxDetails/IODetails/IODetails.tsx @@ -13,9 +13,9 @@ import { FormattedCryptoAmount, Translation } from 'src/components/suite'; import { useSelector } from 'src/hooks/suite/useSelector'; import { UtxoAnonymity } from 'src/components/wallet'; import { FormattedNftAmount } from 'src/components/suite/FormattedNftAmount'; +import { IOAddress } from 'src/components/suite/copy/IOAddress'; import { AnalyzeInExplorerBanner } from './AnalyzeInExplorerBanner'; -import { IOAddress } from '../../IOAddress'; export const blurFix = css` margin-left: -10px; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/BasicTxDetails.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/BasicTxDetails.tsx index d228dab9c46..cfb5df9d7bb 100644 --- a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/BasicTxDetails.tsx +++ b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/BasicTxDetails.tsx @@ -15,9 +15,10 @@ import { } from '@trezor/theme'; import { CoinLogo } from '@trezor/product-components'; +import { TransactionHeader } from 'src/components/wallet/TransactionItem/TransactionHeader'; +import { IOAddress } from 'src/components/suite/copy/IOAddress'; import { Translation, FormattedDateWithBullet } from 'src/components/suite'; import { WalletAccountTransaction } from 'src/types/wallet'; -import { TransactionHeader } from 'src/components/wallet/TransactionItem/TransactionHeader'; import { IOAddress } from './IOAddress'; diff --git a/packages/suite/src/views/wallet/coinmarket/common/CoinmarketForm/CoinmarketFormInput/CoinmarketFormInputCryptoSelect.tsx b/packages/suite/src/views/wallet/coinmarket/common/CoinmarketForm/CoinmarketFormInput/CoinmarketFormInputCryptoSelect.tsx index 7aa01d2bc10..1b8fc7f9921 100644 --- a/packages/suite/src/views/wallet/coinmarket/common/CoinmarketForm/CoinmarketFormInput/CoinmarketFormInputCryptoSelect.tsx +++ b/packages/suite/src/views/wallet/coinmarket/common/CoinmarketForm/CoinmarketFormInput/CoinmarketFormInputCryptoSelect.tsx @@ -2,8 +2,17 @@ import { Controller } from 'react-hook-form'; import { useMemo, useState } from 'react'; import { Select, useElevation } from '@trezor/components'; -import { SelectAssetModal } from '@trezor/product-components'; -import { networks, NetworkSymbol } from '@suite-common/wallet-config'; +import { SelectAssetModal , + SelectAssetOptionCurrencyProps, + SelectAssetOptionProps, +} from '@trezor/product-components'; +import { networks, NetworkSymbol , + getNetworkByCoingeckoNativeId, +} from '@suite-common/wallet-config'; +import { + NetworkFilterCategories, + NetworkFilterCategory, +} from '@trezor/product-components/src/components/SelectAssetModal/SelectAssetModal'; import { CoinmarketAccountOptionsGroupOptionProps, @@ -29,21 +38,12 @@ import { parseCryptoId, } from 'src/utils/wallet/coinmarket/coinmarketUtils'; import { useCoinmarketFormContext } from 'src/hooks/wallet/coinmarket/form/useCoinmarketCommonForm'; -import { - SelectAssetModal, - SelectAssetOptionCurrencyProps, - SelectAssetOptionProps, -} from '@trezor/product-components'; -import { networks, NetworkSymbol } from '@suite-common/wallet-config'; import { FORM_CRYPTO_CURRENCY_SELECT, FORM_RECEIVE_CRYPTO_CURRENCY_SELECT, } from 'src/constants/wallet/coinmarket/form'; import { isCoinmarketExchangeContext } from 'src/utils/wallet/coinmarket/coinmarketTypingUtils'; -import { - NetworkFilterCategories, - NetworkFilterCategory, -} from '@trezor/product-components/src/components/SelectAssetModal/SelectAssetModal'; + export const CoinmarketFormInputCryptoSelect = < TFieldValues extends CoinmarketBuyFormProps | CoinmarketExchangeFormProps, @@ -138,7 +138,6 @@ export const CoinmarketFormInputCryptoSelect = < filterCategories={networkCategories} onSelectAssetModal={handleSelectChange} onClose={() => setIsModalActive(false)} - selectAssetModalHeight="tall" /> )} { - // native token option - const result: Option[] = [ - { - options: [{ value: null, label: symbol.toUpperCase() }], - }, - ]; - - if (accountTokens) { - const tokens = getTokens(accountTokens, symbol, coinDefinitions); - - tokens.shownWithBalance.forEach(token => { - result[0].options.push({ - value: token.contract, - label: formatTokenSymbol(token.symbol || token.contract), - }); - }); - - if (tokens.hiddenWithBalance.length) { - result.push({ - label: ( - - - - ), - options: tokens.hiddenWithBalance.map(token => ({ - value: token.contract, - label: formatTokenSymbol(token.symbol || token.contract), - })), - }); - } - - if (tokens.unverifiedWithBalance.length) { - result.push({ - label: ( - - - } - /> - - ), - options: tokens.unverifiedWithBalance.map(token => ({ - value: token.contract, - label: formatTokenSymbol(token.symbol || token.contract), - })), - }); - } - } - - return result; -}; - -interface TokenSelectProps { - output: Partial; - outputId: number; -} - -export const TokenSelect = ({ output, outputId }: TokenSelectProps) => { - const { - account, - clearErrors, - control, - setAmount, - getValues, - getDefaultValue, - toggleOption, - composeTransaction, - watch, - setValue, - setDraftSaveRequest, - } = useSendFormContext(); - const coinDefinitions = useSelector(state => selectCoinDefinitions(state, account.symbol)); - const sendFormPrefill = useSelector(state => state.suite.prefillFields.sendForm); - const localCurrency = useSelector(selectLocalCurrency); - const fiatRates = useSelector(selectCurrentFiatRates); - const tokensWithRates = enhanceTokensWithRates( - account.tokens, - localCurrency, - account.symbol, - fiatRates, - ); - const dispatch = useDispatch(); - - const sortedTokens = useMemo(() => { - return tokensWithRates.sort(sortTokensWithRates); - }, [tokensWithRates]); - - const tokenInputName = `outputs.${outputId}.token` as const; - const amountInputName = `outputs.${outputId}.amount` as const; - const currencyInputName = `outputs.${outputId}.currency` as const; - const tokenValue = getDefaultValue(tokenInputName, output.token); - const isSetMaxActive = getDefaultValue('setMaxOutputId') === outputId; - const dataEnabled = getDefaultValue('options', []).includes('ethereumData'); - const options = buildTokenOptions(sortedTokens, account.symbol, coinDefinitions); - - // Amount needs to be re-validated again AFTER token change propagation (decimal places, available balance) - // watch token change and use "useSendFormFields.setAmount" util for validation (if amount is set) - // if Amount is not valid 'react-hook-form' will set an error to it, and composeTransaction will be prevented - // N0TE: do this conditionally only for ETH and when set-max is not enabled - const tokenWatch = watch(tokenInputName, null); - const currencyValue = watch(currencyInputName); - - useEffect(() => { - if (account.networkType === 'ethereum' && !isSetMaxActive) { - const amountValue = getValues(`outputs.${outputId}.amount`) as string; - if (amountValue) setAmount(outputId, amountValue); - } - }, [outputId, tokenWatch, setAmount, getValues, account.networkType, isSetMaxActive]); - - useEffect(() => { - if (sendFormPrefill) { - setValue(tokenInputName, sendFormPrefill, { shouldValidate: true, shouldDirty: true }); - setDraftSaveRequest(true); - dispatch({ - type: SUITE.SET_SEND_FORM_PREFILL, - payload: '', - }); - } - }, [sendFormPrefill, setValue, tokenInputName, setDraftSaveRequest, dispatch]); - - return ( - ( - option.contractAddress === value)} - options={options.map(option => ({ - value: option.contractAddress, - label: option.symbol, - coingeckoId: option.coingeckoId, - }))} - isDisabled={options.length === 1} - formatOptionLabel={(option: SelectAssetOptionCurrencyProps) => { - return ( - - {selectedOption && - selectedOption.contractAddress !== null ? ( - 1 + ? () => { + setIsTokensModalActive(true); + } + : undefined + } + > + + + {selectedOption && selectedOption.contractAddress !== null ? ( + + ) : ( + + )} + + + + {selectedOption.cryptoName} + + + + + + + + {rate && ( + <> + {' ≈ '} + - ) : ( - - )} - - - - {option.cryptoName} - - - - - {option.balance ?? '0'}{' '} - {option.symbol.toUpperCase()} ≈{' '} - - - - - {option.contractAddress && option.cryptoName && ( - - - {account.symbol === 'ada' ? ( - - ) : ( - - )}{' '} - - onCopyAddress( - option.contractAddress || - '', - 'contract', - shouldShowCopyAddressModal, - dispatch, - ) - } - /> - - + + )} + + + {selectedOption.contractAddress && selectedOption.cryptoName && ( + + + + {account.networkType === 'cardano' ? ( + + ) : ( + + )} + - - - ); - }} - styles={{ - valueContainer: base => ({ - ...base, - justifyContent: 'flex-start !important', - }), - indicatorsContainer: base => ({ - ...base, - backgroundColor: theme.backgroundSurfaceElevation2, - borderRadius: borders.radii.full, - height: 36, - width: 36, - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }), - }} - data-testid="@amount-select" - isClean - isClearable={false} - isMenuOpen={false} + shouldAllowCopy={true} + typographyStyle="hint" + variant="tertiary" + tokenExplorerUrl={selectedOption.tokenExplorerUrl} + onCopy={() => + selectedOption.contractAddress && + dispatch( + shouldShowCopyAddressModal + ? showCopyAddressModal( + selectedOption.contractAddress, + 'contract', + ) + : copyAddressToClipboard( + selectedOption.contractAddress, + ), + ) + } + /> + + + + )} + + + + - - )} - /> + + + ); }; diff --git a/packages/suite/src/views/wallet/send/Outputs/TokenSelect/tokenSelectUtils.ts b/packages/suite/src/views/wallet/send/Outputs/TokenSelect/tokenSelectUtils.ts deleted file mode 100644 index 91ad582e6c4..00000000000 --- a/packages/suite/src/views/wallet/send/Outputs/TokenSelect/tokenSelectUtils.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { Account, AddressType } from '@suite-common/wallet-types'; -import { TokenDefinitions } from '@suite-common/token-definitions'; -import { getTokens } from 'src/utils/wallet/tokenUtils'; -import { networks } from '@suite-common/wallet-config'; -import { SelectAssetOptionCurrencyProps } from '@trezor/product-components'; -import { getCoingeckoId } from '@suite-common/wallet-config'; -import { getTokenExplorerUrl } from '@suite-common/token-definitions'; -import { TranslationFunction } from 'src/hooks/suite/useTranslation'; -import { TokenFilterCategories } from '@trezor/product-components/src/components/SelectAssetModal/SelectAssetModal'; -import { Card } from '@trezor/components'; -import { Elevation } from '@trezor/theme'; -import styled, { css } from 'styled-components'; -import { mapElevationToBackground } from '@trezor/theme'; -import { copyToClipboard } from '@trezor/dom-utils'; -import { notificationsActions } from '@suite-common/toast-notifications'; -import { openModal } from 'src/actions/suite/modalActions'; -import { Dispatch } from 'redux'; - -export const buildTokenOptions = ( - accountTokens: Account['tokens'], - symbol: Account['symbol'], - coinDefinitions: TokenDefinitions['coin'], - nativeTokenBalance: Account['formattedBalance'], - translationString: TranslationFunction, -) => { - // native token option - const result: SelectAssetOptionCurrencyProps[] = [ - { - type: 'currency', - symbol, - networkSymbol: symbol, - coingeckoId: networks[symbol].coingeckoNativeId || '', - contractAddress: null, - cryptoName: networks[symbol].name, - balance: nativeTokenBalance, - }, - ]; - - if (accountTokens) { - const tokens = getTokens(accountTokens, symbol, coinDefinitions); - - tokens.shownWithBalance.forEach(token => { - result.push({ - type: 'currency', - symbol: token.symbol ?? symbol, - networkSymbol: symbol, - coingeckoId: getCoingeckoId(symbol) ?? '', - contractAddress: token.contract, - cryptoName: token.name, - balance: token.balance, - tokenExplorerUrl: getTokenExplorerUrl(networks[symbol], token), - }); - }); - - if (tokens.hiddenWithBalance.length) { - tokens.hiddenWithBalance.forEach(token => { - result.push({ - type: 'currency', - symbol: token.symbol ?? symbol, - networkSymbol: symbol, - hidden: true, - coingeckoId: getCoingeckoId(symbol) ?? '', - contractAddress: token.contract, - cryptoName: token.name, - balance: token.balance, - }); - }); - } - - if (tokens.unverifiedWithBalance.length) { - tokens.unverifiedWithBalance.forEach(token => { - result.push({ - type: 'currency', - unverified: true, - symbol: token.symbol ?? symbol, - networkSymbol: symbol, - coingeckoId: getCoingeckoId(symbol) ?? '', - contractAddress: token.contract, - cryptoName: token.name, - balance: token.balance, - badge: translationString('TR_UNRECOGNIZED'), - }); - }); - } - } - - return result; -}; - -export const sendTokenCategories = ( - translationString: TranslationFunction, -): TokenFilterCategories => ({ - categoriesType: 'tokens', - categories: [ - { - type: 'visibleWithBalance', - label: translationString('TR_TOKENS'), - }, - { - type: 'hiddenWithBalance', - label: translationString('TR_HIDDEN'), - }, - ], -}); - -export const TokenSelectContainer = styled(Card)<{ $isDisabled: boolean; $elevation: Elevation }>` - background: ${({ theme, $elevation }) => mapElevationToBackground({ $elevation, theme })}; - ${({ $elevation, theme }) => - $elevation === 1 && - css` - box-shadow: ${theme.boxShadowBase}; - `} - cursor: ${({ $isDisabled }) => ($isDisabled ? 'not-allowed' : 'pointer')}; -`; - -export const onCopyAddress = ( - address: string, - addressType: AddressType, - shouldShowCopyAddressModal: boolean, - dispatch: Dispatch, -) => { - if (shouldShowCopyAddressModal) { - dispatch( - openModal({ - type: 'copy-address', - addressType, - address, - }), - ); - } else { - const result = copyToClipboard(address); - if (typeof result !== 'string') { - dispatch(notificationsActions.addToast({ type: 'copy-to-clipboard' })); - } - } -}; - -export type TokenSelectProps = { - outputId: number; -}; diff --git a/packages/suite/src/views/wallet/tokens/common/TokensTable/TokenRow.tsx b/packages/suite/src/views/wallet/tokens/common/TokensTable/TokenRow.tsx index 2a91e8448ed..5e1a28f78b6 100644 --- a/packages/suite/src/views/wallet/tokens/common/TokensTable/TokenRow.tsx +++ b/packages/suite/src/views/wallet/tokens/common/TokensTable/TokenRow.tsx @@ -1,7 +1,7 @@ import styled from 'styled-components'; import { selectDevice } from '@suite-common/wallet-core'; -import { Account, AddressType, TokenAddress } from '@suite-common/wallet-types'; +import { Account, TokenAddress } from '@suite-common/wallet-types'; import { Network, getCoingeckoId } from '@suite-common/wallet-config'; import { DefinitionType, @@ -10,8 +10,6 @@ import { selectIsSpecificCoinDefinitionKnown, tokenDefinitionsActions, } from '@suite-common/token-definitions'; -import { notificationsActions } from '@suite-common/toast-notifications'; -import { copyToClipboard } from '@trezor/dom-utils'; import { Dropdown, IconButton, @@ -27,6 +25,7 @@ import { } from '@trezor/components'; import { spacings, spacingsPx } from '@trezor/theme'; import { EventType, analytics } from '@trezor/suite-analytics'; +import { getContractAddressForNetwork, getTokenExplorerUrl } from '@suite-common/wallet-utils'; import { FiatValue, @@ -52,7 +51,7 @@ import { selectIsUnhideTokenModalShown, } from 'src/reducers/suite/suiteReducer'; import { SUITE } from 'src/actions/suite/constants'; -import { getTokenExplorerUrl } from '@suite-common/token-definitions'; +import { copyAddressToClipboard, showCopyAddressModal } from 'src/actions/suite/copyAddressActions'; import { BlurUrls } from '../BlurUrls'; @@ -99,6 +98,7 @@ export const TokenRow = ({ selectIsSpecificCoinDefinitionKnown(state, account.symbol, token.contract as TokenAddress), ); const isDeviceLocked = isLocked(true); + const networkContractAddress = getContractAddressForNetwork(account.symbol, token.contract); const coingeckoId = getCoingeckoId(account.symbol); if (!unusedAddress || !device) return null; @@ -123,23 +123,6 @@ export const TokenRow = ({ } }; - const onCopyAddress = (address: string, addressType: AddressType) => { - if (shouldShowCopyAddressModal) { - dispatch( - openModal({ - type: 'copy-address', - addressType, - address, - }), - ); - } else { - const result = copyToClipboard(address); - if (typeof result !== 'string') { - dispatch(notificationsActions.addToast({ type: 'copy-to-clipboard' })); - } - } - }; - const isReceiveButtonDisabled = isDeviceLocked || !!device.authConfirm; return ( @@ -149,7 +132,7 @@ export const TokenRow = ({ @@ -297,7 +280,14 @@ export const TokenRow = ({ ), onClick: () => - onCopyAddress(token.contract, 'contract'), + dispatch( + shouldShowCopyAddressModal + ? showCopyAddressModal( + token.contract, + 'contract', + ) + : copyAddressToClipboard(token.contract), + ), }, ], }, @@ -315,9 +305,14 @@ export const TokenRow = ({ ), onClick: () => - onCopyAddress( - token.fingerprint as string, - 'fingerprint', + token.fingerprint && + dispatch( + shouldShowCopyAddressModal + ? showCopyAddressModal( + token.fingerprint, + 'fingerprint', + ) + : copyAddressToClipboard(token.contract), ), }, ], @@ -336,7 +331,15 @@ export const TokenRow = ({ ), onClick: () => - onCopyAddress(token.policyId as string, 'policyId'), + token.policyId && + dispatch( + shouldShowCopyAddressModal + ? showCopyAddressModal( + token.policyId, + 'policyId', + ) + : copyAddressToClipboard(token.contract), + ), }, ], }, diff --git a/suite-common/wallet-utils/src/tokenUtils.ts b/suite-common/wallet-utils/src/tokenUtils.ts index 8551e118750..71bc9b785ff 100644 --- a/suite-common/wallet-utils/src/tokenUtils.ts +++ b/suite-common/wallet-utils/src/tokenUtils.ts @@ -1,13 +1,14 @@ -import { NetworkSymbol } from '@suite-common/wallet-config'; +import { Network, NetworkSymbol } from '@suite-common/wallet-config'; +import { TokenInfo } from '@trezor/blockchain-link-types'; import { parseAsset } from '@trezor/blockchain-link-utils/src/blockfrost'; export const getContractAddressForNetwork = ( - networkSymbol: NetworkSymbol, + networkSymbol: NetworkSymbol | (string & {}), // unknown symbols will result to lowerCase contractAddress: string, ) => { switch (networkSymbol) { case 'eth': - // Specyfing most common network as first case improves performance little bit + // Specifying most common network as first case improves performance little bit return contractAddress.toLowerCase(); case 'sol': case 'dsol': @@ -22,3 +23,12 @@ export const getContractAddressForNetwork = ( return contractAddress.toLowerCase(); } }; + +export const getTokenExplorerUrl = (network: Network, token: TokenInfo) => { + const explorerUrl = + network.networkType === 'cardano' ? network.explorer.token : network.explorer.account; + const contractAddress = network.networkType === 'cardano' ? token.fingerprint : token.contract; + const queryString = network.explorer.queryString ?? ''; + + return `${explorerUrl}${contractAddress}${queryString}`; +};