Skip to content

Commit

Permalink
add gas fee info
Browse files Browse the repository at this point in the history
  • Loading branch information
nguyenhoaidanh committed Sep 27, 2023
1 parent a3d2d13 commit ccfb4bf
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 21 deletions.
6 changes: 4 additions & 2 deletions src/components/swapv2/LimitOrder/EditOrderModal.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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 && (
Expand All @@ -123,7 +125,7 @@ export default function EditOrderModal({
)}
<CancelButtons
isEdit
order={order}
orders={orders}
supportCancelGasless={supportCancelGasless}
loading={flowState.attemptingTxn}
cancelStatus={cancelStatus}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { t } from '@lingui/macro'
import { BigNumber } from 'ethers'
import { useState } from 'react'
import { useCallback, useState } from 'react'
import {
useCancelOrdersMutation,
useCreateCancelOrderSignatureMutation,
Expand All @@ -21,6 +21,36 @@ import { sendEVMTransaction } from 'utils/sendTransaction'
import { formatAmountOrder, formatSignature, getErrorMessage, getPayloadTracking } from '../helpers'
import { CancelOrderType, LimitOrder } from '../type'

export const useGetEncodeLimitOrder = () => {
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 }) // todo consider Promise.all 2 times
}
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,
Expand All @@ -35,11 +65,12 @@ const useRequestCancelOrder = ({
const { library } = useWeb3React()
const [flowState, setFlowState] = useState<TransactionFlowState>(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')

Expand Down Expand Up @@ -95,23 +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[],
}).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
}

Expand Down
50 changes: 47 additions & 3 deletions src/components/swapv2/LimitOrder/Modals/CancelButtons.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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/sendTransaction'

const ButtonWrapper = styled.div`
display: flex;
Expand All @@ -33,6 +38,7 @@ const CancelButtons = ({
totalOrder,
disabledGasLessCancel = false,
disabledHardCancel = false,
orders = EMPTY_ARRAY,
}: {
cancelStatus: CancelStatus
onOkay: () => void
Expand All @@ -45,12 +51,50 @@ 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 (
<ButtonWrapper style={{ justifyContent: isTimeout ? 'flex-end' : undefined }}>
{isCancelDone ? (
Expand Down Expand Up @@ -114,9 +158,9 @@ const CancelButtons = ({
</ButtonOutlined>
<Text color={theme.subText} fontSize={'10px'} lineHeight={'14px'}>
{isEdit ? (
<Trans>Edit immediately by paying gas fees. </Trans>
<Trans>Edit immediately by paying {gasAmountDisplay} gas fees. </Trans>
) : (
<Trans>Cancel immediately by paying gas fees. </Trans>
<Trans>Cancel immediately by paying {gasAmountDisplay} gas fees. </Trans>
)}{' '}
<ExternalLink href="/todo">
<Trans>Learn more ↗︎</Trans>
Expand Down
2 changes: 2 additions & 0 deletions src/components/swapv2/LimitOrder/Modals/CancelOrderModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ function ContentCancel({
? t`all`
: `${ordersSoftCancel.length}/${orders.length}`

const formatOrders = useMemo(() => (isCancelAll ? orders : order ? [order] : []), [order, isCancelAll, orders])
return (
<Modal maxWidth={isCancelAll && !isCancelDone ? 600 : 480} isOpen={isOpen} onDismiss={onDismiss}>
<Container>
Expand Down Expand Up @@ -191,6 +192,7 @@ function ContentCancel({
flowState={flowState}
/>
<CancelButtons
orders={formatOrders}
supportCancelGasless={supportGasLessCancel}
loading={flowState.attemptingTxn}
cancelStatus={cancelStatus}
Expand Down
45 changes: 44 additions & 1 deletion src/utils/sendTransaction.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,60 @@
import { BigNumber } from '@ethersproject/bignumber'
import { TransactionResponse } from '@ethersproject/providers'
import { ChainId } from '@kyberswap/ks-sdk-core'
import { ChainId, WETH } from '@kyberswap/ks-sdk-core'
import { t } from '@lingui/macro'
import { SignerWalletAdapter } from '@solana/wallet-adapter-base'
import { Connection, Transaction, VersionedTransaction } from '@solana/web3.js'
import { ethers } from 'ethers'
import { useCallback, useMemo } from 'react'

import { useActiveWeb3React, useWeb3React } from 'hooks'
import { SolanaEncode } from 'state/swap/types'
import { useTokenPrices } from 'state/tokenPrices/hooks'
import { TRANSACTION_TYPE, TransactionHistory } from 'state/transactions/type'
import { calculateGasMargin } from 'utils'

import { TransactionError } from './sentry'

type EstimateParams = { contractAddress: string; encodedData: string; value?: BigNumber }
export function useEstimateGasTxs(): (
v: EstimateParams,
) => 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 async function sendEVMTransaction(
account: string,
library: ethers.providers.Web3Provider | undefined,
Expand Down

0 comments on commit ccfb4bf

Please sign in to comment.