From 690fdb36804683c4d5d961dee3406a36d2a55084 Mon Sep 17 00:00:00 2001 From: DMY <147dmy@gmail.com> Date: Thu, 1 Aug 2024 12:03:38 +0800 Subject: [PATCH 1/3] fix: ui --- .../ChainSelector/components/SelectChainItem.tsx | 2 +- src/ui/views/Bridge/Component/BridgeTokenPair.tsx | 7 ++++--- src/ui/views/GasTopUp/index.tsx | 11 +++++++---- src/ui/views/SendToken/components/MaxButton.tsx | 1 + 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/ui/component/ChainSelector/components/SelectChainItem.tsx b/src/ui/component/ChainSelector/components/SelectChainItem.tsx index 727ce0ac9a8..b51e5b5f945 100644 --- a/src/ui/component/ChainSelector/components/SelectChainItem.tsx +++ b/src/ui/component/ChainSelector/components/SelectChainItem.tsx @@ -86,7 +86,7 @@ export const SelectChainItem = forwardRef(
@@ -147,6 +147,7 @@ const RenderWrapper = styled.div` background: var(--r-neutral-card-2, #f2f4f7); border-radius: 6px; padding: 0 12px; + padding-right: 10px; width: 100%; display: flex; align-items: center; diff --git a/src/ui/views/GasTopUp/index.tsx b/src/ui/views/GasTopUp/index.tsx index aefb87f677a..7842a4d3b1a 100644 --- a/src/ui/views/GasTopUp/index.tsx +++ b/src/ui/views/GasTopUp/index.tsx @@ -317,12 +317,15 @@ export const GasTopUp = () => {
-
+
{t('page.gasTopUp.title')} diff --git a/src/ui/views/SendToken/components/MaxButton.tsx b/src/ui/views/SendToken/components/MaxButton.tsx index e035e634d9d..d2bf6ba13cf 100644 --- a/src/ui/views/SendToken/components/MaxButton.tsx +++ b/src/ui/views/SendToken/components/MaxButton.tsx @@ -2,6 +2,7 @@ import styled from 'styled-components'; export const MaxButton = styled.div` font-size: 11px; + font-weight: 500; line-height: 1; padding: 4px 5px; cursor: pointer; From d4d7adda09cd2a3eacfb8fee5b44ec1455bd74bc Mon Sep 17 00:00:00 2001 From: DMY <147dmy@gmail.com> Date: Thu, 1 Aug 2024 14:32:17 +0800 Subject: [PATCH 2/3] feat: improve ReserveGas --- .../views/Swap/Component/ReserveGasPopup.tsx | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/ui/views/Swap/Component/ReserveGasPopup.tsx b/src/ui/views/Swap/Component/ReserveGasPopup.tsx index f3b115e303e..303336e7de9 100644 --- a/src/ui/views/Swap/Component/ReserveGasPopup.tsx +++ b/src/ui/views/Swap/Component/ReserveGasPopup.tsx @@ -19,6 +19,7 @@ interface ReserveGasContentProps { limit: number; selectedItem?: GasLevelType | string; onGasChange: (gasLevel: GasLevel) => void; + rawHexBalance?: string | number; } const SORT_SCORE = { @@ -41,6 +42,7 @@ const ReserveGasContent = React.forwardRef< limit = 1000000, selectedItem = 'normal', onGasChange, + rawHexBalance, } = props; const [currentSelectedItem, setCurrentSelectedItem] = useState(selectedItem); @@ -90,12 +92,30 @@ const ReserveGasContent = React.forwardRef< [limit, decimals] ); + const checkIsInsufficient = useCallback( + (price: number) => { + if (rawHexBalance !== undefined && rawHexBalance !== null) { + return false; + } + return new BigNumber(rawHexBalance || 0, 16).lt( + new BigNumber(limit).times(price) + ); + }, + [rawHexBalance, limit] + ); + return (
{sortedList?.map((item) => { const checked = currentSelectedItem === item.level; + + const gasIsSufficient = checkIsInsufficient(item.price); + const onChecked = () => { + if (gasIsSufficient) { + return; + } setGasLevel(item); setCurrentSelectedItem(item.level as any); }; @@ -111,9 +131,12 @@ const ReserveGasContent = React.forwardRef< key={item.level} className={clsx( 'flex justify-between', - 'py-[22px] px-16 rounded-[8px] cursor-pointer', - 'bg-r-neutral-card-1 border border-solid hover:border-rabby-blue-default', - checked ? 'border-rabby-blue-default' : 'border-transparent' + 'py-[22px] px-16 rounded-[8px] ', + 'bg-r-neutral-card-1 border border-solid ', + checked ? 'border-rabby-blue-default' : 'border-transparent', + gasIsSufficient + ? 'opacity-50 cursor-not-allowed' + : 'hover:border-rabby-blue-default cursor-pointer' )} onClick={onChecked} > @@ -203,6 +226,7 @@ export const ReserveGasPopup = (props: ReserveGasContentProps & PopupProps) => { onGasChange, limit, selectedItem, + rawHexBalance, ...otherPopupProps } = props; const { t } = useTranslation(); @@ -222,6 +246,7 @@ export const ReserveGasPopup = (props: ReserveGasContentProps & PopupProps) => { limit={limit} selectedItem={selectedItem} onGasChange={onGasChange} + rawHexBalance={rawHexBalance} /> )} From 79a114a34004ef67f62fa92a3f784e2132aea7c3 Mon Sep 17 00:00:00 2001 From: DMY <147dmy@gmail.com> Date: Thu, 1 Aug 2024 14:32:48 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20debounce=20input=20on=20swap?= =?UTF-8?q?=E3=80=81birdge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/Bridge/Component/BridgeContent.tsx | 43 +++++++++------ src/ui/views/Bridge/hooks/token.tsx | 26 +++++---- src/ui/views/Swap/Component/Main.tsx | 52 +++++++++++------- src/ui/views/Swap/hooks/token.tsx | 54 +++++++++++++------ 4 files changed, 112 insertions(+), 63 deletions(-) diff --git a/src/ui/views/Bridge/Component/BridgeContent.tsx b/src/ui/views/Bridge/Component/BridgeContent.tsx index d4736c33fc7..f5e244b188b 100644 --- a/src/ui/views/Bridge/Component/BridgeContent.tsx +++ b/src/ui/views/Bridge/Component/BridgeContent.tsx @@ -104,7 +104,10 @@ export const BridgeContent = () => { handleAmountChange, handleBalance, - payAmount, + + debouncePayAmount, + inputAmount, + inSufficient, openQuotesList, @@ -118,6 +121,13 @@ export const BridgeContent = () => { expired, } = useTokenPair(userAddress); + const payAmountLoading = useMemo(() => inputAmount !== debouncePayAmount, [ + inputAmount, + debouncePayAmount, + ]); + + const quoteOrAmountLoading = quoteLoading || payAmountLoading; + const aggregatorIds = useRabbySelector( (s) => s.bridge.aggregatorsList.map((e) => e.id) || [] ); @@ -179,7 +189,7 @@ export const BridgeContent = () => { from_token_id: payToken.id, user_addr: userAddress, from_chain_id: payToken.chain, - from_token_raw_amount: new BigNumber(payAmount) + from_token_raw_amount: new BigNumber(debouncePayAmount) .times(10 ** payToken.decimals) .toFixed(0, 1) .toString(), @@ -202,7 +212,7 @@ export const BridgeContent = () => { to: tx.to, value: tx.value, data: tx.data, - payTokenRawAmount: new BigNumber(payAmount) + payTokenRawAmount: new BigNumber(debouncePayAmount) .times(10 ** payToken.decimals) .toFixed(0, 1) .toString(), @@ -216,7 +226,7 @@ export const BridgeContent = () => { bridge_id: selectedBridgeQuote.bridge_id, from_chain_id: payToken.chain, from_token_id: payToken.id, - from_token_amount: payAmount, + from_token_amount: debouncePayAmount, to_chain_id: receiveToken.chain, to_token_id: receiveToken.id, to_token_amount: selectedBridgeQuote.to_token_amount, @@ -260,7 +270,7 @@ export const BridgeContent = () => { selectedBridgeQuote?.bridge_id, selectedBridgeQuote?.to_token_amount, wallet, - payAmount, + debouncePayAmount, rbiSource, ]); @@ -358,15 +368,15 @@ export const BridgeContent = () => { - {payAmount + {inputAmount ? `≈ ${formatUsdValue( - new BigNumber(payAmount) + new BigNumber(inputAmount) .times(payToken?.price || 0) .toString(10) )}` @@ -375,20 +385,20 @@ export const BridgeContent = () => { } /> - {quoteLoading && !inSufficient && !selectedBridgeQuote?.manualClick && ( - - )} + {quoteOrAmountLoading && + !inSufficient && + !selectedBridgeQuote?.manualClick && } {payToken && !inSufficient && receiveToken && - Number(payAmount) > 0 && - (!quoteLoading || selectedBridgeQuote?.manualClick) && ( + Number(debouncePayAmount) > 0 && + (!quoteOrAmountLoading || selectedBridgeQuote?.manualClick) && ( { disabled={ !payToken || !receiveToken || - !payAmount || - Number(payAmount) === 0 || inSufficient || + payAmountLoading || !selectedBridgeQuote } > @@ -494,7 +503,7 @@ export const BridgeContent = () => { userAddress={userAddress} chain={chain} payToken={payToken} - payAmount={payAmount} + payAmount={debouncePayAmount} receiveToken={receiveToken} inSufficient={inSufficient} setSelectedBridgeQuote={setSelectedBridgeQuote} diff --git a/src/ui/views/Bridge/hooks/token.tsx b/src/ui/views/Bridge/hooks/token.tsx index 1f67b254186..ed862f01150 100644 --- a/src/ui/views/Bridge/hooks/token.tsx +++ b/src/ui/views/Bridge/hooks/token.tsx @@ -18,6 +18,7 @@ import { ETH_USDT_CONTRACT } from '@/constant'; import { findChain } from '@/utils/chain'; import { BridgeQuote } from '@/background/service/openapi'; import stats from '@/stats'; +import useDebounceValue from '@/ui/hooks/useDebounceValue'; const useTokenInfo = ({ userAddress, @@ -150,7 +151,9 @@ export const useTokenPair = (userAddress: string) => { dispatch.bridge.setSelectedToToken(receiveToken); }, [receiveToken]); - const [payAmount, setPayAmount] = useState(''); + const [inputAmount, setPayAmount] = useState(''); + + const debouncePayAmount = useDebounceValue(inputAmount, 300); const [selectedBridgeQuote, setOriSelectedBridgeQuote] = useState< SelectedBridgeQuote | undefined @@ -179,9 +182,9 @@ export const useTokenPair = (userAddress: string) => { const inSufficient = useMemo( () => payToken - ? tokenAmountBn(payToken).lt(payAmount) - : new BigNumber(0).lt(payAmount), - [payToken, payAmount] + ? tokenAmountBn(payToken).lt(debouncePayAmount) + : new BigNumber(0).lt(debouncePayAmount), + [payToken, debouncePayAmount] ); const [quoteList, setQuotesList] = useState([]); @@ -189,7 +192,7 @@ export const useTokenPair = (userAddress: string) => { useEffect(() => { setQuotesList([]); setSelectedBridgeQuote(undefined); - }, [payToken?.id, receiveToken?.id, chain, payAmount, inSufficient]); + }, [payToken?.id, receiveToken?.id, chain, debouncePayAmount, inSufficient]); const visible = useQuoteVisible(); @@ -215,7 +218,7 @@ export const useTokenPair = (userAddress: string) => { receiveToken?.id && receiveToken && chain && - Number(payAmount) > 0 && + Number(debouncePayAmount) > 0 && aggregatorsList.length > 0 ) { fetchIdRef.current += 1; @@ -239,7 +242,7 @@ export const useTokenPair = (userAddress: string) => { from_token_id: payToken.id, user_addr: userAddress, from_chain_id: payToken.chain, - from_token_raw_amount: new BigNumber(payAmount) + from_token_raw_amount: new BigNumber(debouncePayAmount) .times(10 ** payToken.decimals) .toFixed(0, 1) .toString(), @@ -301,7 +304,7 @@ export const useTokenPair = (userAddress: string) => { quote.approve_contract_id ); tokenApproved = new BigNumber(allowance).gte( - new BigNumber(payAmount).times(10 ** payToken.decimals) + new BigNumber(debouncePayAmount).times(10 ** payToken.decimals) ); } let shouldTwoStepApprove = false; @@ -356,7 +359,7 @@ export const useTokenPair = (userAddress: string) => { payToken?.id, receiveToken?.id, chain, - payAmount, + debouncePayAmount, ]); const [bestQuoteId, setBestQuoteId] = useState< @@ -412,7 +415,7 @@ export const useTokenPair = (userAddress: string) => { useEffect(() => { setExpired(false); setSelectedBridgeQuote(undefined); - }, [payToken?.id, receiveToken?.id, chain, payAmount, inSufficient]); + }, [payToken?.id, receiveToken?.id, chain, debouncePayAmount, inSufficient]); return { chain, @@ -425,7 +428,8 @@ export const useTokenPair = (userAddress: string) => { handleAmountChange, handleBalance, - payAmount, + debouncePayAmount, + inputAmount, inSufficient, diff --git a/src/ui/views/Swap/Component/Main.tsx b/src/ui/views/Swap/Component/Main.tsx index 61a6b1c2a6a..2389559637d 100644 --- a/src/ui/views/Swap/Component/Main.tsx +++ b/src/ui/views/Swap/Component/Main.tsx @@ -128,7 +128,9 @@ export const Main = () => { handleAmountChange, handleBalance, - payAmount, + inputAmount, + debouncePayAmount, + payTokenIsNativeToken, isWrapToken, inSufficient, @@ -186,6 +188,13 @@ export const Main = () => { const setVisible = useSetQuoteVisible(); const { t } = useTranslation(); + const payAmountLoading = useMemo(() => inputAmount !== debouncePayAmount, [ + inputAmount, + debouncePayAmount, + ]); + + const quoteOrAmountLoading = quoteLoading || payAmountLoading; + const btnText = useMemo(() => { if (slippageChanged) { return t('page.swap.slippage-adjusted-refresh-quote'); @@ -201,7 +210,7 @@ export const Main = () => { name: isWrapToken ? 'Wrap Contract' : DexDisplayName, }); } - if (quoteLoading) { + if (quoteOrAmountLoading) { return t('page.swap.title'); } @@ -213,7 +222,7 @@ export const Main = () => { payToken, isWrapToken, DexDisplayName, - quoteLoading, + quoteOrAmountLoading, ]); const wallet = useWallet(); @@ -241,7 +250,7 @@ export const Main = () => { postSwapParams: { quote: { pay_token_id: payToken.id, - pay_token_amount: Number(payAmount), + pay_token_amount: Number(debouncePayAmount), receive_token_id: receiveToken!.id, receive_token_amount: new BigNumber( activeProvider?.quote.toTokenAmount @@ -351,10 +360,10 @@ export const Main = () => { onChange={switchChain} disabledTips={getDisabledTips} supportChains={SWAP_SUPPORT_CHAINS} - chainRenderClassName={clsx('text-[16px] font-medium')} + chainRenderClassName={clsx('text-[16px] font-medium rounded-[4px]')} /> -
+
{t('page.swap.swap-from')} {t('page.swap.to')}
@@ -424,14 +433,14 @@ export const Main = () => { - {payAmount + {inputAmount ? `≈ ${formatUsdValue( - new BigNumber(payAmount) + new BigNumber(inputAmount) .times(payToken?.price || 0) .toString(10) )}` @@ -440,13 +449,14 @@ export const Main = () => { } /> - {quoteLoading && !inSufficient && !activeProvider?.manualClick && ( - - )} + {quoteOrAmountLoading && + !inSufficient && + !activeProvider?.manualClick && } - {Number(payAmount) > 0 && + {Number(debouncePayAmount) > 0 && !inSufficient && - (!quoteLoading || (activeProvider && !!activeProvider.manualClick)) && + (!quoteOrAmountLoading || + (activeProvider && !!activeProvider.manualClick)) && payToken && receiveToken && ( <> @@ -455,7 +465,7 @@ export const Main = () => { activeProvider={activeProvider} isWrapToken={isWrapToken} className="section" - payAmount={payAmount} + payAmount={debouncePayAmount} receiveRawAmount={activeProvider?.actualReceiveAmount || 0} payToken={payToken} receiveToken={receiveToken} @@ -466,8 +476,8 @@ export const Main = () => { )} - {Number(payAmount) > 0 && - (!quoteLoading || !!activeProvider?.manualClick) && + {Number(debouncePayAmount) > 0 && + (!quoteOrAmountLoading || !!activeProvider?.manualClick) && !!activeProvider && !!activeProvider?.quote?.toTokenAmount && payToken && @@ -579,8 +589,9 @@ export const Main = () => { disabled={ !payToken || !receiveToken || - !payAmount || - Number(payAmount) === 0 || + !debouncePayAmount || + inSufficient || + payAmountLoading || !activeProvider } > @@ -596,6 +607,7 @@ export const Main = () => { visible={reserveGasOpen} onCancel={closeReserveGasOpen} onClose={closeReserveGasOpen} + rawHexBalance={payToken?.raw_amount_hex_str} /> {payToken && receiveToken && chain ? ( { chain={chain} slippage={slippage} payToken={payToken} - payAmount={payAmount} + payAmount={debouncePayAmount} receiveToken={receiveToken} fee={feeRate} inSufficient={inSufficient} diff --git a/src/ui/views/Swap/hooks/token.tsx b/src/ui/views/Swap/hooks/token.tsx index 785d3c0f8c2..19e8d74fad1 100644 --- a/src/ui/views/Swap/hooks/token.tsx +++ b/src/ui/views/Swap/hooks/token.tsx @@ -21,6 +21,7 @@ import { useAsyncInitializeChainList } from '@/ui/hooks/useChain'; import { SWAP_SUPPORT_CHAINS } from '@/constant'; import { findChain } from '@/utils/chain'; import { GasLevelType } from '../Component/ReserveGasPopup'; +import useDebounceValue from '@/ui/hooks/useDebounceValue'; const useTokenInfo = ({ userAddress, @@ -186,7 +187,8 @@ export const useTokenPair = (userAddress: string) => { dispatch.swap.setSelectedToToken(receiveToken); }, [receiveToken]); - const [payAmount, setPayAmount] = useState(''); + const [inputAmount, setPayAmount] = useState(''); + const debouncePayAmount = useDebounceValue(inputAmount, 300); const [feeRate, setFeeRate] = useState('0'); @@ -244,12 +246,8 @@ export const useTokenPair = (userAddress: string) => { [gasList] ); - useEffect(() => { - gasPriceRef.current = normalGasPrice; - }, [normalGasPrice]); - const nativeTokenDecimals = useMemo( - () => CHAINS?.[chain]?.nativeTokenDecimals, + () => findChain({ enum: chain })?.nativeTokenDecimals || 1e18, [CHAINS?.[chain]] ); @@ -258,6 +256,31 @@ export const useTokenPair = (userAddress: string) => { [chain] ); + useEffect(() => { + if (payTokenIsNativeToken && gasList) { + const checkGasIsEnough = (price: number) => { + return new BigNumber(payToken?.raw_amount_hex_str || 0, 16).gte( + new BigNumber(gasLimit).times(price) + ); + }; + const normalPrice = + gasList?.find((e) => e.level === 'normal')?.price || 0; + const slowPrice = gasList?.find((e) => e.level === 'slow')?.price || 0; + const isNormalEnough = checkGasIsEnough(normalPrice); + const isSlowEnough = checkGasIsEnough(slowPrice); + if (isNormalEnough) { + setGasLevel('normal'); + gasPriceRef.current = normalGasPrice; + } else if (isSlowEnough) { + setGasLevel('slow'); + gasPriceRef.current = slowPrice; + } else { + setGasLevel('custom'); + gasPriceRef.current = 0; + } + } + }, [payTokenIsNativeToken, gasList, gasLimit, payToken?.raw_amount_hex_str]); + const closeReserveGasOpen = useCallback(() => { setReserveGasOpen(false); if (payToken && gasPriceRef.current !== undefined) { @@ -322,9 +345,9 @@ export const useTokenPair = (userAddress: string) => { const inSufficient = useMemo( () => payToken - ? tokenAmountBn(payToken).lt(payAmount) - : new BigNumber(0).lt(payAmount), - [payToken, payAmount] + ? tokenAmountBn(payToken).lt(debouncePayAmount) + : new BigNumber(0).lt(debouncePayAmount), + [payToken, debouncePayAmount] ); useEffect(() => { @@ -345,7 +368,7 @@ export const useTokenPair = (userAddress: string) => { useEffect(() => { setQuotesList([]); setActiveProvider(undefined); - }, [payToken?.id, receiveToken?.id, chain, payAmount, inSufficient]); + }, [payToken?.id, receiveToken?.id, chain, debouncePayAmount, inSufficient]); const setQuote = useCallback( (id: number) => (quote: TDexQuoteData) => { @@ -382,7 +405,7 @@ export const useTokenPair = (userAddress: string) => { receiveToken?.id && receiveToken && chain && - Number(payAmount) > 0 && + Number(debouncePayAmount) > 0 && feeRate && !inSufficient ) { @@ -396,7 +419,7 @@ export const useTokenPair = (userAddress: string) => { receiveToken, slippage: slippage || '0.1', chain, - payAmount: payAmount, + payAmount: debouncePayAmount, fee: feeRate, setQuote: setQuote(currentFetchId), }).finally(() => {}); @@ -411,7 +434,7 @@ export const useTokenPair = (userAddress: string) => { payToken?.id, receiveToken?.id, chain, - payAmount, + debouncePayAmount, feeRate, // slippage, ]); @@ -521,7 +544,7 @@ export const useTokenPair = (userAddress: string) => { setExpired(false); setActiveProvider(undefined); setSlippageChanged(false); - }, [payToken?.id, receiveToken?.id, chain, payAmount, inSufficient]); + }, [payToken?.id, receiveToken?.id, chain, debouncePayAmount, inSufficient]); useEffect(() => { if (searchObj.chain && searchObj.payTokenId) { @@ -570,7 +593,8 @@ export const useTokenPair = (userAddress: string) => { handleAmountChange, handleBalance, - payAmount, + inputAmount, + debouncePayAmount, isWrapToken, wrapTokenSymbol,