Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adds fetch-retry to isBlocked #2055

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/arb-token-bridge-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
19 changes: 17 additions & 2 deletions packages/arb-token-bridge-ui/src/components/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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<boolean>(TOS_LOCALSTORAGE_KEY, false)
const { openConnectModal } = useConnectModal()

Expand Down Expand Up @@ -213,6 +213,21 @@ function AppContent() {
)
}

if (address && hasConnectionError) {
return (
<ConnectionErrorDialog
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={() => {}}
/>
)
}

return (
<>
<Header>
Expand Down
60 changes: 46 additions & 14 deletions packages/arb-token-bridge-ui/src/components/App/BlockedDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<Dialog
{...props}
title={
<div className="flex flex-row items-center space-x-2">
<ExclamationTriangleIcon height={25} width={25} />
<span>This wallet address is blocked</span>
<span>{title}</span>
</div>
}
isFooterHidden={true}
>
<div className="flex flex-col space-y-4 break-words py-4 text-gray-3">
<span>{props.address.toLowerCase()}</span>
<span>This address is affiliated with a blocked activity.</span>
<span>
If you think this was an error, you can request a review by filing a{' '}
<ExternalLink
href={GET_HELP_LINK}
className="arb-hover text-white underline"
>
support ticket
</ExternalLink>
.
</span>
{children}
</div>
</Dialog>
)
}

export function BlockedDialog(props: DialogProps & { address: string }) {
return (
<ErrorDialog {...props} title="This wallet address is blocked">
<span>{props.address.toLowerCase()}</span>
<span>This address is affiliated with a blocked activity.</span>
<span>
If you think this was an error, you can request a review with{' '}
<ExternalLink
href={GET_HELP_LINK}
className="arb-hover text-white underline"
>
[email protected]
</ExternalLink>
</span>
</ErrorDialog>
)
}

export function ConnectionErrorDialog(
props: DialogProps & { address: string }
) {
return (
<ErrorDialog {...props} title="Connection error">
<span>There was an error when connecting to:</span>
<span>{props.address.toLowerCase()}</span>
<span>
Try refreshing the page. If the issue continues, you can contact{' '}
<a
href="mailto:[email protected]"
className="arb-hover text-white underline"
>
[email protected]
</a>
</span>
</ErrorDialog>
)
}
44 changes: 31 additions & 13 deletions packages/arb-token-bridge-ui/src/hooks/useAccountIsBlocked.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,51 @@
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 * 500 // should be short because it blocks the user
})

type BlockedResponse = { blocked: boolean; hasConnectionError: boolean }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we determine if the user is blocked or if they have a connection error, and display the appropriate dialog if so


/**
* Checks if an address is blocked using the external Screenings API service.
* @param {Address} address - The address to check.
* @returns {Promise<boolean>} true if blocked or the request fails
* @returns {Promise<BlockedResponse>}
*/
async function isBlocked(address: Address): Promise<boolean> {
async function isBlocked(address: Address): Promise<BlockedResponse> {
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 ?? '')
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()
return blocked
return { blocked, hasConnectionError: false }
} catch (error) {
console.error('Failed to check if address is blocked', error)
captureSentryErrorWithExtraData({
Expand All @@ -43,18 +54,18 @@ async function isBlocked(address: Address): Promise<boolean> {
additionalData: { address }
})

return false
return { blocked: false, hasConnectionError: true }
}
}

async function fetcher(address: Address): Promise<boolean> {
const accountIsBlocked = await isBlocked(address)
async function fetcher(address: Address): Promise<BlockedResponse> {
const result = await isBlocked(address)

if (accountIsBlocked) {
if (result.blocked) {
trackEvent('Address Block', { address })
}

return accountIsBlocked
return result
}

export function useAccountIsBlocked() {
Expand All @@ -69,11 +80,18 @@ export function useAccountIsBlocked() {
return [address.toLowerCase(), 'useAccountIsBlocked']
}, [address])

const { data: isBlocked } = useSWRImmutable<boolean>(
const { data, isLoading } = useSWRImmutable<BlockedResponse>(
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
}
}
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7740,6 +7740,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"
Expand Down
Loading