From 09dcda769c56febf4e35c73f7b6be99e5d928706 Mon Sep 17 00:00:00 2001 From: spsjvc Date: Fri, 26 Jan 2024 17:45:59 +0100 Subject: [PATCH 01/52] sanitize query params server-side --- .../arb-token-bridge-ui/src/pages/index.tsx | 43 +++++++++++++++++++ .../arb-token-bridge-ui/src/util/networks.ts | 5 +++ 2 files changed, 48 insertions(+) diff --git a/packages/arb-token-bridge-ui/src/pages/index.tsx b/packages/arb-token-bridge-ui/src/pages/index.tsx index 4c448da14f..65d3ea72ca 100644 --- a/packages/arb-token-bridge-ui/src/pages/index.tsx +++ b/packages/arb-token-bridge-ui/src/pages/index.tsx @@ -1,4 +1,5 @@ import React, { useEffect } from 'react' +import { GetServerSidePropsContext, GetServerSidePropsResult } from 'next' import dynamic from 'next/dynamic' import { addCustomChain, addCustomNetwork } from '@arbitrum/sdk' @@ -9,6 +10,11 @@ import { mapCustomChainToNetworkData } from '../util/networks' import { getOrbitChains } from '../util/orbitChainsList' +import { sanitizeQueryParams } from '../hooks/useNetworks' +import { + decodeChainQueryParam, + encodeChainQueryParam +} from '../hooks/useArbQueryParams' const App = dynamic(() => import('../components/App/App'), { ssr: false, @@ -21,6 +27,43 @@ const App = dynamic(() => import('../components/App/App'), { ) }) +function getDestinationWithSanitizedQueryParams(sanitized: { + sourceChainId: number + destinationChainId: number +}) { + const sourceChain = encodeChainQueryParam(sanitized.sourceChainId)! + const destinationChain = encodeChainQueryParam(sanitized.destinationChainId)! + + return `/?sourceChain=${sourceChain}&destinationChain=${destinationChain}` +} + +export function getServerSideProps({ + query +}: GetServerSidePropsContext): GetServerSidePropsResult<{}> { + const sourceChainId = decodeChainQueryParam(query.sourceChain) + const destinationChainId = decodeChainQueryParam(query.destinationChain) + + // sanitize the query params + const sanitized = sanitizeQueryParams({ sourceChainId, destinationChainId }) + + // if the sanitized query params are different from the initial values, redirect to the url with sanitized query params + if ( + sourceChainId !== sanitized.sourceChainId || + destinationChainId !== sanitized.destinationChainId + ) { + return { + redirect: { + permanent: false, + destination: getDestinationWithSanitizedQueryParams(sanitized) + } + } + } + + return { + props: {} + } +} + export default function Index() { useEffect(() => { ;[...getOrbitChains(), ...getCustomChainsFromLocalStorage()].forEach( diff --git a/packages/arb-token-bridge-ui/src/util/networks.ts b/packages/arb-token-bridge-ui/src/util/networks.ts index b8b24ca113..808b733450 100644 --- a/packages/arb-token-bridge-ui/src/util/networks.ts +++ b/packages/arb-token-bridge-ui/src/util/networks.ts @@ -65,6 +65,11 @@ export function getBaseChainIdByChainId({ } export function getCustomChainsFromLocalStorage(): ChainWithRpcUrl[] { + // for server-side rendering + if (typeof window === 'undefined') { + return [] + } + const customChainsFromLocalStorage = localStorage.getItem( customChainLocalStorageKey ) From 0c96f54867bde3d95b823e3623f237ea4dd11fc7 Mon Sep 17 00:00:00 2001 From: spsjvc Date: Fri, 26 Jan 2024 17:49:37 +0100 Subject: [PATCH 02/52] forgot to export --- packages/arb-token-bridge-ui/src/hooks/useArbQueryParams.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useArbQueryParams.tsx b/packages/arb-token-bridge-ui/src/hooks/useArbQueryParams.tsx index e43ccf2e44..46cd5442d3 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useArbQueryParams.tsx +++ b/packages/arb-token-bridge-ui/src/hooks/useArbQueryParams.tsx @@ -108,7 +108,7 @@ export const AmountQueryParam = { } // Parse chainId to ChainQueryParam or ChainId for orbit chain -function encodeChainQueryParam( +export function encodeChainQueryParam( chainId: number | null | undefined ): string | undefined { if (!chainId) { @@ -133,7 +133,7 @@ function isValidNumber(value: number | null | undefined): value is number { // Parse ChainQueryParam/ChainId to ChainId // URL accept both chainId and chainQueryParam (string) -function decodeChainQueryParam( +export function decodeChainQueryParam( value: string | (string | null)[] | null | undefined // ChainId type doesn't include custom orbit chain, we need to add number type ): ChainId | number | undefined { From c64cb4fed51a6578eb0b8d98e59bcfbde9da94d8 Mon Sep 17 00:00:00 2001 From: spsjvc Date: Fri, 26 Jan 2024 17:56:44 +0100 Subject: [PATCH 03/52] fix --- packages/arb-token-bridge-ui/src/pages/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/pages/index.tsx b/packages/arb-token-bridge-ui/src/pages/index.tsx index 65d3ea72ca..6dbf361ab2 100644 --- a/packages/arb-token-bridge-ui/src/pages/index.tsx +++ b/packages/arb-token-bridge-ui/src/pages/index.tsx @@ -39,7 +39,7 @@ function getDestinationWithSanitizedQueryParams(sanitized: { export function getServerSideProps({ query -}: GetServerSidePropsContext): GetServerSidePropsResult<{}> { +}: GetServerSidePropsContext): GetServerSidePropsResult> { const sourceChainId = decodeChainQueryParam(query.sourceChain) const destinationChainId = decodeChainQueryParam(query.destinationChain) From 7ee3ba699cfdc2db5dc7abb5a6ec0e275d669036 Mon Sep 17 00:00:00 2001 From: Christophe Date: Thu, 8 Feb 2024 20:48:38 +0100 Subject: [PATCH 04/52] Fix server-side sanitizing of network query param for orbit chains --- .../arb-token-bridge-ui/src/pages/index.tsx | 26 ++++++++++++------- .../arb-token-bridge-ui/src/util/networks.ts | 4 +-- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/pages/index.tsx b/packages/arb-token-bridge-ui/src/pages/index.tsx index 391aa637d8..5b43fe116f 100644 --- a/packages/arb-token-bridge-ui/src/pages/index.tsx +++ b/packages/arb-token-bridge-ui/src/pages/index.tsx @@ -37,12 +37,27 @@ function getDestinationWithSanitizedQueryParams(sanitized: { return `/?sourceChain=${sourceChain}&destinationChain=${destinationChain}` } +function addOrbitChainsToArbitrumSDK() { + ;[...getOrbitChains(), ...getCustomChainsFromLocalStorage()].forEach( + chain => { + try { + addCustomNetwork({ customL2Network: chain }) + mapCustomChainToNetworkData(chain) + } catch (_) { + // already added + } + } + ) +} + export function getServerSideProps({ query }: GetServerSidePropsContext): GetServerSidePropsResult> { const sourceChainId = decodeChainQueryParam(query.sourceChain) const destinationChainId = decodeChainQueryParam(query.destinationChain) + addOrbitChainsToArbitrumSDK() + // sanitize the query params const sanitized = sanitizeQueryParams({ sourceChainId, destinationChainId }) @@ -66,16 +81,7 @@ export function getServerSideProps({ export default function Index() { useEffect(() => { - ;[...getOrbitChains(), ...getCustomChainsFromLocalStorage()].forEach( - chain => { - try { - addCustomNetwork({ customL2Network: chain }) - mapCustomChainToNetworkData(chain) - } catch (_) { - // already added - } - } - ) + addOrbitChainsToArbitrumSDK() }, []) return diff --git a/packages/arb-token-bridge-ui/src/util/networks.ts b/packages/arb-token-bridge-ui/src/util/networks.ts index 75e3bcf137..cb4fa366e4 100644 --- a/packages/arb-token-bridge-ui/src/util/networks.ts +++ b/packages/arb-token-bridge-ui/src/util/networks.ts @@ -8,7 +8,7 @@ import { networks as arbitrumSdkChains } from '@arbitrum/sdk/dist/lib/dataEntiti import { loadEnvironmentVariableWithFallback } from './index' import { getBridgeUiConfigForChain } from './bridgeUiConfig' -import { orbitMainnets, orbitTestnets } from './orbitChainsList' +import { getOrbitChains, orbitMainnets, orbitTestnets } from './orbitChainsList' export const getChains = () => { const chains = Object.values(arbitrumSdkChains) @@ -438,7 +438,7 @@ function isArbitrumChain(chain: L1Network | L2Network): chain is L2Network { } export function getDestinationChainIds(chainId: ChainId): ChainId[] { - const chains = getChains() + const chains = [...getChains(), ...getOrbitChains()] const arbitrumSdkChain = chains.find(chain => chain.chainID === chainId) if (!arbitrumSdkChain) { From b78af5c7ac9908d76908564d8db1f4bc2aa61d52 Mon Sep 17 00:00:00 2001 From: Christophe Date: Thu, 8 Feb 2024 22:02:41 +0100 Subject: [PATCH 05/52] Omit server sanitization if both chains are missing --- packages/arb-token-bridge-ui/src/pages/index.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/arb-token-bridge-ui/src/pages/index.tsx b/packages/arb-token-bridge-ui/src/pages/index.tsx index 5b43fe116f..bb8afd9995 100644 --- a/packages/arb-token-bridge-ui/src/pages/index.tsx +++ b/packages/arb-token-bridge-ui/src/pages/index.tsx @@ -56,6 +56,13 @@ export function getServerSideProps({ const sourceChainId = decodeChainQueryParam(query.sourceChain) const destinationChainId = decodeChainQueryParam(query.destinationChain) + // If both sourceChain and destinationChain are not present, let the client sync with Metamask + if (!sourceChainId && !destinationChainId) { + return { + props: {} + } + } + addOrbitChainsToArbitrumSDK() // sanitize the query params From 6d347e68b02b186ae7babc7a4cbc19eead038f9b Mon Sep 17 00:00:00 2001 From: Christophe Date: Mon, 12 Feb 2024 17:30:45 +0100 Subject: [PATCH 06/52] Keep previous params --- .../arb-token-bridge-ui/src/pages/index.tsx | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/pages/index.tsx b/packages/arb-token-bridge-ui/src/pages/index.tsx index bb8afd9995..da73b5db64 100644 --- a/packages/arb-token-bridge-ui/src/pages/index.tsx +++ b/packages/arb-token-bridge-ui/src/pages/index.tsx @@ -27,14 +27,28 @@ const App = dynamic(() => import('../components/App/App'), { ) }) -function getDestinationWithSanitizedQueryParams(sanitized: { - sourceChainId: number - destinationChainId: number -}) { +function getDestinationWithSanitizedQueryParams( + sanitized: { + sourceChainId: number + destinationChainId: number + }, + query: GetServerSidePropsContext['query'] +) { const sourceChain = encodeChainQueryParam(sanitized.sourceChainId)! const destinationChain = encodeChainQueryParam(sanitized.destinationChainId)! - return `/?sourceChain=${sourceChain}&destinationChain=${destinationChain}` + const params = new URLSearchParams() + for (const key in query) { + const value = query[key] + if (typeof value === 'string') { + params.set(key, value) + } + } + + params.set('sourceChain', sourceChain) + params.set('destinationChain', destinationChain) + + return `/?${params.toString()}` } function addOrbitChainsToArbitrumSDK() { @@ -76,7 +90,7 @@ export function getServerSideProps({ return { redirect: { permanent: false, - destination: getDestinationWithSanitizedQueryParams(sanitized) + destination: getDestinationWithSanitizedQueryParams(sanitized, query) } } } From eed4192c84af7bfec5a552b177cc5320b8d6c606 Mon Sep 17 00:00:00 2001 From: Christophe Date: Tue, 13 Feb 2024 16:51:45 +0100 Subject: [PATCH 07/52] Properly encode amount param --- .../arb-token-bridge-ui/src/pages/index.tsx | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/pages/index.tsx b/packages/arb-token-bridge-ui/src/pages/index.tsx index da73b5db64..936038d3cd 100644 --- a/packages/arb-token-bridge-ui/src/pages/index.tsx +++ b/packages/arb-token-bridge-ui/src/pages/index.tsx @@ -13,7 +13,8 @@ import { getOrbitChains } from '../util/orbitChainsList' import { sanitizeQueryParams } from '../hooks/useNetworks' import { decodeChainQueryParam, - encodeChainQueryParam + encodeChainQueryParam, + AmountQueryParam } from '../hooks/useArbQueryParams' const App = dynamic(() => import('../components/App/App'), { @@ -34,9 +35,6 @@ function getDestinationWithSanitizedQueryParams( }, query: GetServerSidePropsContext['query'] ) { - const sourceChain = encodeChainQueryParam(sanitized.sourceChainId)! - const destinationChain = encodeChainQueryParam(sanitized.destinationChainId)! - const params = new URLSearchParams() for (const key in query) { const value = query[key] @@ -45,8 +43,19 @@ function getDestinationWithSanitizedQueryParams( } } - params.set('sourceChain', sourceChain) - params.set('destinationChain', destinationChain) + const amount = AmountQueryParam.encode( + Array.isArray(query['amount']) ? '' : query['amount'] + ) + if (amount) { + params.set('amount', AmountQueryParam.encode(amount)) + } else { + params.delete('amount') + } + params.set('sourceChain', encodeChainQueryParam(sanitized.sourceChainId)!) + params.set( + 'destinationChain', + encodeChainQueryParam(sanitized.destinationChainId)! + ) return `/?${params.toString()}` } From fb6e2d7e48a7cf0344a33967467c1922c0476c44 Mon Sep 17 00:00:00 2001 From: Christophe Date: Fri, 8 Mar 2024 13:40:29 +0000 Subject: [PATCH 08/52] simplify getDestinationChainIds --- packages/arb-token-bridge-ui/src/util/networks.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/util/networks.ts b/packages/arb-token-bridge-ui/src/util/networks.ts index 53b8016c54..a11a2e20a6 100644 --- a/packages/arb-token-bridge-ui/src/util/networks.ts +++ b/packages/arb-token-bridge-ui/src/util/networks.ts @@ -8,7 +8,7 @@ import { networks as arbitrumSdkChains } from '@arbitrum/sdk/dist/lib/dataEntiti import { loadEnvironmentVariableWithFallback } from './index' import { getBridgeUiConfigForChain } from './bridgeUiConfig' -import { getOrbitChains, orbitMainnets, orbitTestnets } from './orbitChainsList' +import { orbitMainnets, orbitTestnets } from './orbitChainsList' export const getChains = () => { const chains = Object.values(arbitrumSdkChains) @@ -455,7 +455,7 @@ function isArbitrumChain(chain: L1Network | L2Network): chain is L2Network { } export function getDestinationChainIds(chainId: ChainId): ChainId[] { - const chains = [...getChains(), ...getOrbitChains()] + const chains = getChains() const arbitrumSdkChain = chains.find(chain => chain.chainID === chainId) if (!arbitrumSdkChain) { From 934418ff441ca3dac7ccfd362d4bf6d92b2e5392 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 19 Jul 2024 18:14:31 +0200 Subject: [PATCH 09/52] token-query-params --- .../src/components/App/App.tsx | 13 +- .../TransferPanel/AdvancedSettings.tsx | 7 +- .../CustomFeeTokenApprovalDialog.tsx | 5 +- .../components/TransferPanel/EstimatedGas.tsx | 6 +- .../TransferPanel/NativeCurrencyPrice.tsx | 6 +- .../TransferPanel/OneNovaTransferDialog.tsx | 6 +- .../components/TransferPanel/TokenButton.tsx | 3 +- .../TransferPanel/TokenImportDialog.tsx | 10 +- .../components/TransferPanel/TokenSearch.tsx | 67 +------- .../TransferPanel/TransferDisabledDialog.tsx | 8 +- .../TransferPanel/TransferPanel.tsx | 42 ++++- .../TransferPanel/TransferPanelMain.tsx | 15 +- .../TransferPanel/TransferPanelMain/hooks.ts | 74 --------- .../TransferPanel/TransferPanelMainInput.tsx | 6 +- .../TransferPanel/TransferPanelSummary.tsx | 6 +- .../TransferPanel/TransferPanelUtils.ts | 22 ++- .../USDCDepositConfirmationDialog.tsx | 6 +- .../WithdrawalConfirmationDialog.tsx | 6 +- .../TransferPanel/useTransferReadiness.ts | 6 +- .../src/components/syncers/BalanceUpdater.tsx | 4 +- .../src/hooks/TransferPanel/useGasSummary.ts | 22 ++- .../TransferPanel/useSelectedTokenBalances.ts | 5 +- .../TransferPanel/useSelectedTokenDecimals.ts | 8 +- .../src/hooks/useSelectedToken.ts | 156 ++++++++++++++++++ .../src/state/app/actions.ts | 24 +-- .../src/state/app/state.ts | 2 - 26 files changed, 276 insertions(+), 259 deletions(-) delete mode 100644 packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/hooks.ts create mode 100644 packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts diff --git a/packages/arb-token-bridge-ui/src/components/App/App.tsx b/packages/arb-token-bridge-ui/src/components/App/App.tsx index 9d7a24bd96..a3dd0b1d17 100644 --- a/packages/arb-token-bridge-ui/src/components/App/App.tsx +++ b/packages/arb-token-bridge-ui/src/components/App/App.tsx @@ -19,7 +19,7 @@ import { TokenBridgeParams } from '../../hooks/useArbTokenBridge' import { WelcomeDialog } from './WelcomeDialog' import { BlockedDialog } from './BlockedDialog' import { AppContextProvider } from './AppContext' -import { config, useActions, useAppState } from '../../state' +import { config, useActions } from '../../state' import { MainContent } from '../MainContent/MainContent' import { ArbTokenBridgeStoreSync } from '../syncers/ArbTokenBridgeStoreSync' import { BalanceUpdater } from '../syncers/BalanceUpdater' @@ -42,6 +42,7 @@ import { HeaderConnectWalletButton } from '../common/HeaderConnectWalletButton' import { ProviderName, trackEvent } from '../../util/AnalyticsUtils' import { onDisconnectHandler } from '../../util/walletConnectUtils' import { addressIsSmartContract } from '../../util/AddressUtils' +import { useSelectedToken } from '../../hooks/useSelectedToken' declare global { interface Window { @@ -60,9 +61,7 @@ const rainbowkitTheme = merge(darkTheme(), { const ArbTokenBridgeStoreSyncWrapper = (): JSX.Element | null => { const actions = useActions() - const { - app: { selectedToken } - } = useAppState() + const { selectedToken, setSelectedToken } = useSelectedToken() const [networks] = useNetworks() const { childChain, childChainProvider, parentChain, parentChainProvider } = useNetworksRelationship(networks) @@ -90,9 +89,9 @@ const ArbTokenBridgeStoreSyncWrapper = (): JSX.Element | null => { selectedTokenAddress === nativeCurrency.address || selectedTokenL2Address === nativeCurrency.address ) { - actions.app.setSelectedToken(null) + setSelectedToken(null) } - }, [selectedToken, nativeCurrency]) + }, [selectedToken, nativeCurrency, setSelectedToken]) // Listen for account and network changes useEffect(() => { @@ -108,7 +107,7 @@ const ArbTokenBridgeStoreSyncWrapper = (): JSX.Element | null => { parentChain.id ).isEthereumMainnetOrTestnet - actions.app.reset(networks.sourceChain.id) + actions.app.reset() actions.app.setChainIds({ l1NetworkChainId: parentChain.id, l2NetworkChainId: childChain.id diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/AdvancedSettings.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/AdvancedSettings.tsx index b9ae27fb08..fd47b215ad 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/AdvancedSettings.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/AdvancedSettings.tsx @@ -3,14 +3,12 @@ import { twMerge } from 'tailwind-merge' import { useAccount } from 'wagmi' import { create } from 'zustand' import { isAddress } from 'ethers/lib/utils' -import { Provider } from '@ethersproject/providers' import { ArrowDownTrayIcon, ChevronDownIcon } from '@heroicons/react/24/outline' import { LockClosedIcon, LockOpenIcon } from '@heroicons/react/24/solid' import { getExplorerUrl } from '../../util/networks' import { ExternalLink } from '../common/ExternalLink' -import { useAppState } from '../../state' import { useAccountType } from '../../hooks/useAccountType' import { addressIsSmartContract, @@ -19,6 +17,7 @@ import { import { useNetworks } from '../../hooks/useNetworks' import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' import { Transition } from '../common/Transition' +import { useSelectedToken } from '../../hooks/useSelectedToken' export enum DestinationAddressErrors { INVALID_ADDRESS = 'The destination address is not a valid address.', @@ -113,9 +112,7 @@ async function getDestinationAddressWarning({ } export const AdvancedSettings = () => { - const { - app: { selectedToken } - } = useAppState() + const { selectedToken } = useSelectedToken() const [networks] = useNetworks() const { childChain, diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/CustomFeeTokenApprovalDialog.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/CustomFeeTokenApprovalDialog.tsx index dd3332e9f5..b1ef1d4c68 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/CustomFeeTokenApprovalDialog.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/CustomFeeTokenApprovalDialog.tsx @@ -10,12 +10,12 @@ import { formatAmount, formatUSD } from '../../util/NumberUtils' import { getExplorerUrl, isNetwork } from '../../util/networks' import { useGasPrice } from '../../hooks/useGasPrice' import { NativeCurrencyErc20 } from '../../hooks/useNativeCurrency' -import { useAppState } from '../../state' import { useNetworks } from '../../hooks/useNetworks' import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' import { shortenAddress } from '../../util/CommonUtils' import { NoteBox } from '../common/NoteBox' import { BridgeTransferStarterFactory } from '@/token-bridge-sdk/BridgeTransferStarterFactory' +import { useSelectedToken } from '../../hooks/useSelectedToken' export type CustomFeeTokenApprovalDialogProps = UseDialogProps & { customFeeToken: NativeCurrencyErc20 @@ -27,8 +27,7 @@ export function CustomFeeTokenApprovalDialog( const { customFeeToken, isOpen } = props const { ethToUSD } = useETHPrice() - const { app } = useAppState() - const { selectedToken } = app + const { selectedToken } = useSelectedToken() const [networks] = useNetworks() const { sourceChain, destinationChain } = networks diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/EstimatedGas.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/EstimatedGas.tsx index be0675afcc..feba3ee1cc 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/EstimatedGas.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/EstimatedGas.tsx @@ -2,7 +2,6 @@ import { InformationCircleIcon } from '@heroicons/react/24/outline' import { useMemo } from 'react' import { twMerge } from 'tailwind-merge' -import { useAppState } from '../../state' import { ChainId, getNetworkName, isNetwork } from '../../util/networks' import { Tooltip } from '../common/Tooltip' import { formatAmount } from '../../util/NumberUtils' @@ -13,6 +12,7 @@ import { useNetworks } from '../../hooks/useNetworks' import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' import { NativeCurrencyPrice, useIsBridgingEth } from './NativeCurrencyPrice' import { isTokenNativeUSDC } from '../../util/TokenUtils' +import { useSelectedToken } from '../../hooks/useSelectedToken' function getGasFeeTooltip(chainId: ChainId) { const { isEthereumMainnetOrTestnet } = isNetwork(chainId) @@ -53,9 +53,7 @@ export function EstimatedGas({ }: { chainType: 'source' | 'destination' }) { - const { - app: { selectedToken } - } = useAppState() + const { selectedToken } = useSelectedToken() const [networks] = useNetworks() const { childChain, diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/NativeCurrencyPrice.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/NativeCurrencyPrice.tsx index c1460b5948..c247646ec3 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/NativeCurrencyPrice.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/NativeCurrencyPrice.tsx @@ -1,15 +1,13 @@ import { NativeCurrency } from '../../hooks/useNativeCurrency' import { useNetworks } from '../../hooks/useNetworks' import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' -import { useAppState } from '../../state' import { isNetwork } from '../../util/networks' import { useETHPrice } from '../../hooks/useETHPrice' import { formatUSD } from '../../util/NumberUtils' +import { useSelectedToken } from '../../hooks/useSelectedToken' export function useIsBridgingEth(childChainNativeCurrency: NativeCurrency) { - const { - app: { selectedToken } - } = useAppState() + const { selectedToken } = useSelectedToken() const isBridgingEth = selectedToken === null && !childChainNativeCurrency.isCustom return isBridgingEth diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/OneNovaTransferDialog.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/OneNovaTransferDialog.tsx index ceaf080e43..d798d7afff 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/OneNovaTransferDialog.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/OneNovaTransferDialog.tsx @@ -1,7 +1,6 @@ import { Tab } from '@headlessui/react' import Hop from '@/images/bridge/hop.png' -import { useAppState } from '../../state' import { TabButton } from '../common/Tab' import { BridgesTable } from '../common/BridgesTable' import { SecurityNotGuaranteed } from './SecurityLabels' @@ -9,6 +8,7 @@ import { Dialog, UseDialogProps } from '../common/Dialog' import { FastBridgeInfo, FastBridgeNames } from '../../util/fastBridges' import { ChainId, getNetworkName } from '../../util/networks' import { ether } from '../../constants' +import { useSelectedToken } from '../../hooks/useSelectedToken' export function OneNovaTransferDialog( props: UseDialogProps & { @@ -16,9 +16,7 @@ export function OneNovaTransferDialog( amount: string } ) { - const { - app: { selectedToken } - } = useAppState() + const { selectedToken } = useSelectedToken() const { destinationChainId } = props 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 1d3d4065e1..72b8c79aca 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenButton.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenButton.tsx @@ -16,15 +16,16 @@ import { import { useNetworks } from '../../hooks/useNetworks' import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' import { Transition } from '../common/Transition' +import { useSelectedToken } from '../../hooks/useSelectedToken' export function TokenButton(): JSX.Element { const { app: { - selectedToken, arbTokenBridge: { bridgeTokens }, arbTokenBridgeLoaded } } = useAppState() + const { selectedToken } = useSelectedToken() const [networks] = useNetworks() const { childChainProvider } = useNetworksRelationship(networks) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx index 2806fdaf07..1a913823bd 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx @@ -23,6 +23,7 @@ import { useTransferDisabledDialogStore } from './TransferDisabledDialog' import { TokenInfo } from './TokenInfo' import { NoteBox } from '../common/NoteBox' import { isTeleportEnabledToken } from '../../util/TokenTeleportEnabledUtils' +import { useSelectedToken } from '../../hooks/useSelectedToken' enum ImportStatus { LOADING, @@ -67,10 +68,10 @@ export function TokenImportDialog({ const { address: walletAddress } = useAccount() const { app: { - arbTokenBridge: { bridgeTokens, token }, - selectedToken + arbTokenBridge: { bridgeTokens, token } } } = useAppState() + const { selectedToken, setSelectedToken } = useSelectedToken() const [networks] = useNetworks() const { childChain, @@ -179,9 +180,9 @@ export function TokenImportDialog({ const selectToken = useCallback( async (_token: ERC20BridgeToken) => { await token.updateTokenData(_token.address) - actions.app.setSelectedToken(_token) + setSelectedToken(_token.address) }, - [token, actions] + [token, setSelectedToken] ) useEffect(() => { @@ -260,7 +261,6 @@ export function TokenImportDialog({ l1Address, onClose, selectToken, - selectedToken, tokensFromUser ]) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx index a1ffe4a0fe..9fade18c6a 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx @@ -5,7 +5,7 @@ import { useAccount } from 'wagmi' import { AutoSizer, List, ListRowProps } from 'react-virtualized' import { twMerge } from 'tailwind-merge' -import { useActions, useAppState } from '../../state' +import { useAppState } from '../../state' import { BRIDGE_TOKEN_LISTS, BridgeTokenList, @@ -14,11 +14,9 @@ import { } from '../../util/TokenListUtils' import { fetchErc20Data, - erc20DataToErc20BridgeToken, isTokenArbitrumOneNativeUSDC, isTokenArbitrumSepoliaNativeUSDC, - isTokenArbitrumOneUSDCe, - getL2ERC20Address + isTokenArbitrumOneUSDCe } from '../../util/TokenUtils' import { Button } from '../common/Button' import { useTokensFromLists, useTokensFromUser } from './TokenSearchUtils' @@ -29,8 +27,6 @@ import { warningToast } from '../common/atoms/Toast' import { CommonAddress } from '../../util/CommonAddressUtils' import { ArbOneNativeUSDC } from '../../util/L2NativeUtils' import { getNetworkName, isNetwork } from '../../util/networks' -import { useUpdateUSDCBalances } from '../../hooks/CCTP/useUpdateUSDCBalances' -import { useAccountType } from '../../hooks/useAccountType' import { useNativeCurrency } from '../../hooks/useNativeCurrency' import { SearchPanelTable } from '../common/SearchPanel/SearchPanelTable' import { SearchPanel } from '../common/SearchPanel/SearchPanel' @@ -43,6 +39,7 @@ import { isTransferDisabledToken } from '../../util/TokenTransferDisabledUtils' import { useTokenFromSearchParams } from './TransferPanelUtils' import { Switch } from '../common/atoms/Switch' import { isTeleportEnabledToken } from '../../util/TokenTeleportEnabledUtils' +import { useSelectedToken } from '../../hooks/useSelectedToken' export const ARB_ONE_NATIVE_USDC_TOKEN = { ...ArbOneNativeUSDC, @@ -372,9 +369,11 @@ function TokensPanel({ isDepositMode, isArbitrumOne, isArbitrumSepolia, + isParentChainArbitrumOne, + isParentChainArbitrumSepolia, isOrbitChain, - getBalance, - nativeCurrency + nativeCurrency, + getBalance ]) const storeNewToken = async () => { @@ -526,20 +525,15 @@ export function TokenSearch({ arbTokenBridge: { token, bridgeTokens } } } = useAppState() - const { - app: { setSelectedToken } - } = useActions() + const { setSelectedToken } = useSelectedToken() const [networks] = useNetworks() const { childChain, - childChainProvider, parentChain, parentChainProvider, isDepositMode, isTeleportMode } = useNetworksRelationship(networks) - const { updateUSDCBalances } = useUpdateUSDCBalances({ walletAddress }) - const { isLoading: isLoadingAccountType } = useAccountType() const { openDialog: openTransferDisabledDialog } = useTransferDisabledDialogStore() const { setTokenQueryParam } = useTokenFromSearchParams() @@ -563,46 +557,6 @@ export function TokenSearch({ } try { - // Native USDC on L2 won't have a corresponding L1 address - const isNativeUSDC = - isTokenArbitrumOneNativeUSDC(_token.address) || - isTokenArbitrumSepoliaNativeUSDC(_token.address) - - if (isNativeUSDC) { - if (isLoadingAccountType) { - return - } - - await updateUSDCBalances() - - // if an Orbit chain is selected we need to fetch its USDC address - let childChainUsdcAddress - try { - childChainUsdcAddress = isNetwork(childChain.id).isOrbitChain - ? ( - await getL2ERC20Address({ - erc20L1Address: _token.address, - l1Provider: parentChainProvider, - l2Provider: childChainProvider - }) - ).toLowerCase() - : undefined - } catch { - // could be never bridged before - } - - setSelectedToken({ - name: 'USD Coin', - type: TokenType.ERC20, - symbol: 'USDC', - address: _token.address, - l2Address: childChainUsdcAddress, - decimals: 6, - listIds: new Set() - }) - return - } - // Token not added to the bridge, so we'll handle importing it if (typeof bridgeTokens[_token.address] === 'undefined') { setTokenQueryParam(_token.address) @@ -620,10 +574,7 @@ export function TokenSearch({ if (data) { token.updateTokenData(_token.address) - setSelectedToken({ - ...erc20DataToErc20BridgeToken(data), - l2Address: _token.l2Address - }) + setSelectedToken(_token.address) } // do not allow import of withdraw-only tokens at deposit mode diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferDisabledDialog.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferDisabledDialog.tsx index 672b666ba8..6c6971ec9f 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferDisabledDialog.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferDisabledDialog.tsx @@ -1,7 +1,6 @@ import { create } from 'zustand' import { useEffect, useState } from 'react' -import { useActions, useAppState } from '../../state' import { Dialog } from '../common/Dialog' import { sanitizeTokenSymbol } from '../../util/TokenUtils' import { useNetworks } from '../../hooks/useNetworks' @@ -10,6 +9,7 @@ import { ChainId, getNetworkName } from '../../util/networks' import { getL2ConfigForTeleport } from '../../token-bridge-sdk/teleport' import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' import { withdrawOnlyTokens } from '../../util/WithdrawOnlyUtils' +import { useSelectedToken } from '../../hooks/useSelectedToken' type TransferDisabledDialogStore = { isOpen: boolean @@ -27,11 +27,7 @@ export const useTransferDisabledDialogStore = export function TransferDisabledDialog() { const [networks] = useNetworks() const { isTeleportMode } = useNetworksRelationship(networks) - const { app } = useAppState() - const { selectedToken } = app - const { - app: { setSelectedToken } - } = useActions() + const { selectedToken, setSelectedToken } = useSelectedToken() const { isOpen: isOpenTransferDisabledDialog, closeDialog: closeTransferDisabledDialog diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx index d466ee9e0b..e8e55e3ca9 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx @@ -1,5 +1,5 @@ import dayjs from 'dayjs' -import { useState, useMemo } from 'react' +import { useState, useMemo, useEffect } from 'react' import Tippy from '@tippyjs/react' import { constants, utils } from 'ethers' import { useLatest } from 'react-use' @@ -75,6 +75,8 @@ import { useBalance } from '../../hooks/useBalance' import { getBridgeTransferProperties } from '../../token-bridge-sdk/utils' import { useSetInputAmount } from '../../hooks/TransferPanel/useSetInputAmount' import { getSmartContractWalletTeleportTransfersNotSupportedErrorMessage } from './useTransferReadinessUtils' +import { useTokensFromLists, useTokensFromUser } from './TokenSearchUtils' +import { useSelectedToken } from '../../hooks/useSelectedToken' const networkConnectionWarningToast = () => warningToast( @@ -98,16 +100,19 @@ export function TransferPanel() { useState(ImportTokenModalStatus.IDLE) const [showSmartContractWalletTooltip, setShowSmartContractWalletTooltip] = useState(false) + const [importDialogTokenAddress, setImportDialogTokenAddress] = useState< + string | undefined + >() const { app: { connectionState, - selectedToken, arbTokenBridgeLoaded, arbTokenBridge: { eth, token }, warningTokens } } = useAppState() + const { selectedToken } = useSelectedToken() const { layout } = useAppContextState() const { isTransferring } = layout const { address: walletAddress, isConnected } = useAccount() @@ -125,6 +130,8 @@ export function TransferPanel() { isTeleportMode } = useNetworksRelationship(networks) const latestNetworks = useLatest(networks) + const tokensFromLists = useTokensFromLists() + const tokensFromUser = useTokensFromUser() const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) @@ -210,7 +217,7 @@ export function TransferPanel() { ) function closeWithResetTokenImportDialog() { - setTokenQueryParam(undefined) + setTokenQueryParam(null) setImportTokenModalStatus(ImportTokenModalStatus.CLOSED) tokenImportDialogProps.onClose(false) } @@ -225,6 +232,31 @@ export function TransferPanel() { connectionState }) + const isTokenAlreadyImported = useMemo(() => { + if ( + !tokensFromLists || + !tokensFromUser || + Object.keys(tokensFromLists).length === 0 + ) { + return undefined + } + + return Object.keys({ ...tokensFromLists, ...tokensFromUser }).some( + address => address.toLowerCase() === tokenFromSearchParams?.toLowerCase() + ) + }, [tokenFromSearchParams, tokensFromLists, tokensFromUser]) + + useEffect(() => { + if ( + typeof isTokenAlreadyImported === 'undefined' || + isTokenAlreadyImported + ) { + setImportDialogTokenAddress(undefined) + } else { + setImportDialogTokenAddress(tokenFromSearchParams) + } + }, [isTokenAlreadyImported, tokenFromSearchParams]) + const isBridgingANewStandardToken = useMemo(() => { const isUnbridgedToken = selectedToken !== null && typeof selectedToken.l2Address === 'undefined' @@ -1093,11 +1125,11 @@ export function TransferPanel() { )} - {typeof tokenFromSearchParams !== 'undefined' && ( + {typeof importDialogTokenAddress !== 'undefined' && ( )} 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 3e94dd9d15..e43d93407d 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain.tsx @@ -6,7 +6,6 @@ import { Chain, useAccount } from 'wagmi' import { useMedia } from 'react-use' import { Loader } from '../common/atoms/Loader' -import { useActions, useAppState } from '../../state' import { formatAmount } from '../../util/NumberUtils' import { ChainId, @@ -61,12 +60,12 @@ import { } from './TransferDisabledDialog' import { getBridgeUiConfigForChain } from '../../util/bridgeUiConfig' import { useGasSummary } from '../../hooks/TransferPanel/useGasSummary' -import { useUpdateUSDCTokenData } from './TransferPanelMain/hooks' import { Balances, useSelectedTokenBalances } from '../../hooks/TransferPanel/useSelectedTokenBalances' import { useSetInputAmount } from '../../hooks/TransferPanel/useSetInputAmount' +import { useSelectedToken } from '../../hooks/useSelectedToken' enum NetworkType { l1 = 'l1', @@ -334,7 +333,6 @@ export function TransferPanelMain({ amount: string errorMessage?: TransferReadinessRichErrorMessage | string }) { - const actions = useActions() const [networks, setNetworks] = useNetworks() const { childChain, @@ -349,10 +347,7 @@ export function TransferPanelMain({ useAccountType() const { isArbitrumOne, isArbitrumSepolia } = isNetwork(childChain.id) const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) - - const { - app: { selectedToken } - } = useAppState() + const { selectedToken, setSelectedToken } = useSelectedToken() const { address: walletAddress } = useAccount() @@ -579,8 +574,6 @@ export function TransferPanelMain({ } }, [errorMessage, openTransferDisabledDialog]) - useUpdateUSDCTokenData() - type NetworkListboxesProps = { from: Pick to: Omit @@ -636,7 +629,7 @@ export function TransferPanelMain({ destinationChainId: networks.destinationChain.id }) - actions.app.setSelectedToken(null) + setSelectedToken(null) } }, to: { @@ -657,7 +650,7 @@ export function TransferPanelMain({ sourceChainId: networks.sourceChain.id, destinationChainId: network.id }) - actions.app.setSelectedToken(null) + setSelectedToken(null) } } } diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/hooks.ts b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/hooks.ts deleted file mode 100644 index 4d748b11ca..0000000000 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/hooks.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { useEffect } from 'react' - -import { - isTokenArbitrumOneNativeUSDC, - isTokenArbitrumSepoliaNativeUSDC -} from '../../../util/TokenUtils' -import { useActions, useAppState } from '../../../state' -import { useNetworks } from '../../../hooks/useNetworks' -import { useNetworksRelationship } from '../../../hooks/useNetworksRelationship' -import { TokenType } from '../../../hooks/arbTokenBridge.types' -import { CommonAddress } from '../../../util/CommonAddressUtils' -import { isNetwork } from '../../../util/networks' - -const commonUSDC = { - name: 'USD Coin', - type: TokenType.ERC20, - symbol: 'USDC', - decimals: 6, - listIds: new Set() -} - -export function useUpdateUSDCTokenData() { - const actions = useActions() - const { - app: { - arbTokenBridge: { token }, - selectedToken - } - } = useAppState() - const [networks] = useNetworks() - const { isDepositMode } = useNetworksRelationship(networks) - const { - isArbitrumOne: isDestinationChainArbitrumOne, - isArbitrumSepolia: isDestinationChainArbitrumSepolia - } = isNetwork(networks.destinationChain.id) - - useEffect(() => { - const isArbOneUSDC = isTokenArbitrumOneNativeUSDC(selectedToken?.address) - const isArbSepoliaUSDC = isTokenArbitrumSepoliaNativeUSDC( - selectedToken?.address - ) - - // If user select native USDC on L2, when switching to deposit mode, - // we need to default to set the corresponding USDC on L1 - if (!isDepositMode) { - return - } - - if (isArbOneUSDC && isDestinationChainArbitrumOne) { - token.updateTokenData(CommonAddress.Ethereum.USDC) - actions.app.setSelectedToken({ - ...commonUSDC, - address: CommonAddress.Ethereum.USDC, - l2Address: CommonAddress.ArbitrumOne['USDC.e'] - }) - } - - if (isArbSepoliaUSDC && isDestinationChainArbitrumSepolia) { - token.updateTokenData(CommonAddress.Sepolia.USDC) - actions.app.setSelectedToken({ - ...commonUSDC, - address: CommonAddress.Sepolia.USDC, - l2Address: CommonAddress.ArbitrumSepolia['USDC.e'] - }) - } - }, [ - actions.app, - isDepositMode, - isDestinationChainArbitrumOne, - isDestinationChainArbitrumSepolia, - selectedToken, - token - ]) -} diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMainInput.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMainInput.tsx index bd4d9f14c6..50d3ca6512 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMainInput.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMainInput.tsx @@ -9,10 +9,10 @@ import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' import { useDestinationAddressStore } from './AdvancedSettings' import { useBalance } from '../../hooks/useBalance' import { useSelectedTokenBalances } from '../../hooks/TransferPanel/useSelectedTokenBalances' -import { useAppState } from '../../state' import { useSetInputAmount } from '../../hooks/TransferPanel/useSetInputAmount' import { countDecimals } from '../../util/NumberUtils' import { useSelectedTokenDecimals } from '../../hooks/TransferPanel/useSelectedTokenDecimals' +import { useSelectedToken } from '../../hooks/useSelectedToken' type MaxButtonProps = React.ButtonHTMLAttributes & { loading: boolean @@ -21,9 +21,7 @@ type MaxButtonProps = React.ButtonHTMLAttributes & { function MaxButton(props: MaxButtonProps) { const { loading, className = '', ...rest } = props - const { - app: { selectedToken } - } = useAppState() + const { selectedToken } = useSelectedToken() const { address: walletAddress } = useAccount() const [networks] = useNetworks() const { childChainProvider, parentChainProvider, isDepositMode } = diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelSummary.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelSummary.tsx index 17f950eb33..b6c1fb0f34 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelSummary.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelSummary.tsx @@ -11,11 +11,11 @@ import { ERC20BridgeToken } from '../../hooks/arbTokenBridge.types' import { useNetworks } from '../../hooks/useNetworks' import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' import { NativeCurrencyPrice, useIsBridgingEth } from './NativeCurrencyPrice' -import { useAppState } from '../../state' import { Loader } from '../common/atoms/Loader' import { isTokenNativeUSDC } from '../../util/TokenUtils' import { NoteBox } from '../common/NoteBox' import { DISABLED_CHAIN_IDS } from './useTransferReadiness' +import { useSelectedToken } from '../../hooks/useSelectedToken' export type TransferPanelSummaryToken = { symbol: string @@ -37,9 +37,7 @@ function StyledLoader() { } function TotalGasFees() { - const { - app: { selectedToken } - } = useAppState() + const { selectedToken } = useSelectedToken() const { status: gasSummaryStatus, diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelUtils.ts b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelUtils.ts index abe451bb6d..f643620253 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelUtils.ts +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelUtils.ts @@ -1,6 +1,4 @@ -import * as Sentry from '@sentry/react' - -import { isUserRejectedError } from '../../util/isUserRejectedError' +import { utils } from 'ethers' import { useArbQueryParams } from '../../hooks/useArbQueryParams' export enum ImportTokenModalStatus { @@ -21,14 +19,26 @@ export function getWarningTokenDescription(warningTokenType: number) { } } +function sanitizeTokenSearchParams( + tokenAddress: string | null +): string | undefined { + if (!tokenAddress) { + return undefined + } + if (utils.isAddress(tokenAddress)) { + return tokenAddress + } + return undefined +} + export function useTokenFromSearchParams(): { tokenFromSearchParams: string | undefined - setTokenQueryParam: (token: string | undefined) => void + setTokenQueryParam: (token: string | null) => void } { const [{ token: tokenFromSearchParams }, setQueryParams] = useArbQueryParams() - const setTokenQueryParam = (token: string | undefined) => - setQueryParams({ token }) + const setTokenQueryParam = (token: string | null) => + setQueryParams({ token: sanitizeTokenSearchParams(token) }) if (!tokenFromSearchParams) { return { diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/USDCDeposit/USDCDepositConfirmationDialog.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/USDCDeposit/USDCDepositConfirmationDialog.tsx index a49a89ad0c..0124dd9042 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/USDCDeposit/USDCDepositConfirmationDialog.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/USDCDeposit/USDCDepositConfirmationDialog.tsx @@ -10,7 +10,6 @@ import { } from '../../../util/fastBridges' import { TabButton } from '../../common/Tab' import { BridgesTable } from '../../common/BridgesTable' -import { useAppState } from '../../../state' import { getExplorerUrl, getNetworkName, @@ -29,6 +28,7 @@ import { useNetworks } from '../../../hooks/useNetworks' import { useNetworksRelationship } from '../../../hooks/useNetworksRelationship' import { SecurityGuaranteed, SecurityNotGuaranteed } from '../SecurityLabels' import { getUSDCAddresses } from '../../../state/cctpState' +import { useSelectedToken } from '../../../hooks/useSelectedToken' type Props = UseDialogProps & { amount: string @@ -43,9 +43,7 @@ enum SelectedTabName { const defaultSelectedTabName: SelectedTabName = SelectedTabName.Cctp export function USDCDepositConfirmationDialog(props: Props) { - const { - app: { selectedToken } - } = useAppState() + const { selectedToken } = useSelectedToken() const [networks] = useNetworks() const { childChain, parentChain } = useNetworksRelationship(networks) const { isArbitrumSepolia } = isNetwork(childChain.id) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/WithdrawalConfirmationDialog.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/WithdrawalConfirmationDialog.tsx index b8167f25df..f37ff0ba70 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/WithdrawalConfirmationDialog.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/WithdrawalConfirmationDialog.tsx @@ -8,7 +8,6 @@ import { Checkbox } from '../common/Checkbox' import { ExternalLink } from '../common/ExternalLink' import { TabButton } from '../common/Tab' import { BridgesTable } from '../common/BridgesTable' -import { useAppState } from '../../state' import { trackEvent } from '../../util/AnalyticsUtils' import { getNetworkName, isNetwork } from '../../util/networks' import { getFastBridges } from '../../util/fastBridges' @@ -18,6 +17,7 @@ import { useNetworks } from '../../hooks/useNetworks' import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' import { getTxConfirmationDate } from '../common/WithdrawalCountdown' import { SecurityGuaranteed, SecurityNotGuaranteed } from './SecurityLabels' +import { useSelectedToken } from '../../hooks/useSelectedToken' function getCalendarUrl( withdrawalDate: dayjs.Dayjs, @@ -46,9 +46,7 @@ export function WithdrawalConfirmationDialog( const destinationNetworkName = getNetworkName(parentChain.id) - const { - app: { selectedToken } - } = useAppState() + const { selectedToken } = useSelectedToken() const nativeCurrency = useNativeCurrency({ provider: childChainProvider diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/useTransferReadiness.ts b/packages/arb-token-bridge-ui/src/components/TransferPanel/useTransferReadiness.ts index 7600ef47aa..a7dca6080f 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/useTransferReadiness.ts +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/useTransferReadiness.ts @@ -3,7 +3,6 @@ import { useAccount } from 'wagmi' import { utils } from 'ethers' import { useAccountType } from '../../hooks/useAccountType' -import { useAppState } from '../../state' import { useBalance } from '../../hooks/useBalance' import { useNativeCurrency } from '../../hooks/useNativeCurrency' import { @@ -27,6 +26,7 @@ import { useNetworks } from '../../hooks/useNetworks' import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' import { isTeleportEnabledToken } from '../../util/TokenTeleportEnabledUtils' import { isNetwork } from '../../util/networks' +import { useSelectedToken } from '../../hooks/useSelectedToken' // Add chains IDs that are currently down or disabled // It will block transfers and display an info box in the transfer panel @@ -116,9 +116,7 @@ export function useTransferReadiness({ amount: string gasSummary: UseGasSummaryResult }): UseTransferReadinessResult { - const { - app: { selectedToken } - } = useAppState() + const { selectedToken } = useSelectedToken() const { layout: { isTransferring } } = useAppContextState() diff --git a/packages/arb-token-bridge-ui/src/components/syncers/BalanceUpdater.tsx b/packages/arb-token-bridge-ui/src/components/syncers/BalanceUpdater.tsx index 6a35e1cea2..c35bb00a45 100644 --- a/packages/arb-token-bridge-ui/src/components/syncers/BalanceUpdater.tsx +++ b/packages/arb-token-bridge-ui/src/components/syncers/BalanceUpdater.tsx @@ -4,12 +4,14 @@ import { useAccount } from 'wagmi' import { useAppState } from '../../state' import { useUpdateUSDCBalances } from '../../hooks/CCTP/useUpdateUSDCBalances' +import { useSelectedToken } from '../../hooks/useSelectedToken' // Updates all balances periodically const BalanceUpdater = (): JSX.Element => { const { - app: { arbTokenBridge, selectedToken } + app: { arbTokenBridge } } = useAppState() + const { selectedToken } = useSelectedToken() const { address: walletAddress } = useAccount() const latestTokenBridge = useLatest(arbTokenBridge) diff --git a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useGasSummary.ts b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useGasSummary.ts index 0baf591ff6..97e4f29a30 100644 --- a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useGasSummary.ts +++ b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useGasSummary.ts @@ -3,7 +3,6 @@ import { useAccount } from 'wagmi' import { useCallback, useEffect, useMemo, useState } from 'react' import { useDebounce } from '@uidotdev/usehooks' -import { useAppState } from '../../state' import { useGasPrice } from '../useGasPrice' import { isTokenArbitrumSepoliaNativeUSDC, @@ -19,6 +18,7 @@ import { truncateExtraDecimals } from '../../util/NumberUtils' import { useSelectedTokenDecimals } from './useSelectedTokenDecimals' import { percentIncrease } from '@/token-bridge-sdk/utils' import { DEFAULT_GAS_PRICE_PERCENT_INCREASE } from '@/token-bridge-sdk/Erc20DepositStarter' +import { useSelectedToken } from '../useSelectedToken' const INITIAL_GAS_SUMMARY_RESULT: UseGasSummaryResult = { status: 'loading', @@ -40,9 +40,7 @@ export type UseGasSummaryResult = { } export function useGasSummary(): UseGasSummaryResult { - const { - app: { selectedToken: token } - } = useAppState() + const { selectedToken } = useSelectedToken() const [networks] = useNetworks() const { childChainProvider, parentChainProvider, isDepositMode } = useNetworksRelationship(networks) @@ -78,7 +76,7 @@ export function useGasSummary(): UseGasSummaryResult { [] ) - const balance = useBalanceOnSourceChain(token) + const balance = useBalanceOnSourceChain(selectedToken) const { gasEstimates: estimateGasResult, error: gasEstimatesError } = useGasEstimates({ @@ -87,11 +85,11 @@ export function useGasSummary(): UseGasSummaryResult { destinationChainId: networks.destinationChain.id, amount: amountBigNumber, sourceChainErc20Address: isDepositMode - ? token?.address - : token?.l2Address, + ? selectedToken?.address + : selectedToken?.l2Address, destinationChainErc20Address: isDepositMode - ? token?.l2Address - : token?.address, + ? selectedToken?.l2Address + : selectedToken?.address, sourceChainBalance: balance }) @@ -140,8 +138,8 @@ export function useGasSummary(): UseGasSummaryResult { useEffect(() => { if ( !isDepositMode && - (isTokenArbitrumOneNativeUSDC(token?.address) || - isTokenArbitrumSepoliaNativeUSDC(token?.address)) + (isTokenArbitrumOneNativeUSDC(selectedToken?.address) || + isTokenArbitrumSepoliaNativeUSDC(selectedToken?.address)) ) { setGasSummaryStatus('unavailable') return @@ -179,7 +177,7 @@ export function useGasSummary(): UseGasSummaryResult { }, [ walletAddress, balance, - token, + selectedToken, childChainProvider, setGasSummaryStatus, isDepositMode, diff --git a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenBalances.ts b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenBalances.ts index 1f13778e27..7ac239629b 100644 --- a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenBalances.ts +++ b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenBalances.ts @@ -1,6 +1,5 @@ import { BigNumber, constants } from 'ethers' import { useMemo } from 'react' -import { useAppState } from '../../state' import { useNetworks } from '../useNetworks' import { useNetworksRelationship } from '../useNetworksRelationship' import { useDestinationAddressStore } from '../../components/TransferPanel/AdvancedSettings' @@ -12,6 +11,7 @@ import { } from '../../util/TokenUtils' import { CommonAddress } from '../../util/CommonAddressUtils' import { isNetwork } from '../../util/networks' +import { useSelectedToken } from '../useSelectedToken' export type Balances = { l1: BigNumber | null @@ -19,8 +19,7 @@ export type Balances = { } export function useSelectedTokenBalances(): Balances { - const { app } = useAppState() - const { selectedToken } = app + const { selectedToken } = useSelectedToken() const { address: walletAddress } = useAccount() const [networks] = useNetworks() const { childChainProvider, parentChainProvider, isDepositMode } = diff --git a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenDecimals.ts b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenDecimals.ts index 8ea1f78e62..e6a39885c4 100644 --- a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenDecimals.ts +++ b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenDecimals.ts @@ -1,14 +1,10 @@ -import { useMemo } from 'react' - -import { useAppState } from '../../state' import { useNativeCurrency } from '../useNativeCurrency' import { useNetworksRelationship } from '../useNetworksRelationship' import { useNetworks } from '../useNetworks' +import { useSelectedToken } from '../useSelectedToken' export function useSelectedTokenDecimals() { - const { - app: { selectedToken } - } = useAppState() + const { selectedToken } = useSelectedToken() const [networks] = useNetworks() const { childChainProvider } = useNetworksRelationship(networks) diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts new file mode 100644 index 0000000000..10cfc8ba27 --- /dev/null +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -0,0 +1,156 @@ +import { useCallback, useEffect, useMemo, useState } from 'react' + +import { + useTokensFromLists, + useTokensFromUser +} from '../components/TransferPanel/TokenSearchUtils' +import { useTokenFromSearchParams } from '../components/TransferPanel/TransferPanelUtils' +import { ERC20BridgeToken, TokenType } from './arbTokenBridge.types' +import { + getL2ERC20Address, + isTokenArbitrumOneNativeUSDC, + isTokenArbitrumSepoliaNativeUSDC +} from '../util/TokenUtils' +import { useNetworks } from './useNetworks' +import { isNetwork } from '../util/networks' +import { CommonAddress } from '../util/CommonAddressUtils' +import { useAppState } from '../state' +import { useNetworksRelationship } from './useNetworksRelationship' + +type UseSelectedTokenProps = { + selectedToken: ERC20BridgeToken | null + setSelectedToken: (erc20ParentAddress: string | null) => void +} + +const commonUSDC = { + name: 'USD Coin', + type: TokenType.ERC20, + symbol: 'USDC', + decimals: 6, + listIds: new Set() +} + +export const useSelectedToken = (): UseSelectedTokenProps => { + const { + app: { + arbTokenBridge: { token } + } + } = useAppState() + const { tokenFromSearchParams, setTokenQueryParam } = + useTokenFromSearchParams() + const tokensFromLists = useTokensFromLists() + const tokensFromUser = useTokensFromUser() + const [networks] = useNetworks() + const { childChain, childChainProvider, parentChainProvider } = + useNetworksRelationship(networks) + const { + isArbitrumOne: isDestinationChainArbitrumOne, + isArbitrumSepolia: isDestinationChainArbitrumSepolia + } = isNetwork(networks.destinationChain.id) + + const [_selectedToken, _setSelectedToken] = useState( + null + ) + + useEffect(() => { + async function getSelectedToken() { + if (!tokenFromSearchParams) { + _setSelectedToken(null) + return + } + + const isArbitrumOneUsdc = isTokenArbitrumOneNativeUSDC( + tokenFromSearchParams + ) + const isArbitrumSepoliaUsdc = isTokenArbitrumSepoliaNativeUSDC( + tokenFromSearchParams + ) + + if (isArbitrumOneUsdc && isDestinationChainArbitrumOne) { + token.updateTokenData(CommonAddress.Ethereum.USDC) + _setSelectedToken({ + ...commonUSDC, + address: CommonAddress.Ethereum.USDC, + l2Address: CommonAddress.ArbitrumOne['USDC.e'] + }) + return + } + + if (isArbitrumSepoliaUsdc && isDestinationChainArbitrumSepolia) { + token.updateTokenData(CommonAddress.Sepolia.USDC) + _setSelectedToken({ + ...commonUSDC, + address: CommonAddress.Sepolia.USDC, + l2Address: CommonAddress.ArbitrumSepolia['USDC.e'] + }) + return + } + + if (isArbitrumOneUsdc || isArbitrumSepoliaUsdc) { + // USDC with Orbit chains + let childChainUsdcAddress + try { + childChainUsdcAddress = isNetwork(childChain.id).isOrbitChain + ? ( + await getL2ERC20Address({ + erc20L1Address: tokenFromSearchParams, + l1Provider: parentChainProvider, + l2Provider: childChainProvider + }) + ).toLowerCase() + : undefined + } catch { + // could be never bridged before + } + + _setSelectedToken({ + name: 'USD Coin', + type: TokenType.ERC20, + symbol: 'USDC', + address: tokenFromSearchParams, + l2Address: childChainUsdcAddress, + decimals: 6, + listIds: new Set() + }) + } + + if (!tokensFromLists || !tokensFromUser) { + _setSelectedToken(null) + return + } + + _setSelectedToken( + Object.values({ ...tokensFromLists, ...tokensFromUser }).find( + tokenFromLists => + tokenFromLists?.address.toLowerCase() === + tokenFromSearchParams.toLowerCase() + ) || null + ) + } + + getSelectedToken() + }, [ + childChain.id, + childChainProvider, + isDestinationChainArbitrumOne, + isDestinationChainArbitrumSepolia, + parentChainProvider, + _setSelectedToken, + token, + tokenFromSearchParams, + tokensFromLists, + tokensFromUser + ]) + + const setSelectedToken = useCallback( + (erc20ParentAddress: string | null) => { + setTokenQueryParam(erc20ParentAddress) + }, + [setTokenQueryParam] + ) + + return { + selectedToken: _selectedToken, + setSelectedToken + } +} diff --git a/packages/arb-token-bridge-ui/src/state/app/actions.ts b/packages/arb-token-bridge-ui/src/state/app/actions.ts index d24b954a93..0252b3b890 100644 --- a/packages/arb-token-bridge-ui/src/state/app/actions.ts +++ b/packages/arb-token-bridge-ui/src/state/app/actions.ts @@ -1,7 +1,4 @@ -import { - ArbTokenBridge, - ERC20BridgeToken -} from '../../hooks/arbTokenBridge.types' +import { ArbTokenBridge } from '../../hooks/arbTokenBridge.types' import { Context } from '..' import { ConnectionState } from '../../util' import { WhiteListState, WarningTokens } from './state' @@ -21,24 +18,7 @@ export const setChainIds = ( state.app.l2NetworkChainId = payload.l2NetworkChainId } -export const setSelectedToken = ( - { state }: Context, - token: ERC20BridgeToken | null -) => { - state.app.selectedToken = token ? { ...token } : null -} - -export const reset = ({ state }: Context, newChainId: number) => { - if ( - state.app.l1NetworkChainId !== newChainId && - state.app.l2NetworkChainId !== newChainId - ) { - // only reset the selected token if we are not switching between the pair of l1-l2 networks. - // we dont want to reset the token if we are switching from Mainnet to Arbitrum One for example - // because we are maybe in the process of auto switching the network and triggering deposit or withdraw - state.app.selectedToken = null - } - +export const reset = ({ state }: Context) => { state.app.arbTokenBridge = {} as ArbTokenBridge state.app.verifying = WhiteListState.ALLOWED state.app.connectionState = ConnectionState.LOADING diff --git a/packages/arb-token-bridge-ui/src/state/app/state.ts b/packages/arb-token-bridge-ui/src/state/app/state.ts index f025ddffdd..8af640d098 100644 --- a/packages/arb-token-bridge-ui/src/state/app/state.ts +++ b/packages/arb-token-bridge-ui/src/state/app/state.ts @@ -87,7 +87,6 @@ export type AppState = { arbTokenBridge: ArbTokenBridge warningTokens: WarningTokens connectionState: number - selectedToken: ERC20BridgeToken | null verifying: WhiteListState l1NetworkChainId: number | null l2NetworkChainId: number | null @@ -101,7 +100,6 @@ export const defaultState: AppState = { l1NetworkChainId: null, l2NetworkChainId: null, verifying: WhiteListState.ALLOWED, - selectedToken: null, arbTokenBridgeLoaded: false } export const state: AppState = { From 941a8a9c8293fca5fe210d35eae34eaa6532b562 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 19 Jul 2024 21:32:10 +0200 Subject: [PATCH 10/52] fix --- packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index 10cfc8ba27..41db0241a0 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -112,6 +112,7 @@ export const useSelectedToken = (): UseSelectedTokenProps => { decimals: 6, listIds: new Set() }) + return } if (!tokensFromLists || !tokensFromUser) { From 44b8608bf139c5f4f0b60cc65a450412e9819278 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 22 Jul 2024 14:49:08 +0200 Subject: [PATCH 11/52] fixes --- .../src/components/App/App.tsx | 24 ---- .../components/TransferPanel/TokenSearch.tsx | 9 +- .../TransferPanel/TransferPanel.tsx | 8 +- .../TransferPanel/TransferPanelMain.tsx | 3 + .../TransferPanel/TransferPanelMain/hooks.ts | 59 ++++++++++ .../src/hooks/useIsTestnetMode.ts | 5 +- .../src/hooks/useSelectedToken.ts | 108 +++++++++--------- 7 files changed, 134 insertions(+), 82 deletions(-) create mode 100644 packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/hooks.ts diff --git a/packages/arb-token-bridge-ui/src/components/App/App.tsx b/packages/arb-token-bridge-ui/src/components/App/App.tsx index a3dd0b1d17..539f2fdcdf 100644 --- a/packages/arb-token-bridge-ui/src/components/App/App.tsx +++ b/packages/arb-token-bridge-ui/src/components/App/App.tsx @@ -35,14 +35,12 @@ import { TOS_LOCALSTORAGE_KEY } from '../../constants' import { getProps } from '../../util/wagmi/setup' import { useAccountIsBlocked } from '../../hooks/useAccountIsBlocked' import { useCCTPIsBlocked } from '../../hooks/CCTP/useCCTPIsBlocked' -import { useNativeCurrency } from '../../hooks/useNativeCurrency' import { sanitizeQueryParams, useNetworks } from '../../hooks/useNetworks' import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' import { HeaderConnectWalletButton } from '../common/HeaderConnectWalletButton' import { ProviderName, trackEvent } from '../../util/AnalyticsUtils' import { onDisconnectHandler } from '../../util/walletConnectUtils' import { addressIsSmartContract } from '../../util/AddressUtils' -import { useSelectedToken } from '../../hooks/useSelectedToken' declare global { interface Window { @@ -61,11 +59,9 @@ const rainbowkitTheme = merge(darkTheme(), { const ArbTokenBridgeStoreSyncWrapper = (): JSX.Element | null => { const actions = useActions() - const { selectedToken, setSelectedToken } = useSelectedToken() const [networks] = useNetworks() const { childChain, childChainProvider, parentChain, parentChainProvider } = useNetworksRelationship(networks) - const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) // We want to be sure this fetch is completed by the time we open the USDC modals useCCTPIsBlocked() @@ -73,26 +69,6 @@ const ArbTokenBridgeStoreSyncWrapper = (): JSX.Element | null => { const [tokenBridgeParams, setTokenBridgeParams] = useState(null) - useEffect(() => { - if (!nativeCurrency.isCustom) { - return - } - - const selectedTokenAddress = selectedToken?.address.toLowerCase() - const selectedTokenL2Address = selectedToken?.l2Address?.toLowerCase() - // This handles a super weird edge case where, for example: - // - // Your setup is: from Arbitrum One to Mainnet, and you have $ARB selected as the token you want to bridge over. - // You then switch your destination network to a network that has $ARB as its native currency. - // For this network, $ARB can only be bridged as the native currency, and not as a standard ERC-20, which is why we have to reset the selected token. - if ( - selectedTokenAddress === nativeCurrency.address || - selectedTokenL2Address === nativeCurrency.address - ) { - setSelectedToken(null) - } - }, [selectedToken, nativeCurrency, setSelectedToken]) - // Listen for account and network changes useEffect(() => { // Any time one of those changes diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx index 9fade18c6a..f9087cd424 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx @@ -16,7 +16,8 @@ import { fetchErc20Data, isTokenArbitrumOneNativeUSDC, isTokenArbitrumSepoliaNativeUSDC, - isTokenArbitrumOneUSDCe + isTokenArbitrumOneUSDCe, + isTokenNativeUSDC } from '../../util/TokenUtils' import { Button } from '../common/Button' import { useTokensFromLists, useTokensFromUser } from './TokenSearchUtils' @@ -557,6 +558,12 @@ export function TokenSearch({ } try { + if (isTokenNativeUSDC(_token.address)) { + // We don't need to fetch token data because USDC will be hardcoded + setSelectedToken(_token.address) + return + } + // Token not added to the bridge, so we'll handle importing it if (typeof bridgeTokens[_token.address] === 'undefined') { setTokenQueryParam(_token.address) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx index e8e55e3ca9..e89ca5b937 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx @@ -29,7 +29,8 @@ import { isTokenArbitrumOneNativeUSDC, isTokenSepoliaUSDC, isTokenMainnetUSDC, - isGatewayRegistered + isGatewayRegistered, + isTokenNativeUSDC } from '../../util/TokenUtils' import { useSwitchNetworkWithConfig } from '../../hooks/useSwitchNetworkWithConfig' import { useIsConnectedToArbitrum } from '../../hooks/useIsConnectedToArbitrum' @@ -233,9 +234,14 @@ export function TransferPanel() { }) const isTokenAlreadyImported = useMemo(() => { + if (isTokenNativeUSDC(tokenFromSearchParams)) { + return true + } + if ( !tokensFromLists || !tokensFromUser || + // Wait if token list hasn't been populated yet Object.keys(tokensFromLists).length === 0 ) { return undefined 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 e43d93407d..2c287e38d4 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain.tsx @@ -60,6 +60,7 @@ import { } from './TransferDisabledDialog' import { getBridgeUiConfigForChain } from '../../util/bridgeUiConfig' import { useGasSummary } from '../../hooks/TransferPanel/useGasSummary' +import { useUpdateUSDCTokenData } from './TransferPanelMain/hooks' import { Balances, useSelectedTokenBalances @@ -574,6 +575,8 @@ export function TransferPanelMain({ } }, [errorMessage, openTransferDisabledDialog]) + useUpdateUSDCTokenData() + type NetworkListboxesProps = { from: Pick to: Omit diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/hooks.ts b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/hooks.ts new file mode 100644 index 0000000000..ebbc79c236 --- /dev/null +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/hooks.ts @@ -0,0 +1,59 @@ +import { useEffect } from 'react' + +import { + isTokenArbitrumOneNativeUSDC, + isTokenArbitrumSepoliaNativeUSDC +} from '../../../util/TokenUtils' +import { useActions, useAppState } from '../../../state' +import { useNetworks } from '../../../hooks/useNetworks' +import { useNetworksRelationship } from '../../../hooks/useNetworksRelationship' +import { CommonAddress } from '../../../util/CommonAddressUtils' +import { isNetwork } from '../../../util/networks' +import { useSelectedToken } from '../../../hooks/useSelectedToken' + +export function useUpdateUSDCTokenData() { + const actions = useActions() + const { + app: { + arbTokenBridge: { token } + } + } = useAppState() + const { selectedToken, setSelectedToken } = useSelectedToken() + const [networks] = useNetworks() + const { isDepositMode } = useNetworksRelationship(networks) + const { + isArbitrumOne: isDestinationChainArbitrumOne, + isArbitrumSepolia: isDestinationChainArbitrumSepolia + } = isNetwork(networks.destinationChain.id) + + useEffect(() => { + const isArbOneUSDC = isTokenArbitrumOneNativeUSDC(selectedToken?.address) + const isArbSepoliaUSDC = isTokenArbitrumSepoliaNativeUSDC( + selectedToken?.address + ) + + // If user select native USDC on L2, when switching to deposit mode, + // we need to default to set the corresponding USDC on L1 + if (!isDepositMode) { + return + } + + if (isArbOneUSDC && isDestinationChainArbitrumOne) { + token.updateTokenData(CommonAddress.Ethereum.USDC) + setSelectedToken(CommonAddress.Ethereum.USDC) + } + + if (isArbSepoliaUSDC && isDestinationChainArbitrumSepolia) { + token.updateTokenData(CommonAddress.Sepolia.USDC) + setSelectedToken(CommonAddress.Sepolia.USDC) + } + }, [ + actions.app, + isDepositMode, + isDestinationChainArbitrumOne, + isDestinationChainArbitrumSepolia, + selectedToken, + setSelectedToken, + token + ]) +} diff --git a/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts b/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts index 3917b493f6..4dcabd909f 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts @@ -1,9 +1,11 @@ import { useCallback } from 'react' import { useNetworks } from './useNetworks' import { ChainId, isNetwork } from '../util/networks' +import { useSelectedToken } from './useSelectedToken' export const useIsTestnetMode = () => { const [networks, setNetworks] = useNetworks() + const { setSelectedToken } = useSelectedToken() const isTestnetMode = isNetwork(networks.sourceChain.id).isTestnet @@ -11,7 +13,8 @@ export const useIsTestnetMode = () => { setNetworks({ sourceChainId: isTestnetMode ? ChainId.Ethereum : ChainId.Sepolia }) - }, [isTestnetMode, setNetworks]) + setSelectedToken(null) + }, [isTestnetMode, setNetworks, setSelectedToken]) return [isTestnetMode, toggleTestnetMode] as const } diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index 41db0241a0..ce3b40fa6a 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -1,4 +1,5 @@ -import { useCallback, useEffect, useMemo, useState } from 'react' +import { useCallback } from 'react' +import useSWRImmutable from 'swr/immutable' import { useTokensFromLists, @@ -9,12 +10,13 @@ import { ERC20BridgeToken, TokenType } from './arbTokenBridge.types' import { getL2ERC20Address, isTokenArbitrumOneNativeUSDC, - isTokenArbitrumSepoliaNativeUSDC + isTokenArbitrumSepoliaNativeUSDC, + isTokenMainnetUSDC, + isTokenSepoliaUSDC } from '../util/TokenUtils' import { useNetworks } from './useNetworks' import { isNetwork } from '../util/networks' import { CommonAddress } from '../util/CommonAddressUtils' -import { useAppState } from '../state' import { useNetworksRelationship } from './useNetworksRelationship' type UseSelectedTokenProps = { @@ -31,32 +33,24 @@ const commonUSDC = { } export const useSelectedToken = (): UseSelectedTokenProps => { - const { - app: { - arbTokenBridge: { token } - } - } = useAppState() const { tokenFromSearchParams, setTokenQueryParam } = useTokenFromSearchParams() const tokensFromLists = useTokensFromLists() const tokensFromUser = useTokensFromUser() const [networks] = useNetworks() - const { childChain, childChainProvider, parentChainProvider } = + const { childChain, childChainProvider, parentChain, parentChainProvider } = useNetworksRelationship(networks) const { - isArbitrumOne: isDestinationChainArbitrumOne, - isArbitrumSepolia: isDestinationChainArbitrumSepolia - } = isNetwork(networks.destinationChain.id) - - const [_selectedToken, _setSelectedToken] = useState( - null - ) - - useEffect(() => { - async function getSelectedToken() { + isEthereumMainnet: isParentChainEthereumMainnet, + isSepolia: isParentChainSepolia, + isArbitrumOne: isParentChainArbitrumOne, + isArbitrumSepolia: isParentChainArbitrumSepolia + } = isNetwork(parentChain.id) + + const fetcher: () => Promise = + useCallback(async () => { if (!tokenFromSearchParams) { - _setSelectedToken(null) - return + return null } const isArbitrumOneUsdc = isTokenArbitrumOneNativeUSDC( @@ -66,28 +60,32 @@ export const useSelectedToken = (): UseSelectedTokenProps => { tokenFromSearchParams ) - if (isArbitrumOneUsdc && isDestinationChainArbitrumOne) { - token.updateTokenData(CommonAddress.Ethereum.USDC) - _setSelectedToken({ + // Ethereum Mainnet USDC + if ( + isTokenMainnetUSDC(tokenFromSearchParams) && + isParentChainEthereumMainnet + ) { + return { ...commonUSDC, address: CommonAddress.Ethereum.USDC, l2Address: CommonAddress.ArbitrumOne['USDC.e'] - }) - return + } } - if (isArbitrumSepoliaUsdc && isDestinationChainArbitrumSepolia) { - token.updateTokenData(CommonAddress.Sepolia.USDC) - _setSelectedToken({ + // Ethereum Sepolia USDC + if (isTokenSepoliaUSDC(tokenFromSearchParams) && isParentChainSepolia) { + return { ...commonUSDC, address: CommonAddress.Sepolia.USDC, l2Address: CommonAddress.ArbitrumSepolia['USDC.e'] - }) - return + } } - if (isArbitrumOneUsdc || isArbitrumSepoliaUsdc) { - // USDC with Orbit chains + // USDC when depositing to an Orbit chain + if ( + (isArbitrumOneUsdc && isParentChainArbitrumOne) || + (isArbitrumSepoliaUsdc && isParentChainArbitrumSepolia) + ) { let childChainUsdcAddress try { childChainUsdcAddress = isNetwork(childChain.id).isOrbitChain @@ -103,7 +101,7 @@ export const useSelectedToken = (): UseSelectedTokenProps => { // could be never bridged before } - _setSelectedToken({ + return { name: 'USD Coin', type: TokenType.ERC20, symbol: 'USDC', @@ -111,37 +109,37 @@ export const useSelectedToken = (): UseSelectedTokenProps => { l2Address: childChainUsdcAddress, decimals: 6, listIds: new Set() - }) - return + } } if (!tokensFromLists || !tokensFromUser) { - _setSelectedToken(null) - return + return null } - _setSelectedToken( + return ( Object.values({ ...tokensFromLists, ...tokensFromUser }).find( tokenFromLists => tokenFromLists?.address.toLowerCase() === tokenFromSearchParams.toLowerCase() ) || null ) - } - - getSelectedToken() - }, [ - childChain.id, - childChainProvider, - isDestinationChainArbitrumOne, - isDestinationChainArbitrumSepolia, - parentChainProvider, - _setSelectedToken, - token, - tokenFromSearchParams, - tokensFromLists, - tokensFromUser - ]) + }, [ + childChain.id, + childChainProvider, + isParentChainArbitrumOne, + isParentChainArbitrumSepolia, + isParentChainEthereumMainnet, + isParentChainSepolia, + parentChainProvider, + tokenFromSearchParams, + tokensFromLists, + tokensFromUser + ]) + + const { data } = useSWRImmutable( + [parentChain.id, childChain.id, tokenFromSearchParams], + fetcher + ) const setSelectedToken = useCallback( (erc20ParentAddress: string | null) => { @@ -151,7 +149,7 @@ export const useSelectedToken = (): UseSelectedTokenProps => { ) return { - selectedToken: _selectedToken, + selectedToken: data ?? null, setSelectedToken } } From 6047fc43c6dc2e72b225a95a199c6bed597cfa9a Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 22 Jul 2024 15:10:03 +0200 Subject: [PATCH 12/52] unused var --- packages/arb-token-bridge-ui/src/state/app/state.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/state/app/state.ts b/packages/arb-token-bridge-ui/src/state/app/state.ts index 8af640d098..15e4d39995 100644 --- a/packages/arb-token-bridge-ui/src/state/app/state.ts +++ b/packages/arb-token-bridge-ui/src/state/app/state.ts @@ -2,7 +2,6 @@ import { BigNumber } from 'ethers' import { ArbTokenBridge, AssetType, - ERC20BridgeToken, NodeBlockDeadlineStatus } from '../../hooks/arbTokenBridge.types' import { From b53430eeb11f9f8cc3242c5976348eaa0b8edb73 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 22 Jul 2024 15:31:13 +0200 Subject: [PATCH 13/52] fix --- .../arb-token-bridge-ui/src/hooks/useSelectedToken.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index ce3b40fa6a..f5b9b1f0e7 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -112,10 +112,6 @@ export const useSelectedToken = (): UseSelectedTokenProps => { } } - if (!tokensFromLists || !tokensFromUser) { - return null - } - return ( Object.values({ ...tokensFromLists, ...tokensFromUser }).find( tokenFromLists => @@ -137,7 +133,11 @@ export const useSelectedToken = (): UseSelectedTokenProps => { ]) const { data } = useSWRImmutable( - [parentChain.id, childChain.id, tokenFromSearchParams], + !tokensFromLists || + !tokensFromUser || + Object.keys(tokensFromLists).length === 0 + ? null + : [parentChain.id, childChain.id, tokenFromSearchParams], fetcher ) From 3252775e5445bac3b91444ba0121f3aa65e7636c Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 22 Jul 2024 16:51:10 +0200 Subject: [PATCH 14/52] try --- .../src/hooks/useSelectedToken.ts | 40 ++++++++++++++----- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index f5b9b1f0e7..3c3773667b 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -81,6 +81,22 @@ export const useSelectedToken = (): UseSelectedTokenProps => { } } + if (isArbitrumOneUsdc && !isParentChainArbitrumOne) { + return { + ...commonUSDC, + address: CommonAddress.ArbitrumOne.USDC, + l2Address: CommonAddress.ArbitrumOne.USDC + } + } + + if (isArbitrumSepoliaUsdc && !isParentChainArbitrumOne) { + return { + ...commonUSDC, + address: CommonAddress.ArbitrumSepolia.USDC, + l2Address: CommonAddress.ArbitrumSepolia.USDC + } + } + // USDC when depositing to an Orbit chain if ( (isArbitrumOneUsdc && isParentChainArbitrumOne) || @@ -112,12 +128,14 @@ export const useSelectedToken = (): UseSelectedTokenProps => { } } + if (!tokensFromLists || !tokensFromUser) { + return null + } + return ( - Object.values({ ...tokensFromLists, ...tokensFromUser }).find( - tokenFromLists => - tokenFromLists?.address.toLowerCase() === - tokenFromSearchParams.toLowerCase() - ) || null + tokensFromLists[tokenFromSearchParams] || + tokensFromUser[tokenFromSearchParams] || + null ) }, [ childChain.id, @@ -133,11 +151,13 @@ export const useSelectedToken = (): UseSelectedTokenProps => { ]) const { data } = useSWRImmutable( - !tokensFromLists || - !tokensFromUser || - Object.keys(tokensFromLists).length === 0 - ? null - : [parentChain.id, childChain.id, tokenFromSearchParams], + [ + parentChain.id, + childChain.id, + tokenFromSearchParams, + tokensFromLists, + tokensFromUser + ], fetcher ) From 12d6a41191e0fc3bb67033b2dbcbb9650be3f322 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 22 Jul 2024 17:36:42 +0200 Subject: [PATCH 15/52] fix e2e --- .../tests/e2e/specs/importToken.cy.ts | 38 +++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts index ccb7effdf4..761685d7d5 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts @@ -197,9 +197,19 @@ describe('Import token', () => { cy.wait(3000) // Modal is displayed - cy.get('h2') - .contains(/import unknown token/i) - .should('be.visible') + cy.waitUntil( + () => + cy + .get('h2') + .contains(/import unknown token/i) + .should('be.visible'), + { + errorMsg: '/import unknown token/ header not found', + timeout: 50000, + interval: 500 + } + ) + cy.findByText(new RegExp(ERC20TokenName, 'i')).should('be.visible') cy.findByText(new RegExp(ERC20TokenAddressL1, 'i')).should('be.visible') @@ -237,9 +247,18 @@ describe('Import token', () => { cy.wait(3000) // Modal is displayed - cy.get('h2') - .contains(/import unknown token/i) - .should('be.visible') + cy.waitUntil( + () => + cy + .get('h2') + .contains(/import unknown token/i) + .should('be.visible'), + { + errorMsg: '/import unknown token/ header not found', + timeout: 50000, + interval: 500 + } + ) cy.findByText(new RegExp(ERC20TokenName, 'i')).should('be.visible') // Modal should always display L1 address regardless of query parameter cy.findByText(new RegExp(ERC20TokenAddressL1, 'i')).should('be.visible') @@ -278,7 +297,12 @@ describe('Import token', () => { }) // Modal is displayed - cy.get('h2').contains(/invalid token address/i) + cy.waitUntil(() => cy.get('h2').contains(/invalid token address/i), { + errorMsg: '/invalid token address/ header not found', + timeout: 50000, + interval: 500 + }) + cy.findByText(new RegExp(ERC20TokenAddressL1, 'i')).should('not.exist') cy.findByRole('button', { name: 'Import token' }).should('not.exist') From 038c544da96b4df0a166aa3685c63338e4cefd1e Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 22 Jul 2024 18:14:35 +0200 Subject: [PATCH 16/52] e2e fixes and clean up --- .../TransferPanel/TokenImportDialog.tsx | 3 +- .../src/hooks/useSelectedToken.ts | 186 ++++++++++-------- .../tests/e2e/specs/importToken.cy.ts | 51 +++-- 3 files changed, 128 insertions(+), 112 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx index 1a913823bd..ff385b1083 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx @@ -4,7 +4,7 @@ import { useLatest } from 'react-use' import { create } from 'zustand' import { useERC20L1Address } from '../../hooks/useERC20L1Address' -import { useActions, useAppState } from '../../state' +import { useAppState } from '../../state' import { erc20DataToErc20BridgeToken, fetchErc20Data, @@ -81,7 +81,6 @@ export function TokenImportDialog({ isDepositMode, isTeleportMode } = useNetworksRelationship(networks) - const actions = useActions() const tokensFromUser = useTokensFromUser() const latestTokensFromUser = useLatest(tokensFromUser) diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index 3c3773667b..a0b0450b2d 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -12,12 +12,15 @@ import { isTokenArbitrumOneNativeUSDC, isTokenArbitrumSepoliaNativeUSDC, isTokenMainnetUSDC, + isTokenNativeUSDC, isTokenSepoliaUSDC } from '../util/TokenUtils' import { useNetworks } from './useNetworks' import { isNetwork } from '../util/networks' import { CommonAddress } from '../util/CommonAddressUtils' import { useNetworksRelationship } from './useNetworksRelationship' +import { Provider } from '@ethersproject/providers' +import { getChainIdFromProvider } from '@/token-bridge-sdk/utils' type UseSelectedTokenProps = { selectedToken: ERC20BridgeToken | null @@ -40,12 +43,6 @@ export const useSelectedToken = (): UseSelectedTokenProps => { const [networks] = useNetworks() const { childChain, childChainProvider, parentChain, parentChainProvider } = useNetworksRelationship(networks) - const { - isEthereumMainnet: isParentChainEthereumMainnet, - isSepolia: isParentChainSepolia, - isArbitrumOne: isParentChainArbitrumOne, - isArbitrumSepolia: isParentChainArbitrumSepolia - } = isNetwork(parentChain.id) const fetcher: () => Promise = useCallback(async () => { @@ -53,79 +50,12 @@ export const useSelectedToken = (): UseSelectedTokenProps => { return null } - const isArbitrumOneUsdc = isTokenArbitrumOneNativeUSDC( - tokenFromSearchParams - ) - const isArbitrumSepoliaUsdc = isTokenArbitrumSepoliaNativeUSDC( - tokenFromSearchParams - ) - - // Ethereum Mainnet USDC - if ( - isTokenMainnetUSDC(tokenFromSearchParams) && - isParentChainEthereumMainnet - ) { - return { - ...commonUSDC, - address: CommonAddress.Ethereum.USDC, - l2Address: CommonAddress.ArbitrumOne['USDC.e'] - } - } - - // Ethereum Sepolia USDC - if (isTokenSepoliaUSDC(tokenFromSearchParams) && isParentChainSepolia) { - return { - ...commonUSDC, - address: CommonAddress.Sepolia.USDC, - l2Address: CommonAddress.ArbitrumSepolia['USDC.e'] - } - } - - if (isArbitrumOneUsdc && !isParentChainArbitrumOne) { - return { - ...commonUSDC, - address: CommonAddress.ArbitrumOne.USDC, - l2Address: CommonAddress.ArbitrumOne.USDC - } - } - - if (isArbitrumSepoliaUsdc && !isParentChainArbitrumOne) { - return { - ...commonUSDC, - address: CommonAddress.ArbitrumSepolia.USDC, - l2Address: CommonAddress.ArbitrumSepolia.USDC - } - } - - // USDC when depositing to an Orbit chain - if ( - (isArbitrumOneUsdc && isParentChainArbitrumOne) || - (isArbitrumSepoliaUsdc && isParentChainArbitrumSepolia) - ) { - let childChainUsdcAddress - try { - childChainUsdcAddress = isNetwork(childChain.id).isOrbitChain - ? ( - await getL2ERC20Address({ - erc20L1Address: tokenFromSearchParams, - l1Provider: parentChainProvider, - l2Provider: childChainProvider - }) - ).toLowerCase() - : undefined - } catch { - // could be never bridged before - } - - return { - name: 'USD Coin', - type: TokenType.ERC20, - symbol: 'USDC', - address: tokenFromSearchParams, - l2Address: childChainUsdcAddress, - decimals: 6, - listIds: new Set() - } + if (isTokenNativeUSDC(tokenFromSearchParams)) { + return getUsdcToken({ + tokenAddress: tokenFromSearchParams, + parentChainProvider, + childChainProvider + }) } if (!tokensFromLists || !tokensFromUser) { @@ -138,12 +68,7 @@ export const useSelectedToken = (): UseSelectedTokenProps => { null ) }, [ - childChain.id, childChainProvider, - isParentChainArbitrumOne, - isParentChainArbitrumSepolia, - isParentChainEthereumMainnet, - isParentChainSepolia, parentChainProvider, tokenFromSearchParams, tokensFromLists, @@ -173,3 +98,96 @@ export const useSelectedToken = (): UseSelectedTokenProps => { setSelectedToken } } + +async function getUsdcToken({ + tokenAddress, + parentChainProvider, + childChainProvider +}: { + tokenAddress: string + parentChainProvider: Provider + childChainProvider: Provider +}) { + const parentChainId = await getChainIdFromProvider(parentChainProvider) + const childChainId = await getChainIdFromProvider(childChainProvider) + + const { + isEthereumMainnet: isParentChainEthereumMainnet, + isSepolia: isParentChainSepolia, + isArbitrumOne: isParentChainArbitrumOne, + isArbitrumSepolia: isParentChainArbitrumSepolia + } = isNetwork(parentChainId) + + // Ethereum Mainnet USDC + if (isTokenMainnetUSDC(tokenAddress) && isParentChainEthereumMainnet) { + return { + ...commonUSDC, + address: CommonAddress.Ethereum.USDC, + l2Address: CommonAddress.ArbitrumOne['USDC.e'] + } + } + + // Ethereum Sepolia USDC + if (isTokenSepoliaUSDC(tokenAddress) && isParentChainSepolia) { + return { + ...commonUSDC, + address: CommonAddress.Sepolia.USDC, + l2Address: CommonAddress.ArbitrumSepolia['USDC.e'] + } + } + + // Arbitrum One USDC when Ethereum is the parent chain + if (isTokenArbitrumOneNativeUSDC(tokenAddress) && !isParentChainArbitrumOne) { + return { + ...commonUSDC, + address: CommonAddress.ArbitrumOne.USDC, + l2Address: CommonAddress.ArbitrumOne.USDC + } + } + + // Arbitrum Sepolia USDC when Ethereum is the parent chain + if ( + isTokenArbitrumSepoliaNativeUSDC(tokenAddress) && + !isParentChainArbitrumOne + ) { + return { + ...commonUSDC, + address: CommonAddress.ArbitrumSepolia.USDC, + l2Address: CommonAddress.ArbitrumSepolia.USDC + } + } + + // Arbitrum USDC with Orbit chains + if ( + (isTokenArbitrumOneNativeUSDC(tokenAddress) && isParentChainArbitrumOne) || + (isTokenArbitrumSepoliaNativeUSDC(tokenAddress) && + isParentChainArbitrumSepolia) + ) { + let childChainUsdcAddress + try { + childChainUsdcAddress = isNetwork(childChainId).isOrbitChain + ? ( + await getL2ERC20Address({ + erc20L1Address: tokenAddress, + l1Provider: parentChainProvider, + l2Provider: childChainProvider + }) + ).toLowerCase() + : undefined + } catch { + // could be never bridged before + } + + return { + name: 'USD Coin', + type: TokenType.ERC20, + symbol: 'USDC', + address: tokenAddress, + l2Address: childChainUsdcAddress, + decimals: 6, + listIds: new Set() + } + } + + return null +} diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts index 761685d7d5..a15dc65200 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts @@ -197,18 +197,15 @@ describe('Import token', () => { cy.wait(3000) // Modal is displayed - cy.waitUntil( - () => - cy - .get('h2') - .contains(/import unknown token/i) - .should('be.visible'), - { - errorMsg: '/import unknown token/ header not found', - timeout: 50000, - interval: 500 - } - ) + cy.waitUntil(() => cy.findByText(/import unknown token/i), { + errorMsg: '/import unknown token/ header not found', + timeout: 50000, + interval: 500 + }).then(() => { + cy.get('h2') + .contains(/import unknown token/i) + .should('be.visible') + }) cy.findByText(new RegExp(ERC20TokenName, 'i')).should('be.visible') cy.findByText(new RegExp(ERC20TokenAddressL1, 'i')).should('be.visible') @@ -247,18 +244,16 @@ describe('Import token', () => { cy.wait(3000) // Modal is displayed - cy.waitUntil( - () => - cy - .get('h2') - .contains(/import unknown token/i) - .should('be.visible'), - { - errorMsg: '/import unknown token/ header not found', - timeout: 50000, - interval: 500 - } - ) + cy.waitUntil(() => cy.findByText(/import unknown token/i), { + errorMsg: '/import unknown token/ header not found', + timeout: 50000, + interval: 500 + }).then(() => { + cy.get('h2') + .contains(/import unknown token/i) + .should('be.visible') + }) + cy.findByText(new RegExp(ERC20TokenName, 'i')).should('be.visible') // Modal should always display L1 address regardless of query parameter cy.findByText(new RegExp(ERC20TokenAddressL1, 'i')).should('be.visible') @@ -297,10 +292,14 @@ describe('Import token', () => { }) // Modal is displayed - cy.waitUntil(() => cy.get('h2').contains(/invalid token address/i), { - errorMsg: '/invalid token address/ header not found', + cy.waitUntil(() => cy.findByText(/invalid token address/i), { + errorMsg: '/import unknown token/ header not found', timeout: 50000, interval: 500 + }).then(() => { + cy.get('h2') + .contains(/invalid token address/i) + .should('be.visible') }) cy.findByText(new RegExp(ERC20TokenAddressL1, 'i')).should('not.exist') From 059a969d0797d5cf09460d702853411c3a33b47f Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 22 Jul 2024 18:48:47 +0200 Subject: [PATCH 17/52] clean ups and fixes e2e --- .../TransferPanel/AdvancedSettings.tsx | 2 +- .../CustomFeeTokenApprovalDialog.tsx | 2 +- .../components/TransferPanel/EstimatedGas.tsx | 2 +- .../TransferPanel/NativeCurrencyPrice.tsx | 2 +- .../TransferPanel/OneNovaTransferDialog.tsx | 2 +- .../components/TransferPanel/TokenButton.tsx | 33 +-------------- .../TransferPanel/TokenImportDialog.tsx | 2 +- .../components/TransferPanel/TokenSearch.tsx | 2 +- .../TransferPanel/TransferDisabledDialog.tsx | 2 +- .../TransferPanel/TransferPanel.tsx | 2 +- .../TransferPanel/TransferPanelMain.tsx | 2 +- .../TransferPanel/TransferPanelMain/hooks.ts | 2 +- .../TransferPanel/TransferPanelMainInput.tsx | 2 +- .../TransferPanel/TransferPanelSummary.tsx | 2 +- .../USDCDepositConfirmationDialog.tsx | 2 +- .../WithdrawalConfirmationDialog.tsx | 2 +- .../TransferPanel/useTransferReadiness.ts | 2 +- .../src/components/syncers/BalanceUpdater.tsx | 2 +- .../src/hooks/TransferPanel/useGasSummary.ts | 2 +- .../TransferPanel/useSelectedTokenBalances.ts | 2 +- .../TransferPanel/useSelectedTokenDecimals.ts | 2 +- .../src/hooks/useIsTestnetMode.ts | 2 +- .../src/hooks/useSelectedToken.ts | 20 ++------- .../tests/e2e/specs/depositERC20.cy.ts | 8 ++++ .../tests/e2e/specs/importToken.cy.ts | 42 ++++++------------- .../tests/e2e/specs/withdrawERC20.cy.ts | 8 ++++ 26 files changed, 55 insertions(+), 98 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/AdvancedSettings.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/AdvancedSettings.tsx index fd47b215ad..6103740fef 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/AdvancedSettings.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/AdvancedSettings.tsx @@ -112,7 +112,7 @@ async function getDestinationAddressWarning({ } export const AdvancedSettings = () => { - const { selectedToken } = useSelectedToken() + const [selectedToken] = useSelectedToken() const [networks] = useNetworks() const { childChain, diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/CustomFeeTokenApprovalDialog.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/CustomFeeTokenApprovalDialog.tsx index b1ef1d4c68..e25b305a36 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/CustomFeeTokenApprovalDialog.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/CustomFeeTokenApprovalDialog.tsx @@ -27,7 +27,7 @@ export function CustomFeeTokenApprovalDialog( const { customFeeToken, isOpen } = props const { ethToUSD } = useETHPrice() - const { selectedToken } = useSelectedToken() + const [selectedToken] = useSelectedToken() const [networks] = useNetworks() const { sourceChain, destinationChain } = networks diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/EstimatedGas.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/EstimatedGas.tsx index feba3ee1cc..aae2bf96e7 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/EstimatedGas.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/EstimatedGas.tsx @@ -53,7 +53,7 @@ export function EstimatedGas({ }: { chainType: 'source' | 'destination' }) { - const { selectedToken } = useSelectedToken() + const [selectedToken] = useSelectedToken() const [networks] = useNetworks() const { childChain, diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/NativeCurrencyPrice.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/NativeCurrencyPrice.tsx index c247646ec3..be724a608a 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/NativeCurrencyPrice.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/NativeCurrencyPrice.tsx @@ -7,7 +7,7 @@ import { formatUSD } from '../../util/NumberUtils' import { useSelectedToken } from '../../hooks/useSelectedToken' export function useIsBridgingEth(childChainNativeCurrency: NativeCurrency) { - const { selectedToken } = useSelectedToken() + const [selectedToken] = useSelectedToken() const isBridgingEth = selectedToken === null && !childChainNativeCurrency.isCustom return isBridgingEth diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/OneNovaTransferDialog.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/OneNovaTransferDialog.tsx index d798d7afff..9e2750e13a 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/OneNovaTransferDialog.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/OneNovaTransferDialog.tsx @@ -16,7 +16,7 @@ export function OneNovaTransferDialog( amount: string } ) { - const { selectedToken } = useSelectedToken() + const [selectedToken] = useSelectedToken() const { destinationChainId } = props 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 72b8c79aca..0b8e40ff2d 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenButton.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenButton.tsx @@ -3,8 +3,6 @@ import { Popover } from '@headlessui/react' import { ChevronDownIcon } from '@heroicons/react/24/outline' import { twMerge } from 'tailwind-merge' -import { useAppState } from '../../state' -import { sanitizeImageSrc } from '../../util' import { TokenSearch } from '../TransferPanel/TokenSearch' import { sanitizeTokenSymbol } from '../../util/TokenUtils' import { useNativeCurrency } from '../../hooks/useNativeCurrency' @@ -19,41 +17,12 @@ import { Transition } from '../common/Transition' import { useSelectedToken } from '../../hooks/useSelectedToken' export function TokenButton(): JSX.Element { - const { - app: { - arbTokenBridge: { bridgeTokens }, - arbTokenBridgeLoaded - } - } = useAppState() - const { selectedToken } = useSelectedToken() + const [selectedToken] = useSelectedToken() const [networks] = useNetworks() const { childChainProvider } = useNetworksRelationship(networks) const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) - const tokenLogo = useMemo(() => { - const selectedAddress = selectedToken?.address - if (!selectedAddress) { - return nativeCurrency.logoUrl - } - if (!arbTokenBridgeLoaded) { - return undefined - } - if (typeof bridgeTokens === 'undefined') { - return undefined - } - const logo = bridgeTokens[selectedAddress]?.logoURI - if (logo) { - return sanitizeImageSrc(logo) - } - return undefined - }, [ - nativeCurrency, - bridgeTokens, - selectedToken?.address, - arbTokenBridgeLoaded - ]) - const tokenSymbol = useMemo(() => { if (!selectedToken) { return nativeCurrency.symbol diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx index ff385b1083..a56e6e15b0 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx @@ -71,7 +71,7 @@ export function TokenImportDialog({ arbTokenBridge: { bridgeTokens, token } } } = useAppState() - const { selectedToken, setSelectedToken } = useSelectedToken() + const [selectedToken, setSelectedToken] = useSelectedToken() const [networks] = useNetworks() const { childChain, diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx index 31a1da8b08..b429d27970 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx @@ -521,7 +521,7 @@ export function TokenSearch({ arbTokenBridge: { token, bridgeTokens } } } = useAppState() - const { setSelectedToken } = useSelectedToken() + const [, setSelectedToken] = useSelectedToken() const [networks] = useNetworks() const { childChain, diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferDisabledDialog.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferDisabledDialog.tsx index 6c6971ec9f..9e60b34ea6 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferDisabledDialog.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferDisabledDialog.tsx @@ -27,7 +27,7 @@ export const useTransferDisabledDialogStore = export function TransferDisabledDialog() { const [networks] = useNetworks() const { isTeleportMode } = useNetworksRelationship(networks) - const { selectedToken, setSelectedToken } = useSelectedToken() + const [selectedToken, setSelectedToken] = useSelectedToken() const { isOpen: isOpenTransferDisabledDialog, closeDialog: closeTransferDisabledDialog diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx index 8716d80848..f47ef4864f 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx @@ -113,7 +113,7 @@ export function TransferPanel() { warningTokens } } = useAppState() - const { selectedToken } = useSelectedToken() + const [selectedToken] = useSelectedToken() const { layout } = useAppContextState() const { isTransferring } = layout const { address: walletAddress, isConnected } = useAccount() 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 3f2abc028b..4f3989ad79 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain.tsx @@ -348,7 +348,7 @@ export function TransferPanelMain({ useAccountType() const { isArbitrumOne, isArbitrumSepolia } = isNetwork(childChain.id) const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) - const { selectedToken, setSelectedToken } = useSelectedToken() + const [selectedToken, setSelectedToken] = useSelectedToken() const { address: walletAddress } = useAccount() diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/hooks.ts b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/hooks.ts index ebbc79c236..f7923aa51c 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/hooks.ts +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/hooks.ts @@ -18,7 +18,7 @@ export function useUpdateUSDCTokenData() { arbTokenBridge: { token } } } = useAppState() - const { selectedToken, setSelectedToken } = useSelectedToken() + const [selectedToken, setSelectedToken] = useSelectedToken() const [networks] = useNetworks() const { isDepositMode } = useNetworksRelationship(networks) const { diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMainInput.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMainInput.tsx index eb3dc7ad10..cfa4c7bf59 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMainInput.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMainInput.tsx @@ -21,7 +21,7 @@ type MaxButtonProps = React.ButtonHTMLAttributes & { function MaxButton(props: MaxButtonProps) { const { loading, className = '', ...rest } = props - const { selectedToken } = useSelectedToken() + const [selectedToken] = useSelectedToken() const { address: walletAddress } = useAccount() const [networks] = useNetworks() const { childChain, parentChain, isDepositMode } = diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelSummary.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelSummary.tsx index b6c1fb0f34..f7169ab13f 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelSummary.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelSummary.tsx @@ -37,7 +37,7 @@ function StyledLoader() { } function TotalGasFees() { - const { selectedToken } = useSelectedToken() + const [selectedToken] = useSelectedToken() const { status: gasSummaryStatus, diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/USDCDeposit/USDCDepositConfirmationDialog.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/USDCDeposit/USDCDepositConfirmationDialog.tsx index 0124dd9042..f64c994248 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/USDCDeposit/USDCDepositConfirmationDialog.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/USDCDeposit/USDCDepositConfirmationDialog.tsx @@ -43,7 +43,7 @@ enum SelectedTabName { const defaultSelectedTabName: SelectedTabName = SelectedTabName.Cctp export function USDCDepositConfirmationDialog(props: Props) { - const { selectedToken } = useSelectedToken() + const [selectedToken] = useSelectedToken() const [networks] = useNetworks() const { childChain, parentChain } = useNetworksRelationship(networks) const { isArbitrumSepolia } = isNetwork(childChain.id) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/WithdrawalConfirmationDialog.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/WithdrawalConfirmationDialog.tsx index f37ff0ba70..66251562ec 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/WithdrawalConfirmationDialog.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/WithdrawalConfirmationDialog.tsx @@ -46,7 +46,7 @@ export function WithdrawalConfirmationDialog( const destinationNetworkName = getNetworkName(parentChain.id) - const { selectedToken } = useSelectedToken() + const [selectedToken] = useSelectedToken() const nativeCurrency = useNativeCurrency({ provider: childChainProvider diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/useTransferReadiness.ts b/packages/arb-token-bridge-ui/src/components/TransferPanel/useTransferReadiness.ts index 3e15d73ff6..8f2a6bcb07 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/useTransferReadiness.ts +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/useTransferReadiness.ts @@ -116,7 +116,7 @@ export function useTransferReadiness({ amount: string gasSummary: UseGasSummaryResult }): UseTransferReadinessResult { - const { selectedToken } = useSelectedToken() + const [selectedToken] = useSelectedToken() const { layout: { isTransferring } } = useAppContextState() diff --git a/packages/arb-token-bridge-ui/src/components/syncers/BalanceUpdater.tsx b/packages/arb-token-bridge-ui/src/components/syncers/BalanceUpdater.tsx index c35bb00a45..f34365d89e 100644 --- a/packages/arb-token-bridge-ui/src/components/syncers/BalanceUpdater.tsx +++ b/packages/arb-token-bridge-ui/src/components/syncers/BalanceUpdater.tsx @@ -11,7 +11,7 @@ const BalanceUpdater = (): JSX.Element => { const { app: { arbTokenBridge } } = useAppState() - const { selectedToken } = useSelectedToken() + const [selectedToken] = useSelectedToken() const { address: walletAddress } = useAccount() const latestTokenBridge = useLatest(arbTokenBridge) diff --git a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useGasSummary.ts b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useGasSummary.ts index 97e4f29a30..712fe19fc9 100644 --- a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useGasSummary.ts +++ b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useGasSummary.ts @@ -40,7 +40,7 @@ export type UseGasSummaryResult = { } export function useGasSummary(): UseGasSummaryResult { - const { selectedToken } = useSelectedToken() + const [selectedToken] = useSelectedToken() const [networks] = useNetworks() const { childChainProvider, parentChainProvider, isDepositMode } = useNetworksRelationship(networks) diff --git a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenBalances.ts b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenBalances.ts index 46dbe18493..9798dde630 100644 --- a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenBalances.ts +++ b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenBalances.ts @@ -19,7 +19,7 @@ export type Balances = { } export function useSelectedTokenBalances(): Balances { - const { selectedToken } = useSelectedToken() + const [selectedToken] = useSelectedToken() const { address: walletAddress } = useAccount() const [networks] = useNetworks() const { childChain, parentChain, isDepositMode } = diff --git a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenDecimals.ts b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenDecimals.ts index e6a39885c4..2d42666429 100644 --- a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenDecimals.ts +++ b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenDecimals.ts @@ -4,7 +4,7 @@ import { useNetworks } from '../useNetworks' import { useSelectedToken } from '../useSelectedToken' export function useSelectedTokenDecimals() { - const { selectedToken } = useSelectedToken() + const [selectedToken] = useSelectedToken() const [networks] = useNetworks() const { childChainProvider } = useNetworksRelationship(networks) diff --git a/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts b/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts index 4dcabd909f..4f0b0446c7 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts @@ -5,7 +5,7 @@ import { useSelectedToken } from './useSelectedToken' export const useIsTestnetMode = () => { const [networks, setNetworks] = useNetworks() - const { setSelectedToken } = useSelectedToken() + const [, setSelectedToken] = useSelectedToken() const isTestnetMode = isNetwork(networks.sourceChain.id).isTestnet diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index a0b0450b2d..e82444bd6d 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -22,11 +22,6 @@ import { useNetworksRelationship } from './useNetworksRelationship' import { Provider } from '@ethersproject/providers' import { getChainIdFromProvider } from '@/token-bridge-sdk/utils' -type UseSelectedTokenProps = { - selectedToken: ERC20BridgeToken | null - setSelectedToken: (erc20ParentAddress: string | null) => void -} - const commonUSDC = { name: 'USD Coin', type: TokenType.ERC20, @@ -35,7 +30,7 @@ const commonUSDC = { listIds: new Set() } -export const useSelectedToken = (): UseSelectedTokenProps => { +export const useSelectedToken = () => { const { tokenFromSearchParams, setTokenQueryParam } = useTokenFromSearchParams() const tokensFromLists = useTokensFromLists() @@ -93,10 +88,7 @@ export const useSelectedToken = (): UseSelectedTokenProps => { [setTokenQueryParam] ) - return { - selectedToken: data ?? null, - setSelectedToken - } + return [data ?? null, setSelectedToken] as const } async function getUsdcToken({ @@ -179,13 +171,9 @@ async function getUsdcToken({ } return { - name: 'USD Coin', - type: TokenType.ERC20, - symbol: 'USDC', + ...commonUSDC, address: tokenAddress, - l2Address: childChainUsdcAddress, - decimals: 6, - listIds: new Set() + l2Address: childChainUsdcAddress } } diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts index 90bdd4e51d..b90ca747cf 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts @@ -52,6 +52,10 @@ describe('Deposit ERC20 Token', () => { }) }) + // give some time for the token to update + // eslint-disable-next-line + cy.wait(10_000) + context('should show ERC-20 balance correctly', () => { cy.findByLabelText('WETH balance amount on l1') .should('be.visible') @@ -113,6 +117,10 @@ describe('Deposit ERC20 Token', () => { }) }) + // give some time for the token to update + // eslint-disable-next-line + cy.wait(10_000) + context('should show summary', () => { cy.findByPlaceholderText('Enter amount') .typeRecursively(String(ERC20AmountToSend)) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts index a15dc65200..d74b4a4f7b 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts @@ -194,18 +194,12 @@ describe('Import token', () => { // waiting for metamask notification to disappear // eslint-disable-next-line - cy.wait(3000) + cy.wait(15_000) // Modal is displayed - cy.waitUntil(() => cy.findByText(/import unknown token/i), { - errorMsg: '/import unknown token/ header not found', - timeout: 50000, - interval: 500 - }).then(() => { - cy.get('h2') - .contains(/import unknown token/i) - .should('be.visible') - }) + cy.get('h2') + .contains(/import unknown token/i) + .should('be.visible') cy.findByText(new RegExp(ERC20TokenName, 'i')).should('be.visible') cy.findByText(new RegExp(ERC20TokenAddressL1, 'i')).should('be.visible') @@ -241,18 +235,12 @@ describe('Import token', () => { // waiting for metamask notification to disappear // eslint-disable-next-line - cy.wait(3000) + cy.wait(15_000) // Modal is displayed - cy.waitUntil(() => cy.findByText(/import unknown token/i), { - errorMsg: '/import unknown token/ header not found', - timeout: 50000, - interval: 500 - }).then(() => { - cy.get('h2') - .contains(/import unknown token/i) - .should('be.visible') - }) + cy.get('h2') + .contains(/import unknown token/i) + .should('be.visible') cy.findByText(new RegExp(ERC20TokenName, 'i')).should('be.visible') // Modal should always display L1 address regardless of query parameter @@ -291,16 +279,12 @@ describe('Import token', () => { } }) + // make sure the import dialog appears + // eslint-disable-next-line + cy.wait(15_000) + // Modal is displayed - cy.waitUntil(() => cy.findByText(/invalid token address/i), { - errorMsg: '/import unknown token/ header not found', - timeout: 50000, - interval: 500 - }).then(() => { - cy.get('h2') - .contains(/invalid token address/i) - .should('be.visible') - }) + cy.get('h2').contains(/invalid token address/i) cy.findByText(new RegExp(ERC20TokenAddressL1, 'i')).should('not.exist') diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts index 7b91e3b2d2..91ae2ca66b 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts @@ -62,6 +62,10 @@ describe('Withdraw ERC20 Token', () => { }) }) + // give some time for the token to update + // eslint-disable-next-line + cy.wait(10_000) + context('should show summary', () => { cy.findByPlaceholderText('Enter amount') .typeRecursively(String(ERC20AmountToSend)) @@ -142,6 +146,10 @@ describe('Withdraw ERC20 Token', () => { }) }) + // give some time for the token to update + // eslint-disable-next-line + cy.wait(10_000) + context('should show summary', () => { cy.findByPlaceholderText('Enter amount') .typeRecursively(String(ERC20AmountToSend)) From 5e16815e23a934305414b4fd64be6ea5724ad8af Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 22 Jul 2024 20:54:32 +0200 Subject: [PATCH 18/52] fixes --- .../src/hooks/useSelectedToken.ts | 25 +++++++------------ .../tests/e2e/specs/importToken.cy.ts | 6 ++--- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index e82444bd6d..82ad0e882a 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -1,10 +1,6 @@ import { useCallback } from 'react' import useSWRImmutable from 'swr/immutable' -import { - useTokensFromLists, - useTokensFromUser -} from '../components/TransferPanel/TokenSearchUtils' import { useTokenFromSearchParams } from '../components/TransferPanel/TransferPanelUtils' import { ERC20BridgeToken, TokenType } from './arbTokenBridge.types' import { @@ -21,6 +17,7 @@ import { CommonAddress } from '../util/CommonAddressUtils' import { useNetworksRelationship } from './useNetworksRelationship' import { Provider } from '@ethersproject/providers' import { getChainIdFromProvider } from '@/token-bridge-sdk/utils' +import { useTokenLists } from './useTokenLists' const commonUSDC = { name: 'USD Coin', @@ -33,11 +30,10 @@ const commonUSDC = { export const useSelectedToken = () => { const { tokenFromSearchParams, setTokenQueryParam } = useTokenFromSearchParams() - const tokensFromLists = useTokensFromLists() - const tokensFromUser = useTokensFromUser() const [networks] = useNetworks() const { childChain, childChainProvider, parentChain, parentChainProvider } = useNetworksRelationship(networks) + const tokenList = useTokenLists(childChain.id) const fetcher: () => Promise = useCallback(async () => { @@ -53,30 +49,27 @@ export const useSelectedToken = () => { }) } - if (!tokensFromLists || !tokensFromUser) { + const tokens = tokenList.data?.flat() + + if (!tokens) { return null } - return ( - tokensFromLists[tokenFromSearchParams] || - tokensFromUser[tokenFromSearchParams] || - null - ) + return null }, [ childChainProvider, parentChainProvider, tokenFromSearchParams, - tokensFromLists, - tokensFromUser + tokenList.data ]) const { data } = useSWRImmutable( [ + 'useSelectedToken', parentChain.id, childChain.id, tokenFromSearchParams, - tokensFromLists, - tokensFromUser + tokenList.data ], fetcher ) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts index d74b4a4f7b..b2014b43d7 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts @@ -194,7 +194,7 @@ describe('Import token', () => { // waiting for metamask notification to disappear // eslint-disable-next-line - cy.wait(15_000) + cy.wait(30_000) // Modal is displayed cy.get('h2') @@ -235,7 +235,7 @@ describe('Import token', () => { // waiting for metamask notification to disappear // eslint-disable-next-line - cy.wait(15_000) + cy.wait(30_000) // Modal is displayed cy.get('h2') @@ -281,7 +281,7 @@ describe('Import token', () => { // make sure the import dialog appears // eslint-disable-next-line - cy.wait(15_000) + cy.wait(30_000) // Modal is displayed cy.get('h2').contains(/invalid token address/i) From 62d766832665294d3ecfa9db4095c2d908412206 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 23 Jul 2024 10:05:33 +0200 Subject: [PATCH 19/52] fixes --- .../src/hooks/useSelectedToken.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index 82ad0e882a..2e0a36f5ec 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -17,7 +17,10 @@ import { CommonAddress } from '../util/CommonAddressUtils' import { useNetworksRelationship } from './useNetworksRelationship' import { Provider } from '@ethersproject/providers' import { getChainIdFromProvider } from '@/token-bridge-sdk/utils' -import { useTokenLists } from './useTokenLists' +import { + useTokensFromLists, + useTokensFromUser +} from '../components/TransferPanel/TokenSearchUtils' const commonUSDC = { name: 'USD Coin', @@ -33,7 +36,8 @@ export const useSelectedToken = () => { const [networks] = useNetworks() const { childChain, childChainProvider, parentChain, parentChainProvider } = useNetworksRelationship(networks) - const tokenList = useTokenLists(childChain.id) + const tokensFromLists = useTokensFromLists() + const tokensFromUsers = useTokensFromUser() const fetcher: () => Promise = useCallback(async () => { @@ -49,9 +53,7 @@ export const useSelectedToken = () => { }) } - const tokens = tokenList.data?.flat() - - if (!tokens) { + if (!tokensFromLists || tokensFromUsers) { return null } @@ -60,7 +62,8 @@ export const useSelectedToken = () => { childChainProvider, parentChainProvider, tokenFromSearchParams, - tokenList.data + tokensFromLists, + tokensFromUsers ]) const { data } = useSWRImmutable( @@ -69,7 +72,8 @@ export const useSelectedToken = () => { parentChain.id, childChain.id, tokenFromSearchParams, - tokenList.data + tokensFromLists, + tokensFromUsers ], fetcher ) From 0e26cb0427306e1ac3230485f391815a68da0db1 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 23 Jul 2024 10:15:50 +0200 Subject: [PATCH 20/52] fixes --- .../src/hooks/useSelectedToken.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index 2e0a36f5ec..9fa275c5d5 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -41,23 +41,29 @@ export const useSelectedToken = () => { const fetcher: () => Promise = useCallback(async () => { - if (!tokenFromSearchParams) { + const tokenAddressLowercased = tokenFromSearchParams?.toLowerCase() + + if (!tokenAddressLowercased) { return null } - if (isTokenNativeUSDC(tokenFromSearchParams)) { + if (isTokenNativeUSDC(tokenAddressLowercased)) { return getUsdcToken({ - tokenAddress: tokenFromSearchParams, + tokenAddress: tokenAddressLowercased, parentChainProvider, childChainProvider }) } - if (!tokensFromLists || tokensFromUsers) { + if (!tokensFromLists || !tokensFromUsers) { return null } - return null + return ( + tokensFromLists[tokenAddressLowercased] || + tokensFromUsers[tokenAddressLowercased] || + null + ) }, [ childChainProvider, parentChainProvider, From 707cd1243cefee92d72cb3236b70bff1ab5e17a1 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 23 Jul 2024 11:24:18 +0200 Subject: [PATCH 21/52] video --- packages/arb-token-bridge-ui/synpress.config.ts | 2 +- .../arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts | 4 ---- .../arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts | 4 ---- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/arb-token-bridge-ui/synpress.config.ts b/packages/arb-token-bridge-ui/synpress.config.ts index 1e2123d8f3..954a595d4d 100644 --- a/packages/arb-token-bridge-ui/synpress.config.ts +++ b/packages/arb-token-bridge-ui/synpress.config.ts @@ -27,7 +27,7 @@ if (process.env.TEST_FILE) { tests = specFiles.map(file => file.file) } -const shouldRecordVideo = process.env.CYPRESS_RECORD_VIDEO === 'true' +const shouldRecordVideo = true export default defineConfig({ userAgent: 'synpress', diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts index b90ca747cf..34445b1617 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts @@ -117,10 +117,6 @@ describe('Deposit ERC20 Token', () => { }) }) - // give some time for the token to update - // eslint-disable-next-line - cy.wait(10_000) - context('should show summary', () => { cy.findByPlaceholderText('Enter amount') .typeRecursively(String(ERC20AmountToSend)) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts index 91ae2ca66b..889ce5c083 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts @@ -62,10 +62,6 @@ describe('Withdraw ERC20 Token', () => { }) }) - // give some time for the token to update - // eslint-disable-next-line - cy.wait(10_000) - context('should show summary', () => { cy.findByPlaceholderText('Enter amount') .typeRecursively(String(ERC20AmountToSend)) From 3c1379da722c1a02fc2bdf6710e6a7dda9e33a80 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 23 Jul 2024 11:48:19 +0200 Subject: [PATCH 22/52] video --- .../src/components/TransferPanel/TransferPanel.tsx | 12 ++++-------- .../arb-token-bridge-ui/tests/e2e/specfiles.json | 10 +++++----- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx index f47ef4864f..91dee6a1dd 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx @@ -1,5 +1,5 @@ import dayjs from 'dayjs' -import { useState, useMemo, useEffect } from 'react' +import { useState, useMemo } from 'react' import Tippy from '@tippyjs/react' import { constants, utils } from 'ethers' import { useLatest } from 'react-use' @@ -101,9 +101,6 @@ export function TransferPanel() { useState(ImportTokenModalStatus.IDLE) const [showSmartContractWalletTooltip, setShowSmartContractWalletTooltip] = useState(false) - const [importDialogTokenAddress, setImportDialogTokenAddress] = useState< - string | undefined - >() const { app: { @@ -252,15 +249,14 @@ export function TransferPanel() { ) }, [tokenFromSearchParams, tokensFromLists, tokensFromUser]) - useEffect(() => { + const importDialogTokenAddress = useMemo(() => { if ( typeof isTokenAlreadyImported === 'undefined' || isTokenAlreadyImported ) { - setImportDialogTokenAddress(undefined) - } else { - setImportDialogTokenAddress(tokenFromSearchParams) + return undefined } + return tokenFromSearchParams }, [isTokenAlreadyImported, tokenFromSearchParams]) const isBridgingANewStandardToken = useMemo(() => { diff --git a/packages/arb-token-bridge-ui/tests/e2e/specfiles.json b/packages/arb-token-bridge-ui/tests/e2e/specfiles.json index 7ca5456726..e8bf65a474 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specfiles.json +++ b/packages/arb-token-bridge-ui/tests/e2e/specfiles.json @@ -17,12 +17,12 @@ { "name": "Deposit ERC20", "file": "tests/e2e/specs/**/depositERC20.cy.{js,jsx,ts,tsx}", - "recordVideo": "false" + "recordVideo": "true" }, { "name": "Withdraw ERC20", "file": "tests/e2e/specs/**/withdrawERC20.cy.{js,jsx,ts,tsx}", - "recordVideo": "false" + "recordVideo": "true" }, { "name": "TX history", @@ -32,12 +32,12 @@ { "name": "Approve ERC20", "file": "tests/e2e/specs/**/approveToken.cy.{js,jsx,ts,tsx}", - "recordVideo": "false" + "recordVideo": "true" }, { "name": "Import test ERC20", "file": "tests/e2e/specs/**/importToken.cy.{js,jsx,ts,tsx}", - "recordVideo": "false" + "recordVideo": "true" }, { "name": "Read classic deposits", @@ -47,7 +47,7 @@ { "name": "Redeem Retryable", "file": "tests/e2e/specs/**/redeemRetryable.cy.{js,jsx,ts,tsx}", - "recordVideo": "false" + "recordVideo": "true" }, { "name": "Switch network", From 76965c3029f34102a7089dfa487982c527ce26fd Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 23 Jul 2024 12:19:56 +0200 Subject: [PATCH 23/52] revert --- .../TransferPanel/TransferPanel.tsx | 7 +- .../src/hooks/useArbQueryParams.tsx | 4 +- .../arb-token-bridge-ui/src/pages/index.tsx | 99 ++----------------- 3 files changed, 13 insertions(+), 97 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx index 91dee6a1dd..42d516760e 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx @@ -235,12 +235,7 @@ export function TransferPanel() { return true } - if ( - !tokensFromLists || - !tokensFromUser || - // Wait if token list hasn't been populated yet - Object.keys(tokensFromLists).length === 0 - ) { + if (!tokensFromLists || !tokensFromUser) { return undefined } diff --git a/packages/arb-token-bridge-ui/src/hooks/useArbQueryParams.tsx b/packages/arb-token-bridge-ui/src/hooks/useArbQueryParams.tsx index b66553598c..a86a823de0 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useArbQueryParams.tsx +++ b/packages/arb-token-bridge-ui/src/hooks/useArbQueryParams.tsx @@ -108,7 +108,7 @@ export const AmountQueryParam = { } // Parse chainId to ChainQueryParam or ChainId for orbit chain -export function encodeChainQueryParam( +function encodeChainQueryParam( chainId: number | null | undefined ): string | undefined { if (!chainId) { @@ -133,7 +133,7 @@ function isValidNumber(value: number | null | undefined): value is number { // Parse ChainQueryParam/ChainId to ChainId // URL accept both chainId and chainQueryParam (string) -export function decodeChainQueryParam( +function decodeChainQueryParam( value: string | (string | null)[] | null | undefined // ChainId type doesn't include custom orbit chain, we need to add number type ): ChainId | number | undefined { diff --git a/packages/arb-token-bridge-ui/src/pages/index.tsx b/packages/arb-token-bridge-ui/src/pages/index.tsx index d685989c9e..0b7d279b2c 100644 --- a/packages/arb-token-bridge-ui/src/pages/index.tsx +++ b/packages/arb-token-bridge-ui/src/pages/index.tsx @@ -1,5 +1,4 @@ import React, { useEffect } from 'react' -import { GetServerSidePropsContext, GetServerSidePropsResult } from 'next' import dynamic from 'next/dynamic' import { registerCustomArbitrumNetwork } from '@arbitrum/sdk' @@ -9,12 +8,6 @@ import { mapCustomChainToNetworkData } from '../util/networks' import { getOrbitChains } from '../util/orbitChainsList' -import { sanitizeQueryParams } from '../hooks/useNetworks' -import { - decodeChainQueryParam, - encodeChainQueryParam, - AmountQueryParam -} from '../hooks/useArbQueryParams' const App = dynamic(() => import('../components/App/App'), { ssr: false, @@ -28,90 +21,18 @@ const App = dynamic(() => import('../components/App/App'), { ) }) -function getDestinationWithSanitizedQueryParams( - sanitized: { - sourceChainId: number - destinationChainId: number - }, - query: GetServerSidePropsContext['query'] -) { - const params = new URLSearchParams() - for (const key in query) { - const value = query[key] - if (typeof value === 'string') { - params.set(key, value) - } - } - - const amount = AmountQueryParam.encode( - Array.isArray(query['amount']) ? '' : query['amount'] - ) - if (amount) { - params.set('amount', AmountQueryParam.encode(amount)) - } else { - params.delete('amount') - } - params.set('sourceChain', encodeChainQueryParam(sanitized.sourceChainId)!) - params.set( - 'destinationChain', - encodeChainQueryParam(sanitized.destinationChainId)! - ) - - return `/?${params.toString()}` -} - -function addOrbitChainsToArbitrumSDK() { - ;[...getOrbitChains(), ...getCustomChainsFromLocalStorage()].forEach( - chain => { - try { - registerCustomArbitrumNetwork(chain) - mapCustomChainToNetworkData(chain) - } catch (_) { - // already added - } - } - ) -} - -export function getServerSideProps({ - query -}: GetServerSidePropsContext): GetServerSidePropsResult> { - const sourceChainId = decodeChainQueryParam(query.sourceChain) - const destinationChainId = decodeChainQueryParam(query.destinationChain) - - // If both sourceChain and destinationChain are not present, let the client sync with Metamask - if (!sourceChainId && !destinationChainId) { - return { - props: {} - } - } - - addOrbitChainsToArbitrumSDK() - - // sanitize the query params - const sanitized = sanitizeQueryParams({ sourceChainId, destinationChainId }) - - // if the sanitized query params are different from the initial values, redirect to the url with sanitized query params - if ( - sourceChainId !== sanitized.sourceChainId || - destinationChainId !== sanitized.destinationChainId - ) { - return { - redirect: { - permanent: false, - destination: getDestinationWithSanitizedQueryParams(sanitized, query) - } - } - } - - return { - props: {} - } -} - export default function Index() { useEffect(() => { - addOrbitChainsToArbitrumSDK() + ;[...getOrbitChains(), ...getCustomChainsFromLocalStorage()].forEach( + chain => { + try { + registerCustomArbitrumNetwork(chain) + mapCustomChainToNetworkData(chain) + } catch (_) { + // already added + } + } + ) }, []) return From 6dcdf2ad4c88c015336ca02c6f71ab84a39792f4 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 23 Jul 2024 12:43:23 +0200 Subject: [PATCH 24/52] try --- .../arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts | 4 ---- .../arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts | 4 ---- packages/arb-token-bridge-ui/tests/support/commands.ts | 2 ++ 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts index 34445b1617..90bdd4e51d 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts @@ -52,10 +52,6 @@ describe('Deposit ERC20 Token', () => { }) }) - // give some time for the token to update - // eslint-disable-next-line - cy.wait(10_000) - context('should show ERC-20 balance correctly', () => { cy.findByLabelText('WETH balance amount on l1') .should('be.visible') diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts index 889ce5c083..7b91e3b2d2 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts @@ -142,10 +142,6 @@ describe('Withdraw ERC20 Token', () => { }) }) - // give some time for the token to update - // eslint-disable-next-line - cy.wait(10_000) - context('should show summary', () => { cy.findByPlaceholderText('Enter amount') .typeRecursively(String(ERC20AmountToSend)) diff --git a/packages/arb-token-bridge-ui/tests/support/commands.ts b/packages/arb-token-bridge-ui/tests/support/commands.ts index c12efc04de..62097f27f3 100644 --- a/packages/arb-token-bridge-ui/tests/support/commands.ts +++ b/packages/arb-token-bridge-ui/tests/support/commands.ts @@ -230,6 +230,8 @@ export const searchAndSelectToken = ({ cy.findByRole('button', { name: 'Select Token' }) .should('be.visible') .should('have.text', tokenName) + + cy.wait(5_000) }) } From 4b60ae86a86adf03ace1dd3db1bf587aecb81daa Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 23 Jul 2024 13:36:01 +0200 Subject: [PATCH 25/52] reduce rerendering --- .../src/hooks/useSelectedToken.ts | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index 9fa275c5d5..01772433ce 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -1,4 +1,4 @@ -import { useCallback } from 'react' +import { useCallback, useMemo } from 'react' import useSWRImmutable from 'swr/immutable' import { useTokenFromSearchParams } from '../components/TransferPanel/TransferPanelUtils' @@ -64,30 +64,28 @@ export const useSelectedToken = () => { tokensFromUsers[tokenAddressLowercased] || null ) - }, [ - childChainProvider, - parentChainProvider, - tokenFromSearchParams, - tokensFromLists, - tokensFromUsers - ]) - - const { data } = useSWRImmutable( - [ - 'useSelectedToken', - parentChain.id, - childChain.id, - tokenFromSearchParams, - tokensFromLists, - tokensFromUsers - ], - fetcher - ) + }, [childChainProvider, parentChainProvider, tokenFromSearchParams]) + + const shouldFetch = useMemo(() => { + return Boolean(tokensFromLists && tokensFromUsers) + }, [tokensFromLists, tokensFromUsers]) + + const swrKey = useMemo(() => { + return shouldFetch + ? [ + 'useSelectedToken', + parentChain.id, + childChain.id, + tokenFromSearchParams + ] + : null + }, [shouldFetch, parentChain.id, childChain.id, tokenFromSearchParams]) + + const { data } = useSWRImmutable(swrKey, fetcher) const setSelectedToken = useCallback( - (erc20ParentAddress: string | null) => { - setTokenQueryParam(erc20ParentAddress) - }, + (erc20ParentAddress: string | null) => + setTokenQueryParam(erc20ParentAddress), [setTokenQueryParam] ) From ea5e0bc7cc5079e707329cf0c4f623bad6975ad2 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 23 Jul 2024 15:29:23 +0200 Subject: [PATCH 26/52] fixes --- .../TransferPanel/TokenImportDialog.tsx | 23 ++++++++++++------- .../src/hooks/useSelectedToken.ts | 14 +++++++---- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx index a56e6e15b0..c07398ad70 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx @@ -71,7 +71,8 @@ export function TokenImportDialog({ arbTokenBridge: { bridgeTokens, token } } } = useAppState() - const [selectedToken, setSelectedToken] = useSelectedToken() + const [selectedToken, setSelectedToken, refreshSelectedToken] = + useSelectedToken() const [networks] = useNetworks() const { childChain, @@ -264,13 +265,19 @@ export function TokenImportDialog({ ]) async function storeNewToken(newToken: string) { - return token.add(newToken).catch((ex: Error) => { - setStatus(ImportStatus.ERROR) - - if (ex.name === 'TokenDisabledError') { - warningToast('This token is currently paused in the bridge') - } - }) + return ( + token + .add(newToken) + .catch((ex: Error) => { + setStatus(ImportStatus.ERROR) + + if (ex.name === 'TokenDisabledError') { + warningToast('This token is currently paused in the bridge') + } + }) + // slight timeout to make sure token is added to the list + .then(() => setTimeout(refreshSelectedToken, 2_000)) + ) } function handleTokenImport() { diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index 01772433ce..d1c7715996 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -64,13 +64,19 @@ export const useSelectedToken = () => { tokensFromUsers[tokenAddressLowercased] || null ) - }, [childChainProvider, parentChainProvider, tokenFromSearchParams]) + }, [ + childChainProvider, + parentChainProvider, + tokenFromSearchParams, + tokensFromLists, + tokensFromUsers + ]) const shouldFetch = useMemo(() => { return Boolean(tokensFromLists && tokensFromUsers) }, [tokensFromLists, tokensFromUsers]) - const swrKey = useMemo(() => { + const queryKey = useMemo(() => { return shouldFetch ? [ 'useSelectedToken', @@ -81,7 +87,7 @@ export const useSelectedToken = () => { : null }, [shouldFetch, parentChain.id, childChain.id, tokenFromSearchParams]) - const { data } = useSWRImmutable(swrKey, fetcher) + const { data, mutate } = useSWRImmutable(queryKey, fetcher) const setSelectedToken = useCallback( (erc20ParentAddress: string | null) => @@ -89,7 +95,7 @@ export const useSelectedToken = () => { [setTokenQueryParam] ) - return [data ?? null, setSelectedToken] as const + return [data ?? null, setSelectedToken, mutate] as const } async function getUsdcToken({ From 921927fcc6459061aac8c81a99aa2f3c4c4f163e Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 23 Jul 2024 16:11:31 +0200 Subject: [PATCH 27/52] log --- .../arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts index 351547d768..2f8aee2d81 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts @@ -55,6 +55,9 @@ describe('Redeem ERC20 Deposit', () => { tokenAddress: wethTokenAddressL2 }) + console.warn({ l2ERC20bal }) + cy.log(l2ERC20bal) + // check the balance on the destination chain before redeeming context('should show ERC-20 balance correctly', () => { cy.findByLabelText('WETH balance amount on l2') From 552f7f696253ea24596b3cbf2268e1467de72367 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 23 Jul 2024 16:19:26 +0200 Subject: [PATCH 28/52] optimization --- .../src/components/TransferPanel/TransferPanel.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx index 42d516760e..c7a501ec78 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx @@ -231,7 +231,13 @@ export function TransferPanel() { }) const isTokenAlreadyImported = useMemo(() => { - if (isTokenNativeUSDC(tokenFromSearchParams)) { + const tokenLowercased = tokenFromSearchParams?.toLowerCase() + + if (!tokenLowercased) { + return true + } + + if (isTokenNativeUSDC(tokenLowercased)) { return true } @@ -239,8 +245,8 @@ export function TransferPanel() { return undefined } - return Object.keys({ ...tokensFromLists, ...tokensFromUser }).some( - address => address.toLowerCase() === tokenFromSearchParams?.toLowerCase() + return Boolean( + tokensFromLists[tokenLowercased] || tokensFromUser[tokenLowercased] ) }, [tokenFromSearchParams, tokensFromLists, tokensFromUser]) From 12fcae307dfefdf0fa338d31d6c484df543512f3 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 23 Jul 2024 16:54:38 +0200 Subject: [PATCH 29/52] try fix tests --- .../tests/e2e/specs/approveToken.cy.ts | 2 +- .../tests/e2e/specs/depositCctp.cy.ts | 96 +++++++++---------- .../tests/e2e/specs/depositERC20.cy.ts | 4 +- .../tests/e2e/specs/depositETH.cy.ts | 2 +- .../tests/e2e/specs/redeemRetryable.cy.ts | 2 +- .../tests/e2e/specs/withdrawCctp.cy.ts | 96 +++++++++---------- .../tests/e2e/specs/withdrawERC20.cy.ts | 4 +- .../tests/e2e/specs/withdrawETH.cy.ts | 18 ++-- 8 files changed, 113 insertions(+), 111 deletions(-) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/approveToken.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/approveToken.cy.ts index 4ded265c8c..97773feae5 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/approveToken.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/approveToken.cy.ts @@ -63,7 +63,7 @@ describe('Approve token and deposit afterwards', () => { cy.findByRole('button', { name: /Pay approval fee of/ }).click() - cy.confirmMetamaskPermissionToSpend('1') + cy.confirmMetamaskPermissionToSpend({ spendLimit: '1' }) }) }) }) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/depositCctp.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/depositCctp.cy.ts index 4c65f8cc17..58b4e2ae96 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/depositCctp.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/depositCctp.cy.ts @@ -125,20 +125,20 @@ describe('Deposit USDC through CCTP', () => { context('Should display CCTP modal', () => { confirmAndApproveCctpDeposit() - cy.confirmMetamaskPermissionToSpend(USDCAmountToSend.toString()).then( - () => { - // eslint-disable-next-line - cy.wait(40_000) - cy.confirmMetamaskTransaction().then(() => { - cy.findByText('Pending transactions').should('be.visible') // tx history should be opened - cy.findByText( - `${formatAmount(USDCAmountToSend, { - symbol: 'USDC' - })}` - ).should('be.visible') - }) - } - ) + cy.confirmMetamaskPermissionToSpend({ + spendLimit: USDCAmountToSend.toString() + }).then(() => { + // eslint-disable-next-line + cy.wait(40_000) + cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { + cy.findByText('Pending transactions').should('be.visible') // tx history should be opened + cy.findByText( + `${formatAmount(USDCAmountToSend, { + symbol: 'USDC' + })}` + ).should('be.visible') + }) + }) }) }) @@ -157,40 +157,40 @@ describe('Deposit USDC through CCTP', () => { context('Should display CCTP modal', () => { confirmAndApproveCctpDeposit() - cy.confirmMetamaskPermissionToSpend(USDCAmountToSend.toString()).then( - () => { - // eslint-disable-next-line - cy.wait(40_000) - cy.confirmMetamaskTransaction().then(() => { - cy.findByText('Pending transactions').should('be.visible') // tx history should be opened - cy.findByText( - `${formatAmount(USDCAmountToSend, { - symbol: 'USDC' - })}` - ).should('be.visible') - - // open the tx details popup - cy.findAllByLabelText('Transaction details button') - .first() - .click() - .then(() => { - cy.findByText('Transaction details').should('be.visible') - - cy.findByText(/CUSTOM ADDRESS/i).should('be.visible') - - // custom destination label in pending tx history should be visible - cy.findByLabelText( - `Custom address: ${shortenAddress( - Cypress.env('CUSTOM_DESTINATION_ADDRESS') - )}` - ).should('be.visible') - }) - - // close popup - cy.findByLabelText('Close transaction details popup').click() - }) - } - ) + cy.confirmMetamaskPermissionToSpend({ + spendLimit: USDCAmountToSend.toString() + }).then(() => { + // eslint-disable-next-line + cy.wait(40_000) + cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { + cy.findByText('Pending transactions').should('be.visible') // tx history should be opened + cy.findByText( + `${formatAmount(USDCAmountToSend, { + symbol: 'USDC' + })}` + ).should('be.visible') + + // open the tx details popup + cy.findAllByLabelText('Transaction details button') + .first() + .click() + .then(() => { + cy.findByText('Transaction details').should('be.visible') + + cy.findByText(/CUSTOM ADDRESS/i).should('be.visible') + + // custom destination label in pending tx history should be visible + cy.findByLabelText( + `Custom address: ${shortenAddress( + Cypress.env('CUSTOM_DESTINATION_ADDRESS') + )}` + ).should('be.visible') + }) + + // close popup + cy.findByLabelText('Close transaction details popup').click() + }) + }) }) }) }) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts index 90bdd4e51d..bcf3490a16 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts @@ -90,7 +90,7 @@ describe('Deposit ERC20 Token', () => { .should('be.enabled') .click() .then(() => { - cy.confirmMetamaskTransaction().then(() => { + cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { cy.findByText('10 minutes').should('be.visible') cy.findByText( `${formatAmount(ERC20AmountToSend, { @@ -138,7 +138,7 @@ describe('Deposit ERC20 Token', () => { .should('be.enabled') .click() .then(() => { - cy.confirmMetamaskTransaction().then(() => { + cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { cy.findByText('10 minutes').should('be.visible') cy.findByText( `${formatAmount(ERC20AmountToSend, { diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/depositETH.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/depositETH.cy.ts index ab7db8c2e7..990646ff15 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/depositETH.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/depositETH.cy.ts @@ -41,7 +41,7 @@ describe('Deposit ETH', () => { cy.findByRole('button', { name: 'Move funds to Arbitrum Local' }).click() - cy.confirmMetamaskTransaction().then(() => { + cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { cy.findByText('10 minutes').should('be.visible') cy.findByText( `${formatAmount(ETHAmountToDeposit, { diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts index 2f8aee2d81..8e4c7639a9 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts @@ -85,7 +85,7 @@ describe('Redeem ERC20 Deposit', () => { .click() // approve redeem transaction - cy.confirmMetamaskTransaction().then(() => { + cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { cy.wait(15_000).then(() => { // switch to settled transactions cy.findByLabelText('show settled transactions') diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts index 4851a7fccb..57b1e597b7 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts @@ -96,20 +96,20 @@ describe('Withdraw USDC through CCTP', () => { context('Should display CCTP modal', () => { confirmAndApproveCctpWithdrawal() - cy.confirmMetamaskPermissionToSpend(USDCAmountToSend.toString()).then( - () => { - // eslint-disable-next-line - cy.wait(40_000) - cy.confirmMetamaskTransaction().then(() => { - cy.findByText('Pending transactions').should('be.visible') // tx history should be opened - cy.findByText( - `${formatAmount(USDCAmountToSend, { - symbol: 'USDC' - })}` - ).should('be.visible') - }) - } - ) + cy.confirmMetamaskPermissionToSpend({ + spendLimit: USDCAmountToSend.toString() + }).then(() => { + // eslint-disable-next-line + cy.wait(40_000) + cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { + cy.findByText('Pending transactions').should('be.visible') // tx history should be opened + cy.findByText( + `${formatAmount(USDCAmountToSend, { + symbol: 'USDC' + })}` + ).should('be.visible') + }) + }) }) }) @@ -134,40 +134,40 @@ describe('Withdraw USDC through CCTP', () => { context('Should display CCTP modal', () => { confirmAndApproveCctpWithdrawal() - cy.confirmMetamaskPermissionToSpend(USDCAmountToSend.toString()).then( - () => { - // eslint-disable-next-line - cy.wait(40_000) - cy.confirmMetamaskTransaction().then(() => { - cy.findByText('Pending transactions').should('be.visible') // tx history should be opened - cy.findByText( - `${formatAmount(USDCAmountToSend, { - symbol: 'USDC' - })}` - ).should('be.visible') - - // open the tx details popup - cy.findAllByLabelText('Transaction details button') - .first() - .click() - .then(() => { - cy.findByText('Transaction details').should('be.visible') - - cy.findByText(/CUSTOM ADDRESS/i).should('be.visible') - - // custom destination label in pending tx history should be visible - cy.findByLabelText( - `Custom address: ${shortenAddress( - Cypress.env('CUSTOM_DESTINATION_ADDRESS') - )}` - ).should('be.visible') - }) - - // close popup - cy.findByLabelText('Close transaction details popup').click() - }) - } - ) + cy.confirmMetamaskPermissionToSpend({ + spendLimit: USDCAmountToSend.toString() + }).then(() => { + // eslint-disable-next-line + cy.wait(40_000) + cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { + cy.findByText('Pending transactions').should('be.visible') // tx history should be opened + cy.findByText( + `${formatAmount(USDCAmountToSend, { + symbol: 'USDC' + })}` + ).should('be.visible') + + // open the tx details popup + cy.findAllByLabelText('Transaction details button') + .first() + .click() + .then(() => { + cy.findByText('Transaction details').should('be.visible') + + cy.findByText(/CUSTOM ADDRESS/i).should('be.visible') + + // custom destination label in pending tx history should be visible + cy.findByLabelText( + `Custom address: ${shortenAddress( + Cypress.env('CUSTOM_DESTINATION_ADDRESS') + )}` + ).should('be.visible') + }) + + // close popup + cy.findByLabelText('Close transaction details popup').click() + }) + }) }) }) }) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts index 7b91e3b2d2..bd242433ab 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts @@ -119,7 +119,7 @@ describe('Withdraw ERC20 Token', () => { .should('be.enabled') .click() - cy.confirmMetamaskTransaction().then(() => { + cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { cy.findByText( `${formatAmount(ERC20AmountToSend, { symbol: 'WETH' @@ -195,7 +195,7 @@ describe('Withdraw ERC20 Token', () => { .should('be.enabled') .click() - cy.confirmMetamaskTransaction().then(() => { + cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { cy.findByText( `${formatAmount(ERC20AmountToSend, { symbol: 'WETH' diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts index 0528435786..2a64a3c6b6 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts @@ -88,14 +88,16 @@ describe('Withdraw ETH', () => { .should('be.enabled') .click() .then(() => { - cy.confirmMetamaskTransaction().then(() => { - cy.findByText('an hour').should('be.visible') - cy.findByText( - `${formatAmount(ETHToWithdraw, { - symbol: 'ETH' - })}` - ).should('be.visible') - }) + cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then( + () => { + cy.findByText('an hour').should('be.visible') + cy.findByText( + `${formatAmount(ETHToWithdraw, { + symbol: 'ETH' + })}` + ).should('be.visible') + } + ) }) }) }) From 93529c982554dcbe8c7a936f2436aa7fac149a5f Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 23 Jul 2024 17:44:08 +0200 Subject: [PATCH 30/52] Revert "try fix tests" This reverts commit 12fcae307dfefdf0fa338d31d6c484df543512f3. --- .../tests/e2e/specs/approveToken.cy.ts | 2 +- .../tests/e2e/specs/depositCctp.cy.ts | 96 +++++++++---------- .../tests/e2e/specs/depositERC20.cy.ts | 4 +- .../tests/e2e/specs/depositETH.cy.ts | 2 +- .../tests/e2e/specs/redeemRetryable.cy.ts | 2 +- .../tests/e2e/specs/withdrawCctp.cy.ts | 96 +++++++++---------- .../tests/e2e/specs/withdrawERC20.cy.ts | 4 +- .../tests/e2e/specs/withdrawETH.cy.ts | 18 ++-- 8 files changed, 111 insertions(+), 113 deletions(-) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/approveToken.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/approveToken.cy.ts index 97773feae5..4ded265c8c 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/approveToken.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/approveToken.cy.ts @@ -63,7 +63,7 @@ describe('Approve token and deposit afterwards', () => { cy.findByRole('button', { name: /Pay approval fee of/ }).click() - cy.confirmMetamaskPermissionToSpend({ spendLimit: '1' }) + cy.confirmMetamaskPermissionToSpend('1') }) }) }) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/depositCctp.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/depositCctp.cy.ts index 58b4e2ae96..4c65f8cc17 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/depositCctp.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/depositCctp.cy.ts @@ -125,20 +125,20 @@ describe('Deposit USDC through CCTP', () => { context('Should display CCTP modal', () => { confirmAndApproveCctpDeposit() - cy.confirmMetamaskPermissionToSpend({ - spendLimit: USDCAmountToSend.toString() - }).then(() => { - // eslint-disable-next-line - cy.wait(40_000) - cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { - cy.findByText('Pending transactions').should('be.visible') // tx history should be opened - cy.findByText( - `${formatAmount(USDCAmountToSend, { - symbol: 'USDC' - })}` - ).should('be.visible') - }) - }) + cy.confirmMetamaskPermissionToSpend(USDCAmountToSend.toString()).then( + () => { + // eslint-disable-next-line + cy.wait(40_000) + cy.confirmMetamaskTransaction().then(() => { + cy.findByText('Pending transactions').should('be.visible') // tx history should be opened + cy.findByText( + `${formatAmount(USDCAmountToSend, { + symbol: 'USDC' + })}` + ).should('be.visible') + }) + } + ) }) }) @@ -157,40 +157,40 @@ describe('Deposit USDC through CCTP', () => { context('Should display CCTP modal', () => { confirmAndApproveCctpDeposit() - cy.confirmMetamaskPermissionToSpend({ - spendLimit: USDCAmountToSend.toString() - }).then(() => { - // eslint-disable-next-line - cy.wait(40_000) - cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { - cy.findByText('Pending transactions').should('be.visible') // tx history should be opened - cy.findByText( - `${formatAmount(USDCAmountToSend, { - symbol: 'USDC' - })}` - ).should('be.visible') - - // open the tx details popup - cy.findAllByLabelText('Transaction details button') - .first() - .click() - .then(() => { - cy.findByText('Transaction details').should('be.visible') - - cy.findByText(/CUSTOM ADDRESS/i).should('be.visible') - - // custom destination label in pending tx history should be visible - cy.findByLabelText( - `Custom address: ${shortenAddress( - Cypress.env('CUSTOM_DESTINATION_ADDRESS') - )}` - ).should('be.visible') - }) - - // close popup - cy.findByLabelText('Close transaction details popup').click() - }) - }) + cy.confirmMetamaskPermissionToSpend(USDCAmountToSend.toString()).then( + () => { + // eslint-disable-next-line + cy.wait(40_000) + cy.confirmMetamaskTransaction().then(() => { + cy.findByText('Pending transactions').should('be.visible') // tx history should be opened + cy.findByText( + `${formatAmount(USDCAmountToSend, { + symbol: 'USDC' + })}` + ).should('be.visible') + + // open the tx details popup + cy.findAllByLabelText('Transaction details button') + .first() + .click() + .then(() => { + cy.findByText('Transaction details').should('be.visible') + + cy.findByText(/CUSTOM ADDRESS/i).should('be.visible') + + // custom destination label in pending tx history should be visible + cy.findByLabelText( + `Custom address: ${shortenAddress( + Cypress.env('CUSTOM_DESTINATION_ADDRESS') + )}` + ).should('be.visible') + }) + + // close popup + cy.findByLabelText('Close transaction details popup').click() + }) + } + ) }) }) }) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts index bcf3490a16..90bdd4e51d 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts @@ -90,7 +90,7 @@ describe('Deposit ERC20 Token', () => { .should('be.enabled') .click() .then(() => { - cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { + cy.confirmMetamaskTransaction().then(() => { cy.findByText('10 minutes').should('be.visible') cy.findByText( `${formatAmount(ERC20AmountToSend, { @@ -138,7 +138,7 @@ describe('Deposit ERC20 Token', () => { .should('be.enabled') .click() .then(() => { - cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { + cy.confirmMetamaskTransaction().then(() => { cy.findByText('10 minutes').should('be.visible') cy.findByText( `${formatAmount(ERC20AmountToSend, { diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/depositETH.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/depositETH.cy.ts index 990646ff15..ab7db8c2e7 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/depositETH.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/depositETH.cy.ts @@ -41,7 +41,7 @@ describe('Deposit ETH', () => { cy.findByRole('button', { name: 'Move funds to Arbitrum Local' }).click() - cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { + cy.confirmMetamaskTransaction().then(() => { cy.findByText('10 minutes').should('be.visible') cy.findByText( `${formatAmount(ETHAmountToDeposit, { diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts index 8e4c7639a9..2f8aee2d81 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts @@ -85,7 +85,7 @@ describe('Redeem ERC20 Deposit', () => { .click() // approve redeem transaction - cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { + cy.confirmMetamaskTransaction().then(() => { cy.wait(15_000).then(() => { // switch to settled transactions cy.findByLabelText('show settled transactions') diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts index 57b1e597b7..4851a7fccb 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts @@ -96,20 +96,20 @@ describe('Withdraw USDC through CCTP', () => { context('Should display CCTP modal', () => { confirmAndApproveCctpWithdrawal() - cy.confirmMetamaskPermissionToSpend({ - spendLimit: USDCAmountToSend.toString() - }).then(() => { - // eslint-disable-next-line - cy.wait(40_000) - cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { - cy.findByText('Pending transactions').should('be.visible') // tx history should be opened - cy.findByText( - `${formatAmount(USDCAmountToSend, { - symbol: 'USDC' - })}` - ).should('be.visible') - }) - }) + cy.confirmMetamaskPermissionToSpend(USDCAmountToSend.toString()).then( + () => { + // eslint-disable-next-line + cy.wait(40_000) + cy.confirmMetamaskTransaction().then(() => { + cy.findByText('Pending transactions').should('be.visible') // tx history should be opened + cy.findByText( + `${formatAmount(USDCAmountToSend, { + symbol: 'USDC' + })}` + ).should('be.visible') + }) + } + ) }) }) @@ -134,40 +134,40 @@ describe('Withdraw USDC through CCTP', () => { context('Should display CCTP modal', () => { confirmAndApproveCctpWithdrawal() - cy.confirmMetamaskPermissionToSpend({ - spendLimit: USDCAmountToSend.toString() - }).then(() => { - // eslint-disable-next-line - cy.wait(40_000) - cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { - cy.findByText('Pending transactions').should('be.visible') // tx history should be opened - cy.findByText( - `${formatAmount(USDCAmountToSend, { - symbol: 'USDC' - })}` - ).should('be.visible') - - // open the tx details popup - cy.findAllByLabelText('Transaction details button') - .first() - .click() - .then(() => { - cy.findByText('Transaction details').should('be.visible') - - cy.findByText(/CUSTOM ADDRESS/i).should('be.visible') - - // custom destination label in pending tx history should be visible - cy.findByLabelText( - `Custom address: ${shortenAddress( - Cypress.env('CUSTOM_DESTINATION_ADDRESS') - )}` - ).should('be.visible') - }) - - // close popup - cy.findByLabelText('Close transaction details popup').click() - }) - }) + cy.confirmMetamaskPermissionToSpend(USDCAmountToSend.toString()).then( + () => { + // eslint-disable-next-line + cy.wait(40_000) + cy.confirmMetamaskTransaction().then(() => { + cy.findByText('Pending transactions').should('be.visible') // tx history should be opened + cy.findByText( + `${formatAmount(USDCAmountToSend, { + symbol: 'USDC' + })}` + ).should('be.visible') + + // open the tx details popup + cy.findAllByLabelText('Transaction details button') + .first() + .click() + .then(() => { + cy.findByText('Transaction details').should('be.visible') + + cy.findByText(/CUSTOM ADDRESS/i).should('be.visible') + + // custom destination label in pending tx history should be visible + cy.findByLabelText( + `Custom address: ${shortenAddress( + Cypress.env('CUSTOM_DESTINATION_ADDRESS') + )}` + ).should('be.visible') + }) + + // close popup + cy.findByLabelText('Close transaction details popup').click() + }) + } + ) }) }) }) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts index bd242433ab..7b91e3b2d2 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts @@ -119,7 +119,7 @@ describe('Withdraw ERC20 Token', () => { .should('be.enabled') .click() - cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { + cy.confirmMetamaskTransaction().then(() => { cy.findByText( `${formatAmount(ERC20AmountToSend, { symbol: 'WETH' @@ -195,7 +195,7 @@ describe('Withdraw ERC20 Token', () => { .should('be.enabled') .click() - cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then(() => { + cy.confirmMetamaskTransaction().then(() => { cy.findByText( `${formatAmount(ERC20AmountToSend, { symbol: 'WETH' diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts index 2a64a3c6b6..0528435786 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts @@ -88,16 +88,14 @@ describe('Withdraw ETH', () => { .should('be.enabled') .click() .then(() => { - cy.confirmMetamaskTransaction({ gasConfig: 'market' }).then( - () => { - cy.findByText('an hour').should('be.visible') - cy.findByText( - `${formatAmount(ETHToWithdraw, { - symbol: 'ETH' - })}` - ).should('be.visible') - } - ) + cy.confirmMetamaskTransaction().then(() => { + cy.findByText('an hour').should('be.visible') + cy.findByText( + `${formatAmount(ETHToWithdraw, { + symbol: 'ETH' + })}` + ).should('be.visible') + }) }) }) }) From 9acd0270c10b709bb423c3bf3335ef84012982b0 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 23 Jul 2024 17:47:57 +0200 Subject: [PATCH 31/52] fix --- .../tests/e2e/specs/importToken.cy.ts | 10 +--------- .../tests/e2e/specs/redeemRetryable.cy.ts | 3 --- packages/arb-token-bridge-ui/tests/support/commands.ts | 2 -- 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts index b2014b43d7..924b0228d0 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts @@ -194,7 +194,7 @@ describe('Import token', () => { // waiting for metamask notification to disappear // eslint-disable-next-line - cy.wait(30_000) + cy.wait(3000) // Modal is displayed cy.get('h2') @@ -233,10 +233,6 @@ describe('Import token', () => { } }) - // waiting for metamask notification to disappear - // eslint-disable-next-line - cy.wait(30_000) - // Modal is displayed cy.get('h2') .contains(/import unknown token/i) @@ -279,10 +275,6 @@ describe('Import token', () => { } }) - // make sure the import dialog appears - // eslint-disable-next-line - cy.wait(30_000) - // Modal is displayed cy.get('h2').contains(/invalid token address/i) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts index 2f8aee2d81..351547d768 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts @@ -55,9 +55,6 @@ describe('Redeem ERC20 Deposit', () => { tokenAddress: wethTokenAddressL2 }) - console.warn({ l2ERC20bal }) - cy.log(l2ERC20bal) - // check the balance on the destination chain before redeeming context('should show ERC-20 balance correctly', () => { cy.findByLabelText('WETH balance amount on l2') diff --git a/packages/arb-token-bridge-ui/tests/support/commands.ts b/packages/arb-token-bridge-ui/tests/support/commands.ts index 62097f27f3..c12efc04de 100644 --- a/packages/arb-token-bridge-ui/tests/support/commands.ts +++ b/packages/arb-token-bridge-ui/tests/support/commands.ts @@ -230,8 +230,6 @@ export const searchAndSelectToken = ({ cy.findByRole('button', { name: 'Select Token' }) .should('be.visible') .should('have.text', tokenName) - - cy.wait(5_000) }) } From f06e4b254e12f967926b1c59146f3995179e186b Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 23 Jul 2024 17:48:46 +0200 Subject: [PATCH 32/52] fix --- .../arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts index 924b0228d0..3b31c77750 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts @@ -233,11 +233,14 @@ describe('Import token', () => { } }) + // waiting for metamask notification to disappear + // eslint-disable-next-line + cy.wait(3000) + // Modal is displayed cy.get('h2') .contains(/import unknown token/i) .should('be.visible') - cy.findByText(new RegExp(ERC20TokenName, 'i')).should('be.visible') // Modal should always display L1 address regardless of query parameter cy.findByText(new RegExp(ERC20TokenAddressL1, 'i')).should('be.visible') From 9eb34000d19db321f4535f04d00a4e300bfed2e2 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 23 Jul 2024 18:08:57 +0200 Subject: [PATCH 33/52] remove videos --- packages/arb-token-bridge-ui/synpress.config.ts | 2 +- packages/arb-token-bridge-ui/tests/e2e/specfiles.json | 10 +++++----- .../tests/e2e/specs/importToken.cy.ts | 2 -- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/arb-token-bridge-ui/synpress.config.ts b/packages/arb-token-bridge-ui/synpress.config.ts index 954a595d4d..1e2123d8f3 100644 --- a/packages/arb-token-bridge-ui/synpress.config.ts +++ b/packages/arb-token-bridge-ui/synpress.config.ts @@ -27,7 +27,7 @@ if (process.env.TEST_FILE) { tests = specFiles.map(file => file.file) } -const shouldRecordVideo = true +const shouldRecordVideo = process.env.CYPRESS_RECORD_VIDEO === 'true' export default defineConfig({ userAgent: 'synpress', diff --git a/packages/arb-token-bridge-ui/tests/e2e/specfiles.json b/packages/arb-token-bridge-ui/tests/e2e/specfiles.json index e8bf65a474..7ca5456726 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specfiles.json +++ b/packages/arb-token-bridge-ui/tests/e2e/specfiles.json @@ -17,12 +17,12 @@ { "name": "Deposit ERC20", "file": "tests/e2e/specs/**/depositERC20.cy.{js,jsx,ts,tsx}", - "recordVideo": "true" + "recordVideo": "false" }, { "name": "Withdraw ERC20", "file": "tests/e2e/specs/**/withdrawERC20.cy.{js,jsx,ts,tsx}", - "recordVideo": "true" + "recordVideo": "false" }, { "name": "TX history", @@ -32,12 +32,12 @@ { "name": "Approve ERC20", "file": "tests/e2e/specs/**/approveToken.cy.{js,jsx,ts,tsx}", - "recordVideo": "true" + "recordVideo": "false" }, { "name": "Import test ERC20", "file": "tests/e2e/specs/**/importToken.cy.{js,jsx,ts,tsx}", - "recordVideo": "true" + "recordVideo": "false" }, { "name": "Read classic deposits", @@ -47,7 +47,7 @@ { "name": "Redeem Retryable", "file": "tests/e2e/specs/**/redeemRetryable.cy.{js,jsx,ts,tsx}", - "recordVideo": "true" + "recordVideo": "false" }, { "name": "Switch network", diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts index 3b31c77750..ccb7effdf4 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/importToken.cy.ts @@ -200,7 +200,6 @@ describe('Import token', () => { cy.get('h2') .contains(/import unknown token/i) .should('be.visible') - cy.findByText(new RegExp(ERC20TokenName, 'i')).should('be.visible') cy.findByText(new RegExp(ERC20TokenAddressL1, 'i')).should('be.visible') @@ -280,7 +279,6 @@ describe('Import token', () => { // Modal is displayed cy.get('h2').contains(/invalid token address/i) - cy.findByText(new RegExp(ERC20TokenAddressL1, 'i')).should('not.exist') cy.findByRole('button', { name: 'Import token' }).should('not.exist') From e2c0bac5b068a02a999cdadc19aa3edfff6b245b Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 29 Jul 2024 11:25:51 +0200 Subject: [PATCH 34/52] merge changes --- .../TransferPanelMain/DestinationNetworkBox.tsx | 6 ++---- .../TransferPanelMain/SourceNetworkBox.tsx | 13 ++++++------- .../src/hooks/useSelectedToken.ts | 7 +++++-- 3 files changed, 13 insertions(+), 13 deletions(-) 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 3d2c3327aa..59903112ed 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 @@ -13,7 +13,6 @@ import { TokenBalance } from './TokenBalance' import { useAccount } from 'wagmi' 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' @@ -24,6 +23,7 @@ import { useSelectedTokenBalances } from '../../../hooks/TransferPanel/useSelectedTokenBalances' import { useNativeCurrency } from '../../../hooks/useNativeCurrency' +import { useSelectedToken } from '../../../hooks/useSelectedToken' export function DestinationNetworkBox({ customFeeTokenBalances, @@ -39,9 +39,7 @@ export function DestinationNetworkBox({ const { childChain, childChainProvider, isDepositMode } = useNetworksRelationship(networks) const { isArbitrumOne } = isNetwork(childChain.id) - const { - app: { selectedToken } - } = useAppState() + const [selectedToken] = useSelectedToken() const { ethParentBalance, ethChildBalance, erc20ChildBalances } = useBalances() const selectedTokenBalances = useSelectedTokenBalances() diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/SourceNetworkBox.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/SourceNetworkBox.tsx index 159e5ed993..de984cb2d1 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/SourceNetworkBox.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/SourceNetworkBox.tsx @@ -12,7 +12,7 @@ import { } from '../TransferPanelMain' import { TokenBalance } from './TokenBalance' import { NetworkType } from './utils' -import { useActions, useAppState } from '../../../state' +import { useActions } from '../../../state' import { useNetworks } from '../../../hooks/useNetworks' import { useNativeCurrency } from '../../../hooks/useNativeCurrency' import { useNetworksRelationship } from '../../../hooks/useNetworksRelationship' @@ -31,6 +31,7 @@ import { TransferPanelMainInput } from '../TransferPanelMainInput' import { getBridgeUiConfigForChain } from '../../../util/bridgeUiConfig' import { AmountQueryParamEnum } from '../../../hooks/useArbQueryParams' import { TransferReadinessRichErrorMessage } from '../useTransferReadinessUtils' +import { useSelectedToken } from '../../../hooks/useSelectedToken' export function SourceNetworkBox({ amount, @@ -51,9 +52,7 @@ export function SourceNetworkBox({ const [networks, setNetworks] = useNetworks() const { childChain, childChainProvider, isDepositMode } = useNetworksRelationship(networks) - const { - app: { selectedToken } - } = useAppState() + const [selectedToken, setSelectedToken] = useSelectedToken() const { ethParentBalance, ethChildBalance } = useBalances() const selectedTokenBalances = useSelectedTokenBalances() const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) @@ -81,13 +80,13 @@ export function SourceNetworkBox({ destinationChainId: networks.destinationChain.id }) - actions.app.setSelectedToken(null) + setSelectedToken(null) }, [ - actions.app, networks.destinationChain.id, networks.sourceChain.id, - setNetworks + setNetworks, + setSelectedToken ] ) diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index d1c7715996..e994537333 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -87,7 +87,10 @@ export const useSelectedToken = () => { : null }, [shouldFetch, parentChain.id, childChain.id, tokenFromSearchParams]) - const { data, mutate } = useSWRImmutable(queryKey, fetcher) + const { data, mutate: refreshSelectedToken } = useSWRImmutable( + queryKey, + fetcher + ) const setSelectedToken = useCallback( (erc20ParentAddress: string | null) => @@ -95,7 +98,7 @@ export const useSelectedToken = () => { [setTokenQueryParam] ) - return [data ?? null, setSelectedToken, mutate] as const + return [data ?? null, setSelectedToken, refreshSelectedToken] as const } async function getUsdcToken({ From be37c032e39e9111021199983baa5c957c5205a2 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 29 Jul 2024 12:22:44 +0200 Subject: [PATCH 35/52] clean up --- .../components/TransferPanel/TransferPanel.tsx | 5 +++-- .../src/hooks/useSelectedToken.ts | 15 +++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx index b56fa4c2ee..8dfc63384a 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx @@ -227,8 +227,9 @@ export function TransferPanel() { return undefined } - return Boolean( - tokensFromLists[tokenLowercased] || tokensFromUser[tokenLowercased] + return ( + typeof tokensFromLists[tokenLowercased] !== 'undefined' || + typeof tokensFromUser[tokenLowercased] !== 'undefined' ) }, [tokenFromSearchParams, tokensFromLists, tokensFromUser]) diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index e994537333..be4db68b01 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -37,7 +37,7 @@ export const useSelectedToken = () => { const { childChain, childChainProvider, parentChain, parentChainProvider } = useNetworksRelationship(networks) const tokensFromLists = useTokensFromLists() - const tokensFromUsers = useTokensFromUser() + const tokensFromUser = useTokensFromUser() const fetcher: () => Promise = useCallback(async () => { @@ -55,13 +55,13 @@ export const useSelectedToken = () => { }) } - if (!tokensFromLists || !tokensFromUsers) { + if (!tokensFromLists || !tokensFromUser) { return null } return ( tokensFromLists[tokenAddressLowercased] || - tokensFromUsers[tokenAddressLowercased] || + tokensFromUser[tokenAddressLowercased] || null ) }, [ @@ -69,12 +69,15 @@ export const useSelectedToken = () => { parentChainProvider, tokenFromSearchParams, tokensFromLists, - tokensFromUsers + tokensFromUser ]) const shouldFetch = useMemo(() => { - return Boolean(tokensFromLists && tokensFromUsers) - }, [tokensFromLists, tokensFromUsers]) + return ( + typeof tokensFromLists !== 'undefined' && + typeof tokensFromUser !== 'undefined' + ) + }, [tokensFromLists, tokensFromUser]) const queryKey = useMemo(() => { return shouldFetch From b5dd720c3629728f8b09d98715a268558b5a2ac2 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 29 Jul 2024 12:44:50 +0200 Subject: [PATCH 36/52] fix usdce bals --- .../src/hooks/CCTP/useUpdateUSDCBalances.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/CCTP/useUpdateUSDCBalances.ts b/packages/arb-token-bridge-ui/src/hooks/CCTP/useUpdateUSDCBalances.ts index 48e1bca0db..d01811b774 100644 --- a/packages/arb-token-bridge-ui/src/hooks/CCTP/useUpdateUSDCBalances.ts +++ b/packages/arb-token-bridge-ui/src/hooks/CCTP/useUpdateUSDCBalances.ts @@ -32,7 +32,9 @@ export function useUpdateUSDCBalances({ const { isEthereumMainnet, isSepolia, isArbitrumOne, isArbitrumSepolia } = isNetwork(parentChain.id) - let parentChainUsdcAddress, childChainUsdcAddress: string | undefined + let parentChainUsdcAddress, + childChainUsdcAddress, + childChainUsdceAddress: string | undefined if (isEthereumMainnet || isSepolia) { parentChainUsdcAddress = isEthereumMainnet @@ -42,6 +44,10 @@ export function useUpdateUSDCBalances({ childChainUsdcAddress = isEthereumMainnet ? CommonAddress.ArbitrumOne.USDC : CommonAddress.ArbitrumSepolia.USDC + + childChainUsdceAddress = isEthereumMainnet + ? CommonAddress.ArbitrumOne['USDC.e'] + : CommonAddress.ArbitrumSepolia['USDC.e'] } if (isArbitrumOne || isArbitrumSepolia) { @@ -76,6 +82,10 @@ export function useUpdateUSDCBalances({ if (childChainUsdcAddress) { updateErc20ChildBalance([childChainUsdcAddress]) } + + if (childChainUsdceAddress) { + updateErc20ChildBalance([childChainUsdceAddress]) + } }, [ childChainProvider, parentChain.id, From afa23a3445444d4d22ff3c7494abeaf9f534fbb0 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 5 Aug 2024 12:26:40 +0200 Subject: [PATCH 37/52] fix --- .../TransferPanel/TransferPanelMain/useMaxAmount.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/useMaxAmount.ts b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/useMaxAmount.ts index d023eba854..6449878d07 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/useMaxAmount.ts +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/useMaxAmount.ts @@ -3,7 +3,6 @@ import { utils } from 'ethers' import { useNetworksRelationship } from '../../../hooks/useNetworksRelationship' import { useNetworks } from '../../../hooks/useNetworks' -import { useAppState } from '../../../state' import { Balances, useSelectedTokenBalances @@ -12,15 +11,14 @@ import { defaultErc20Decimals } from '../../../defaults' import { useGasSummary } from '../../../hooks/TransferPanel/useGasSummary' import { useNativeCurrency } from '../../../hooks/useNativeCurrency' import { useBalances } from '../../../hooks/useBalances' +import { useSelectedToken } from '../../../hooks/useSelectedToken' export function useMaxAmount({ customFeeTokenBalances }: { customFeeTokenBalances: Balances }) { - const { - app: { selectedToken } - } = useAppState() + const [selectedToken] = useSelectedToken() const selectedTokenBalances = useSelectedTokenBalances() const [networks] = useNetworks() const { childChainProvider, isDepositMode } = From b8b35c5d37a097e52f547bd87a57cb66f50fd3b2 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 6 Aug 2024 10:08:48 +0200 Subject: [PATCH 38/52] address hook comments --- .../src/hooks/useSelectedToken.ts | 89 +++++++++---------- 1 file changed, 42 insertions(+), 47 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index be4db68b01..bbab178e30 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -1,5 +1,10 @@ import { useCallback, useMemo } from 'react' import useSWRImmutable from 'swr/immutable' +import { Provider } from '@ethersproject/providers' +import { + getChainIdFromProvider, + getProviderForChainId +} from '@/token-bridge-sdk/utils' import { useTokenFromSearchParams } from '../components/TransferPanel/TransferPanelUtils' import { ERC20BridgeToken, TokenType } from './arbTokenBridge.types' @@ -15,8 +20,6 @@ import { useNetworks } from './useNetworks' import { isNetwork } from '../util/networks' import { CommonAddress } from '../util/CommonAddressUtils' import { useNetworksRelationship } from './useNetworksRelationship' -import { Provider } from '@ethersproject/providers' -import { getChainIdFromProvider } from '@/token-bridge-sdk/utils' import { useTokensFromLists, useTokensFromUser @@ -34,15 +37,35 @@ export const useSelectedToken = () => { const { tokenFromSearchParams, setTokenQueryParam } = useTokenFromSearchParams() const [networks] = useNetworks() - const { childChain, childChainProvider, parentChain, parentChainProvider } = - useNetworksRelationship(networks) + const { childChain, parentChain } = useNetworksRelationship(networks) const tokensFromLists = useTokensFromLists() const tokensFromUser = useTokensFromUser() - const fetcher: () => Promise = - useCallback(async () => { + const queryKey = useMemo(() => { + return tokensFromLists && tokensFromUser + ? ([ + parentChain.id, + childChain.id, + tokenFromSearchParams, + 'useSelectedToken' + ] as const) + : null + }, [ + tokensFromLists, + tokensFromUser, + parentChain.id, + childChain.id, + tokenFromSearchParams + ]) + + const { data, mutate: refreshSelectedToken } = useSWRImmutable( + queryKey, + async ([parentChainId, childChainId]) => { const tokenAddressLowercased = tokenFromSearchParams?.toLowerCase() + const parentProvider = getProviderForChainId(parentChainId) + const childProvider = getProviderForChainId(childChainId) + if (!tokenAddressLowercased) { return null } @@ -50,8 +73,8 @@ export const useSelectedToken = () => { if (isTokenNativeUSDC(tokenAddressLowercased)) { return getUsdcToken({ tokenAddress: tokenAddressLowercased, - parentChainProvider, - childChainProvider + parentProvider, + childProvider }) } @@ -64,35 +87,7 @@ export const useSelectedToken = () => { tokensFromUser[tokenAddressLowercased] || null ) - }, [ - childChainProvider, - parentChainProvider, - tokenFromSearchParams, - tokensFromLists, - tokensFromUser - ]) - - const shouldFetch = useMemo(() => { - return ( - typeof tokensFromLists !== 'undefined' && - typeof tokensFromUser !== 'undefined' - ) - }, [tokensFromLists, tokensFromUser]) - - const queryKey = useMemo(() => { - return shouldFetch - ? [ - 'useSelectedToken', - parentChain.id, - childChain.id, - tokenFromSearchParams - ] - : null - }, [shouldFetch, parentChain.id, childChain.id, tokenFromSearchParams]) - - const { data, mutate: refreshSelectedToken } = useSWRImmutable( - queryKey, - fetcher + } ) const setSelectedToken = useCallback( @@ -106,15 +101,15 @@ export const useSelectedToken = () => { async function getUsdcToken({ tokenAddress, - parentChainProvider, - childChainProvider + parentProvider, + childProvider }: { tokenAddress: string - parentChainProvider: Provider - childChainProvider: Provider -}) { - const parentChainId = await getChainIdFromProvider(parentChainProvider) - const childChainId = await getChainIdFromProvider(childChainProvider) + parentProvider: Provider + childProvider: Provider +}): Promise { + const parentChainId = await getChainIdFromProvider(parentProvider) + const childChainId = await getChainIdFromProvider(childProvider) const { isEthereumMainnet: isParentChainEthereumMainnet, @@ -141,7 +136,7 @@ async function getUsdcToken({ } } - // Arbitrum One USDC when Ethereum is the parent chain + // Arbitrum One USDC when Ethereum is the par if (isTokenArbitrumOneNativeUSDC(tokenAddress) && !isParentChainArbitrumOne) { return { ...commonUSDC, @@ -174,8 +169,8 @@ async function getUsdcToken({ ? ( await getL2ERC20Address({ erc20L1Address: tokenAddress, - l1Provider: parentChainProvider, - l2Provider: childChainProvider + l1Provider: parentProvider, + l2Provider: childProvider }) ).toLowerCase() : undefined From 15245361f725a0b607bd77fab007140cca4f7a8f Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 6 Aug 2024 11:43:57 +0200 Subject: [PATCH 39/52] fixes --- .../components/TransferPanel/TokenSearch.tsx | 4 +- .../TransferPanel/TransferPanel.tsx | 13 +++---- .../TransferPanel/TransferPanelUtils.ts | 37 ------------------- .../src/hooks/useArbQueryParams.tsx | 3 +- .../src/hooks/useSelectedToken.ts | 20 +++++++--- 5 files changed, 23 insertions(+), 54 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx index f6b57989f7..79c45fa3f7 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx @@ -36,7 +36,6 @@ import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' import { useTransferDisabledDialogStore } from './TransferDisabledDialog' import { isWithdrawOnlyToken } from '../../util/WithdrawOnlyUtils' import { isTransferDisabledToken } from '../../util/TokenTransferDisabledUtils' -import { useTokenFromSearchParams } from './TransferPanelUtils' import { Switch } from '../common/atoms/Switch' import { isTeleportEnabledToken } from '../../util/TokenTeleportEnabledUtils' import { useSelectedToken } from '../../hooks/useSelectedToken' @@ -533,7 +532,6 @@ export function TokenSearch({ } = useNetworksRelationship(networks) const { openDialog: openTransferDisabledDialog } = useTransferDisabledDialogStore() - const { setTokenQueryParam } = useTokenFromSearchParams() const { isValidating: isFetchingTokenLists } = useTokenLists(childChain.id) // to show a small loader while token-lists are loading when search panel opens @@ -562,7 +560,7 @@ export function TokenSearch({ // Token not added to the bridge, so we'll handle importing it if (typeof bridgeTokens[_token.address] === 'undefined') { - setTokenQueryParam(_token.address) + setSelectedToken(_token.address) return } diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx index 8dfc63384a..8747735c5b 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx @@ -54,8 +54,7 @@ import { useNativeCurrency } from '../../hooks/useNativeCurrency' import { AssetType } from '../../hooks/arbTokenBridge.types' import { ImportTokenModalStatus, - getWarningTokenDescription, - useTokenFromSearchParams + getWarningTokenDescription } from './TransferPanelUtils' import { useImportTokenModal } from '../../hooks/TransferPanel/useImportTokenModal' import { useTransferReadiness } from './useTransferReadiness' @@ -92,9 +91,7 @@ const networkConnectionWarningToast = () => ) export function TransferPanel() { - const { tokenFromSearchParams, setTokenQueryParam } = - useTokenFromSearchParams() - + const [{ token: tokenFromSearchParams }] = useArbQueryParams() const [tokenDepositCheckDialogType, setTokenDepositCheckDialogType] = useState('new-token') const [importTokenModalStatus, setImportTokenModalStatus] = @@ -110,7 +107,7 @@ export function TransferPanel() { warningTokens } } = useAppState() - const [selectedToken] = useSelectedToken() + const [selectedToken, setSelectedToken] = useSelectedToken() const { layout } = useAppContextState() const { isTransferring } = layout const { address: walletAddress, isConnected } = useAccount() @@ -197,7 +194,7 @@ export function TransferPanel() { ) function closeWithResetTokenImportDialog() { - setTokenQueryParam(null) + setSelectedToken(null) setImportTokenModalStatus(ImportTokenModalStatus.CLOSED) tokenImportDialogProps.onClose(false) } @@ -1114,7 +1111,7 @@ export function TransferPanel() { )} - {typeof importDialogTokenAddress !== 'undefined' && ( + {importDialogTokenAddress && ( void -} { - const [{ token: tokenFromSearchParams }, setQueryParams] = useArbQueryParams() - - const setTokenQueryParam = (token: string | null) => - setQueryParams({ token: sanitizeTokenSearchParams(token) }) - - if (!tokenFromSearchParams) { - return { - tokenFromSearchParams: undefined, - setTokenQueryParam - } - } - - return { - tokenFromSearchParams, - setTokenQueryParam - } -} diff --git a/packages/arb-token-bridge-ui/src/hooks/useArbQueryParams.tsx b/packages/arb-token-bridge-ui/src/hooks/useArbQueryParams.tsx index b66553598c..f53a7d781e 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useArbQueryParams.tsx +++ b/packages/arb-token-bridge-ui/src/hooks/useArbQueryParams.tsx @@ -174,7 +174,8 @@ export function ArbQueryParamProvider({ searchStringToObject: queryString.parse, objectToSearchString: queryString.stringify, updateType: 'replaceIn', // replace just a single parameter when updating query-state, leaving the rest as is - removeDefaultsFromUrl: true + removeDefaultsFromUrl: true, + enableBatching: true }} > {children} diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index bbab178e30..024ee0e663 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -1,4 +1,5 @@ import { useCallback, useMemo } from 'react' +import { utils } from 'ethers' import useSWRImmutable from 'swr/immutable' import { Provider } from '@ethersproject/providers' import { @@ -6,7 +7,6 @@ import { getProviderForChainId } from '@/token-bridge-sdk/utils' -import { useTokenFromSearchParams } from '../components/TransferPanel/TransferPanelUtils' import { ERC20BridgeToken, TokenType } from './arbTokenBridge.types' import { getL2ERC20Address, @@ -24,6 +24,7 @@ import { useTokensFromLists, useTokensFromUser } from '../components/TransferPanel/TokenSearchUtils' +import { useArbQueryParams } from './useArbQueryParams' const commonUSDC = { name: 'USD Coin', @@ -34,8 +35,7 @@ const commonUSDC = { } export const useSelectedToken = () => { - const { tokenFromSearchParams, setTokenQueryParam } = - useTokenFromSearchParams() + const [{ token: tokenFromSearchParams }, setQueryParams] = useArbQueryParams() const [networks] = useNetworks() const { childChain, parentChain } = useNetworksRelationship(networks) const tokensFromLists = useTokensFromLists() @@ -92,13 +92,23 @@ export const useSelectedToken = () => { const setSelectedToken = useCallback( (erc20ParentAddress: string | null) => - setTokenQueryParam(erc20ParentAddress), - [setTokenQueryParam] + setQueryParams({ token: sanitizeTokenAddress(erc20ParentAddress) }), + [setQueryParams] ) return [data ?? null, setSelectedToken, refreshSelectedToken] as const } +function sanitizeTokenAddress(tokenAddress: string | null): string | undefined { + if (!tokenAddress) { + return undefined + } + if (utils.isAddress(tokenAddress)) { + return tokenAddress + } + return undefined +} + async function getUsdcToken({ tokenAddress, parentProvider, From 99bca1bead592afa3b1722fe115d21360637192c Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 6 Aug 2024 16:49:03 +0200 Subject: [PATCH 40/52] updates --- .../src/hooks/useSelectedToken.ts | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index 024ee0e663..5032e59dc7 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -1,4 +1,4 @@ -import { useCallback, useMemo } from 'react' +import { useCallback } from 'react' import { utils } from 'ethers' import useSWRImmutable from 'swr/immutable' import { Provider } from '@ethersproject/providers' @@ -41,8 +41,8 @@ export const useSelectedToken = () => { const tokensFromLists = useTokensFromLists() const tokensFromUser = useTokensFromUser() - const queryKey = useMemo(() => { - return tokensFromLists && tokensFromUser + const queryKey = + tokensFromLists && tokensFromUser ? ([ parentChain.id, childChain.id, @@ -50,18 +50,11 @@ export const useSelectedToken = () => { 'useSelectedToken' ] as const) : null - }, [ - tokensFromLists, - tokensFromUser, - parentChain.id, - childChain.id, - tokenFromSearchParams - ]) const { data, mutate: refreshSelectedToken } = useSWRImmutable( queryKey, - async ([parentChainId, childChainId]) => { - const tokenAddressLowercased = tokenFromSearchParams?.toLowerCase() + async ([parentChainId, childChainId, _tokenFromSearchParams]) => { + const tokenAddressLowercased = _tokenFromSearchParams?.toLowerCase() const parentProvider = getProviderForChainId(parentChainId) const childProvider = getProviderForChainId(childChainId) From 2bd4938f35cb2ad553b3000842828edda7e6d5f4 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 6 Aug 2024 18:26:20 +0200 Subject: [PATCH 41/52] fix --- .../TransferPanel/TokenImportDialog.tsx | 23 +++++++------------ .../src/hooks/useSelectedToken.ts | 6 +++-- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx index c07398ad70..a56e6e15b0 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx @@ -71,8 +71,7 @@ export function TokenImportDialog({ arbTokenBridge: { bridgeTokens, token } } } = useAppState() - const [selectedToken, setSelectedToken, refreshSelectedToken] = - useSelectedToken() + const [selectedToken, setSelectedToken] = useSelectedToken() const [networks] = useNetworks() const { childChain, @@ -265,19 +264,13 @@ export function TokenImportDialog({ ]) async function storeNewToken(newToken: string) { - return ( - token - .add(newToken) - .catch((ex: Error) => { - setStatus(ImportStatus.ERROR) - - if (ex.name === 'TokenDisabledError') { - warningToast('This token is currently paused in the bridge') - } - }) - // slight timeout to make sure token is added to the list - .then(() => setTimeout(refreshSelectedToken, 2_000)) - ) + return token.add(newToken).catch((ex: Error) => { + setStatus(ImportStatus.ERROR) + + if (ex.name === 'TokenDisabledError') { + warningToast('This token is currently paused in the bridge') + } + }) } function handleTokenImport() { diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index 5032e59dc7..3dfa032ede 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -47,11 +47,13 @@ export const useSelectedToken = () => { parentChain.id, childChain.id, tokenFromSearchParams, + Object.keys(tokensFromLists), + Object.keys(tokensFromUser), 'useSelectedToken' ] as const) : null - const { data, mutate: refreshSelectedToken } = useSWRImmutable( + const { data } = useSWRImmutable( queryKey, async ([parentChainId, childChainId, _tokenFromSearchParams]) => { const tokenAddressLowercased = _tokenFromSearchParams?.toLowerCase() @@ -89,7 +91,7 @@ export const useSelectedToken = () => { [setQueryParams] ) - return [data ?? null, setSelectedToken, refreshSelectedToken] as const + return [data ?? null, setSelectedToken] as const } function sanitizeTokenAddress(tokenAddress: string | null): string | undefined { From c70da1082cdaa50f2326f1e92e7371584b37575f Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 6 Aug 2024 19:08:39 +0200 Subject: [PATCH 42/52] fix --- .../src/components/TransferPanel/TransferPanelMain/hooks.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/hooks.ts b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/hooks.ts index f7923aa51c..e47939309a 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/hooks.ts +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/hooks.ts @@ -53,7 +53,6 @@ export function useUpdateUSDCTokenData() { isDestinationChainArbitrumOne, isDestinationChainArbitrumSepolia, selectedToken, - setSelectedToken, token ]) } From 2fc92cf3e2ae204562cf13d11573a6f098b07ac3 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 9 Aug 2024 12:52:29 +0200 Subject: [PATCH 43/52] token loaders --- .../components/TransferPanel/TokenButton.tsx | 41 +++++++++++++++---- .../TransferPanel/TransferPanel.tsx | 8 +++- 2 files changed, 41 insertions(+), 8 deletions(-) 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 0b8e40ff2d..2159421c1d 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenButton.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenButton.tsx @@ -1,4 +1,5 @@ import { useMemo } from 'react' +import { utils } from 'ethers' import { Popover } from '@headlessui/react' import { ChevronDownIcon } from '@heroicons/react/24/outline' import { twMerge } from 'tailwind-merge' @@ -15,11 +16,16 @@ import { useNetworks } from '../../hooks/useNetworks' import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' import { Transition } from '../common/Transition' import { useSelectedToken } from '../../hooks/useSelectedToken' +import { Loader } from '../common/atoms/Loader' +import { useTokensFromLists } from './TokenSearchUtils' +import { useArbQueryParams } from '../../hooks/useArbQueryParams' export function TokenButton(): JSX.Element { const [selectedToken] = useSelectedToken() const [networks] = useNetworks() const { childChainProvider } = useNetworksRelationship(networks) + const tokensFromLists = useTokensFromLists() + const [{ token: tokenFromSearchParams }] = useArbQueryParams() const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) @@ -34,6 +40,24 @@ export function TokenButton(): JSX.Element { }) }, [selectedToken, networks.sourceChain.id, nativeCurrency.symbol]) + const isLoadingToken = useMemo(() => { + // don't show loader if native currency is selected + if (!tokenFromSearchParams) { + return false + } + if (!utils.isAddress(tokenFromSearchParams)) { + return false + } + // show loader for undefined or empty token lists + if (!tokensFromLists) { + return true + } + if (Object.keys(tokensFromLists).length === 0) { + return true + } + return false + }, [tokensFromLists, tokenFromSearchParams]) + return ( <> @@ -43,6 +67,7 @@ export function TokenButton(): JSX.Element { className="arb-hover h-full w-max rounded-bl rounded-tl px-3 py-3 text-white" aria-label="Select Token" onClick={onPopoverButtonClick} + disabled={isLoadingToken} >
{/* Commenting it out until we update the token image source files to be of better quality */} @@ -58,14 +83,16 @@ export function TokenButton(): JSX.Element { /> )} */} - {tokenSymbol} + {isLoadingToken ? : tokenSymbol} - + {!isLoadingToken && ( + + )}
diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx index 8747735c5b..5e759e0ea8 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx @@ -220,7 +220,13 @@ export function TransferPanel() { return true } - if (!tokensFromLists || !tokensFromUser) { + // only show import token dialog if the token is not part of the list + // otherwise we show a loader in the TokenButton + if (!tokensFromLists || Object.keys(tokensFromLists).length === 0) { + return undefined + } + + if (!tokensFromUser) { return undefined } From 4ffe9126ba20cc54125c92efbcd7cac760833193 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 9 Aug 2024 13:01:22 +0200 Subject: [PATCH 44/52] loader color --- .../src/components/TransferPanel/TokenButton.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 2159421c1d..e4763b2a1a 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenButton.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenButton.tsx @@ -83,7 +83,11 @@ export function TokenButton(): JSX.Element { /> )} */} - {isLoadingToken ? : tokenSymbol} + {isLoadingToken ? ( + + ) : ( + tokenSymbol + )} {!isLoadingToken && ( Date: Wed, 28 Aug 2024 16:11:35 +0200 Subject: [PATCH 45/52] updates --- .../src/components/TransferPanel/TokenSearch.tsx | 6 ------ .../components/common/NetworkSelectionContainer.tsx | 12 ++++++++++-- .../TransferPanel/useIsBatchTransferSupported.ts | 6 ++---- .../src/hooks/useSelectedToken.ts | 1 - 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx index be26a3d2c8..1c72f68d7b 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx @@ -549,12 +549,6 @@ export function TokenSearch({ } try { - if (isTokenNativeUSDC(_token.address)) { - // We don't need to fetch token data because USDC will be hardcoded - setSelectedToken(_token.address) - return - } - if (typeof bridgeTokens === 'undefined') { return } diff --git a/packages/arb-token-bridge-ui/src/components/common/NetworkSelectionContainer.tsx b/packages/arb-token-bridge-ui/src/components/common/NetworkSelectionContainer.tsx index 95994fa261..c6af26b06c 100644 --- a/packages/arb-token-bridge-ui/src/components/common/NetworkSelectionContainer.tsx +++ b/packages/arb-token-bridge-ui/src/components/common/NetworkSelectionContainer.tsx @@ -31,6 +31,7 @@ import { shouldOpenOneNovaDialog } from '../TransferPanel/TransferPanelMain/util import { useActions } from '../../state' import { useChainIdsForNetworkSelection } from '../../hooks/TransferPanel/useChainIdsForNetworkSelection' import { useAccountType } from '../../hooks/useAccountType' +import { useSelectedToken } from '../../hooks/useSelectedToken' type NetworkType = 'core' | 'orbit' @@ -367,6 +368,7 @@ export const NetworkSelectionContainer = ( } ) => { const actions = useActions() + const [, setSelectedToken] = useSelectedToken() const [networks, setNetworks] = useNetworks() const [oneNovaTransferDialogProps, openOneNovaTransferDialog] = useDialog() @@ -404,9 +406,15 @@ export const NetworkSelectionContainer = ( destinationChainId: isSource ? networks.destinationChain.id : value.id }) - actions.app.setSelectedToken(null) + setSelectedToken(null) }, - [actions.app, isSource, networks, openOneNovaTransferDialog, setNetworks] + [ + isSource, + networks, + openOneNovaTransferDialog, + setNetworks, + setSelectedToken + ] ) return ( diff --git a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useIsBatchTransferSupported.ts b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useIsBatchTransferSupported.ts index 13c592d0cb..40cdd26de5 100644 --- a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useIsBatchTransferSupported.ts +++ b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useIsBatchTransferSupported.ts @@ -1,16 +1,14 @@ -import { useAppState } from '../../state' import { isExperimentalFeatureEnabled } from '../../util' import { useNativeCurrency } from '../useNativeCurrency' import { useNetworks } from '../useNetworks' import { useNetworksRelationship } from '../useNetworksRelationship' +import { useSelectedToken } from '../useSelectedToken' export const useIsBatchTransferSupported = () => { const [networks] = useNetworks() const { isDepositMode, isTeleportMode, childChainProvider } = useNetworksRelationship(networks) - const { - app: { selectedToken } - } = useAppState() + const [selectedToken] = useSelectedToken() const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) if (!isExperimentalFeatureEnabled('batch')) { diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index 3dfa032ede..3b0372aed5 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -47,7 +47,6 @@ export const useSelectedToken = () => { parentChain.id, childChain.id, tokenFromSearchParams, - Object.keys(tokensFromLists), Object.keys(tokensFromUser), 'useSelectedToken' ] as const) From b9b567ec6581fabfd7d6e576e50d4193c85db9b2 Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 28 Aug 2024 16:51:41 +0200 Subject: [PATCH 46/52] fix --- packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index 3b0372aed5..131abc3633 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -42,7 +42,7 @@ export const useSelectedToken = () => { const tokensFromUser = useTokensFromUser() const queryKey = - tokensFromLists && tokensFromUser + tokensFromLists && Object.keys(tokensFromLists).length > 0 && tokensFromUser ? ([ parentChain.id, childChain.id, From 04ea158c7f3b418353b329beee4ab96e9ef92162 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 29 Aug 2024 15:50:52 +0200 Subject: [PATCH 47/52] fix tests --- .../components/TransferPanel/TokenButton.tsx | 17 ++++--------- .../src/hooks/useSelectedToken.ts | 25 ++++++++++--------- 2 files changed, 18 insertions(+), 24 deletions(-) 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 67f95177ee..15577ff745 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenButton.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenButton.tsx @@ -17,8 +17,8 @@ import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' import { Transition } from '../common/Transition' import { useSelectedToken } from '../../hooks/useSelectedToken' import { Loader } from '../common/atoms/Loader' -import { useTokensFromLists } from './TokenSearchUtils' import { useArbQueryParams } from '../../hooks/useArbQueryParams' +import { useTokenLists } from '../../hooks/useTokenLists' export type TokenButtonOptions = { symbol?: string @@ -34,8 +34,8 @@ export function TokenButton({ const disabled = options?.disabled ?? false const [networks] = useNetworks() - const { childChainProvider } = useNetworksRelationship(networks) - const tokensFromLists = useTokensFromLists() + const { childChain, childChainProvider } = useNetworksRelationship(networks) + const { isLoading: isLoadingTokenLists } = useTokenLists(childChain.id) const [{ token: tokenFromSearchParams }] = useArbQueryParams() const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) @@ -63,15 +63,8 @@ export function TokenButton({ if (!utils.isAddress(tokenFromSearchParams)) { return false } - // show loader for undefined or empty token lists - if (!tokensFromLists) { - return true - } - if (Object.keys(tokensFromLists).length === 0) { - return true - } - return false - }, [tokensFromLists, tokenFromSearchParams]) + return isLoadingTokenLists + }, [tokenFromSearchParams, isLoadingTokenLists]) return ( <> diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index 131abc3633..4b64a5ed24 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -25,6 +25,7 @@ import { useTokensFromUser } from '../components/TransferPanel/TokenSearchUtils' import { useArbQueryParams } from './useArbQueryParams' +import { useTokenLists } from './useTokenLists' const commonUSDC = { name: 'USD Coin', @@ -40,21 +41,21 @@ export const useSelectedToken = () => { const { childChain, parentChain } = useNetworksRelationship(networks) const tokensFromLists = useTokensFromLists() const tokensFromUser = useTokensFromUser() - - const queryKey = - tokensFromLists && Object.keys(tokensFromLists).length > 0 && tokensFromUser - ? ([ - parentChain.id, - childChain.id, - tokenFromSearchParams, - Object.keys(tokensFromUser), - 'useSelectedToken' - ] as const) - : null + const { isLoading: isLoadingTokenLists } = useTokenLists(childChain.id) + + const queryKey = !isLoadingTokenLists + ? ([ + parentChain.id, + childChain.id, + tokenFromSearchParams, + Object.keys(tokensFromUser), + 'useSelectedToken' + ] as const) + : null const { data } = useSWRImmutable( queryKey, - async ([parentChainId, childChainId, _tokenFromSearchParams]) => { + async ([parentChainId, childChainId, _tokenFromSearchParams, _keys]) => { const tokenAddressLowercased = _tokenFromSearchParams?.toLowerCase() const parentProvider = getProviderForChainId(parentChainId) From 012ae4ad94cbe0d022796d255b4bc5d1dc1ea671 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 2 Sep 2024 13:36:31 +0200 Subject: [PATCH 48/52] fix --- .../components/TransferPanel/TransferPanel.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx index 6b43658c5b..16c1a59b9b 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx @@ -83,6 +83,7 @@ import { useSelectedToken } from '../../hooks/useSelectedToken' import { useBalances } from '../../hooks/useBalances' import { captureSentryErrorWithExtraData } from '../../util/SentryUtils' import { useIsBatchTransferSupported } from '../../hooks/TransferPanel/useIsBatchTransferSupported' +import { useTokenLists } from '../../hooks/useTokenLists' const networkConnectionWarningToast = () => warningToast( @@ -133,6 +134,7 @@ export function TransferPanel() { const latestNetworks = useLatest(networks) const tokensFromLists = useTokensFromLists() const tokensFromUser = useTokensFromUser() + const { isLoading: isLoadingTokenLists } = useTokenLists(childChain.id) const isBatchTransferSupported = useIsBatchTransferSupported() const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) @@ -223,9 +225,13 @@ export function TransferPanel() { return true } + if (isLoadingTokenLists) { + return undefined + } + // only show import token dialog if the token is not part of the list // otherwise we show a loader in the TokenButton - if (!tokensFromLists || Object.keys(tokensFromLists).length === 0) { + if (!tokensFromLists) { return undefined } @@ -237,7 +243,12 @@ export function TransferPanel() { typeof tokensFromLists[tokenLowercased] !== 'undefined' || typeof tokensFromUser[tokenLowercased] !== 'undefined' ) - }, [tokenFromSearchParams, tokensFromLists, tokensFromUser]) + }, [ + isLoadingTokenLists, + tokenFromSearchParams, + tokensFromLists, + tokensFromUser + ]) const importDialogTokenAddress = useMemo(() => { if ( From d7d4dec87b90f0bff18900ddf05e3772bd0a724a Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 9 Sep 2024 11:03:17 +0200 Subject: [PATCH 49/52] fix --- .../src/hooks/TransferPanel/useGasEstimates.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useGasEstimates.ts b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useGasEstimates.ts index a1be6322c8..5e81cfab5c 100644 --- a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useGasEstimates.ts +++ b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useGasEstimates.ts @@ -5,9 +5,9 @@ import { useAccount, useSigner } from 'wagmi' import { DepositGasEstimates, GasEstimates } from '../arbTokenBridge.types' import { BridgeTransferStarterFactory } from '@/token-bridge-sdk/BridgeTransferStarterFactory' import { getProviderForChainId } from '@/token-bridge-sdk/utils' -import { useAppState } from '../../state' import { useBalanceOnSourceChain } from '../useBalanceOnSourceChain' import { useNetworks } from '../useNetworks' +import { useSelectedToken } from '../useSelectedToken' async function fetcher([ signer, @@ -51,11 +51,9 @@ export function useGasEstimates({ error: any } { const [{ sourceChain, destinationChain }] = useNetworks() - const { - app: { selectedToken: token } - } = useAppState() + const [selectedToken] = useSelectedToken() const { address: walletAddress } = useAccount() - const balance = useBalanceOnSourceChain(token) + const balance = useBalanceOnSourceChain(selectedToken) const { data: signer } = useSigner() const amountToTransfer = From 1c408bf7e0adbbd1f02fad2603d3059698a285d2 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 9 Sep 2024 18:38:53 +0200 Subject: [PATCH 50/52] fix --- .../src/components/TransferPanel/TokenButton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ca9e86f946..d991280ca0 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenButton.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenButton.tsx @@ -90,7 +90,7 @@ export function TokenButton({ className="h-5 w-5 sm:h-7 sm:w-7" /> )} */} - + {isLoadingToken ? ( ) : ( From a326ce0943158b27fe0e51859758d9b8797f6ab3 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 25 Nov 2024 16:47:15 +0100 Subject: [PATCH 51/52] fix --- .../components/TransferPanel/hooks/useAmountBigNumber.ts | 6 ++---- .../src/components/TransferPanel/hooks/useIsCctpTransfer.ts | 6 ++---- .../TransferPanel/hooks/useSelectedTokenIsWithdrawOnly.ts | 6 ++---- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/hooks/useAmountBigNumber.ts b/packages/arb-token-bridge-ui/src/components/TransferPanel/hooks/useAmountBigNumber.ts index df90187285..1ee522410f 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/hooks/useAmountBigNumber.ts +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/hooks/useAmountBigNumber.ts @@ -1,13 +1,11 @@ import { useMemo } from 'react' import { useArbQueryParams } from '../../../hooks/useArbQueryParams' -import { useAppState } from '../../../state' import { constants, utils } from 'ethers' import { useSourceChainNativeCurrencyDecimals } from '../../../hooks/useSourceChainNativeCurrencyDecimals' +import { useSelectedToken } from '../../../hooks/useSelectedToken' export function useAmountBigNumber() { - const { - app: { selectedToken } - } = useAppState() + const [selectedToken] = useSelectedToken() const [{ amount }] = useArbQueryParams() const nativeCurrencyDecimalsOnSourceChain = useSourceChainNativeCurrencyDecimals() diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/hooks/useIsCctpTransfer.ts b/packages/arb-token-bridge-ui/src/components/TransferPanel/hooks/useIsCctpTransfer.ts index 57bc41258c..ebac5e5e22 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/hooks/useIsCctpTransfer.ts +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/hooks/useIsCctpTransfer.ts @@ -1,6 +1,6 @@ import { useNetworks } from '../../../hooks/useNetworks' import { useNetworksRelationship } from '../../../hooks/useNetworksRelationship' -import { useAppState } from '../../../state' +import { useSelectedToken } from '../../../hooks/useSelectedToken' import { isNetwork } from '../../../util/networks' import { isTokenArbitrumOneNativeUSDC, @@ -10,9 +10,7 @@ import { } from '../../../util/TokenUtils' export const useIsCctpTransfer = function () { - const { - app: { selectedToken } - } = useAppState() + const [selectedToken] = useSelectedToken() const [networks] = useNetworks() const { childChain, isDepositMode, isTeleportMode } = useNetworksRelationship(networks) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/hooks/useSelectedTokenIsWithdrawOnly.ts b/packages/arb-token-bridge-ui/src/components/TransferPanel/hooks/useSelectedTokenIsWithdrawOnly.ts index df55d80a5f..afa36e37d1 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/hooks/useSelectedTokenIsWithdrawOnly.ts +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/hooks/useSelectedTokenIsWithdrawOnly.ts @@ -1,15 +1,13 @@ import useSWRImmutable from 'swr/immutable' import { useMemo } from 'react' -import { useAppState } from '../../../state' import { useNetworks } from '../../../hooks/useNetworks' import { useNetworksRelationship } from '../../../hooks/useNetworksRelationship' import { isWithdrawOnlyToken } from '../../../util/WithdrawOnlyUtils' +import { useSelectedToken } from '../../../hooks/useSelectedToken' export function useSelectedTokenIsWithdrawOnly() { - const { - app: { selectedToken } - } = useAppState() + const [selectedToken] = useSelectedToken() const [networks] = useNetworks() const { isDepositMode, parentChain, childChain } = useNetworksRelationship(networks) From 402aed46908b576cab2c39cc0cb3978e98085eb0 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 23 Dec 2024 12:13:20 +0100 Subject: [PATCH 52/52] fix build --- packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts index 4b64a5ed24..a7380bef0c 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useSelectedToken.ts @@ -32,7 +32,7 @@ const commonUSDC = { type: TokenType.ERC20, symbol: 'USDC', decimals: 6, - listIds: new Set() + listIds: new Set() } export const useSelectedToken = () => {