diff --git a/src/lib/components/Error/ErrorBoundary.tsx b/src/lib/components/Error/ErrorBoundary.tsx index 0118447381..9071616e60 100644 --- a/src/lib/components/Error/ErrorBoundary.tsx +++ b/src/lib/components/Error/ErrorBoundary.tsx @@ -34,7 +34,7 @@ export default class ErrorBoundary extends React.Component Reload the page to try again} + header={Something went wrong.} action={Reload the page} onAction={() => window.location.reload()} /> diff --git a/src/lib/components/Error/ErrorDialog.tsx b/src/lib/components/Error/ErrorDialog.tsx index 95db5a8eca..920582b950 100644 --- a/src/lib/components/Error/ErrorDialog.tsx +++ b/src/lib/components/Error/ErrorDialog.tsx @@ -117,7 +117,10 @@ export default function ErrorDialog({ header, error, action, onAction }: ErrorDi - {error.message} + + {error.name} + {error.message ? `: ${error.message}` : ''} + {action} diff --git a/src/lib/components/Error/ErrorGenerator.tsx b/src/lib/components/Error/ErrorGenerator.tsx new file mode 100644 index 0000000000..b5d38bcdd9 --- /dev/null +++ b/src/lib/components/Error/ErrorGenerator.tsx @@ -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 +} diff --git a/src/lib/components/Swap/index.tsx b/src/lib/components/Swap/index.tsx index 1dc1e50049..28d8c3dda6 100644 --- a/src/lib/components/Swap/index.tsx +++ b/src/lib/components/Swap/index.tsx @@ -39,6 +39,8 @@ function useSwapDefaults(defaults: Partial = {}): SwapDefaults { } export interface SwapProps { + convenienceFee?: number + convenienceFeeRecipient?: string // TODO: improve typing to require recipient when fee is set defaults?: Partial } diff --git a/src/lib/components/Widget.tsx b/src/lib/components/Widget.tsx index 25c731ab33..0aad902099 100644 --- a/src/lib/components/Widget.tsx +++ b/src/lib/components/Widget.tsx @@ -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,19 +91,19 @@ export type WidgetProps | 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) { +export default function Widget(props: PropsWithChildren) { + const { + children, + theme, + locale = DEFAULT_LOCALE, + provider, + jsonRpcEndpoint, + width = 360, + dialog, + className, + onError, + } = props const wrapper = useRef(null) - return ( @@ -114,6 +115,7 @@ export default function Widget({ + {children} diff --git a/src/lib/errors.ts b/src/lib/errors.ts new file mode 100644 index 0000000000..31080a1cd1 --- /dev/null +++ b/src/lib/errors.ts @@ -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' + } +}