From bfbf43cb49cc4d44a7a7ba3dfbc7bd7d7093e3a0 Mon Sep 17 00:00:00 2001 From: Danh Date: Wed, 27 Sep 2023 17:39:53 +0700 Subject: [PATCH] add gas fee info update text add gas fee info s s --- .../swapv2/LimitOrder/EditOrderModal.tsx | 5 +- .../ListOrder/useRequestCancelOrder.tsx | 56 +++++++++++++------ .../LimitOrder/Modals/CancelButtons.tsx | 54 ++++++++++++++++-- .../LimitOrder/Modals/CancelOrderModal.tsx | 2 + src/utils/useEstimateGasTxs.ts | 45 +++++++++++++++ 5 files changed, 140 insertions(+), 22 deletions(-) create mode 100644 src/utils/useEstimateGasTxs.ts diff --git a/src/components/swapv2/LimitOrder/EditOrderModal.tsx b/src/components/swapv2/LimitOrder/EditOrderModal.tsx index e6ba8afcaf..2635afd97c 100644 --- a/src/components/swapv2/LimitOrder/EditOrderModal.tsx +++ b/src/components/swapv2/LimitOrder/EditOrderModal.tsx @@ -1,6 +1,6 @@ import { Trans } from '@lingui/macro' import { ethers } from 'ethers' -import { useCallback, useState } from 'react' +import { useCallback, useMemo, useState } from 'react' import { X } from 'react-feather' import { Flex, Text } from 'rebass' import { useGetTotalActiveMakingAmountQuery } from 'services/limitOrder' @@ -111,6 +111,8 @@ export default function EditOrderModal({ const isSupportSoftCancelOrder = useIsSupportSoftCancelOrder() const supportCancelGasless = isSupportSoftCancelOrder(order) + const orders = useMemo(() => (order ? [order] : []), [order]) + const renderCancelButtons = (showCancelStatus = true, disableButtons = false) => ( <> {showCancelStatus && ( @@ -123,6 +125,7 @@ export default function EditOrderModal({ )} { + const { account } = useActiveWeb3React() + const [getEncodeData] = useGetEncodeDataMutation() + const { library } = useWeb3React() + return useCallback( + async ({ orders, isCancelAll }: { orders: LimitOrder[]; isCancelAll: boolean | undefined }) => { + if (!library) throw new Error() + if (isCancelAll) { + const contracts = [...new Set(orders.map(e => e.contractAddress))] + const result = [] + for (const address of contracts) { + const limitOrderContract = getContract(address, LIMIT_ORDER_ABI, library, account) + const [{ encodedData }, nonce] = await Promise.all([ + getEncodeData({ orderIds: [], isCancelAll }).unwrap(), + limitOrderContract?.nonce?.(account), + ]) + result.push({ encodedData, nonce, contractAddress: address }) + } + return result + } + // cancel single order + const { encodedData } = await getEncodeData({ + orderIds: orders.map(e => e.id), + }).unwrap() + return [{ encodedData, contractAddress: orders[0]?.contractAddress, nonce: '' }] + }, + [account, getEncodeData, library], + ) +} + const useRequestCancelOrder = ({ orders, isCancelAll, @@ -35,11 +65,12 @@ const useRequestCancelOrder = ({ const { library } = useWeb3React() const [flowState, setFlowState] = useState(TRANSACTION_STATE_DEFAULT) const [insertCancellingOrder] = useInsertCancellingOrderMutation() - const [getEncodeData] = useGetEncodeDataMutation() const [createCancelSignature] = useCreateCancelOrderSignatureMutation() const [cancelOrderRequest] = useCancelOrdersMutation() const addTransactionWithType = useTransactionAdder() + const getEncodeData = useGetEncodeLimitOrder() + const requestHardCancelOrder = async (order: LimitOrder | undefined) => { if (!library || !account) return Promise.reject('Wrong input') @@ -95,24 +126,17 @@ const useRequestCancelOrder = ({ } if (isCancelAll) { - const contracts = [...new Set(orders.map(e => e.contractAddress))] - for (const address of contracts) { - const limitOrderContract = getContract(address, LIMIT_ORDER_ABI, library, account) - const [{ encodedData }, nonce] = await Promise.all([ - getEncodeData({ orderIds: [], isCancelAll }).unwrap(), - limitOrderContract?.nonce?.(account), - ]) - await sendTransaction(encodedData, address, { nonce: nonce.toNumber() }) + const data = await getEncodeData({ isCancelAll, orders }) + for (const item of data) { + const { contractAddress, nonce, encodedData } = item + await sendTransaction(encodedData, contractAddress, { nonce: nonce.toNumber() }) } } else { - const { encodedData } = await getEncodeData({ - orderIds: [order?.id].filter(Boolean) as number[], - isCancelAll, - }).unwrap() - await sendTransaction(encodedData, order?.contractAddress ?? '', { orderIds: newOrders }) + const data = await getEncodeData({ isCancelAll, orders: order ? [order] : [] }) + const { contractAddress, encodedData } = data[0] || {} + await sendTransaction(encodedData, contractAddress ?? '', { orderIds: newOrders }) } setCancellingOrders(cancellingOrdersIds.concat(newOrders)) - return } diff --git a/src/components/swapv2/LimitOrder/Modals/CancelButtons.tsx b/src/components/swapv2/LimitOrder/Modals/CancelButtons.tsx index e7be179c01..7007679067 100644 --- a/src/components/swapv2/LimitOrder/Modals/CancelButtons.tsx +++ b/src/components/swapv2/LimitOrder/Modals/CancelButtons.tsx @@ -1,5 +1,5 @@ import { Trans } from '@lingui/macro' -import { ReactNode } from 'react' +import { ReactNode, useEffect, useState } from 'react' import { Check } from 'react-feather' import { Text } from 'rebass' import styled from 'styled-components' @@ -8,9 +8,14 @@ import { ReactComponent as GasLessIcon } from 'assets/svg/gas_less_icon.svg' import { ButtonLight, ButtonOutlined } from 'components/Button' import Column from 'components/Column' import { GasStation } from 'components/Icons' +import { useGetEncodeLimitOrder } from 'components/swapv2/LimitOrder/ListOrder/useRequestCancelOrder' import { CancelStatus } from 'components/swapv2/LimitOrder/Modals/CancelOrderModal' +import { LimitOrder } from 'components/swapv2/LimitOrder/type' +import { EMPTY_ARRAY } from 'constants/index' import useTheme from 'hooks/useTheme' import { ExternalLink } from 'theme' +import { formatDisplayNumber } from 'utils/numbers' +import useEstimateGasTxs from 'utils/useEstimateGasTxs' const ButtonWrapper = styled.div` display: flex; @@ -33,6 +38,7 @@ const CancelButtons = ({ totalOrder, disabledGasLessCancel = false, disabledHardCancel = false, + orders = EMPTY_ARRAY, }: { cancelStatus: CancelStatus onOkay: () => void @@ -45,17 +51,55 @@ const CancelButtons = ({ totalOrder?: ReactNode disabledGasLessCancel?: boolean disabledHardCancel?: boolean + orders?: LimitOrder[] }) => { const theme = useTheme() const isCountDown = cancelStatus === CancelStatus.COUNTDOWN const isTimeout = cancelStatus === CancelStatus.TIMEOUT const isCancelDone = cancelStatus === CancelStatus.CANCEL_DONE + const getEncodeData = useGetEncodeLimitOrder() + const estimateGas = useEstimateGasTxs() + const [gasFeeHardCancel, setGasFeeHardCancel] = useState('') + + useEffect(() => { + const controller = new AbortController() + const signal = controller.signal + const fetchEncode = async () => { + try { + if (!orders.length) throw new Error() + const resp = await getEncodeData({ orders, isCancelAll }) + if (signal.aborted) return + const data = await Promise.all(resp.map(estimateGas)) + if (signal.aborted) return + const gas = data.reduce((rs, item) => rs + (item.gasInUsd || 0), 0) + setGasFeeHardCancel(gas + '') + } catch (error) { + if (signal.aborted) return + setGasFeeHardCancel('') + } + } + + setTimeout(() => { + if (signal.aborted) return + fetchEncode() + }, 100) + + return () => controller.abort() + }, [getEncodeData, orders, estimateGas, isCancelAll]) + + const gasAmountDisplay = gasFeeHardCancel + ? `~${formatDisplayNumber(gasFeeHardCancel + '', { + style: 'currency', + significantDigits: 4, + })}` + : '' + return ( {isCancelDone ? ( -  Okay +  Close ) : isTimeout ? ( @@ -74,7 +118,7 @@ const CancelButtons = ({ {isCountDown ? : }   {isCountDown ? ( - Okay + Close ) : isCancelAll ? ( totalOrder ) : isEdit ? ( @@ -114,9 +158,9 @@ const CancelButtons = ({ {isEdit ? ( - Edit immediately by paying gas fees. + Edit immediately by paying {gasAmountDisplay} gas fees. ) : ( - Cancel immediately by paying gas fees. + Cancel immediately by paying {gasAmountDisplay} gas fees. )}{' '} Learn more ↗︎ diff --git a/src/components/swapv2/LimitOrder/Modals/CancelOrderModal.tsx b/src/components/swapv2/LimitOrder/Modals/CancelOrderModal.tsx index e1c306eb84..04fa05c004 100644 --- a/src/components/swapv2/LimitOrder/Modals/CancelOrderModal.tsx +++ b/src/components/swapv2/LimitOrder/Modals/CancelOrderModal.tsx @@ -159,6 +159,7 @@ function ContentCancel({ ? t`all` : `${ordersSoftCancel.length}/${orders.length}` + const formatOrders = useMemo(() => (isCancelAll ? orders : order ? [order] : []), [order, isCancelAll, orders]) return ( @@ -191,6 +192,7 @@ function ContentCancel({ flowState={flowState} /> Promise<{ gas: BigNumber | null; gasInUsd: number | null }> { + const { account, chainId } = useActiveWeb3React() + const { library } = useWeb3React() + + const addressParam = useMemo(() => [WETH[chainId].wrapped.address], [chainId]) + const tokensPrices = useTokenPrices(addressParam) + const usdPriceNative = tokensPrices[WETH[chainId].wrapped.address] ?? 0 + + return useCallback( + async ({ contractAddress, encodedData, value = BigNumber.from(0) }: EstimateParams) => { + const estimateGasOption = { + from: account, + to: contractAddress, + data: encodedData, + value, + } + let formatGas: number | null = null + let gas: BigNumber | null = null + try { + if (!account || !library) throw new Error() + const [estimateGas, gasPrice] = await Promise.all([ + library.getSigner().estimateGas(estimateGasOption), + library.getSigner().getGasPrice(), + ]) + gas = gasPrice && estimateGas ? estimateGas.mul(gasPrice) : null + formatGas = gas ? parseFloat(ethers.utils.formatEther(gas)) : null + } catch (error) {} + + return { + gas, + gasInUsd: formatGas && usdPriceNative ? formatGas * usdPriceNative : null, + } + }, + [account, library, usdPriceNative], + ) +} +export default useEstimateGasTxs