feat: dynamic FOT tax fetching (#7252)

* feat: disable exact_output FOT swaps

* fix: pr comments

* test: update snapshots

* feat: dynamically fetch tax rates

* remove tax constants file

* test: update useRoutingAPITrade expected args

* fix: useSwapTaxes nits

* lint: useSwapTaxes dependency array
This commit is contained in:
cartcrom 2023-09-01 14:56:26 -04:00 committed by GitHub
parent 45a138dec1
commit b6e388c68c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 287 additions and 104 deletions

@ -0,0 +1,133 @@
[
{
"inputs": [
{
"internalType": "address",
"name": "_factoryV2",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "PairLookupFailed",
"type": "error"
},
{
"inputs": [],
"name": "SameToken",
"type": "error"
},
{
"inputs": [
{
"internalType": "address[]",
"name": "tokens",
"type": "address[]"
},
{
"internalType": "address",
"name": "baseToken",
"type": "address"
},
{
"internalType": "uint256",
"name": "amountToBorrow",
"type": "uint256"
}
],
"name": "batchValidate",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "buyFeeBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "sellFeeBps",
"type": "uint256"
}
],
"internalType": "struct TokenFees[]",
"name": "fotResults",
"type": "tuple[]"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount0",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "uniswapV2Call",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "baseToken",
"type": "address"
},
{
"internalType": "uint256",
"name": "amountToBorrow",
"type": "uint256"
}
],
"name": "validate",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "buyFeeBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "sellFeeBps",
"type": "uint256"
}
],
"internalType": "struct TokenFees",
"name": "fotResult",
"type": "tuple"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
]

@ -1,75 +0,0 @@
import { ChainId, Currency, Percent } from '@uniswap/sdk-core'
import { ZERO_PERCENT } from './misc'
interface TokenTaxMetadata {
buyFee?: Percent
sellFee?: Percent
}
const CHAIN_TOKEN_TAX_MAP: { [chainId in number]?: { [address in string]?: TokenTaxMetadata } } = {
[ChainId.MAINNET]: {
// BULLET
'0x8ef32a03784c8fd63bbf027251b9620865bd54b6': {
buyFee: new Percent(5, 100), // 5%
sellFee: new Percent(5, 100), // 5%
},
// X
'0xabec00542d141bddf58649bfe860c6449807237c': {
buyFee: new Percent(1, 100), // 1%
sellFee: new Percent(1, 100), // 1%
},
// HarryPotterObamaKnuckles9Inu
'0x2577944fd4b556a99cc5aa0f072e4b944aa088df': {
buyFee: new Percent(1, 100), // 1%
sellFee: new Percent(11, 1000), // 1.1%
},
// QWN
'0xb354b5da5ea39dadb1cea8140bf242eb24b1821a': {
buyFee: new Percent(5, 100), // 5%
sellFee: new Percent(5, 100), // 5%
},
// HarryPotterObamaPacMan8Inu
'0x07e0edf8ce600fb51d44f51e3348d77d67f298ae': {
buyFee: new Percent(2, 100), // 2%
sellFee: new Percent(2, 100), // 2%
},
// KUKU
'0x27206f5a9afd0c51da95f20972885545d3b33647': {
buyFee: new Percent(2, 100), // 2%
sellFee: new Percent(21, 1000), // 2.1%
},
// AIMBOT
'0x0c48250eb1f29491f1efbeec0261eb556f0973c7': {
buyFee: new Percent(5, 100), // 5%
sellFee: new Percent(5, 100), // 5%
},
// PYUSD
'0xe0a8ed732658832fac18141aa5ad3542e2eb503b': {
buyFee: new Percent(1, 100), // 1%
sellFee: new Percent(13, 1000), // 1.3%
},
// ND4
'0x4f849c55180ddf8185c5cc495ed58c3aea9c9a28': {
buyFee: new Percent(1, 100), // 1%
sellFee: new Percent(1, 100), // 1%
},
// COCO
'0xcb50350ab555ed5d56265e096288536e8cac41eb': {
buyFee: new Percent(2, 100), // 2%
sellFee: new Percent(26, 1000), // 2.6%
},
},
}
export function getInputTax(currency: Currency): Percent {
if (currency.isNative) return ZERO_PERCENT
return CHAIN_TOKEN_TAX_MAP[currency.chainId]?.[currency.address.toLowerCase()]?.sellFee ?? ZERO_PERCENT
}
export function getOutputTax(currency: Currency): Percent {
if (currency.isNative) return ZERO_PERCENT
return CHAIN_TOKEN_TAX_MAP[currency.chainId]?.[currency.address.toLowerCase()]?.buyFee ?? ZERO_PERCENT
}

@ -46,8 +46,10 @@ describe('#useBestV3Trade ExactIn', () => {
USDCAmount, USDCAmount,
DAI, DAI,
RouterPreference.CLIENT, RouterPreference.CLIENT,
true, // skipFetch /* skipFetch = */ true,
undefined /* account = */ undefined,
/* inputTax = */ undefined,
/* outputTax = */ undefined
) )
expect(result.current).toEqual({ state: TradeState.NO_ROUTE_FOUND, trade: undefined }) expect(result.current).toEqual({ state: TradeState.NO_ROUTE_FOUND, trade: undefined })
}) })
@ -64,8 +66,10 @@ describe('#useDebouncedTrade ExactOut', () => {
DAIAmount, DAIAmount,
USDC_MAINNET, USDC_MAINNET,
RouterPreference.CLIENT, RouterPreference.CLIENT,
true, // skipFetch /* skipFetch = */ true,
undefined /* account = */ undefined,
/* inputTax = */ undefined,
/* outputTax = */ undefined
) )
expect(result.current).toEqual({ state: TradeState.NO_ROUTE_FOUND, trade: undefined }) expect(result.current).toEqual({ state: TradeState.NO_ROUTE_FOUND, trade: undefined })
}) })

@ -1,4 +1,4 @@
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { WRAPPED_NATIVE_CURRENCY } from 'constants/tokens' import { WRAPPED_NATIVE_CURRENCY } from 'constants/tokens'
import { DebounceSwapQuoteVariant, useDebounceSwapQuoteFlag } from 'featureFlags/flags/debounceSwapQuote' import { DebounceSwapQuoteVariant, useDebounceSwapQuoteFlag } from 'featureFlags/flags/debounceSwapQuote'
@ -22,7 +22,9 @@ export function useDebouncedTrade(
amountSpecified?: CurrencyAmount<Currency>, amountSpecified?: CurrencyAmount<Currency>,
otherCurrency?: Currency, otherCurrency?: Currency,
routerPreferenceOverride?: RouterPreference.X, routerPreferenceOverride?: RouterPreference.X,
account?: string account?: string,
inputTax?: Percent,
outputTax?: Percent
): { ): {
state: TradeState state: TradeState
trade?: InterfaceTrade trade?: InterfaceTrade
@ -34,7 +36,9 @@ export function useDebouncedTrade(
amountSpecified?: CurrencyAmount<Currency>, amountSpecified?: CurrencyAmount<Currency>,
otherCurrency?: Currency, otherCurrency?: Currency,
routerPreferenceOverride?: RouterPreference.API | RouterPreference.CLIENT, routerPreferenceOverride?: RouterPreference.API | RouterPreference.CLIENT,
account?: string account?: string,
inputTax?: Percent,
outputTax?: Percent
): { ): {
state: TradeState state: TradeState
trade?: ClassicTrade trade?: ClassicTrade
@ -54,7 +58,9 @@ export function useDebouncedTrade(
amountSpecified?: CurrencyAmount<Currency>, amountSpecified?: CurrencyAmount<Currency>,
otherCurrency?: Currency, otherCurrency?: Currency,
routerPreferenceOverride?: RouterPreference, routerPreferenceOverride?: RouterPreference,
account?: string account?: string,
inputTax?: Percent,
outputTax?: Percent
): { ): {
state: TradeState state: TradeState
trade?: InterfaceTrade trade?: InterfaceTrade
@ -91,6 +97,8 @@ export function useDebouncedTrade(
otherCurrency, otherCurrency,
routerPreferenceOverride ?? routerPreference, routerPreferenceOverride ?? routerPreference,
skipFetch, skipFetch,
account account,
inputTax,
outputTax
) )
} }

88
src/hooks/useSwapTaxes.ts Normal file

@ -0,0 +1,88 @@
import * as Sentry from '@sentry/react'
import { InterfaceEventName } from '@uniswap/analytics-events'
import { ChainId, Percent } from '@uniswap/sdk-core'
import { WETH_ADDRESS as getWethAddress } from '@uniswap/universal-router-sdk'
import { useWeb3React } from '@web3-react/core'
import FOT_DETECTOR_ABI from 'abis/fee-on-transfer-detector.json'
import { FeeOnTransferDetector } from 'abis/types'
import { sendAnalyticsEvent } from 'analytics'
import { ZERO_PERCENT } from 'constants/misc'
import { useEffect, useState } from 'react'
import { useContract } from './useContract'
const FEE_ON_TRANSFER_DETECTOR_ADDRESS = '0x19C97dc2a25845C7f9d1d519c8C2d4809c58b43f'
function useFeeOnTransferDetectorContract(): FeeOnTransferDetector | null {
const { account } = useWeb3React()
const contract = useContract<FeeOnTransferDetector>(FEE_ON_TRANSFER_DETECTOR_ADDRESS, FOT_DETECTOR_ABI)
useEffect(() => {
if (contract && account) {
sendAnalyticsEvent(InterfaceEventName.WALLET_PROVIDER_USED, {
source: 'useFeeOnTransferDetectorContract',
contract,
})
}
}, [account, contract])
return contract
}
// TODO(WEB-2787): add tax-fetching for other chains
const WETH_ADDRESS = getWethAddress(ChainId.MAINNET)
const AMOUNT_TO_BORROW = 10000 // smallest amount that has full precision over bps
const FEE_CACHE: { [address in string]?: { sellTax?: Percent; buyTax?: Percent } } = {}
async function getSwapTaxes(
fotDetector: FeeOnTransferDetector,
inputTokenAddress: string | undefined,
outputTokenAddress: string | undefined
) {
const addresses = []
if (inputTokenAddress && FEE_CACHE[inputTokenAddress] === undefined) {
addresses.push(inputTokenAddress)
}
if (outputTokenAddress && FEE_CACHE[outputTokenAddress] === undefined) {
addresses.push(outputTokenAddress)
}
try {
if (addresses.length) {
const data = await fotDetector.callStatic.batchValidate(addresses, WETH_ADDRESS, AMOUNT_TO_BORROW)
addresses.forEach((address, index) => {
const { sellFeeBps, buyFeeBps } = data[index]
const sellTax = new Percent(sellFeeBps.toNumber(), 10000)
const buyTax = new Percent(buyFeeBps.toNumber(), 10000)
FEE_CACHE[address] = { sellTax, buyTax }
})
}
} catch (e) {
Sentry.withScope(function (scope) {
scope.setTag('method', 'getSwapTaxes')
scope.setLevel('warning')
Sentry.captureException(e)
})
}
const inputTax = (inputTokenAddress ? FEE_CACHE[inputTokenAddress]?.sellTax : ZERO_PERCENT) ?? ZERO_PERCENT
const outputTax = (outputTokenAddress ? FEE_CACHE[outputTokenAddress]?.buyTax : ZERO_PERCENT) ?? ZERO_PERCENT
return { inputTax, outputTax }
}
export function useSwapTaxes(inputTokenAddress: string | undefined, outputTokenAddress: string | undefined) {
const fotDetector = useFeeOnTransferDetectorContract()
const [{ inputTax, outputTax }, setTaxes] = useState({ inputTax: ZERO_PERCENT, outputTax: ZERO_PERCENT })
const { chainId } = useWeb3React()
useEffect(() => {
if (!fotDetector || chainId !== ChainId.MAINNET) return
getSwapTaxes(fotDetector, inputTokenAddress, outputTokenAddress).then(setTaxes)
}, [fotDetector, inputTokenAddress, outputTokenAddress, chainId])
return { inputTax, outputTax }
}

@ -1,5 +1,5 @@
import { SkipToken, skipToken } from '@reduxjs/toolkit/query/react' import { SkipToken, skipToken } from '@reduxjs/toolkit/query/react'
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
import { useFotAdjustmentsEnabled } from 'featureFlags/flags/fotAdjustments' import { useFotAdjustmentsEnabled } from 'featureFlags/flags/fotAdjustments'
import { useUniswapXEthOutputEnabled } from 'featureFlags/flags/uniswapXEthOutput' import { useUniswapXEthOutputEnabled } from 'featureFlags/flags/uniswapXEthOutput'
import { useUniswapXExactOutputEnabled } from 'featureFlags/flags/uniswapXExactOutput' import { useUniswapXExactOutputEnabled } from 'featureFlags/flags/uniswapXExactOutput'
@ -21,6 +21,8 @@ export function useRoutingAPIArguments({
amount, amount,
tradeType, tradeType,
routerPreference, routerPreference,
inputTax,
outputTax,
}: { }: {
account?: string account?: string
tokenIn?: Currency tokenIn?: Currency
@ -28,6 +30,8 @@ export function useRoutingAPIArguments({
amount?: CurrencyAmount<Currency> amount?: CurrencyAmount<Currency>
tradeType: TradeType tradeType: TradeType
routerPreference: RouterPreference | typeof INTERNAL_ROUTER_PREFERENCE_PRICE routerPreference: RouterPreference | typeof INTERNAL_ROUTER_PREFERENCE_PRICE
inputTax: Percent
outputTax: Percent
}): GetQuoteArgs | SkipToken { }): GetQuoteArgs | SkipToken {
const uniswapXForceSyntheticQuotes = useUniswapXSyntheticQuoteEnabled() const uniswapXForceSyntheticQuotes = useUniswapXSyntheticQuoteEnabled()
const userDisabledUniswapX = useUserDisabledUniswapX() const userDisabledUniswapX = useUserDisabledUniswapX()
@ -58,6 +62,8 @@ export function useRoutingAPIArguments({
uniswapXEthOutputEnabled, uniswapXEthOutputEnabled,
uniswapXExactOutputEnabled, uniswapXExactOutputEnabled,
fotAdjustmentsEnabled, fotAdjustmentsEnabled,
inputTax,
outputTax,
}, },
[ [
account, account,
@ -71,6 +77,8 @@ export function useRoutingAPIArguments({
userDisabledUniswapX, userDisabledUniswapX,
uniswapXEthOutputEnabled, uniswapXEthOutputEnabled,
fotAdjustmentsEnabled, fotAdjustmentsEnabled,
inputTax,
outputTax,
] ]
) )
} }

@ -31,7 +31,6 @@ import { SwitchLocaleLink } from 'components/SwitchLocaleLink'
import TokenSafetyModal from 'components/TokenSafety/TokenSafetyModal' import TokenSafetyModal from 'components/TokenSafety/TokenSafetyModal'
import { getChainInfo } from 'constants/chainInfo' import { getChainInfo } from 'constants/chainInfo'
import { asSupportedChain, isSupportedChain } from 'constants/chains' import { asSupportedChain, isSupportedChain } from 'constants/chains'
import { getInputTax, getOutputTax } from 'constants/tax'
import { getSwapCurrencyId, TOKEN_SHORTHANDS } from 'constants/tokens' import { getSwapCurrencyId, TOKEN_SHORTHANDS } from 'constants/tokens'
import { useCurrency, useDefaultActiveTokens } from 'hooks/Tokens' import { useCurrency, useDefaultActiveTokens } from 'hooks/Tokens'
import { useIsSwapUnsupported } from 'hooks/useIsSwapUnsupported' import { useIsSwapUnsupported } from 'hooks/useIsSwapUnsupported'
@ -279,14 +278,13 @@ export function Swap({
parsedAmount, parsedAmount,
currencies, currencies,
inputError: swapInputError, inputError: swapInputError,
inputTax,
outputTax,
} = swapInfo } = swapInfo
const [inputTokenHasTax, outputTokenHasTax] = useMemo( const [inputTokenHasTax, outputTokenHasTax] = useMemo(
() => [ () => [!inputTax.equalTo(0), !outputTax.equalTo(0)],
!!currencies[Field.INPUT] && !getInputTax(currencies[Field.INPUT]).equalTo(0), [inputTax, outputTax]
!!currencies[Field.OUTPUT] && !getOutputTax(currencies[Field.OUTPUT]).equalTo(0),
],
[currencies]
) )
useEffect(() => { useEffect(() => {

@ -47,6 +47,8 @@ export interface GetQuoteArgs {
uniswapXExactOutputEnabled: boolean uniswapXExactOutputEnabled: boolean
userDisabledUniswapX: boolean userDisabledUniswapX: boolean
fotAdjustmentsEnabled: boolean fotAdjustmentsEnabled: boolean
inputTax: Percent
outputTax: Percent
} }
// from https://github.com/Uniswap/routing-api/blob/main/lib/handlers/schema.ts // from https://github.com/Uniswap/routing-api/blob/main/lib/handlers/schema.ts

@ -1,8 +1,9 @@
import { skipToken } from '@reduxjs/toolkit/query/react' import { skipToken } from '@reduxjs/toolkit/query/react'
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
import { IMetric, MetricLoggerUnit, setGlobalMetric } from '@uniswap/smart-order-router' import { IMetric, MetricLoggerUnit, setGlobalMetric } from '@uniswap/smart-order-router'
import { sendTiming } from 'components/analytics' import { sendTiming } from 'components/analytics'
import { AVERAGE_L1_BLOCK_TIME } from 'constants/chainInfo' import { AVERAGE_L1_BLOCK_TIME } from 'constants/chainInfo'
import { ZERO_PERCENT } from 'constants/misc'
import { useRoutingAPIArguments } from 'lib/hooks/routing/useRoutingAPIArguments' import { useRoutingAPIArguments } from 'lib/hooks/routing/useRoutingAPIArguments'
import ms from 'ms' import ms from 'ms'
import { useMemo } from 'react' import { useMemo } from 'react'
@ -27,7 +28,9 @@ export function useRoutingAPITrade<TTradeType extends TradeType>(
otherCurrency: Currency | undefined, otherCurrency: Currency | undefined,
routerPreference: typeof INTERNAL_ROUTER_PREFERENCE_PRICE, routerPreference: typeof INTERNAL_ROUTER_PREFERENCE_PRICE,
skipFetch?: boolean, skipFetch?: boolean,
account?: string account?: string,
inputTax?: Percent,
outputTax?: Percent
): { ): {
state: TradeState state: TradeState
trade?: ClassicTrade trade?: ClassicTrade
@ -40,7 +43,9 @@ export function useRoutingAPITrade<TTradeType extends TradeType>(
otherCurrency: Currency | undefined, otherCurrency: Currency | undefined,
routerPreference: RouterPreference, routerPreference: RouterPreference,
skipFetch?: boolean, skipFetch?: boolean,
account?: string account?: string,
inputTax?: Percent,
outputTax?: Percent
): { ): {
state: TradeState state: TradeState
trade?: InterfaceTrade trade?: InterfaceTrade
@ -59,7 +64,9 @@ export function useRoutingAPITrade<TTradeType extends TradeType>(
otherCurrency: Currency | undefined, otherCurrency: Currency | undefined,
routerPreference: RouterPreference | typeof INTERNAL_ROUTER_PREFERENCE_PRICE, routerPreference: RouterPreference | typeof INTERNAL_ROUTER_PREFERENCE_PRICE,
skipFetch = false, skipFetch = false,
account?: string account?: string,
inputTax = ZERO_PERCENT,
outputTax = ZERO_PERCENT
): { ): {
state: TradeState state: TradeState
trade?: InterfaceTrade trade?: InterfaceTrade
@ -81,6 +88,8 @@ export function useRoutingAPITrade<TTradeType extends TradeType>(
amount: amountSpecified, amount: amountSpecified,
tradeType, tradeType,
routerPreference, routerPreference,
inputTax,
outputTax,
}) })
const { isError, data: tradeResult, error, currentData } = useGetQuoteQueryState(queryArgs) const { isError, data: tradeResult, error, currentData } = useGetQuoteQueryState(queryArgs)

@ -6,9 +6,7 @@ import { DutchOrderInfo, DutchOrderInfoJSON } from '@uniswap/uniswapx-sdk'
import { Pair, Route as V2Route } from '@uniswap/v2-sdk' import { Pair, Route as V2Route } from '@uniswap/v2-sdk'
import { FeeAmount, Pool, Route as V3Route } from '@uniswap/v3-sdk' import { FeeAmount, Pool, Route as V3Route } from '@uniswap/v3-sdk'
import { asSupportedChain } from 'constants/chains' import { asSupportedChain } from 'constants/chains'
import { ZERO_PERCENT } from 'constants/misc'
import { RPC_PROVIDERS } from 'constants/providers' import { RPC_PROVIDERS } from 'constants/providers'
import { getInputTax, getOutputTax } from 'constants/tax'
import { isAvalanche, isBsc, isMatic, nativeOnChain } from 'constants/tokens' import { isAvalanche, isBsc, isMatic, nativeOnChain } from 'constants/tokens'
import { toSlippagePercent } from 'utils/slippage' import { toSlippagePercent } from 'utils/slippage'
@ -215,9 +213,6 @@ export async function transformRoutesToTrade(
const approveInfo = await getApproveInfo(account, currencyIn, amount, usdCostPerGas) const approveInfo = await getApproveInfo(account, currencyIn, amount, usdCostPerGas)
const inputTax = args.fotAdjustmentsEnabled ? getInputTax(currencyIn) : ZERO_PERCENT
const outputTax = args.fotAdjustmentsEnabled ? getOutputTax(currencyOut) : ZERO_PERCENT
const classicTrade = new ClassicTrade({ const classicTrade = new ClassicTrade({
v2Routes: v2Routes:
routes routes
@ -252,8 +247,8 @@ export async function transformRoutesToTrade(
isUniswapXBetter, isUniswapXBetter,
requestId: data.quote.requestId, requestId: data.quote.requestId,
quoteMethod, quoteMethod,
inputTax, inputTax: args.inputTax,
outputTax, outputTax: args.outputTax,
}) })
// During the opt-in period, only return UniswapX quotes if the user has turned on the setting, // During the opt-in period, only return UniswapX quotes if the user has turned on the setting,

@ -3,6 +3,7 @@ import { ChainId, Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance' import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance'
import { useDebouncedTrade } from 'hooks/useDebouncedTrade' import { useDebouncedTrade } from 'hooks/useDebouncedTrade'
import { useSwapTaxes } from 'hooks/useSwapTaxes'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { ParsedQs } from 'qs' import { ParsedQs } from 'qs'
import { ReactNode, useCallback, useEffect, useMemo } from 'react' import { ReactNode, useCallback, useEffect, useMemo } from 'react'
@ -77,6 +78,8 @@ const BAD_RECIPIENT_ADDRESSES: { [address: string]: true } = {
export type SwapInfo = { export type SwapInfo = {
currencies: { [field in Field]?: Currency | null } currencies: { [field in Field]?: Currency | null }
currencyBalances: { [field in Field]?: CurrencyAmount<Currency> } currencyBalances: { [field in Field]?: CurrencyAmount<Currency> }
inputTax: Percent
outputTax: Percent
parsedAmount?: CurrencyAmount<Currency> parsedAmount?: CurrencyAmount<Currency>
inputError?: ReactNode inputError?: ReactNode
trade: { trade: {
@ -104,6 +107,12 @@ export function useDerivedSwapInfo(state: SwapState, chainId: ChainId | undefine
const inputCurrency = useCurrency(inputCurrencyId, chainId) const inputCurrency = useCurrency(inputCurrencyId, chainId)
const outputCurrency = useCurrency(outputCurrencyId, chainId) const outputCurrency = useCurrency(outputCurrencyId, chainId)
const { inputTax, outputTax } = useSwapTaxes(
inputCurrency?.isToken ? inputCurrency.address : undefined,
outputCurrency?.isToken ? outputCurrency.address : undefined
)
const recipientLookup = useENS(recipient ?? undefined) const recipientLookup = useENS(recipient ?? undefined)
const to: string | null = (recipient === null ? account : recipientLookup.address) ?? null const to: string | null = (recipient === null ? account : recipientLookup.address) ?? null
@ -123,7 +132,9 @@ export function useDerivedSwapInfo(state: SwapState, chainId: ChainId | undefine
parsedAmount, parsedAmount,
(isExactIn ? outputCurrency : inputCurrency) ?? undefined, (isExactIn ? outputCurrency : inputCurrency) ?? undefined,
undefined, undefined,
account account,
inputTax,
outputTax
) )
const currencyBalances = useMemo( const currencyBalances = useMemo(
@ -198,8 +209,10 @@ export function useDerivedSwapInfo(state: SwapState, chainId: ChainId | undefine
trade, trade,
autoSlippage, autoSlippage,
allowedSlippage, allowedSlippage,
inputTax,
outputTax,
}), }),
[allowedSlippage, autoSlippage, currencies, currencyBalances, inputError, parsedAmount, trade] [allowedSlippage, autoSlippage, currencies, currencyBalances, inputError, inputTax, outputTax, parsedAmount, trade]
) )
} }