Skip to content

Commit

Permalink
fix: Validation for spending limits
Browse files Browse the repository at this point in the history
  • Loading branch information
usame-algan committed Jul 12, 2023
1 parent f487649 commit d485355
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 33 deletions.
22 changes: 15 additions & 7 deletions src/components/common/TokenAmountInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AutocompleteItem } from '@/components/tx-flow/flows/TokenTransfer/Creat
import { useFormContext } from 'react-hook-form'
import { type BigNumber } from '@ethersproject/bignumber'
import classNames from 'classnames'
import { useCallback } from 'react'

export enum TokenAmountFields {
tokenAddress = 'tokenAddress',
Expand All @@ -18,11 +19,13 @@ const TokenAmountInput = ({
selectedToken,
onMaxAmountClick,
maxAmount,
validate,
}: {
balances: SafeBalanceResponse['items']
selectedToken: SafeBalanceResponse['items'][number] | undefined
onMaxAmountClick: () => void
maxAmount: BigNumber
onMaxAmountClick?: () => void
maxAmount?: BigNumber
validate?: (value: string) => string | undefined
}) => {
const {
formState: { errors },
Expand All @@ -34,6 +37,14 @@ const TokenAmountInput = ({
const tokenAddress = watch(TokenAmountFields.tokenAddress)
const isAmountError = !!errors[TokenAmountFields.tokenAddress] || !!errors[TokenAmountFields.amount]

const validateAmount = useCallback(
(value: string) => {
const decimals = selectedToken?.tokenInfo.decimals
return validateLimitedAmount(value, decimals, maxAmount?.toString()) || validateDecimalLength(value, decimals)
},
[maxAmount, selectedToken?.tokenInfo.decimals],
)

return (
<FormControl className={classNames(css.outline, { [css.error]: isAmountError })} fullWidth>
<InputLabel shrink required className={css.label}>
Expand All @@ -44,7 +55,7 @@ const TokenAmountInput = ({
variant="standard"
InputProps={{
disableUnderline: true,
endAdornment: (
endAdornment: onMaxAmountClick && (
<Button className={css.max} onClick={onMaxAmountClick}>
Max
</Button>
Expand All @@ -55,10 +66,7 @@ const TokenAmountInput = ({
placeholder="0"
{...register(TokenAmountFields.amount, {
required: true,
validate: (val) => {
const decimals = selectedToken?.tokenInfo.decimals
return validateLimitedAmount(val, decimals, maxAmount.toString()) || validateDecimalLength(val, decimals)
},
validate: validate ?? validateAmount,
})}
/>
<Divider orientation="vertical" flexItem />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo } from 'react'
import { useCallback, useMemo } from 'react'
import { Controller, FormProvider, useForm } from 'react-hook-form'
import { Button, CardActions, FormControl, InputLabel, MenuItem, Select, Typography } from '@mui/material'
import ExpandMoreRoundedIcon from '@mui/icons-material/ExpandMoreRounded'
Expand All @@ -12,9 +12,8 @@ import type { NewSpendingLimitFlowProps } from '.'
import TxCard from '../../common/TxCard'
import css from '@/components/tx/ExecuteCheckbox/styles.module.css'
import TokenAmountInput from '@/components/common/TokenAmountInput'
import { BigNumber } from '@ethersproject/bignumber'
import { safeFormatUnits } from '@/utils/formatters'
import { SpendingLimitFields } from '.'
import { validateAmount, validateDecimalLength } from '@/utils/validation'

export const _validateSpendingLimit = (val: string, decimals?: number) => {
// Allowance amount is uint96 https://github.com/safe-global/safe-modules/blob/master/allowances/contracts/AlowanceModule.sol#L52
Expand Down Expand Up @@ -43,24 +42,23 @@ export const CreateSpendingLimit = ({
mode: 'onChange',
})

const { handleSubmit, setValue, watch, control } = formMethods
const { handleSubmit, watch, control } = formMethods

const tokenAddress = watch(SpendingLimitFields.tokenAddress)
const selectedToken = tokenAddress
? balances.items.find((item) => item.tokenInfo.address === tokenAddress)
: undefined

const totalAmount = BigNumber.from(selectedToken?.balance || 0)

const onMaxAmountClick = () => {
if (!selectedToken) return

const amount = selectedToken.balance

setValue(SpendingLimitFields.amount, safeFormatUnits(amount, selectedToken.tokenInfo.decimals), {
shouldValidate: true,
})
}
const validateSpendingLimit = useCallback(
(value: string) => {
return (
validateAmount(value) ||
validateDecimalLength(value, selectedToken?.tokenInfo.decimals) ||
_validateSpendingLimit(value, selectedToken?.tokenInfo.decimals)
)
},
[selectedToken?.tokenInfo.decimals],
)

return (
<TxCard>
Expand All @@ -70,12 +68,7 @@ export const CreateSpendingLimit = ({
<AddressBookInput name={SpendingLimitFields.beneficiary} label="Beneficiary" />
</FormControl>

<TokenAmountInput
balances={balances.items}
selectedToken={selectedToken}
maxAmount={totalAmount}
onMaxAmountClick={onMaxAmountClick}
/>
<TokenAmountInput balances={balances.items} selectedToken={selectedToken} validate={validateSpendingLimit} />

<Typography variant="h4" fontWeight={700} mt={3}>
Reset Timer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import useChainId from '@/hooks/useChainId'
import { trackEvent, SETTINGS_EVENTS } from '@/services/analytics'
import { createNewSpendingLimitTx } from '@/services/tx/tx-sender'
import { selectSpendingLimits } from '@/store/spendingLimitsSlice'
import { relativeTime } from '@/utils/date'
import { formatVisualAmount } from '@/utils/formatters'
import type { SpendingLimitState } from '@/store/spendingLimitsSlice'
import type { NewSpendingLimitFlowProps } from '.'
Expand Down Expand Up @@ -56,14 +55,22 @@ export const ReviewSpendingLimit = ({ params }: { params: NewSpendingLimitFlowPr
})
}

const existingAmount = existingSpendingLimit
? formatVisualAmount(BigNumber.from(existingSpendingLimit?.amount), decimals)
: undefined

const oldResetTime = existingSpendingLimit
? getResetTimeOptions(chainId).find((time) => time.value === existingSpendingLimit?.resetTimeMin)?.label
: undefined

return (
<SignOrExecuteForm onSubmit={onFormSubmit}>
{token && (
<SendAmountBlock amount={params.amount} tokenInfo={token.tokenInfo} title="Amount">
{!!existingSpendingLimit && (
{existingAmount && existingAmount !== params.amount && (
<>
<Typography color="error" sx={{ textDecoration: 'line-through' }} component="span">
{formatVisualAmount(BigNumber.from(existingSpendingLimit.amount), decimals)}
{existingAmount}
</Typography>
{'→'}
</>
Expand Down Expand Up @@ -109,7 +116,7 @@ export const ReviewSpendingLimit = ({ params }: { params: NewSpendingLimitFlowPr
display="inline"
component="span"
>
{relativeTime(existingSpendingLimit.lastResetMin, existingSpendingLimit.resetTimeMin)}
{oldResetTime}
</Typography>
{' → '}
</>
Expand Down
2 changes: 1 addition & 1 deletion src/components/tx-flow/flows/RemoveGuard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export type RemoveGuardFlowProps = {

const RemoveGuardFlow = ({ address }: RemoveGuardFlowProps) => {
return (
<TxLayout title="Remove guard">
<TxLayout title="Confirm transaction" subtitle="Remove guard">
<ReviewRemoveGuard params={{ address }} />
</TxLayout>
)
Expand Down

0 comments on commit d485355

Please sign in to comment.