diff --git a/packages/plugins/Trader/package.json b/packages/plugins/Trader/package.json index 25c1ffad2f34..bde87d5cdad6 100644 --- a/packages/plugins/Trader/package.json +++ b/packages/plugins/Trader/package.json @@ -17,6 +17,7 @@ }, "dependencies": { "@ethersproject/providers": "^5.7.2", + "@hookform/resolvers": "^3.6.0", "@masknet/icons": "workspace:^", "@masknet/plugin-infra": "workspace:^", "@masknet/plugin-transak": "workspace:^", @@ -36,11 +37,13 @@ "date-fns": "^2.30.0", "fuse.js": "^7.0.0", "immer": "^10.1.1", + "react-hook-form": "^7.53.0", "react-router-dom": "^6.24.0", "react-use": "^17.4.0", "urlcat": "^3.1.0", "use-subscription": "^1.8.0", - "web3-core": "1.10.4" + "web3-core": "1.10.4", + "zod": "^3.23.8" }, "lingui": { "catalogs": [ diff --git a/packages/plugins/Trader/src/SiteAdaptor/constants.ts b/packages/plugins/Trader/src/SiteAdaptor/constants.ts index f81255ac89ff..2dd73bed489e 100644 --- a/packages/plugins/Trader/src/SiteAdaptor/constants.ts +++ b/packages/plugins/Trader/src/SiteAdaptor/constants.ts @@ -16,6 +16,7 @@ export const enum RoutePaths { export const DEFAULT_SLIPPAGE = '0.5' export const QUOTE_STALE_DURATION = 20_000 +export const REFETCH_INTERVAL = 20_000 export const bridges = [ { @@ -33,7 +34,7 @@ export const bridges = [ { id: 315, name: 'Across', - logoUrl: new URL('../assets/accross.svg', import.meta.url).href, + logoUrl: new URL('../assets/across.svg', import.meta.url).href, }, { id: 211, @@ -77,4 +78,9 @@ export const bridges = [ logoUrl: 'https://static.okx.com/cdn/explorer/dex/logo/dex_Circle.png.png?x-oss-process=image/format,webp/ignore-error,1', }, + { + id: 476, + name: 'Bungee Refuel', + logoUrl: 'https://www.okx.com/cdn/web3/dex/logo/b53506a1-fc2b-439d-9696-8eaea55167ca.png', + }, ] diff --git a/packages/plugins/Trader/src/SiteAdaptor/trader/Routes.tsx b/packages/plugins/Trader/src/SiteAdaptor/trader/Routes.tsx index b690ec30009c..e69ff092a18a 100644 --- a/packages/plugins/Trader/src/SiteAdaptor/trader/Routes.tsx +++ b/packages/plugins/Trader/src/SiteAdaptor/trader/Routes.tsx @@ -4,7 +4,7 @@ import { BridgeConfirm } from './views/BridgeConfirm.js' import { BridgeQuoteRoute } from './views/BridgeQuoteRoute.js' import { Confirm } from './views/Confirm.js' import { HistoryView } from './views/History.js' -import { NetworkFee } from './views/NetworkFee.js' +import { NetworkFee } from './views/NetworkFee/index.js' import { QuoteRoute } from './views/QuoteRoute.js' import { SelectLiquidity } from './views/SelectLiquidity.js' import { Slippage } from './views/Slippage.js' diff --git a/packages/plugins/Trader/src/SiteAdaptor/trader/hooks/useBridgeQuotes.ts b/packages/plugins/Trader/src/SiteAdaptor/trader/hooks/useBridgeQuotes.ts index feb0410cb7fb..41349ee89c28 100644 --- a/packages/plugins/Trader/src/SiteAdaptor/trader/hooks/useBridgeQuotes.ts +++ b/packages/plugins/Trader/src/SiteAdaptor/trader/hooks/useBridgeQuotes.ts @@ -1,7 +1,7 @@ import { OKX } from '@masknet/web3-providers' import type { GetBridgeQuoteOptions } from '@masknet/web3-providers/types' import { useQuery } from '@tanstack/react-query' -import { QUOTE_STALE_DURATION } from '../../constants.js' +import { QUOTE_STALE_DURATION, REFETCH_INTERVAL } from '../../constants.js' export function useBridgeQuotes(options: Partial, enabled = true) { const valid = @@ -19,5 +19,6 @@ export function useBridgeQuotes(options: Partial, enabled queryKey: ['okx-bridge', 'get-quotes', options], queryFn: () => OKX.getBridgeQuote(options as GetBridgeQuoteOptions), staleTime: QUOTE_STALE_DURATION, + refetchInterval: REFETCH_INTERVAL, }) } diff --git a/packages/plugins/Trader/src/SiteAdaptor/trader/hooks/useLiquidityResources.ts b/packages/plugins/Trader/src/SiteAdaptor/trader/hooks/useLiquidityResources.ts index 13e1bf706ce7..eb4a811a4feb 100644 --- a/packages/plugins/Trader/src/SiteAdaptor/trader/hooks/useLiquidityResources.ts +++ b/packages/plugins/Trader/src/SiteAdaptor/trader/hooks/useLiquidityResources.ts @@ -1,6 +1,7 @@ import { OKX } from '@masknet/web3-providers' import type { ChainId } from '@masknet/web3-shared-evm' import { skipToken, useQuery } from '@tanstack/react-query' +import { REFETCH_INTERVAL } from '../../constants.js' export function useLiquidityResources(chainId: ChainId, enabled = true) { return useQuery({ @@ -13,5 +14,6 @@ export function useLiquidityResources(chainId: ChainId, enabled = true) { return res?.code === 0 ? res.data : undefined } : skipToken, + refetchInterval: REFETCH_INTERVAL, }) } diff --git a/packages/plugins/Trader/src/SiteAdaptor/trader/hooks/useQuotes.ts b/packages/plugins/Trader/src/SiteAdaptor/trader/hooks/useQuotes.ts index 463cfa2a1f8f..bf52ce3a3148 100644 --- a/packages/plugins/Trader/src/SiteAdaptor/trader/hooks/useQuotes.ts +++ b/packages/plugins/Trader/src/SiteAdaptor/trader/hooks/useQuotes.ts @@ -1,8 +1,8 @@ import { OKX } from '@masknet/web3-providers' import type { GetQuotesOptions } from '@masknet/web3-providers/types' -import { useQuery } from '@tanstack/react-query' -import { QUOTE_STALE_DURATION } from '../../constants.js' import { isGreaterThan } from '@masknet/web3-shared-base' +import { useQuery } from '@tanstack/react-query' +import { QUOTE_STALE_DURATION, REFETCH_INTERVAL } from '../../constants.js' export function useQuotes(options: Partial, enabled = true) { const valid = @@ -12,5 +12,6 @@ export function useQuotes(options: Partial, enabled = true) { queryKey: ['okx-swap', 'get-quotes', options], queryFn: () => OKX.getQuotes(options as GetQuotesOptions), staleTime: QUOTE_STALE_DURATION, + refetchInterval: REFETCH_INTERVAL, }) } diff --git a/packages/plugins/Trader/src/SiteAdaptor/trader/views/NetworkFee.tsx b/packages/plugins/Trader/src/SiteAdaptor/trader/views/NetworkFee/index.tsx similarity index 69% rename from packages/plugins/Trader/src/SiteAdaptor/trader/views/NetworkFee.tsx rename to packages/plugins/Trader/src/SiteAdaptor/trader/views/NetworkFee/index.tsx index a49a44fd3b1c..00c28ac56334 100644 --- a/packages/plugins/Trader/src/SiteAdaptor/trader/views/NetworkFee.tsx +++ b/packages/plugins/Trader/src/SiteAdaptor/trader/views/NetworkFee/index.tsx @@ -1,3 +1,4 @@ +import { zodResolver } from '@hookform/resolvers/zod' import { t, Trans } from '@lingui/macro' import { Icons } from '@masknet/icons' import { ProgressiveText } from '@masknet/shared' @@ -14,11 +15,16 @@ import { } from '@masknet/web3-shared-evm' import { Box, Button, Typography } from '@mui/material' import { BigNumber } from 'bignumber.js' +import { isEmpty } from 'lodash-es' import { memo, useMemo, useRef, useState } from 'react' +import { Controller, useForm } from 'react-hook-form' import { useNavigate } from 'react-router-dom' -import { GasCost } from '../../components/GasCost.js' -import { Warning } from '../../components/Warning.js' -import { useGasManagement, useTrade } from '../contexts/index.js' +import { type z as zod } from 'zod' +import { GasCost } from '../../../components/GasCost.js' +import { Warning } from '../../../components/Warning.js' +import { useGasManagement } from '../../contexts/GasManager.js' +import { useTrade } from '../../contexts/TradeProvider.js' +import { useSchema } from './schema.js' const useStyles = makeStyles()((theme, _, refs) => ({ container: { @@ -114,6 +120,11 @@ const useStyles = makeStyles()((theme, color: theme.palette.maskColor.second, marginLeft: theme.spacing(1.5), }, + error: { + color: theme.palette.maskColor.danger, + fontSize: 13, + lineHeight: '18px', + }, })) function formatTimeCost(seconds: number | undefined) { @@ -124,8 +135,8 @@ function formatTimeCost(seconds: number | undefined) { } const MIN_BASE_FEE = '0.01' -const gweiToWei = (gwei: BigNumber.Value | undefined) => formatGweiToWei(gwei ?? '0').toFixed() -const weiToGwei = (wei: BigNumber.Value | undefined) => formatWeiToGwei(wei ?? '0').toFixed() +const gweiToWei = (gwei: BigNumber.Value | undefined) => (gwei ? formatGweiToWei(gwei).toFixed() : undefined) +const weiToGwei = (wei: BigNumber.Value | undefined) => (wei ? formatWeiToGwei(wei).toFixed() : undefined) export const NetworkFee = memo(function NetworkFee() { const { classes, cx, theme } = useStyles() const { chainId } = useTrade() @@ -152,9 +163,10 @@ export const NetworkFee = memo(function NetworkFee() { const [priorityFee = defaultPriorityFee, setPriorityFee] = useState() const [gasPrice = defaultGasPrice, setGasPrice] = useState() const customFeePrice = useMemo( - () => (isSupport1559 ? plus(baseFee ?? '0', priorityFee ?? '0') : new BigNumber(gasPrice ?? '0')), + () => (isSupport1559 ? plus(baseFee || '0', priorityFee || '0') : new BigNumber(gasPrice ?? '0')), [isSupport1559, baseFee, priorityFee, gasPrice], ) + const isTooHigh = isGreaterThan(customFeePrice, multipliedBy(gasOptions?.fast.suggestedMaxFeePerGas ?? '0', 2)) const customBoxRef = useRef(null) @@ -192,6 +204,20 @@ export const NetworkFee = memo(function NetworkFee() { } }, [gasOptions, customFeePrice]) + const schema = useSchema(isSupport1559) + + const { + control, + setValue, + formState: { errors }, + } = useForm>({ + mode: 'onChange', + resolver: zodResolver(schema), + context: { + gasOptions, + }, + }) + return (
Custom - {formatWeiToGwei(customFeePrice ?? '0').toFixed()} Gwei + {customFeePrice ? formatWeiToGwei(customFeePrice).toFixed() : '--'} Gwei
@@ -350,29 +376,72 @@ export const NetworkFee = memo(function NetworkFee() { Base fee required: {MIN_BASE_FEE} Gwei - { - setBaseFee(gweiToWei(e.target.value)) - }} - InputProps={{ - endAdornment: Gwei, + { + return ( + <> + { + const value = gweiToWei(e.target.value) || '' + setBaseFee(value) + setValue('baseFee', value) + field.onChange(e) + }} + InputProps={{ + endAdornment: ( + Gwei + ), + }} + /> + {fieldState.error ? + + {fieldState.error.message} + + : null} + + ) }} /> Priority fee - { - setPriorityFee(gweiToWei(e.target.value)) - }} - InputProps={{ - endAdornment: Gwei, + + { + return ( + <> + { + const value = gweiToWei(e.target.value) || '' + setPriorityFee(value) + setValue('priorityFee', value) + field.onChange(e) + }} + InputProps={{ + endAdornment: ( + Gwei + ), + }} + /> + {fieldState.error ? + + {fieldState.error.message} + + : null} + + ) }} /> @@ -382,15 +451,36 @@ export const NetworkFee = memo(function NetworkFee() { Gas Price - { - setGasPrice(gweiToWei(e.target.value || '0')) - }} - InputProps={{ - endAdornment: Gwei, + { + return ( + <> + { + const value = gweiToWei(e.target.value) || '' + setGasPrice(value) + setValue('gasPrice', value) + field.onChange(e) + }} + InputProps={{ + endAdornment: ( + Gwei + ), + }} + /> + {fieldState.error ? + + {fieldState.error.message} + + : null} + + ) }} /> @@ -407,7 +497,7 @@ export const NetworkFee = memo(function NetworkFee() {