Skip to content

Commit c9822ff

Browse files
committed
feat: implement slippage UI validation and constraints
1 parent e998a06 commit c9822ff

5 files changed

Lines changed: 96 additions & 25 deletions

File tree

app/page.tsx

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -135,20 +135,21 @@ export default function Home() {
135135
<main className="relative min-h-screen w-full flex flex-col pl-(--tu-padding-left) pr-8 items-center justify-center">
136136
<Header />
137137
<FormProvider {...methods}>
138-
<MainComponent remainingDailyLimit={remainingDailyLimit} />
139-
{isModalOpen && !showModalDetailedTx && (
140-
<MainModal
141-
success={isWrapSuccess}
142-
failed={isFailed}
143-
step={modalStep}
144-
amount={amount}
145-
ethereumAddress={ethAddress}
146-
tariWalletAddress={tariAccount?.address}
147-
feesData={feesData}
148-
closeModalAction={handleCloseModal}
149-
type={ongoingBridgeTx?.type || 'wrap'}
150-
/>
151-
)}
138+
<MainComponent remainingDailyLimit={remainingDailyLimit}>
139+
{isModalOpen && !showModalDetailedTx && (
140+
<MainModal
141+
success={isWrapSuccess}
142+
failed={isFailed}
143+
step={modalStep}
144+
amount={amount}
145+
ethereumAddress={ethAddress}
146+
tariWalletAddress={tariAccount?.address}
147+
feesData={feesData}
148+
closeModalAction={handleCloseModal}
149+
type={ongoingBridgeTx?.type || 'wrap'}
150+
/>
151+
)}
152+
</MainComponent>
152153
</FormProvider>
153154
{detailedTx && <TransactionDetailsModal transaction={detailedTx} closeModalAction={() => setDetailedTx(null)} />}
154155
<FooterText />

components/main/main.component.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { useBridgeStore } from '@/store/bridge'
1414

1515
// TODO - add translation keys
1616

17-
export const MainComponent = ({ remainingDailyLimit }: MainComponentProps) => {
17+
export const MainComponent = ({ remainingDailyLimit, children }: MainComponentProps & { children: React.ReactNode }) => {
1818
const { t } = useTranslation('main', { useSuspense: false })
1919
const [showHistory, setShowHistory] = useState(false)
2020
const exceededDailyLimit = useBridgeStore((s) => s.exceededDailyLimit)
@@ -75,6 +75,7 @@ export const MainComponent = ({ remainingDailyLimit }: MainComponentProps) => {
7575
onClick={() => setShowHistory(true)}
7676
type="button"
7777
>
78+
7879
{t('history')} ({bridgeTxs.length})
7980
</button>
8081
</div>
@@ -88,6 +89,7 @@ export const MainComponent = ({ remainingDailyLimit }: MainComponentProps) => {
8889
{offlineMarkup}
8990
<HomeText />
9091
{mainMarkup}
92+
{children}
9193
</section>
9294
)
9395
}

components/modals/main-modal/main-modal.tsx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import { setIsModalOpen, setModalStep } from '@/store/modal'
2222
const DAILY_LIMIT_ERROR = 'Daily wrap limit exceeded'
2323
const DAILY_LIMIT_ERROR_TYPE = 'Forbidden'
2424

25+
import { useForm, FormProvider } from 'react-hook-form'
26+
2527
export const MainModal = ({
2628
success,
2729
failed,
@@ -36,6 +38,9 @@ export const MainModal = ({
3638
const modalRef = useRef<HTMLDivElement>(null)
3739
const tariAccount = useTariAccountStore((s) => s.tariAccount)
3840
const { address: ethAddress, chain, isConnected } = useConnection()
41+
const methods = useForm({
42+
mode: 'onChange',
43+
})
3944

4045
const { getUserBackendBridgeTxs } = useBridgeTransaction()
4146
const { bridgeToEthereum } = useBridgeToEthereum()
@@ -138,14 +143,16 @@ export const MainModal = ({
138143
}
139144

140145
return (
141-
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
142-
<section
143-
ref={modalRef}
144-
className="w-full max-w-md mx-4 bg-[#E0DFDE] shadow-[0px_4px_74px_0px_rgba(0,0,0,0.15)] backdrop-blur-[54px] rounded-3xl overflow-hidden flex flex-col justify-center items-center"
145-
onClick={(e) => e.stopPropagation()}
146-
>
147-
{renderModal()}
148-
</section>
149-
</div>
146+
<FormProvider {...methods}>
147+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
148+
<section
149+
ref={modalRef}
150+
className="w-full max-w-md mx-4 bg-[#E0DFDE] shadow-[0px_4px_74px_0px_rgba(0,0,0,0.15)] backdrop-blur-[54px] rounded-3xl overflow-hidden flex flex-col justify-center items-center"
151+
onClick={(e) => e.stopPropagation()}
152+
>
153+
{renderModal()}
154+
</section>
155+
</div>
156+
</FormProvider>
150157
)
151158
}

components/modals/review-modal/review-modal.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import { useTranslation } from 'react-i18next'
1111
import { useBridgeStore } from '@/store/bridge'
1212
import { useConnection } from 'wagmi'
1313

14+
import { SlippageSettings } from '@/components/settings/settings'
15+
import { useFormContext } from 'react-hook-form'
16+
1417
export const ReviewModal = ({
1518
closeModalAction,
1619
amount,
@@ -24,6 +27,9 @@ export const ReviewModal = ({
2427
const { amountAfterFee, feeAmount, feePercentage, isOverHighBridgeThreshold } = feesData
2528
const fromNetwork = useBridgeStore((s) => s.fromNetwork)
2629
const toNetwork = useBridgeStore((s) => s.toNetwork)
30+
const {
31+
formState: { isValid },
32+
} = useFormContext()
2733

2834
const { fromToken, toToken, destAddress, bridgeHandler } = useBridgeInfo(
2935
fromNetwork,
@@ -149,6 +155,10 @@ export const ReviewModal = ({
149155

150156
<div className="py-[0.5px] w-full bg-gray-300 my-2"></div>
151157

158+
<SlippageSettings />
159+
160+
<div className="py-[0.5px] w-full bg-gray-300 my-2"></div>
161+
152162
<div className="font-medium">
153163
<div className="text-xs text-gray-500">Estimated time</div>
154164
<div className="text-sm">24h</div>
@@ -162,7 +172,7 @@ export const ReviewModal = ({
162172
</div> */}
163173
</div>
164174

165-
<ModalButton label={t('confirm_and_bridge')} onClick={handleClick} disabled={clicked} />
175+
<ModalButton label={t('confirm_and_bridge')} onClick={handleClick} disabled={clicked || !isValid} />
166176
</div>
167177
</div>
168178
)

components/settings/settings.tsx

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React from 'react'
2+
import { Controller, useFormContext } from 'react-hook-form'
3+
import { TextField } from '@mui/material'
4+
5+
export const SlippageSettings = () => {
6+
const {
7+
control,
8+
formState: { errors },
9+
} = useFormContext()
10+
11+
return (
12+
<div className="flex items-center justify-between">
13+
<div className="text-sm font-medium">Slippage Tolerance</div>
14+
<Controller
15+
name="slippage"
16+
control={control}
17+
defaultValue="0.5"
18+
rules={{
19+
min: {
20+
value: 0,
21+
message: 'Slippage cannot be negative',
22+
},
23+
max: {
24+
value: 50,
25+
message: 'Invalid Slippage',
26+
},
27+
pattern: {
28+
value: /^\d+(\.\d{1,2})?$/,
29+
message: 'Max 2 decimal places',
30+
},
31+
}}
32+
render={({ field }) => (
33+
<TextField
34+
{...field}
35+
type="number"
36+
variant="outlined"
37+
placeholder="0.5"
38+
error={Boolean(errors.slippage)}
39+
helperText={errors.slippage?.message as React.ReactNode}
40+
sx={{
41+
width: '100px',
42+
'& .MuiInputBase-input': {
43+
padding: '8px 12px',
44+
},
45+
}}
46+
/>
47+
)}
48+
/>
49+
</div>
50+
)
51+
}

0 commit comments

Comments
 (0)