Commit 60d35b46 authored by Zach Pomerantz's avatar Zach Pomerantz Committed by GitHub

fix: simplify validation (#3665)

* fix: simplify widget validation

* test: update cosmos to trigger edge cases

* fix: simplify swap validation
parent 3d422cf7
import { SUPPORTED_LOCALES } from 'constants/locales'
import { WidgetProps } from 'lib/components/Widget'
import { IntegrationError } from 'lib/errors'
import { PropsWithChildren, useEffect } from 'react'
export default function WidgetsPropsValidator(props: PropsWithChildren<WidgetProps>) {
const { jsonRpcEndpoint, provider } = props
useEffect(() => {
if (!provider && !jsonRpcEndpoint) {
throw new IntegrationError('This widget requires a provider or jsonRpcEndpoint.')
}
}, [provider, jsonRpcEndpoint])
const { width } = props
useEffect(() => {
if (width && width < 300) {
throw new IntegrationError(`Set widget width to at least 300px. (You set it to ${width}.)`)
}
}, [width])
const { locale } = props
useEffect(() => {
if (locale && locale !== 'pseudo' && !SUPPORTED_LOCALES.includes(locale)) {
console.warn('Unsupported locale: ', locale)
}
}, [locale])
return null
}
......@@ -23,8 +23,8 @@ import ReverseButton from './ReverseButton'
import Settings from './Settings'
import { StatusDialog } from './Status'
import SwapButton from './SwapButton'
import SwapPropValidator from './SwapPropValidator'
import Toolbar from './Toolbar'
import useValidate from './useValidate'
function getTransactionFromMap(
txs: { [hash: string]: Transaction },
......@@ -56,6 +56,8 @@ function Updaters(props: SwapProps & { disabled: boolean }) {
}
export default function Swap(props: SwapProps) {
useValidate(props)
const { active, account } = useActiveWeb3React()
const [wrapper, setWrapper] = useState<HTMLDivElement | null>(null)
......@@ -70,7 +72,6 @@ export default function Swap(props: SwapProps) {
return (
<>
<SwapPropValidator {...props} />
<Updaters {...props} disabled={isDisabled} />
<Header title={<Trans>Swap</Trans>}>
{active && <Wallet disabled={!account} onClick={props.onConnectWallet} />}
......
......@@ -18,12 +18,12 @@ function isAddressOrAddressMap(addressOrMap: DefaultAddress): boolean {
type ValidatorProps = PropsWithChildren<TokenDefaults & FeeOptions>
export default function SwapPropValidator(props: ValidatorProps) {
export default function useValidate(props: ValidatorProps) {
const { convenienceFee, convenienceFeeRecipient } = props
useEffect(() => {
if (convenienceFee) {
if (convenienceFee > 100 || convenienceFee < 0) {
throw new IntegrationError(`convenienceFee must be between 0 and 100. (You set it to ${convenienceFee})`)
throw new IntegrationError(`convenienceFee must be between 0 and 100 (you set it to ${convenienceFee}).`)
}
if (!convenienceFeeRecipient) {
throw new IntegrationError('convenienceFeeRecipient is required when convenienceFee is set.')
......@@ -32,7 +32,7 @@ export default function SwapPropValidator(props: ValidatorProps) {
if (typeof convenienceFeeRecipient === 'string') {
if (!isAddress(convenienceFeeRecipient)) {
throw new IntegrationError(
`convenienceFeeRecipient must be a valid address. (You set it to ${convenienceFeeRecipient}.)`
`convenienceFeeRecipient must be a valid address (you set it to ${convenienceFeeRecipient}).`
)
}
} else if (typeof convenienceFeeRecipient === 'object') {
......@@ -40,7 +40,7 @@ export default function SwapPropValidator(props: ValidatorProps) {
if (!isAddress(recipient)) {
const values = Object.values(convenienceFeeRecipient).join(', ')
throw new IntegrationError(
`All values in convenienceFeeRecipient object must be valid addresses. (You used ${values}.)`
`All values in convenienceFeeRecipient object must be valid addresses (you used ${values}).`
)
}
})
......@@ -54,11 +54,11 @@ export default function SwapPropValidator(props: ValidatorProps) {
throw new IntegrationError('defaultInputAmount and defaultOutputAmount may not both be defined.')
}
if (defaultInputAmount && BigNumber.from(defaultInputAmount).lt(0)) {
throw new IntegrationError(`defaultInputAmount must be a positive number. (You set it to ${defaultInputAmount})`)
throw new IntegrationError(`defaultInputAmount must be a positive number (you set it to ${defaultInputAmount})`)
}
if (defaultOutputAmount && BigNumber.from(defaultOutputAmount).lt(0)) {
throw new IntegrationError(
`defaultOutputAmount must be a positive number. (You set it to ${defaultOutputAmount})`
`defaultOutputAmount must be a positive number (you set it to ${defaultOutputAmount}).`
)
}
if (
......@@ -67,7 +67,7 @@ export default function SwapPropValidator(props: ValidatorProps) {
defaultInputTokenAddress !== 'NATIVE'
) {
throw new IntegrationError(
`defaultInputTokenAddress(es) must be a valid address or "NATIVE". (You set it to ${defaultInputTokenAddress}`
`defaultInputTokenAddress must be a valid address or "NATIVE" (you set it to ${defaultInputTokenAddress}).`
)
}
if (
......@@ -76,10 +76,8 @@ export default function SwapPropValidator(props: ValidatorProps) {
defaultOutputTokenAddress !== 'NATIVE'
) {
throw new IntegrationError(
`defaultOutputTokenAddress(es) must be a valid address or "NATIVE". (You set it to ${defaultOutputTokenAddress}`
`defaultOutputTokenAddress must be a valid address or "NATIVE" (you set it to ${defaultOutputTokenAddress}).`
)
}
}, [defaultInputTokenAddress, defaultInputAmount, defaultOutputTokenAddress, defaultOutputAmount])
return null
}
import { JsonRpcProvider } from '@ethersproject/providers'
import { Provider as Eip1193Provider } from '@web3-react/types'
import { DEFAULT_LOCALE, SupportedLocale } from 'constants/locales'
import { DEFAULT_LOCALE, SUPPORTED_LOCALES, SupportedLocale } from 'constants/locales'
import { Provider as AtomProvider } from 'jotai'
import { TransactionsUpdater } from 'lib/hooks/transactions'
import { ActiveWeb3Provider } from 'lib/hooks/useActiveWeb3React'
......@@ -9,12 +9,11 @@ import { Provider as I18nProvider } from 'lib/i18n'
import { MulticallUpdater, store as multicallStore } from 'lib/state/multicall'
import styled, { keyframes, Theme, ThemeProvider } from 'lib/theme'
import { UNMOUNTING } from 'lib/utils/animations'
import { PropsWithChildren, StrictMode, useState } from 'react'
import { PropsWithChildren, StrictMode, useMemo, useState } from 'react'
import { Provider as ReduxProvider } from 'react-redux'
import { Modal, Provider as DialogProvider } from './Dialog'
import ErrorBoundary, { ErrorHandler } from './Error/ErrorBoundary'
import WidgetPropValidator from './Error/WidgetsPropsValidator'
const WidgetWrapper = styled.div<{ width?: number | string }>`
-moz-osx-font-smoothing: grayscale;
......@@ -103,17 +102,22 @@ export type WidgetProps = {
}
export default function Widget(props: PropsWithChildren<WidgetProps>) {
const {
children,
theme,
locale = DEFAULT_LOCALE,
provider,
jsonRpcEndpoint,
width = 360,
dialog: userDialog,
className,
onError,
} = props
const { children, theme, provider, jsonRpcEndpoint, dialog: userDialog, className, onError } = props
const width = useMemo(() => {
if (props.width && props.width < 300) {
console.warn(`Widget width must be at least 300px (you set it to ${props.width}). Falling back to 300px.`)
return 300
}
return props.width ?? 360
}, [props.width])
const locale = useMemo(() => {
if (props.locale && ![...SUPPORTED_LOCALES, 'pseudo'].includes(props.locale)) {
console.warn(`Unsupported locale: ${props.locale}. Falling back to ${DEFAULT_LOCALE}.`)
return DEFAULT_LOCALE
}
return props.locale ?? DEFAULT_LOCALE
}, [props.locale])
const [dialog, setDialog] = useState<HTMLDivElement | null>(null)
return (
<StrictMode>
......@@ -123,7 +127,6 @@ export default function Widget(props: PropsWithChildren<WidgetProps>) {
<DialogWrapper ref={setDialog} />
<DialogProvider value={userDialog || dialog}>
<ErrorBoundary onError={onError}>
<WidgetPropValidator {...props} />
<ReduxProvider store={multicallStore}>
<AtomProvider>
<ActiveWeb3Provider provider={provider} jsonRpcEndpoint={jsonRpcEndpoint}>
......
......@@ -17,14 +17,15 @@ const [walletConnect] = initializeConnector<WalletConnect>(
export default function Wrapper({ children }: { children: ReactNode }) {
const [width] = useValue('width', { defaultValue: 360 })
const [locale] = useSelect('locale', { defaultValue: DEFAULT_LOCALE, options: ['pseudo', ...SUPPORTED_LOCALES] })
const [locale] = useSelect('locale', {
defaultValue: DEFAULT_LOCALE,
options: ['fa-KE (unsupported)', 'pseudo', ...SUPPORTED_LOCALES],
})
const [darkMode] = useValue('dark mode', { defaultValue: false })
const [theme, setTheme] = useValue('theme', { defaultValue: { ...defaultTheme, ...lightTheme } })
useEffect(() => {
setTheme({ ...defaultTheme, ...(darkMode ? darkTheme : lightTheme) })
// cosmos does not maintain referential equality for setters
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [darkMode])
}, [darkMode, setTheme])
const NO_JSON_RPC = 'None'
const [jsonRpcEndpoint] = useSelect('JSON-RPC', {
......
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