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:
parent
45a138dec1
commit
b6e388c68c
133
src/abis/fee-on-transfer-detector.json
Normal file
133
src/abis/fee-on-transfer-detector.json
Normal file
@ -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
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]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user