Skip to content

Commit

Permalink
feat(widgets): add error reporting component, INTEGRATION ERROR type,…
Browse files Browse the repository at this point in the history
… and Missing provider error (#3110)

* add error reporting component, INTEGRATION ERROR type, and Missing provider error

* rename reporter to generator

* pr feedback

* refactor provider check

* add chainId, convenienceFee, and width errors

* pr feedback and convenienceFeeRecipient address enforcement

* fix imports for utils
  • Loading branch information
JFrankfurt authored Jan 19, 2022
1 parent d54783a commit 8e3b2cb
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 14 deletions.
2 changes: 1 addition & 1 deletion src/lib/components/Error/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default class ErrorBoundary extends React.Component<ErrorBoundaryProps, E
<Dialog color="dialog">
<ErrorDialog
error={this.state.error}
header={<Trans>Reload the page to try again</Trans>}
header={<Trans>Something went wrong.</Trans>}
action={<Trans>Reload the page</Trans>}
onAction={() => window.location.reload()}
/>
Expand Down
5 changes: 4 additions & 1 deletion src/lib/components/Error/ErrorDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,10 @@ export default function ErrorDialog({ header, error, action, onAction }: ErrorDi
<Rule />
<ErrorColumn>
<Column gap={0.5} ref={setDetails} css={scrollbar}>
<ThemedText.Code>{error.message}</ThemedText.Code>
<ThemedText.Code>
{error.name}
{error.message ? `: ${error.message}` : ''}
</ThemedText.Code>
</Column>
</ErrorColumn>
<ActionButton onClick={onAction}>{action}</ActionButton>
Expand Down
57 changes: 57 additions & 0 deletions src/lib/components/Error/ErrorGenerator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { ALL_SUPPORTED_CHAIN_IDS } from 'constants/chains'
import { ChainIdError, IntegrationError } from 'lib/errors'
import useActiveWeb3React from 'lib/hooks/useActiveWeb3React'
import { SwapWidgetProps } from 'lib/index'
import { useEffect } from 'react'

import { isAddress } from '../../../utils'

export default function ErrorGenerator(swapWidgetProps: SwapWidgetProps) {
const { jsonRpcEndpoint, provider } = swapWidgetProps
useEffect(() => {
if (!provider && !jsonRpcEndpoint) {
throw new IntegrationError('This widget requires a provider or jsonRpcEndpoint.')
}
}, [provider, jsonRpcEndpoint])

const { chainId } = useActiveWeb3React()
useEffect(() => {
if (chainId && !ALL_SUPPORTED_CHAIN_IDS.includes(chainId)) {
throw new ChainIdError('Switch to a network supported by the Uniswap Protocol.')
}
}, [chainId])

// size constraints
const { width } = swapWidgetProps
useEffect(() => {
if (width && width < 300) {
throw new IntegrationError('Set widget width to at least 300px.')
}
}, [width])

// convenience fee constraints
const { convenienceFee, convenienceFeeRecipient } = swapWidgetProps
useEffect(() => {
if (convenienceFee) {
if (convenienceFee > 100 || convenienceFee < 0) {
throw new IntegrationError('Set widget convenienceFee to at least 400px.')
}
if (!convenienceFeeRecipient) {
throw new IntegrationError('convenienceFeeRecipient is required when convenienceFee is set.')
}
const MustBeValidAddressError = new IntegrationError('convenienceFeeRecipient must be a valid address.')
if (typeof convenienceFeeRecipient === 'string') {
if (!isAddress(convenienceFeeRecipient)) {
throw MustBeValidAddressError
}
} else if (typeof convenienceFeeRecipient === 'object') {
Object.values(convenienceFeeRecipient).forEach((recipient) => {
if (!isAddress(recipient)) {
throw MustBeValidAddressError
}
})
}
}
}, [convenienceFee, convenienceFeeRecipient])
return null
}
2 changes: 2 additions & 0 deletions src/lib/components/Swap/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ function useSwapDefaults(defaults: Partial<SwapDefaults> = {}): SwapDefaults {
}

export interface SwapProps {
convenienceFee?: number
convenienceFeeRecipient?: string // TODO: improve typing to require recipient when fee is set
defaults?: Partial<SwapDefaults>
}

Expand Down
26 changes: 14 additions & 12 deletions src/lib/components/Widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Provider as EthProvider } from 'widgets-web3-react/types'

import { Provider as DialogProvider } from './Dialog'
import ErrorBoundary, { ErrorHandler } from './Error/ErrorBoundary'
import ErrorGenerator from './Error/ErrorGenerator'
import Web3Provider from './Web3Provider'

const slideDown = keyframes`
Expand Down Expand Up @@ -90,19 +91,19 @@ export type WidgetProps<T extends JSXElementConstructor<any> | undefined = undef
: // eslint-disable-next-line @typescript-eslint/ban-types
{})

export default function Widget({
children,
theme,
locale = DEFAULT_LOCALE,
provider,
jsonRpcEndpoint,
width = 360,
dialog,
className,
onError,
}: PropsWithChildren<WidgetProps>) {
export default function Widget(props: PropsWithChildren<WidgetProps>) {
const {
children,
theme,
locale = DEFAULT_LOCALE,
provider,
jsonRpcEndpoint,
width = 360,
dialog,
className,
onError,
} = props
const wrapper = useRef<HTMLDivElement>(null)

return (
<StrictMode>
<I18nProvider locale={locale}>
Expand All @@ -114,6 +115,7 @@ export default function Widget({
<AtomProvider>
<Web3Provider provider={provider} jsonRpcEndpoint={jsonRpcEndpoint}>
<Updaters />
<ErrorGenerator {...props} />
{children}
</Web3Provider>
</AtomProvider>
Expand Down
13 changes: 13 additions & 0 deletions src/lib/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export class IntegrationError extends Error {
constructor(message: string) {
super(message)
this.name = 'Integration Error'
}
}

export class ChainIdError extends Error {
constructor(message: string) {
super(message)
this.name = 'Unsupported network'
}
}

0 comments on commit 8e3b2cb

Please sign in to comment.