From 40fcea6371e8b460b24472399ae81a598c6be188 Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:50:34 -0500 Subject: [PATCH 1/3] feat: adds fetch-retry to isBlocked --- packages/arb-token-bridge-ui/package.json | 1 + .../src/hooks/useAccountIsBlocked.ts | 13 +++++++++++-- yarn.lock | 5 +++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/arb-token-bridge-ui/package.json b/packages/arb-token-bridge-ui/package.json index 6e272c5df7..f7d7d70dc4 100644 --- a/packages/arb-token-bridge-ui/package.json +++ b/packages/arb-token-bridge-ui/package.json @@ -25,6 +25,7 @@ "cheerio": "^1.0.0-rc.12", "dayjs": "^1.11.8", "ethers": "^5.6.0", + "fetch-retry": "^6.0.0", "graphql": "^16.8.1", "lodash-es": "^4.17.21", "next": "^14.2.12", diff --git a/packages/arb-token-bridge-ui/src/hooks/useAccountIsBlocked.ts b/packages/arb-token-bridge-ui/src/hooks/useAccountIsBlocked.ts index 1ec288e21a..95acf0ab28 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useAccountIsBlocked.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useAccountIsBlocked.ts @@ -1,11 +1,17 @@ import { useMemo } from 'react' import { useAccount } from 'wagmi' import useSWRImmutable from 'swr/immutable' +import fetchRetry from 'fetch-retry' import { trackEvent } from '../util/AnalyticsUtils' import { Address } from '../util/AddressUtils' import { captureSentryErrorWithExtraData } from '../util/SentryUtils' +const fetchWithRetry = fetchRetry(fetch, { + retries: 3, + retryDelay: (attempt: number) => attempt * 250 // should be short because it blocks the user +}) + /** * Checks if an address is blocked using the external Screenings API service. * @param {Address} address - The address to check. @@ -24,13 +30,16 @@ async function isBlocked(address: Address): Promise { url.searchParams.set('address', address) url.searchParams.set('ref', window.location.hostname) - const response = await fetch(url, { + const response = await fetchWithRetry(url, { method: 'GET', headers: { 'Content-Type': 'application/json' } }) if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`) + const errorData = await response + .text() + .catch(() => 'Failed to get response text') + throw new Error(`HTTP ${response.status}: ${errorData}`) } const { blocked } = await response.json() diff --git a/yarn.lock b/yarn.lock index 2d7e5f6169..a246001a2e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7523,6 +7523,11 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +fetch-retry@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/fetch-retry/-/fetch-retry-6.0.0.tgz#4ffdf92c834d72ae819e42a4ee2a63f1e9454426" + integrity sha512-BUFj1aMubgib37I3v4q78fYo63Po7t4HUPTpQ6/QE6yK6cIQrP+W43FYToeTEyg5m2Y7eFUtijUuAv/PDlWuag== + fflate@^0.4.8: version "0.4.8" resolved "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz" From 926ee654d71e2c0ca795e4dbab962baf09ab0083 Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Mon, 18 Nov 2024 12:55:48 -0500 Subject: [PATCH 2/3] adds `ConnectionErrorDialog` --- .../src/components/App/App.tsx | 19 ++++-- .../src/components/App/BlockedDialog.tsx | 60 ++++++++++++++----- .../src/hooks/useAccountIsBlocked.ts | 33 ++++++---- 3 files changed, 80 insertions(+), 32 deletions(-) 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 d23f339fb8..1701008003 100644 --- a/packages/arb-token-bridge-ui/src/components/App/App.tsx +++ b/packages/arb-token-bridge-ui/src/components/App/App.tsx @@ -16,7 +16,7 @@ import { useLocalStorage } from '@uidotdev/usehooks' import { ConnectionState } from '../../util' import { TokenBridgeParams } from '../../hooks/useArbTokenBridge' import { WelcomeDialog } from './WelcomeDialog' -import { BlockedDialog } from './BlockedDialog' +import { BlockedDialog, ConnectionErrorDialog } from './BlockedDialog' import { AppContextProvider } from './AppContext' import { config, useActions, useAppState } from '../../state' import { MainContent } from '../MainContent/MainContent' @@ -160,7 +160,7 @@ const ArbTokenBridgeStoreSyncWrapper = (): JSX.Element | null => { function AppContent() { const { address, isConnected } = useAccount() - const { isBlocked } = useAccountIsBlocked() + const { isBlocked, hasConnectionError } = useAccountIsBlocked() const [tosAccepted] = useLocalStorage(TOS_LOCALSTORAGE_KEY, false) const { openConnectModal } = useConnectModal() @@ -204,10 +204,17 @@ function AppContent() { address={address} isOpen={true} closeable={false} - // ignoring until we use the package - // https://github.com/OffchainLabs/config-monorepo/pull/11 - // - // eslint-disable-next-line + onClose={() => {}} + /> + ) + } + + if (address && hasConnectionError) { + return ( + {}} /> ) diff --git a/packages/arb-token-bridge-ui/src/components/App/BlockedDialog.tsx b/packages/arb-token-bridge-ui/src/components/App/BlockedDialog.tsx index efbe5ea1b9..0e77d82ad3 100644 --- a/packages/arb-token-bridge-ui/src/components/App/BlockedDialog.tsx +++ b/packages/arb-token-bridge-ui/src/components/App/BlockedDialog.tsx @@ -4,32 +4,64 @@ import { Dialog, DialogProps } from '../common/Dialog' import { ExternalLink } from '../common/ExternalLink' import { GET_HELP_LINK } from '../../constants' -export function BlockedDialog(props: DialogProps & { address: string }) { +interface ErrorDialogProps extends DialogProps { + title: string + children: React.ReactNode +} + +function ErrorDialog({ title, children, ...props }: ErrorDialogProps) { return ( - This wallet address is blocked + {title} } isFooterHidden={true} >
- {props.address.toLowerCase()} - This address is affiliated with a blocked activity. - - If you think this was an error, you can request a review by filing a{' '} - - support ticket - - . - + {children}
) } + +export function BlockedDialog(props: DialogProps & { address: string }) { + return ( + + {props.address.toLowerCase()} + This address is affiliated with a blocked activity. + + If you think this was an error, you can request a review with{' '} + + support@arbitrum.io + + + + ) +} + +export function ConnectionErrorDialog( + props: DialogProps & { address: string } +) { + return ( + + There was an error when connecting to: + {props.address.toLowerCase()} + + Try refreshing the page. If the issue continues, you can contact{' '} + + support@arbitrum.io + + + + ) +} diff --git a/packages/arb-token-bridge-ui/src/hooks/useAccountIsBlocked.ts b/packages/arb-token-bridge-ui/src/hooks/useAccountIsBlocked.ts index 95acf0ab28..90a46bde58 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useAccountIsBlocked.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useAccountIsBlocked.ts @@ -9,21 +9,23 @@ import { captureSentryErrorWithExtraData } from '../util/SentryUtils' const fetchWithRetry = fetchRetry(fetch, { retries: 3, - retryDelay: (attempt: number) => attempt * 250 // should be short because it blocks the user + retryDelay: (attempt: number) => attempt * 500 // should be short because it blocks the user }) +type BlockedResponse = { blocked: boolean; hasConnectionError: boolean } + /** * Checks if an address is blocked using the external Screenings API service. * @param {Address} address - The address to check. - * @returns {Promise} true if blocked or the request fails + * @returns {Promise} */ -async function isBlocked(address: Address): Promise { +async function isBlocked(address: Address): Promise { try { if ( process.env.NODE_ENV !== 'production' || process.env.NEXT_PUBLIC_IS_E2E_TEST ) { - return false + return { blocked: false, hasConnectionError: false } } const url = new URL(process.env.NEXT_PUBLIC_SCREENING_API_ENDPOINT ?? '') @@ -43,7 +45,7 @@ async function isBlocked(address: Address): Promise { } const { blocked } = await response.json() - return blocked + return { blocked, hasConnectionError: false } } catch (error) { console.error('Failed to check if address is blocked', error) captureSentryErrorWithExtraData({ @@ -52,18 +54,18 @@ async function isBlocked(address: Address): Promise { additionalData: { address } }) - return false + return { blocked: false, hasConnectionError: true } } } -async function fetcher(address: Address): Promise { - const accountIsBlocked = await isBlocked(address) +async function fetcher(address: Address): Promise { + const result = await isBlocked(address) - if (accountIsBlocked) { + if (result.blocked) { trackEvent('Address Block', { address }) } - return accountIsBlocked + return result } export function useAccountIsBlocked() { @@ -78,11 +80,18 @@ export function useAccountIsBlocked() { return [address.toLowerCase(), 'useAccountIsBlocked'] }, [address]) - const { data: isBlocked } = useSWRImmutable( + const { data, isLoading } = useSWRImmutable( queryKey, // Extracts the first element of the query key as the fetcher param ([_address]) => fetcher(_address) ) - return { isBlocked } + if (!address || isLoading) { + return { isBlocked: false, hasConnectionError: false } + } + + return { + isBlocked: data?.blocked, + hasConnectionError: data?.hasConnectionError + } } From 1fcdbeb7fa82bd34f6cec21ccc5b7b7ea0c63af6 Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Mon, 18 Nov 2024 13:00:42 -0500 Subject: [PATCH 3/3] restore comments --- packages/arb-token-bridge-ui/src/components/App/App.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) 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 1701008003..b527011874 100644 --- a/packages/arb-token-bridge-ui/src/components/App/App.tsx +++ b/packages/arb-token-bridge-ui/src/components/App/App.tsx @@ -204,6 +204,10 @@ function AppContent() { address={address} isOpen={true} closeable={false} + // ignoring until we use the package + // https://github.com/OffchainLabs/config-monorepo/pull/11 + // + // eslint-disable-next-line onClose={() => {}} /> ) @@ -215,6 +219,10 @@ function AppContent() { address={address} isOpen={true} closeable={false} + // ignoring until we use the package + // https://github.com/OffchainLabs/config-monorepo/pull/11 + // + // eslint-disable-next-line onClose={() => {}} /> )