diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenButton.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenButton.tsx index eaa43c8007..d679d1be6b 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenButton.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenButton.tsx @@ -15,9 +15,12 @@ import { import { useNetworks } from '../../hooks/useNetworks' import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' import { Transition } from '../common/Transition' +import { SafeImage } from '../common/SafeImage' +import { useTokensFromLists, useTokensFromUser } from './TokenSearchUtils' export type TokenButtonOptions = { symbol?: string + logoSrc?: string | null disabled?: boolean } @@ -34,6 +37,9 @@ export function TokenButton({ const [networks] = useNetworks() const { childChainProvider } = useNetworksRelationship(networks) + const tokensFromLists = useTokensFromLists() + const tokensFromUser = useTokensFromUser() + const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) const tokenSymbol = useMemo(() => { @@ -51,6 +57,27 @@ export function TokenButton({ }) }, [selectedToken, networks.sourceChain.id, nativeCurrency.symbol, options]) + const tokenLogoSrc = useMemo(() => { + if (typeof options?.logoSrc !== 'undefined') { + return options.logoSrc || nativeCurrency.logoUrl + } + + if (selectedToken) { + return ( + tokensFromLists[selectedToken.address]?.logoURI ?? + tokensFromUser[selectedToken.address]?.logoURI + ) + } + + return nativeCurrency.logoUrl + }, [ + nativeCurrency.logoUrl, + options, + selectedToken, + tokensFromLists, + tokensFromUser + ]) + return ( <> @@ -63,18 +90,11 @@ export function TokenButton({ disabled={disabled} >
- {/* Commenting it out until we update the token image source files to be of better quality */} - {/* {tokenLogo && ( - // SafeImage is used for token logo, we don't know at buildtime - where those images will be loaded from // It would throw error - if it's loaded from external domains // eslint-disable-next-line - @next/next/no-img-element - Token logo - )} */} + {tokenSymbol} {!disabled && ( +
?
) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain.tsx index 463768f240..26017b9b19 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain.tsx @@ -3,7 +3,6 @@ import { ArrowsUpDownIcon, ArrowDownIcon } from '@heroicons/react/24/outline' import { twMerge } from 'tailwind-merge' import { utils } from 'ethers' import { Chain, useAccount } from 'wagmi' -import { useMedia } from 'react-use' import { useAppState } from '../../state' import { getExplorerUrl } from '../../util/networks' @@ -138,7 +137,6 @@ function CustomAddressBanner({ export function NetworkContainer({ network, customAddress, - bgLogoHeight = 58, children }: { network: Chain @@ -147,13 +145,7 @@ export function NetworkContainer({ children: React.ReactNode }) { const { address } = useAccount() - const { - color, - network: { logo: networkLogo } - } = getBridgeUiConfigForChain(network.id) - const isSmallScreen = useMedia('(max-width: 639px)') - - const backgroundImage = `url(${networkLogo})` + const { color } = getBridgeUiConfigForChain(network.id) const walletAddressLowercased = address?.toLowerCase() @@ -182,13 +174,7 @@ export function NetworkContainer({ showCustomAddressBanner && 'rounded-t-none' )} > -
+
{children}
@@ -197,26 +183,6 @@ export function NetworkContainer({ ) } -export function BalancesContainer({ children }: { children: React.ReactNode }) { - return ( -
- {children} -
- ) -} - -export function NetworkListboxPlusBalancesContainer({ - children -}: { - children: React.ReactNode -}) { - return ( -
- {children} -
- ) -} - export function TransferPanelMain() { const [networks] = useNetworks() const { childChainProvider, isTeleportMode } = diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/DestinationNetworkBox.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/DestinationNetworkBox.tsx index 87cf770bef..a1fa8c26ca 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/DestinationNetworkBox.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/DestinationNetworkBox.tsx @@ -1,16 +1,11 @@ +import { useMemo } from 'react' import { constants } from 'ethers' +import Image from 'next/image' import { useNetworks } from '../../../hooks/useNetworks' -import { - BalancesContainer, - NetworkContainer, - NetworkListboxPlusBalancesContainer -} from '../TransferPanelMain' -import { TokenBalance } from './TokenBalance' +import { NetworkContainer } from '../TransferPanelMain' import { useNetworksRelationship } from '../../../hooks/useNetworksRelationship' -import { NetworkType } from './utils' import { useAppState } from '../../../state' -import { sanitizeTokenSymbol } from '../../../util/TokenUtils' import { useBalances } from '../../../hooks/useBalances' import { CommonAddress } from '../../../util/CommonAddressUtils' import { isNetwork } from '../../../util/networks' @@ -24,139 +19,185 @@ import { } from '../../common/NetworkSelectionContainer' import { useNativeCurrencyBalances } from './useNativeCurrencyBalances' import { useIsBatchTransferSupported } from '../../../hooks/TransferPanel/useIsBatchTransferSupported' -import { ether } from '../../../constants' +import { getBridgeUiConfigForChain } from '../../../util/bridgeUiConfig' +import { SafeImage } from '../../common/SafeImage' +import { useTokensFromLists, useTokensFromUser } from '../TokenSearchUtils' import { formatAmount } from '../../../util/NumberUtils' import { Loader } from '../../common/atoms/Loader' import { useAmount2InputVisibility } from './SourceNetworkBox' -import { useIsCctpTransfer } from '../hooks/useIsCctpTransfer' import { useArbQueryParams } from '../../../hooks/useArbQueryParams' +import { useIsCctpTransfer } from '../hooks/useIsCctpTransfer' -function NativeCurrencyDestinationBalance({ prefix }: { prefix?: string }) { - const nativeCurrencyBalances = useNativeCurrencyBalances() +function BalanceRow({ + parentErc20Address, + balance, + symbolOverride +}: { + parentErc20Address?: string + balance: string | undefined + symbolOverride?: string +}) { const [networks] = useNetworks() - const nativeCurrency = useNativeCurrency({ - provider: networks.destinationChainProvider - }) - const { isDepositMode } = useNetworksRelationship(networks) - - if (nativeCurrency.isCustom) { - return ( - - ) - } - if (!nativeCurrencyBalances.destinationBalance) { - return ( -

- {prefix} - -

- ) - } + const { childChainProvider, isDepositMode } = + useNetworksRelationship(networks) + const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) + + const tokensFromLists = useTokensFromLists() + const tokensFromUser = useTokensFromUser() + + const tokenLogoSrc = useMemo(() => { + if (parentErc20Address) { + return ( + tokensFromLists[parentErc20Address]?.logoURI ?? + tokensFromUser[parentErc20Address]?.logoURI + ) + } + + return nativeCurrency.logoUrl + }, [ + nativeCurrency.logoUrl, + parentErc20Address, + tokensFromLists, + tokensFromUser + ]) + + const symbol = useMemo(() => { + if (symbolOverride) { + return symbolOverride + } + + if (parentErc20Address) { + return ( + tokensFromLists[parentErc20Address]?.symbol ?? + tokensFromUser[parentErc20Address]?.symbol + ) + } + + return nativeCurrency.symbol + }, [ + symbolOverride, + nativeCurrency.symbol, + parentErc20Address, + tokensFromLists, + tokensFromUser + ]) return ( -

- {prefix} - - {formatAmount(nativeCurrencyBalances.destinationBalance, { - symbol: ether.symbol - })} - -

+
+
+ + {symbol} +
+
+ Balance: + + {balance ? ( + balance + ) : ( + + )} + +
+
) } -function DestinationNetworkBalance() { +function BalancesContainer() { const { app: { selectedToken } } = useAppState() const [networks] = useNetworks() const { childChain, childChainProvider, isDepositMode } = useNetworksRelationship(networks) + const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) const { isArbitrumOne } = isNetwork(childChain.id) + const isCctpTransfer = useIsCctpTransfer() + + const isBatchTransferSupported = useIsBatchTransferSupported() + const { isAmount2InputVisible } = useAmount2InputVisibility() const { erc20ChildBalances } = useBalances() const nativeCurrencyBalances = useNativeCurrencyBalances() const selectedTokenBalances = useSelectedTokenBalances() - const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) + const selectedTokenDestinationBalance = isDepositMode + ? selectedTokenBalances.childBalance + : selectedTokenBalances.parentBalance - const isCctpTransfer = useIsCctpTransfer() + const selectedTokenOrNativeCurrencyBalance = selectedToken + ? selectedTokenDestinationBalance + : nativeCurrencyBalances.destinationBalance - if (selectedToken) { - return ( - <> - + {isCctpTransfer && isDepositMode && ( + + )} + + {isBatchTransferSupported && isAmount2InputVisible && ( + - {/* In deposit mode, when user selected USDC on mainnet, - the UI shows the Arb One balance of both USDC.e and native USDC */} - {isCctpTransfer && isDepositMode && ( - - )} - - ) - } - - if (nativeCurrency.isCustom) { - return ( - - ) - } - - return + )} +
+ ) } export function DestinationNetworkBox() { const [networks] = useNetworks() const [{ destinationAddress }] = useArbQueryParams() - const isBatchTransferSupported = useIsBatchTransferSupported() const [ destinationNetworkSelectionDialogProps, openDestinationNetworkSelectionDialog ] = useDialog() - const { isAmount2InputVisible } = useAmount2InputVisibility() + const { + network: { logo: networkLogo } + } = getBridgeUiConfigForChain(networks.destinationChain.id) return ( <> @@ -164,18 +205,21 @@ export function DestinationNetworkBox() { network={networks.destinationChain} customAddress={destinationAddress} > - +
- - - {isBatchTransferSupported && isAmount2InputVisible && ( - - )} - - +
+ {`${networks.destinationChain.name} +
+
+ ({ symbol: nativeCurrency.symbol, + logoSrc: null, disabled: true, balance: nativeCurrencyBalances.sourceBalance ? Number( @@ -165,11 +172,21 @@ export function SourceNetworkBox() { return ( <> - - + +
+ +
+ {`${networks.sourceChain.name} +
+
(false) useEffect(() => { + let mounted = true const image = new Image() if (typeof src === 'undefined') { @@ -18,13 +19,17 @@ export function SafeImage(props: SafeImageProps) { } else { const sanitizedImageSrc = sanitizeImageSrc(src) - image.onerror = () => setValidImageSrc(false) - image.onload = () => setValidImageSrc(sanitizedImageSrc) + image.onerror = () => { + if (mounted) setValidImageSrc(false) + } + image.onload = () => { + if (mounted) setValidImageSrc(sanitizedImageSrc) + } image.src = sanitizedImageSrc } - return function cleanup() { - // Abort previous loading + return () => { + mounted = false image.src = '' } }, [src])