Commit 8e3b2cb4 authored by Jordan Frankfurt's avatar Jordan Frankfurt Committed by GitHub

feat(widgets): add error reporting component, INTEGRATION ERROR type, and...

feat(widgets): add error reporting component, INTEGRATION ERROR type, 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
parent d54783a3
......@@ -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()}
/>
......
......@@ -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>
......
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
}
......@@ -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>
}
......
......@@ -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`
......@@ -90,7 +91,8 @@ export type WidgetProps<T extends JSXElementConstructor<any> | undefined = undef
: // eslint-disable-next-line @typescript-eslint/ban-types
{})
export default function Widget({
export default function Widget(props: PropsWithChildren<WidgetProps>) {
const {
children,
theme,
locale = DEFAULT_LOCALE,
......@@ -100,9 +102,8 @@ export default function Widget({
dialog,
className,
onError,
}: PropsWithChildren<WidgetProps>) {
} = props
const wrapper = useRef<HTMLDivElement>(null)
return (
<StrictMode>
<I18nProvider locale={locale}>
......@@ -114,6 +115,7 @@ export default function Widget({
<AtomProvider>
<Web3Provider provider={provider} jsonRpcEndpoint={jsonRpcEndpoint}>
<Updaters />
<ErrorGenerator {...props} />
{children}
</Web3Provider>
</AtomProvider>
......
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'
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment