feat: read token taxes from backend response (#7532)

* read token taxes from backend

* revert env changes

* upgrade router-sdk for updated price impact logic

* add tax information to trade currencies instead of directly on trade object

* consolidate getTradeCurrencies with getSwapCurrenciesWithTaxInfo

* delete feature flag for token taxes!

* run yarn dedupe again

* fix unit tests

* update logic for disabling inputs

* update snapshot again

* fix return value for uniswapx

* remove unused constants and update comment

* pr review

* re-add useSwapTaxes for token descriptor page

* add in client-side tax fetching on currency level

* revert removing newline

* typecheck....

* typecheck...

* remove inputTax, outputTax from routing-api arguments because they are now unused

* dont pass in tax info to preview trade
This commit is contained in:
Tina 2023-11-15 09:57:43 -05:00 committed by GitHub
parent 76157c057e
commit ff6d1cc510
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 259 additions and 297 deletions

@ -202,7 +202,7 @@
"@uniswap/merkle-distributor": "^1.0.1", "@uniswap/merkle-distributor": "^1.0.1",
"@uniswap/permit2-sdk": "^1.2.0", "@uniswap/permit2-sdk": "^1.2.0",
"@uniswap/redux-multicall": "^1.1.8", "@uniswap/redux-multicall": "^1.1.8",
"@uniswap/router-sdk": "^1.6.0", "@uniswap/router-sdk": "^1.7.1",
"@uniswap/sdk-core": "4.0.7", "@uniswap/sdk-core": "4.0.7",
"@uniswap/smart-order-router": "^3.15.0", "@uniswap/smart-order-router": "^3.15.0",
"@uniswap/token-lists": "^1.0.0-beta.33", "@uniswap/token-lists": "^1.0.0-beta.33",

@ -90,7 +90,7 @@ const DescriptionText = styled(ThemedText.LabelMicro)`
function useOrderAmounts( function useOrderAmounts(
orderDetails?: UniswapXOrderDetails orderDetails?: UniswapXOrderDetails
): Pick<InterfaceTrade, 'inputAmount' | 'postTaxOutputAmount'> | undefined { ): Pick<InterfaceTrade, 'inputAmount' | 'outputAmount'> | undefined {
const inputCurrency = useCurrency(orderDetails?.swapInfo?.inputCurrencyId, orderDetails?.chainId) const inputCurrency = useCurrency(orderDetails?.swapInfo?.inputCurrencyId, orderDetails?.chainId)
const outputCurrency = useCurrency(orderDetails?.swapInfo?.outputCurrencyId, orderDetails?.chainId) const outputCurrency = useCurrency(orderDetails?.swapInfo?.outputCurrencyId, orderDetails?.chainId)
@ -106,7 +106,7 @@ function useOrderAmounts(
if (swapInfo.tradeType === TradeType.EXACT_INPUT) { if (swapInfo.tradeType === TradeType.EXACT_INPUT) {
return { return {
inputAmount: CurrencyAmount.fromRawAmount(inputCurrency, swapInfo.inputCurrencyAmountRaw), inputAmount: CurrencyAmount.fromRawAmount(inputCurrency, swapInfo.inputCurrencyAmountRaw),
postTaxOutputAmount: CurrencyAmount.fromRawAmount( outputAmount: CurrencyAmount.fromRawAmount(
outputCurrency, outputCurrency,
swapInfo.settledOutputCurrencyAmountRaw ?? swapInfo.expectedOutputCurrencyAmountRaw swapInfo.settledOutputCurrencyAmountRaw ?? swapInfo.expectedOutputCurrencyAmountRaw
), ),
@ -114,7 +114,7 @@ function useOrderAmounts(
} else { } else {
return { return {
inputAmount: CurrencyAmount.fromRawAmount(inputCurrency, swapInfo.expectedInputCurrencyAmountRaw), inputAmount: CurrencyAmount.fromRawAmount(inputCurrency, swapInfo.expectedInputCurrencyAmountRaw),
postTaxOutputAmount: CurrencyAmount.fromRawAmount(outputCurrency, swapInfo.outputCurrencyAmountRaw), outputAmount: CurrencyAmount.fromRawAmount(outputCurrency, swapInfo.outputCurrencyAmountRaw),
} }
} }
} }

@ -6,7 +6,6 @@ import { useQuickRouteChains } from 'featureFlags/dynamicConfig/quickRouteChains
import { useAndroidGALaunchFlag } from 'featureFlags/flags/androidGALaunch' import { useAndroidGALaunchFlag } from 'featureFlags/flags/androidGALaunch'
import { useCurrencyConversionFlag } from 'featureFlags/flags/currencyConversion' import { useCurrencyConversionFlag } from 'featureFlags/flags/currencyConversion'
import { useFallbackProviderEnabledFlag } from 'featureFlags/flags/fallbackProvider' import { useFallbackProviderEnabledFlag } from 'featureFlags/flags/fallbackProvider'
import { useFotAdjustmentsFlag } from 'featureFlags/flags/fotAdjustments'
import { useInfoExploreFlag } from 'featureFlags/flags/infoExplore' import { useInfoExploreFlag } from 'featureFlags/flags/infoExplore'
import { useInfoLiveViewsFlag } from 'featureFlags/flags/infoLiveViews' import { useInfoLiveViewsFlag } from 'featureFlags/flags/infoLiveViews'
import { useInfoPoolPageFlag } from 'featureFlags/flags/infoPoolPage' import { useInfoPoolPageFlag } from 'featureFlags/flags/infoPoolPage'
@ -293,12 +292,6 @@ export default function FeatureFlagModal() {
featureFlag={FeatureFlag.multichainUX} featureFlag={FeatureFlag.multichainUX}
label="Updated Multichain UX" label="Updated Multichain UX"
/> />
<FeatureFlagOption
variant={BaseVariant}
value={useFotAdjustmentsFlag()}
featureFlag={FeatureFlag.fotAdjustedmentsEnabled}
label="Enable fee-on-transfer UI and slippage adjustments"
/>
<FeatureFlagOption <FeatureFlagOption
variant={BaseVariant} variant={BaseVariant}
value={useProgressIndicatorV2Flag()} value={useProgressIndicatorV2Flag()}

@ -6,7 +6,7 @@ import { useTheme } from 'styled-components'
import { ThemedText } from 'theme/components' import { ThemedText } from 'theme/components'
import { useFormatter } from 'utils/formatNumbers' import { useFormatter } from 'utils/formatNumbers'
export function TradeSummary({ trade }: { trade: Pick<InterfaceTrade, 'inputAmount' | 'postTaxOutputAmount'> }) { export function TradeSummary({ trade }: { trade: Pick<InterfaceTrade, 'inputAmount' | 'outputAmount'> }) {
const theme = useTheme() const theme = useTheme()
const { formatReviewSwapCurrencyAmount } = useFormatter() const { formatReviewSwapCurrencyAmount } = useFormatter()
@ -17,9 +17,9 @@ export function TradeSummary({ trade }: { trade: Pick<InterfaceTrade, 'inputAmou
{formatReviewSwapCurrencyAmount(trade.inputAmount)} {trade.inputAmount.currency.symbol} {formatReviewSwapCurrencyAmount(trade.inputAmount)} {trade.inputAmount.currency.symbol}
</ThemedText.LabelSmall> </ThemedText.LabelSmall>
<ArrowRight color={theme.neutral1} size="12px" /> <ArrowRight color={theme.neutral1} size="12px" />
<CurrencyLogo currency={trade.postTaxOutputAmount.currency} size="16px" /> <CurrencyLogo currency={trade.outputAmount.currency} size="16px" />
<ThemedText.LabelSmall color="neutral1"> <ThemedText.LabelSmall color="neutral1">
{formatReviewSwapCurrencyAmount(trade.postTaxOutputAmount)} {trade.postTaxOutputAmount.currency.symbol} {formatReviewSwapCurrencyAmount(trade.outputAmount)} {trade.outputAmount.currency.symbol}
</ThemedText.LabelSmall> </ThemedText.LabelSmall>
</Row> </Row>
) )

@ -24,7 +24,7 @@ export default function SwapModalHeader({
allowedSlippage: Percent allowedSlippage: Percent
}) { }) {
const fiatValueInput = useUSDPrice(trade.inputAmount) const fiatValueInput = useUSDPrice(trade.inputAmount)
const fiatValueOutput = useUSDPrice(trade.postTaxOutputAmount) const fiatValueOutput = useUSDPrice(trade.outputAmount)
return ( return (
<HeaderContainer gap="sm"> <HeaderContainer gap="sm">
@ -40,7 +40,7 @@ export default function SwapModalHeader({
<SwapModalHeaderAmount <SwapModalHeaderAmount
field={Field.OUTPUT} field={Field.OUTPUT}
label={<Trans>You receive</Trans>} label={<Trans>You receive</Trans>}
amount={trade.postTaxOutputAmount} amount={trade.outputAmount}
currency={trade.outputAmount.currency} currency={trade.outputAmount.currency}
usdAmount={fiatValueOutput.data} usdAmount={fiatValueOutput.data}
isLoading={isPreviewTrade(trade) && trade.tradeType === TradeType.EXACT_INPUT} isLoading={isPreviewTrade(trade) && trade.tradeType === TradeType.EXACT_INPUT}

@ -6882,7 +6882,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = `
<span <span
class="" class=""
> >
~-105566.373% ~-108834.406%
</span> </span>
</div> </div>
</div> </div>
@ -7165,14 +7165,14 @@ exports[`SwapLineItem.tsx fee on buy 1`] = `
<div <div
class="c3 css-obwv3p" class="c3 css-obwv3p"
> >
0.000000000000000952 DEF 0.00000000000000098 DEF
</div> </div>
</div> </div>
<div <div
class="c13" class="c13"
/> />
<div> <div>
If the price moves so that you will receive less than 0.000000000000000952 DEF, your transaction will be reverted. This is the minimum amount you are guaranteed to receive. If the price moves so that you will receive less than 0.00000000000000098 DEF, your transaction will be reverted. This is the minimum amount you are guaranteed to receive.
<a <a
class="c14" class="c14"
href="https://support.uniswap.org/hc/en-us/articles/8643879653261-What-is-Price-Slippage-" href="https://support.uniswap.org/hc/en-us/articles/8643879653261-What-is-Price-Slippage-"
@ -7549,7 +7549,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = `
<div <div
class="c3 c6 css-142zc9n" class="c3 c6 css-142zc9n"
> >
0.000000000000000952 DEF 0.00000000000000098 DEF
</div> </div>
</div> </div>
</div> </div>
@ -8761,7 +8761,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = `
<span <span
class="" class=""
> >
~-105566.373% ~-108834.406%
</span> </span>
</div> </div>
</div> </div>
@ -9044,14 +9044,14 @@ exports[`SwapLineItem.tsx fee on sell 1`] = `
<div <div
class="c3 css-obwv3p" class="c3 css-obwv3p"
> >
0.000000000000000952 DEF 0.00000000000000098 DEF
</div> </div>
</div> </div>
<div <div
class="c13" class="c13"
/> />
<div> <div>
If the price moves so that you will receive less than 0.000000000000000952 DEF, your transaction will be reverted. This is the minimum amount you are guaranteed to receive. If the price moves so that you will receive less than 0.00000000000000098 DEF, your transaction will be reverted. This is the minimum amount you are guaranteed to receive.
<a <a
class="c14" class="c14"
href="https://support.uniswap.org/hc/en-us/articles/8643879653261-What-is-Price-Slippage-" href="https://support.uniswap.org/hc/en-us/articles/8643879653261-What-is-Price-Slippage-"
@ -9428,7 +9428,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = `
<div <div
class="c3 c6 css-142zc9n" class="c3 c6 css-142zc9n"
> >
0.000000000000000952 DEF 0.00000000000000098 DEF
</div> </div>
</div> </div>
</div> </div>

@ -1,9 +0,0 @@
import { BaseVariant, FeatureFlag, useBaseFlag } from '../index'
export function useFotAdjustmentsFlag(): BaseVariant {
return useBaseFlag(FeatureFlag.fotAdjustedmentsEnabled)
}
export function useFotAdjustmentsEnabled(): boolean {
return useFotAdjustmentsFlag() === BaseVariant.Enabled
}

@ -14,7 +14,6 @@ export enum FeatureFlag {
uniswapXExactOutputEnabled = 'uniswapx_exact_output_enabled', uniswapXExactOutputEnabled = 'uniswapx_exact_output_enabled',
multichainUX = 'multichain_ux', multichainUX = 'multichain_ux',
currencyConversion = 'currency_conversion', currencyConversion = 'currency_conversion',
fotAdjustedmentsEnabled = 'fot_dynamic_adjustments_enabled',
infoExplore = 'info_explore', infoExplore = 'info_explore',
infoTDP = 'info_tdp', infoTDP = 'info_tdp',
infoPoolPage = 'info_pool_page', infoPoolPage = 'info_pool_page',

@ -84,13 +84,13 @@ export function useSwapCallback(
? { ? {
tradeType: TradeType.EXACT_INPUT, tradeType: TradeType.EXACT_INPUT,
inputCurrencyAmountRaw: trade.inputAmount.quotient.toString(), inputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
expectedOutputCurrencyAmountRaw: trade.postTaxOutputAmount.quotient.toString(), expectedOutputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
minimumOutputCurrencyAmountRaw: trade.minimumAmountOut(allowedSlippage).quotient.toString(), minimumOutputCurrencyAmountRaw: trade.minimumAmountOut(allowedSlippage).quotient.toString(),
} }
: { : {
tradeType: TradeType.EXACT_OUTPUT, tradeType: TradeType.EXACT_OUTPUT,
maximumInputCurrencyAmountRaw: trade.maximumAmountIn(allowedSlippage).quotient.toString(), maximumInputCurrencyAmountRaw: trade.maximumAmountIn(allowedSlippage).quotient.toString(),
outputCurrencyAmountRaw: trade.postTaxOutputAmount.quotient.toString(), outputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
expectedInputCurrencyAmountRaw: trade.inputAmount.quotient.toString(), expectedInputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
}), }),
} }

@ -73,12 +73,8 @@ export function useUniversalRouterSwapCallback(
setTraceData('slippageTolerance', options.slippageTolerance.toFixed(2)) setTraceData('slippageTolerance', options.slippageTolerance.toFixed(2))
// universal-router-sdk reconstructs V2Trade objects, so rather than updating the trade amounts to account for tax, we adjust the slippage tolerance as a workaround
// TODO(WEB-2725): update universal-router-sdk to not reconstruct trades
const taxAdjustedSlippageTolerance = options.slippageTolerance.add(trade.totalTaxRate)
const { calldata: data, value } = SwapRouter.swapERC20CallParameters(trade, { const { calldata: data, value } = SwapRouter.swapERC20CallParameters(trade, {
slippageTolerance: taxAdjustedSlippageTolerance, slippageTolerance: options.slippageTolerance,
deadlineOrPreviousBlockhash: options.deadline?.toString(), deadlineOrPreviousBlockhash: options.deadline?.toString(),
inputTokenPermit: options.permit, inputTokenPermit: options.permit,
fee: options.feeOptions, fee: options.feeOptions,

@ -1,5 +1,5 @@
import { SkipToken, skipToken } from '@reduxjs/toolkit/query/react' import { SkipToken, skipToken } from '@reduxjs/toolkit/query/react'
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { useUniswapXDefaultEnabled } from 'featureFlags/flags/uniswapXDefault' import { useUniswapXDefaultEnabled } from 'featureFlags/flags/uniswapXDefault'
import { useUniswapXEthOutputEnabled } from 'featureFlags/flags/uniswapXEthOutput' import { useUniswapXEthOutputEnabled } from 'featureFlags/flags/uniswapXEthOutput'
import { useUniswapXExactOutputEnabled } from 'featureFlags/flags/uniswapXExactOutput' import { useUniswapXExactOutputEnabled } from 'featureFlags/flags/uniswapXExactOutput'
@ -22,8 +22,6 @@ export function useRoutingAPIArguments({
amount, amount,
tradeType, tradeType,
routerPreference, routerPreference,
inputTax,
outputTax,
}: { }: {
account?: string account?: string
tokenIn?: Currency tokenIn?: Currency
@ -31,8 +29,6 @@ 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()
@ -70,8 +66,6 @@ export function useRoutingAPIArguments({
uniswapXExactOutputEnabled, uniswapXExactOutputEnabled,
isUniswapXDefaultEnabled, isUniswapXDefaultEnabled,
sendPortionEnabled, sendPortionEnabled,
inputTax,
outputTax,
}, },
[ [
account, account,
@ -87,8 +81,6 @@ export function useRoutingAPIArguments({
uniswapXEthOutputEnabled, uniswapXEthOutputEnabled,
isUniswapXDefaultEnabled, isUniswapXDefaultEnabled,
sendPortionEnabled, sendPortionEnabled,
inputTax,
outputTax,
] ]
) )
} }

@ -51,7 +51,7 @@ export function formatCommonPropertiesForTrade(
token_in_symbol: trade.inputAmount.currency.symbol, token_in_symbol: trade.inputAmount.currency.symbol,
token_out_symbol: trade.outputAmount.currency.symbol, token_out_symbol: trade.outputAmount.currency.symbol,
token_in_amount: formatToDecimal(trade.inputAmount, trade.inputAmount.currency.decimals), token_in_amount: formatToDecimal(trade.inputAmount, trade.inputAmount.currency.decimals),
token_out_amount: formatToDecimal(trade.postTaxOutputAmount, trade.outputAmount.currency.decimals), token_out_amount: formatToDecimal(trade.outputAmount, trade.outputAmount.currency.decimals),
price_impact_basis_points: isClassicTrade(trade) price_impact_basis_points: isClassicTrade(trade)
? formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade)) ? formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade))
: undefined, : undefined,
@ -64,6 +64,8 @@ export function formatCommonPropertiesForTrade(
allowed_slippage: formatPercentNumber(allowedSlippage), allowed_slippage: formatPercentNumber(allowedSlippage),
method: getQuoteMethod(trade), method: getQuoteMethod(trade),
fee_usd: outputFeeFiatValue, fee_usd: outputFeeFiatValue,
token_out_detected_tax: formatPercentNumber(trade.outputTax),
token_in_detected_tax: formatPercentNumber(trade.inputTax),
} }
} }
@ -101,8 +103,6 @@ export const formatSwapQuoteReceivedEventProperties = (
trade: InterfaceTrade, trade: InterfaceTrade,
allowedSlippage: Percent, allowedSlippage: Percent,
swapQuoteLatencyMs: number | undefined, swapQuoteLatencyMs: number | undefined,
inputTax: Percent,
outputTax: Percent,
outputFeeFiatValue: number | undefined outputFeeFiatValue: number | undefined
) => { ) => {
return { return {
@ -112,7 +112,5 @@ export const formatSwapQuoteReceivedEventProperties = (
token_in_amount_max: trade.maximumAmountIn(allowedSlippage).toExact(), token_in_amount_max: trade.maximumAmountIn(allowedSlippage).toExact(),
token_out_amount_min: trade.minimumAmountOut(allowedSlippage).toExact(), token_out_amount_min: trade.minimumAmountOut(allowedSlippage).toExact(),
quote_latency_milliseconds: swapQuoteLatencyMs, quote_latency_milliseconds: swapQuoteLatencyMs,
token_out_detected_tax: formatPercentNumber(outputTax),
token_in_detected_tax: formatPercentNumber(inputTax),
} }
} }

@ -292,9 +292,9 @@ export function Swap({
parsedAmount, parsedAmount,
currencies, currencies,
inputError: swapInputError, inputError: swapInputError,
outputFeeFiatValue,
inputTax, inputTax,
outputTax, outputTax,
outputFeeFiatValue,
} = swapInfo } = swapInfo
const [inputTokenHasTax, outputTokenHasTax] = useMemo( const [inputTokenHasTax, outputTokenHasTax] = useMemo(
@ -323,7 +323,7 @@ export function Swap({
} }
: { : {
[Field.INPUT]: independentField === Field.INPUT ? parsedAmount : trade?.inputAmount, [Field.INPUT]: independentField === Field.INPUT ? parsedAmount : trade?.inputAmount,
[Field.OUTPUT]: independentField === Field.OUTPUT ? parsedAmount : trade?.postTaxOutputAmount, [Field.OUTPUT]: independentField === Field.OUTPUT ? parsedAmount : trade?.outputAmount,
}, },
[independentField, parsedAmount, showWrap, trade] [independentField, parsedAmount, showWrap, trade]
) )
@ -354,7 +354,7 @@ export function Swap({
) )
const fiatValueTradeInput = useUSDPrice(trade?.inputAmount) const fiatValueTradeInput = useUSDPrice(trade?.inputAmount)
const fiatValueTradeOutput = useUSDPrice(trade?.postTaxOutputAmount) const fiatValueTradeOutput = useUSDPrice(trade?.outputAmount)
const preTaxFiatValueTradeOutput = useUSDPrice(trade?.outputAmount) const preTaxFiatValueTradeOutput = useUSDPrice(trade?.outputAmount)
const [stablecoinPriceImpact, preTaxStablecoinPriceImpact] = useMemo( const [stablecoinPriceImpact, preTaxStablecoinPriceImpact] = useMemo(
() => () =>
@ -585,17 +585,10 @@ export function Swap({
if (!trade || prevTrade === trade) return // no new swap quote to log if (!trade || prevTrade === trade) return // no new swap quote to log
sendAnalyticsEvent(SwapEventName.SWAP_QUOTE_RECEIVED, { sendAnalyticsEvent(SwapEventName.SWAP_QUOTE_RECEIVED, {
...formatSwapQuoteReceivedEventProperties( ...formatSwapQuoteReceivedEventProperties(trade, allowedSlippage, swapQuoteLatency, outputFeeFiatValue),
trade,
allowedSlippage,
swapQuoteLatency,
inputTax,
outputTax,
outputFeeFiatValue
),
...trace, ...trace,
}) })
}, [prevTrade, trade, trace, allowedSlippage, swapQuoteLatency, inputTax, outputTax, outputFeeFiatValue]) }, [prevTrade, trade, trace, allowedSlippage, swapQuoteLatency, outputFeeFiatValue])
const showDetailsDropdown = Boolean( const showDetailsDropdown = Boolean(
!showWrap && userHasSpecifiedInputOutput && (trade || routeIsLoading || routeIsSyncing) !showWrap && userHasSpecifiedInputOutput && (trade || routeIsLoading || routeIsSyncing)

@ -19,7 +19,7 @@ import {
URAQuoteResponse, URAQuoteResponse,
URAQuoteType, URAQuoteType,
} from './types' } from './types'
import { isExactInput, transformRoutesToTrade } from './utils' import { isExactInput, transformQuoteToTrade } from './utils'
const UNISWAP_API_URL = process.env.REACT_APP_UNISWAP_API_URL const UNISWAP_API_URL = process.env.REACT_APP_UNISWAP_API_URL
if (UNISWAP_API_URL === undefined) { if (UNISWAP_API_URL === undefined) {
@ -69,6 +69,7 @@ function getRoutingAPIConfig(args: GetQuoteArgs): RoutingConfig {
...DEFAULT_QUERY_PARAMS, ...DEFAULT_QUERY_PARAMS,
routingType: URAQuoteType.CLASSIC, routingType: URAQuoteType.CLASSIC,
recipient: account, recipient: account,
enableFeeOnTransferFeeFetching: true,
} }
const tokenOutIsNative = Object.values(SwapRouterNativeAssets).includes(tokenOutAddress as SwapRouterNativeAssets) const tokenOutIsNative = Object.values(SwapRouterNativeAssets).includes(tokenOutAddress as SwapRouterNativeAssets)
@ -179,7 +180,7 @@ export const routingApi = createApi({
} }
const uraQuoteResponse = response.data as URAQuoteResponse const uraQuoteResponse = response.data as URAQuoteResponse
const tradeResult = await transformRoutesToTrade(args, uraQuoteResponse, QuoteMethod.ROUTING_API) const tradeResult = await transformQuoteToTrade(args, uraQuoteResponse, QuoteMethod.ROUTING_API)
return { data: { ...tradeResult, latencyMs: getQuoteLatencyMeasure(quoteStartMark).duration } } return { data: { ...tradeResult, latencyMs: getQuoteLatencyMeasure(quoteStartMark).duration } }
} catch (error: any) { } catch (error: any) {
console.warn( console.warn(
@ -194,7 +195,7 @@ export const routingApi = createApi({
const router = getRouter(args.tokenInChainId) const router = getRouter(args.tokenInChainId)
const quoteResult = await getClientSideQuote(args, router, CLIENT_PARAMS) const quoteResult = await getClientSideQuote(args, router, CLIENT_PARAMS)
if (quoteResult.state === QuoteState.SUCCESS) { if (quoteResult.state === QuoteState.SUCCESS) {
const trade = await transformRoutesToTrade(args, quoteResult.data, QuoteMethod.CLIENT_SIDE_FALLBACK) const trade = await transformQuoteToTrade(args, quoteResult.data, QuoteMethod.CLIENT_SIDE_FALLBACK)
return { return {
data: { ...trade, latencyMs: getQuoteLatencyMeasure(quoteStartMark).duration }, data: { ...trade, latencyMs: getQuoteLatencyMeasure(quoteStartMark).duration },
} }

@ -51,8 +51,6 @@ export interface GetQuoteArgs {
userOptedOutOfUniswapX: boolean userOptedOutOfUniswapX: boolean
isUniswapXDefaultEnabled: boolean isUniswapXDefaultEnabled: boolean
sendPortionEnabled: boolean sendPortionEnabled: boolean
inputTax: Percent
outputTax: Percent
} }
export type GetQuickQuoteArgs = { export type GetQuickQuoteArgs = {
@ -69,9 +67,12 @@ export type GetQuickQuoteArgs = {
inputTax: Percent inputTax: Percent
outputTax: Percent outputTax: Percent
} }
// from https://github.com/Uniswap/routing-api/blob/main/lib/handlers/schema.ts
type TokenInRoute = Pick<Token, 'address' | 'chainId' | 'symbol' | 'decimals'> // from https://github.com/Uniswap/routing-api/blob/main/lib/handlers/schema.ts
export type TokenInRoute = Pick<Token, 'address' | 'chainId' | 'symbol' | 'decimals'> & {
buyFeeBps?: string
sellFeeBps?: string
}
export type V3PoolInRoute = { export type V3PoolInRoute = {
type: 'v3-pool' type: 'v3-pool'
@ -201,8 +202,6 @@ export class ClassicTrade extends Trade<Currency, Currency, TradeType> {
isUniswapXBetter: boolean | undefined isUniswapXBetter: boolean | undefined
requestId: string | undefined requestId: string | undefined
quoteMethod: QuoteMethod quoteMethod: QuoteMethod
inputTax: Percent
outputTax: Percent
swapFee: SwapFeeInfo | undefined swapFee: SwapFeeInfo | undefined
constructor({ constructor({
@ -212,8 +211,6 @@ export class ClassicTrade extends Trade<Currency, Currency, TradeType> {
requestId, requestId,
quoteMethod, quoteMethod,
approveInfo, approveInfo,
inputTax,
outputTax,
swapFee, swapFee,
...routes ...routes
}: { }: {
@ -224,8 +221,6 @@ export class ClassicTrade extends Trade<Currency, Currency, TradeType> {
requestId?: string requestId?: string
quoteMethod: QuoteMethod quoteMethod: QuoteMethod
approveInfo: ApproveInfo approveInfo: ApproveInfo
inputTax: Percent
outputTax: Percent
swapFee?: SwapFeeInfo swapFee?: SwapFeeInfo
v2Routes: { v2Routes: {
routev2: V2Route<Currency, Currency> routev2: V2Route<Currency, Currency>
@ -251,8 +246,6 @@ export class ClassicTrade extends Trade<Currency, Currency, TradeType> {
this.requestId = requestId this.requestId = requestId
this.quoteMethod = quoteMethod this.quoteMethod = quoteMethod
this.approveInfo = approveInfo this.approveInfo = approveInfo
this.inputTax = inputTax
this.outputTax = outputTax
this.swapFee = swapFee this.swapFee = swapFee
} }
@ -263,10 +256,6 @@ export class ClassicTrade extends Trade<Currency, Currency, TradeType> {
return new Price({ baseAmount: this.inputAmount, quoteAmount: this.postSwapFeeOutputAmount }) return new Price({ baseAmount: this.inputAmount, quoteAmount: this.postSwapFeeOutputAmount })
} }
public get totalTaxRate(): Percent {
return this.inputTax.add(this.outputTax)
}
public get postSwapFeeOutputAmount(): CurrencyAmount<Currency> { public get postSwapFeeOutputAmount(): CurrencyAmount<Currency> {
// Routing api already applies the swap fee to the output amount for exact-in // Routing api already applies the swap fee to the output amount for exact-in
if (this.tradeType === TradeType.EXACT_INPUT) return this.outputAmount if (this.tradeType === TradeType.EXACT_INPUT) return this.outputAmount
@ -275,20 +264,6 @@ export class ClassicTrade extends Trade<Currency, Currency, TradeType> {
return this.outputAmount.subtract(swapFeeAmount) return this.outputAmount.subtract(swapFeeAmount)
} }
public get postTaxOutputAmount() {
// Ideally we should calculate the final output amount by ammending the inputAmount based on the input tax and then applying the output tax,
// but this isn't currently possible because V2Trade reconstructs the total inputAmount based on the swap routes
// TODO(WEB-2761): Amend V2Trade objects in the v2-sdk to have a separate field for post-input tax routes
return this.postSwapFeeOutputAmount.multiply(new Fraction(ONE).subtract(this.totalTaxRate))
}
public minimumAmountOut(slippageTolerance: Percent, amountOut = this.outputAmount): CurrencyAmount<Currency> {
// Since universal-router-sdk reconstructs V2Trade objects, overriding this method does not actually change the minimumAmountOut that gets submitted on-chain
// Our current workaround is to add tax rate to slippage tolerance before we submit the trade to universal-router-sdk in useUniversalRouter.ts
// So the purpose of this override is so the UI displays the same minimum amount out as what is submitted on-chain
return super.minimumAmountOut(slippageTolerance.add(this.totalTaxRate), amountOut)
}
// gas estimate for maybe approve + swap // gas estimate for maybe approve + swap
public get totalGasUseEstimateUSD(): number | undefined { public get totalGasUseEstimateUSD(): number | undefined {
if (this.approveInfo.needsApprove && this.gasUseEstimateUSD) { if (this.approveInfo.needsApprove && this.gasUseEstimateUSD) {
@ -370,11 +345,6 @@ export class DutchOrderTrade extends IDutchOrderTrade<Currency, Currency, TradeT
return 0 return 0
} }
/** For UniswapX, handling token taxes in the output amount is outsourced to quoters */
public get postTaxOutputAmount() {
return this.outputAmount
}
} }
export class PreviewTrade { export class PreviewTrade {
@ -383,38 +353,19 @@ export class PreviewTrade {
public readonly tradeType: TradeType public readonly tradeType: TradeType
public readonly inputAmount: CurrencyAmount<Currency> public readonly inputAmount: CurrencyAmount<Currency>
public readonly outputAmount: CurrencyAmount<Currency> public readonly outputAmount: CurrencyAmount<Currency>
inputTax: Percent
outputTax: Percent
constructor({ constructor({
inputAmount, inputAmount,
outputAmount, outputAmount,
tradeType, tradeType,
inputTax,
outputTax,
}: { }: {
inputAmount: CurrencyAmount<Currency> inputAmount: CurrencyAmount<Currency>
outputAmount: CurrencyAmount<Currency> outputAmount: CurrencyAmount<Currency>
tradeType: TradeType tradeType: TradeType
inputTax: Percent
outputTax: Percent
}) { }) {
this.inputAmount = inputAmount this.inputAmount = inputAmount
this.outputAmount = outputAmount this.outputAmount = outputAmount
this.tradeType = tradeType this.tradeType = tradeType
this.inputTax = inputTax
this.outputTax = outputTax
}
public get totalTaxRate(): Percent {
return this.inputTax.add(this.outputTax)
}
public get postTaxOutputAmount() {
// Ideally we should calculate the final output amount by ammending the inputAmount based on the input tax and then applying the output tax,
// but this isn't currently possible because V2Trade reconstructs the total inputAmount based on the swap routes
// TODO(WEB-2761): Amend V2Trade objects in the v2-sdk to have a separate field for post-input tax routes
return this.outputAmount.multiply(new Fraction(ONE).subtract(this.totalTaxRate))
} }
// below methods are copied from router-sdk // below methods are copied from router-sdk
@ -440,6 +391,26 @@ export class PreviewTrade {
} }
} }
/**
* Returns the sell tax of the input token
*/
public get inputTax(): Percent {
const inputCurrency = this.inputAmount.currency
if (inputCurrency.isNative || !inputCurrency.wrapped.sellFeeBps) return ZERO_PERCENT
return new Percent(inputCurrency.wrapped.sellFeeBps.toNumber(), 10000)
}
/**
* Returns the buy tax of the output token
*/
public get outputTax(): Percent {
const outputCurrency = this.outputAmount.currency
if (outputCurrency.isNative || !outputCurrency.wrapped.buyFeeBps) return ZERO_PERCENT
return new Percent(outputCurrency.wrapped.buyFeeBps.toNumber(), 10000)
}
private _executionPrice: Price<Currency, Currency> | undefined private _executionPrice: Price<Currency, Currency> | undefined
/** /**
* The price expressed in terms of output amount/input amount. * The price expressed in terms of output amount/input amount.

@ -2,7 +2,6 @@ import { skipToken } from '@reduxjs/toolkit/query/react'
import { renderHook } from '@testing-library/react' import { renderHook } from '@testing-library/react'
import { CurrencyAmount, TradeType } from '@uniswap/sdk-core' import { CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { AVERAGE_L1_BLOCK_TIME } from 'constants/chainInfo' import { AVERAGE_L1_BLOCK_TIME } from 'constants/chainInfo'
import { ZERO_PERCENT } from 'constants/misc'
import { USDC_MAINNET } from 'constants/tokens' import { USDC_MAINNET } from 'constants/tokens'
import { useUniswapXDefaultEnabled } from 'featureFlags/flags/uniswapXDefault' import { useUniswapXDefaultEnabled } from 'featureFlags/flags/uniswapXDefault'
import { useUniswapXEthOutputEnabled } from 'featureFlags/flags/uniswapXEthOutput' import { useUniswapXEthOutputEnabled } from 'featureFlags/flags/uniswapXEthOutput'
@ -79,8 +78,6 @@ const MOCK_ARGS: GetQuoteArgs = {
uniswapXExactOutputEnabled: false, uniswapXExactOutputEnabled: false,
isUniswapXDefaultEnabled: false, isUniswapXDefaultEnabled: false,
sendPortionEnabled: true, sendPortionEnabled: true,
inputTax: ZERO_PERCENT,
outputTax: ZERO_PERCENT,
} }
describe('#useRoutingAPITrade ExactIn', () => { describe('#useRoutingAPITrade ExactIn', () => {

@ -1,7 +1,6 @@
import { skipToken } from '@reduxjs/toolkit/query/react' import { skipToken } from '@reduxjs/toolkit/query/react'
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
import { AVERAGE_L1_BLOCK_TIME } from 'constants/chainInfo' import { AVERAGE_L1_BLOCK_TIME } from 'constants/chainInfo'
import { ZERO_PERCENT } from 'constants/misc'
import useIsWindowVisible from 'hooks/useIsWindowVisible' import useIsWindowVisible from 'hooks/useIsWindowVisible'
import { useRoutingAPIArguments } from 'lib/hooks/routing/useRoutingAPIArguments' import { useRoutingAPIArguments } from 'lib/hooks/routing/useRoutingAPIArguments'
import ms from 'ms' import ms from 'ms'
@ -65,9 +64,7 @@ export function useRoutingAPITrade<TTradeType extends TradeType>(
amountSpecified: CurrencyAmount<Currency> | undefined, amountSpecified: CurrencyAmount<Currency> | undefined,
otherCurrency: Currency | undefined, otherCurrency: Currency | undefined,
routerPreference: RouterPreference | typeof INTERNAL_ROUTER_PREFERENCE_PRICE, routerPreference: RouterPreference | typeof INTERNAL_ROUTER_PREFERENCE_PRICE,
account?: string, account?: string
inputTax = ZERO_PERCENT,
outputTax = ZERO_PERCENT
): { ): {
state: TradeState state: TradeState
trade?: SubmittableTrade trade?: SubmittableTrade
@ -90,8 +87,6 @@ export function useRoutingAPITrade<TTradeType extends TradeType>(
amount: amountSpecified, amount: amountSpecified,
tradeType, tradeType,
routerPreference, routerPreference,
inputTax,
outputTax,
}) })
// skip all pricing and quote requests if the window is not focused // skip all pricing and quote requests if the window is not focused
const isWindowVisible = useIsWindowVisible() const isWindowVisible = useIsWindowVisible()

@ -1,26 +1,69 @@
import { ChainId, Token } from '@uniswap/sdk-core' import { ChainId, Currency, Token, TradeType } from '@uniswap/sdk-core'
import { nativeOnChain } from 'constants/tokens' import { nativeOnChain } from 'constants/tokens'
import { PoolType } from './types' import { GetQuoteArgs, PoolType, RouterPreference, TokenInRoute } from './types'
import { computeRoutes } from './utils' import { computeRoutes } from './utils'
const USDC = new Token(1, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6, 'USDC') const USDC = new Token(1, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6, 'USDC', undefined, false)
const DAI = new Token(1, '0x6B175474E89094C44Da98b954EedeAC495271d0F', 6, 'DAI') const USDC_IN_ROUTE = toTokenInRoute(USDC)
const MKR = new Token(1, '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2', 6, 'MKR') const DAI = new Token(1, '0x6B175474E89094C44Da98b954EedeAC495271d0F', 6, 'DAI', undefined, false)
const DAI_IN_ROUTE = toTokenInRoute(DAI)
const MKR = new Token(1, '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2', 6, 'MKR', undefined, false)
const MKR_IN_ROUTE = toTokenInRoute(MKR)
const ETH = nativeOnChain(ChainId.MAINNET) const ETH = nativeOnChain(ChainId.MAINNET)
const WETH_IN_ROUTE = toTokenInRoute(ETH.wrapped)
// helper function to make amounts more readable // helper function to make amounts more readable
const amount = (raw: TemplateStringsArray) => (parseInt(raw[0]) * 1e6).toString() const amount = (raw: TemplateStringsArray) => (parseInt(raw[0]) * 1e6).toString()
const BASE_ARGS = {
amount: '100',
routerPreference: RouterPreference.API,
tradeType: TradeType.EXACT_INPUT,
needsWrapIfUniswapX: false,
uniswapXForceSyntheticQuotes: false,
uniswapXEthOutputEnabled: true,
uniswapXExactOutputEnabled: true,
userDisabledUniswapX: false,
userOptedOutOfUniswapX: false,
isUniswapXDefaultEnabled: false,
sendPortionEnabled: true,
}
function constructArgs(currencyIn: Currency, currencyOut: Currency): GetQuoteArgs {
return {
...BASE_ARGS,
tokenInAddress: currencyIn.isNative ? 'ETH' : currencyIn.address,
tokenInChainId: currencyIn.chainId,
tokenInDecimals: currencyIn.decimals,
tokenInSymbol: currencyIn.symbol,
tokenOutAddress: currencyOut.isNative ? 'ETH' : currencyOut.address,
tokenOutChainId: currencyOut.chainId,
tokenOutDecimals: currencyOut.decimals,
tokenOutSymbol: currencyOut.symbol,
}
}
function toTokenInRoute(token: Token): TokenInRoute {
return {
address: token.address,
chainId: token.chainId,
symbol: token.symbol,
decimals: token.decimals,
buyFeeBps: token.buyFeeBps?.toString(),
sellFeeBps: token.sellFeeBps?.toString(),
}
}
describe('#useRoute', () => { describe('#useRoute', () => {
it('handles empty edges and nodes', () => { it('handles empty edges and nodes', () => {
const result = computeRoutes(USDC, DAI, []) const result = computeRoutes(constructArgs(USDC, DAI), [])
expect(result).toEqual([]) expect(result).toEqual([])
}) })
it('handles a single route trade from DAI to USDC from v3', () => { it('handles a single route trade from DAI to USDC from v3', () => {
const result = computeRoutes(DAI, USDC, [ const result = computeRoutes(constructArgs(DAI, USDC), [
[ [
{ {
type: 'v3-pool', type: 'v3-pool',
@ -31,8 +74,8 @@ describe('#useRoute', () => {
sqrtRatioX96: '2437312313659959819381354528', sqrtRatioX96: '2437312313659959819381354528',
liquidity: '10272714736694327408', liquidity: '10272714736694327408',
tickCurrent: '-69633', tickCurrent: '-69633',
tokenIn: DAI, tokenIn: toTokenInRoute(DAI),
tokenOut: USDC, tokenOut: toTokenInRoute(USDC),
}, },
], ],
]) ])
@ -50,21 +93,21 @@ describe('#useRoute', () => {
}) })
it('handles a single route trade from DAI to USDC from v2', () => { it('handles a single route trade from DAI to USDC from v2', () => {
const result = computeRoutes(DAI, USDC, [ const result = computeRoutes(constructArgs(DAI, USDC), [
[ [
{ {
type: 'v2-pool', type: 'v2-pool',
address: '0x1f8F72aA9304c8B593d555F12eF6589cC3A579A2', address: '0x1f8F72aA9304c8B593d555F12eF6589cC3A579A2',
amountIn: amount`1`, amountIn: amount`1`,
amountOut: amount`5`, amountOut: amount`5`,
tokenIn: DAI, tokenIn: DAI_IN_ROUTE,
tokenOut: USDC, tokenOut: USDC_IN_ROUTE,
reserve0: { reserve0: {
token: DAI, token: DAI_IN_ROUTE,
quotient: amount`100`, quotient: amount`100`,
}, },
reserve1: { reserve1: {
token: USDC, token: USDC_IN_ROUTE,
quotient: amount`200`, quotient: amount`200`,
}, },
}, },
@ -84,21 +127,21 @@ describe('#useRoute', () => {
}) })
it('handles a multi-route trade from DAI to USDC', () => { it('handles a multi-route trade from DAI to USDC', () => {
const result = computeRoutes(DAI, USDC, [ const result = computeRoutes(constructArgs(DAI, USDC), [
[ [
{ {
type: 'v2-pool', type: 'v2-pool',
address: '0x1f8F72aA9304c8B593d555F12eF6589cC3A579A2', address: '0x1f8F72aA9304c8B593d555F12eF6589cC3A579A2',
amountIn: amount`5`, amountIn: amount`5`,
amountOut: amount`6`, amountOut: amount`6`,
tokenIn: DAI, tokenIn: DAI_IN_ROUTE,
tokenOut: USDC, tokenOut: USDC_IN_ROUTE,
reserve0: { reserve0: {
token: DAI, token: DAI_IN_ROUTE,
quotient: amount`1000`, quotient: amount`1000`,
}, },
reserve1: { reserve1: {
token: USDC, token: USDC_IN_ROUTE,
quotient: amount`500`, quotient: amount`500`,
}, },
}, },
@ -110,8 +153,8 @@ describe('#useRoute', () => {
amountIn: amount`10`, amountIn: amount`10`,
amountOut: amount`1`, amountOut: amount`1`,
fee: '3000', fee: '3000',
tokenIn: DAI, tokenIn: DAI_IN_ROUTE,
tokenOut: MKR, tokenOut: MKR_IN_ROUTE,
sqrtRatioX96: '2437312313659959819381354528', sqrtRatioX96: '2437312313659959819381354528',
liquidity: '10272714736694327408', liquidity: '10272714736694327408',
tickCurrent: '-69633', tickCurrent: '-69633',
@ -122,8 +165,8 @@ describe('#useRoute', () => {
amountIn: amount`1`, amountIn: amount`1`,
amountOut: amount`200`, amountOut: amount`200`,
fee: '10000', fee: '10000',
tokenIn: MKR, tokenIn: MKR_IN_ROUTE,
tokenOut: USDC, tokenOut: USDC_IN_ROUTE,
sqrtRatioX96: '2437312313659959819381354528', sqrtRatioX96: '2437312313659959819381354528',
liquidity: '10272714736694327408', liquidity: '10272714736694327408',
tickCurrent: '-69633', tickCurrent: '-69633',
@ -151,7 +194,7 @@ describe('#useRoute', () => {
}) })
it('handles a single route trade with same token pair, different fee tiers', () => { it('handles a single route trade with same token pair, different fee tiers', () => {
const result = computeRoutes(DAI, USDC, [ const result = computeRoutes(constructArgs(DAI, USDC), [
[ [
{ {
type: 'v3-pool', type: 'v3-pool',
@ -159,8 +202,8 @@ describe('#useRoute', () => {
amountIn: amount`1`, amountIn: amount`1`,
amountOut: amount`5`, amountOut: amount`5`,
fee: '500', fee: '500',
tokenIn: DAI, tokenIn: DAI_IN_ROUTE,
tokenOut: USDC, tokenOut: USDC_IN_ROUTE,
sqrtRatioX96: '2437312313659959819381354528', sqrtRatioX96: '2437312313659959819381354528',
liquidity: '10272714736694327408', liquidity: '10272714736694327408',
tickCurrent: '-69633', tickCurrent: '-69633',
@ -173,8 +216,8 @@ describe('#useRoute', () => {
amountIn: amount`10`, amountIn: amount`10`,
amountOut: amount`50`, amountOut: amount`50`,
fee: '3000', fee: '3000',
tokenIn: DAI, tokenIn: DAI_IN_ROUTE,
tokenOut: USDC, tokenOut: USDC_IN_ROUTE,
sqrtRatioX96: '2437312313659959819381354528', sqrtRatioX96: '2437312313659959819381354528',
liquidity: '10272714736694327408', liquidity: '10272714736694327408',
tickCurrent: '-69633', tickCurrent: '-69633',
@ -191,7 +234,7 @@ describe('#useRoute', () => {
}) })
it('computes mixed routes correctly', () => { it('computes mixed routes correctly', () => {
const result = computeRoutes(DAI, MKR, [ const result = computeRoutes(constructArgs(DAI, MKR), [
[ [
{ {
type: PoolType.V3Pool, type: PoolType.V3Pool,
@ -199,8 +242,8 @@ describe('#useRoute', () => {
amountIn: amount`1`, amountIn: amount`1`,
amountOut: amount`5`, amountOut: amount`5`,
fee: '500', fee: '500',
tokenIn: DAI, tokenIn: DAI_IN_ROUTE,
tokenOut: USDC, tokenOut: USDC_IN_ROUTE,
sqrtRatioX96: '2437312313659959819381354528', sqrtRatioX96: '2437312313659959819381354528',
liquidity: '10272714736694327408', liquidity: '10272714736694327408',
tickCurrent: '-69633', tickCurrent: '-69633',
@ -210,14 +253,14 @@ describe('#useRoute', () => {
address: 'x2f8F72aA9304c8B593d555F12eF6589cC3A579A2', address: 'x2f8F72aA9304c8B593d555F12eF6589cC3A579A2',
amountIn: amount`10`, amountIn: amount`10`,
amountOut: amount`50`, amountOut: amount`50`,
tokenIn: USDC, tokenIn: USDC_IN_ROUTE,
tokenOut: MKR, tokenOut: MKR_IN_ROUTE,
reserve0: { reserve0: {
token: USDC, token: USDC_IN_ROUTE,
quotient: amount`100`, quotient: amount`100`,
}, },
reserve1: { reserve1: {
token: MKR, token: MKR_IN_ROUTE,
quotient: amount`200`, quotient: amount`200`,
}, },
}, },
@ -236,7 +279,7 @@ describe('#useRoute', () => {
it('outputs native ETH as input currency', () => { it('outputs native ETH as input currency', () => {
const WETH = ETH.wrapped const WETH = ETH.wrapped
const result = computeRoutes(ETH, USDC, [ const result = computeRoutes(constructArgs(ETH, USDC), [
[ [
{ {
type: 'v3-pool', type: 'v3-pool',
@ -247,8 +290,8 @@ describe('#useRoute', () => {
sqrtRatioX96: '2437312313659959819381354528', sqrtRatioX96: '2437312313659959819381354528',
liquidity: '10272714736694327408', liquidity: '10272714736694327408',
tickCurrent: '-69633', tickCurrent: '-69633',
tokenIn: WETH, tokenIn: WETH_IN_ROUTE,
tokenOut: USDC, tokenOut: USDC_IN_ROUTE,
}, },
], ],
]) ])
@ -263,7 +306,7 @@ describe('#useRoute', () => {
it('outputs native ETH as output currency', () => { it('outputs native ETH as output currency', () => {
const WETH = new Token(1, ETH.wrapped.address, 18, 'WETH') const WETH = new Token(1, ETH.wrapped.address, 18, 'WETH')
const result = computeRoutes(USDC, ETH, [ const result = computeRoutes(constructArgs(USDC, ETH), [
[ [
{ {
type: 'v3-pool', type: 'v3-pool',
@ -274,8 +317,8 @@ describe('#useRoute', () => {
sqrtRatioX96: '2437312313659959819381354528', sqrtRatioX96: '2437312313659959819381354528',
liquidity: '10272714736694327408', liquidity: '10272714736694327408',
tickCurrent: '-69633', tickCurrent: '-69633',
tokenIn: USDC, tokenIn: USDC_IN_ROUTE,
tokenOut: WETH, tokenOut: WETH_IN_ROUTE,
}, },
], ],
]) ])
@ -290,21 +333,21 @@ describe('#useRoute', () => {
it('outputs native ETH as input currency for v2 routes', () => { it('outputs native ETH as input currency for v2 routes', () => {
const WETH = ETH.wrapped const WETH = ETH.wrapped
const result = computeRoutes(ETH, USDC, [ const result = computeRoutes(constructArgs(ETH, USDC), [
[ [
{ {
type: 'v2-pool', type: 'v2-pool',
address: '0x1f8F72aA9304c8B593d555F12eF6589cC3A579A2', address: '0x1f8F72aA9304c8B593d555F12eF6589cC3A579A2',
amountIn: (1e18).toString(), amountIn: (1e18).toString(),
amountOut: amount`5`, amountOut: amount`5`,
tokenIn: WETH, tokenIn: WETH_IN_ROUTE,
tokenOut: USDC, tokenOut: USDC_IN_ROUTE,
reserve0: { reserve0: {
token: WETH, token: WETH_IN_ROUTE,
quotient: amount`100`, quotient: amount`100`,
}, },
reserve1: { reserve1: {
token: USDC, token: USDC_IN_ROUTE,
quotient: amount`200`, quotient: amount`200`,
}, },
}, },
@ -321,21 +364,21 @@ describe('#useRoute', () => {
it('outputs native ETH as output currency for v2 routes', () => { it('outputs native ETH as output currency for v2 routes', () => {
const WETH = new Token(1, ETH.wrapped.address, 18, 'WETH') const WETH = new Token(1, ETH.wrapped.address, 18, 'WETH')
const result = computeRoutes(USDC, ETH, [ const result = computeRoutes(constructArgs(USDC, ETH), [
[ [
{ {
type: 'v2-pool', type: 'v2-pool',
address: '0x1f8F72aA9304c8B593d555F12eF6589cC3A579A2', address: '0x1f8F72aA9304c8B593d555F12eF6589cC3A579A2',
amountIn: amount`5`, amountIn: amount`5`,
amountOut: (1e18).toString(), amountOut: (1e18).toString(),
tokenIn: USDC, tokenIn: USDC_IN_ROUTE,
tokenOut: WETH, tokenOut: WETH_IN_ROUTE,
reserve0: { reserve0: {
token: WETH, token: WETH_IN_ROUTE,
quotient: amount`100`, quotient: amount`100`,
}, },
reserve1: { reserve1: {
token: USDC, token: USDC_IN_ROUTE,
quotient: amount`200`, quotient: amount`200`,
}, },
}, },

@ -26,6 +26,7 @@ import {
SubmittableTrade, SubmittableTrade,
SwapFeeInfo, SwapFeeInfo,
SwapRouterNativeAssets, SwapRouterNativeAssets,
TokenInRoute,
TradeFillType, TradeFillType,
TradeResult, TradeResult,
URADutchOrderQuoteData, URADutchOrderQuoteData,
@ -47,16 +48,9 @@ interface RouteResult {
* Transforms a Routing API quote into an array of routes that can be used to * Transforms a Routing API quote into an array of routes that can be used to
* create a `Trade`. * create a `Trade`.
*/ */
export function computeRoutes( export function computeRoutes(args: GetQuoteArgs, routes: ClassicQuoteData['route']): RouteResult[] | undefined {
currencyIn: Currency,
currencyOut: Currency,
routes: ClassicQuoteData['route']
): RouteResult[] | undefined {
if (routes.length === 0) return [] if (routes.length === 0) return []
const [currencyIn, currencyOut] = getTradeCurrencies(args, false, routes)
const tokenIn = routes[0]?.[0]?.tokenIn
const tokenOut = routes[0]?.[routes[0]?.length - 1]?.tokenOut
if (!tokenIn || !tokenOut) throw new Error('Expected both tokenIn and tokenOut to be present')
try { try {
return routes.map((route) => { return routes.map((route) => {
@ -121,7 +115,11 @@ function toDutchOrderInfo(orderInfoJSON: DutchOrderInfoJSON): DutchOrderInfo {
// Prepares the currencies used for the actual Swap (either UniswapX or Universal Router) // Prepares the currencies used for the actual Swap (either UniswapX or Universal Router)
// May not match `currencyIn` that the user selected because for ETH inputs in UniswapX, the actual // May not match `currencyIn` that the user selected because for ETH inputs in UniswapX, the actual
// swap will use WETH. // swap will use WETH.
function getTradeCurrencies(args: GetQuoteArgs | GetQuickQuoteArgs, isUniswapXTrade: boolean): [Currency, Currency] { function getTradeCurrencies(
args: GetQuoteArgs | GetQuickQuoteArgs,
isUniswapXTrade: boolean,
routes?: ClassicQuoteData['route']
): [Currency, Currency] {
const { const {
tokenInAddress, tokenInAddress,
tokenInChainId, tokenInChainId,
@ -136,9 +134,19 @@ function getTradeCurrencies(args: GetQuoteArgs | GetQuickQuoteArgs, isUniswapXTr
const tokenInIsNative = Object.values(SwapRouterNativeAssets).includes(tokenInAddress as SwapRouterNativeAssets) const tokenInIsNative = Object.values(SwapRouterNativeAssets).includes(tokenInAddress as SwapRouterNativeAssets)
const tokenOutIsNative = Object.values(SwapRouterNativeAssets).includes(tokenOutAddress as SwapRouterNativeAssets) const tokenOutIsNative = Object.values(SwapRouterNativeAssets).includes(tokenOutAddress as SwapRouterNativeAssets)
const serializedTokenIn = routes?.[0]?.[0]?.tokenIn
const serializedTokenOut = routes?.[0]?.[routes[0]?.length - 1]?.tokenOut
const currencyIn = tokenInIsNative const currencyIn = tokenInIsNative
? nativeOnChain(tokenInChainId) ? nativeOnChain(tokenInChainId)
: parseToken({ address: tokenInAddress, chainId: tokenInChainId, decimals: tokenInDecimals, symbol: tokenInSymbol }) : parseToken({
address: tokenInAddress,
chainId: tokenInChainId,
decimals: tokenInDecimals,
symbol: tokenInSymbol,
buyFeeBps: serializedTokenIn?.buyFeeBps,
sellFeeBps: serializedTokenIn?.sellFeeBps,
})
const currencyOut = tokenOutIsNative const currencyOut = tokenOutIsNative
? nativeOnChain(tokenOutChainId) ? nativeOnChain(tokenOutChainId)
: parseToken({ : parseToken({
@ -146,6 +154,8 @@ function getTradeCurrencies(args: GetQuoteArgs | GetQuickQuoteArgs, isUniswapXTr
chainId: tokenOutChainId, chainId: tokenOutChainId,
decimals: tokenOutDecimals, decimals: tokenOutDecimals,
symbol: tokenOutSymbol, symbol: tokenOutSymbol,
buyFeeBps: serializedTokenOut?.buyFeeBps,
sellFeeBps: serializedTokenOut?.sellFeeBps,
}) })
if (!isUniswapXTrade) { if (!isUniswapXTrade) {
@ -168,8 +178,7 @@ function getSwapFee(data: ClassicQuoteData | URADutchOrderQuoteData): SwapFeeInf
} }
function getClassicTradeDetails( function getClassicTradeDetails(
currencyIn: Currency, args: GetQuoteArgs,
currencyOut: Currency,
data: URAQuoteResponse data: URAQuoteResponse
): { ): {
gasUseEstimate?: number gasUseEstimate?: number
@ -181,54 +190,43 @@ function getClassicTradeDetails(
const classicQuote = const classicQuote =
data.routing === URAQuoteType.CLASSIC ? data.quote : data.allQuotes.find(isClassicQuoteResponse)?.quote data.routing === URAQuoteType.CLASSIC ? data.quote : data.allQuotes.find(isClassicQuoteResponse)?.quote
if (!classicQuote) return {} if (!classicQuote) {
return {}
}
return { return {
gasUseEstimate: classicQuote.gasUseEstimate ? parseFloat(classicQuote.gasUseEstimate) : undefined, gasUseEstimate: classicQuote.gasUseEstimate ? parseFloat(classicQuote.gasUseEstimate) : undefined,
gasUseEstimateUSD: classicQuote.gasUseEstimateUSD ? parseFloat(classicQuote.gasUseEstimateUSD) : undefined, gasUseEstimateUSD: classicQuote.gasUseEstimateUSD ? parseFloat(classicQuote.gasUseEstimateUSD) : undefined,
blockNumber: classicQuote.blockNumber, blockNumber: classicQuote.blockNumber,
routes: computeRoutes(currencyIn, currencyOut, classicQuote.route), routes: computeRoutes(args, classicQuote.route),
swapFee: getSwapFee(classicQuote), swapFee: getSwapFee(classicQuote),
} }
} }
export function transformQuickRouteToTrade(args: GetQuickQuoteArgs, data: QuickRouteResponse): PreviewTrade { export function transformQuickRouteToTrade(args: GetQuickQuoteArgs, data: QuickRouteResponse): PreviewTrade {
const { amount, tradeType, inputTax, outputTax } = args const { amount, tradeType } = args
const [currencyIn, currencyOut] = getTradeCurrencies(args, false) const [currencyIn, currencyOut] = getTradeCurrencies(args, false)
const [rawAmountIn, rawAmountOut] = const [rawAmountIn, rawAmountOut] =
data.tradeType === 'EXACT_IN' ? [amount, data.quote.amount] : [data.quote.amount, amount] data.tradeType === 'EXACT_IN' ? [amount, data.quote.amount] : [data.quote.amount, amount]
const inputAmount = CurrencyAmount.fromRawAmount(currencyIn, rawAmountIn) const inputAmount = CurrencyAmount.fromRawAmount(currencyIn, rawAmountIn)
const outputAmount = CurrencyAmount.fromRawAmount(currencyOut, rawAmountOut) const outputAmount = CurrencyAmount.fromRawAmount(currencyOut, rawAmountOut)
return new PreviewTrade({ inputAmount, outputAmount, tradeType, inputTax, outputTax }) return new PreviewTrade({ inputAmount, outputAmount, tradeType })
} }
export async function transformRoutesToTrade( export async function transformQuoteToTrade(
args: GetQuoteArgs, args: GetQuoteArgs,
data: URAQuoteResponse, data: URAQuoteResponse,
quoteMethod: QuoteMethod quoteMethod: QuoteMethod
): Promise<TradeResult> { ): Promise<TradeResult> {
const { const { tradeType, needsWrapIfUniswapX, routerPreference, account, amount, isUniswapXDefaultEnabled } = args
tradeType,
needsWrapIfUniswapX,
routerPreference,
account,
amount,
isUniswapXDefaultEnabled,
inputTax,
outputTax,
} = args
const showUniswapXTrade = const showUniswapXTrade =
data.routing === URAQuoteType.DUTCH_LIMIT && data.routing === URAQuoteType.DUTCH_LIMIT &&
(routerPreference === RouterPreference.X || (isUniswapXDefaultEnabled && routerPreference === RouterPreference.API)) (routerPreference === RouterPreference.X || (isUniswapXDefaultEnabled && routerPreference === RouterPreference.API))
const [currencyIn, currencyOut] = getTradeCurrencies(args, showUniswapXTrade) const [currencyIn, currencyOut] = getTradeCurrencies(args, showUniswapXTrade)
const { gasUseEstimateUSD, blockNumber, routes, gasUseEstimate, swapFee } = getClassicTradeDetails(
currencyIn, const { gasUseEstimateUSD, blockNumber, routes, gasUseEstimate, swapFee } = getClassicTradeDetails(args, data)
currencyOut,
data
)
// If the top-level URA quote type is DUTCH_LIMIT, then UniswapX is better for the user // If the top-level URA quote type is DUTCH_LIMIT, then UniswapX is better for the user
const isUniswapXBetter = data.routing === URAQuoteType.DUTCH_LIMIT const isUniswapXBetter = data.routing === URAQuoteType.DUTCH_LIMIT
@ -272,8 +270,6 @@ export async function transformRoutesToTrade(
isUniswapXBetter, isUniswapXBetter,
requestId: data.quote.requestId, requestId: data.quote.requestId,
quoteMethod, quoteMethod,
inputTax,
outputTax,
swapFee, swapFee,
}) })
@ -310,8 +306,10 @@ export async function transformRoutesToTrade(
return { state: QuoteState.SUCCESS, trade: classicTrade } return { state: QuoteState.SUCCESS, trade: classicTrade }
} }
function parseToken({ address, chainId, decimals, symbol }: ClassicQuoteData['route'][0][0]['tokenIn']): Token { function parseToken({ address, chainId, decimals, symbol, buyFeeBps, sellFeeBps }: TokenInRoute): Token {
return new Token(chainId, address, parseInt(decimals.toString()), symbol) const buyFeeBpsBN = buyFeeBps ? BigNumber.from(buyFeeBps) : undefined
const sellFeeBpsBN = sellFeeBps ? BigNumber.from(sellFeeBps) : undefined
return new Token(chainId, address, parseInt(decimals.toString()), symbol, undefined, false, buyFeeBpsBN, sellFeeBpsBN)
} }
function parsePool({ fee, sqrtRatioX96, liquidity, tickCurrent, tokenIn, tokenOut }: V3PoolInRoute): Pool { function parsePool({ fee, sqrtRatioX96, liquidity, tickCurrent, tokenIn, tokenOut }: V3PoolInRoute): Pool {

@ -2,7 +2,6 @@ import { Trans } from '@lingui/macro'
import { ChainId, Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core' import { ChainId, Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { useConnectionReady } from 'connection/eagerlyConnect' import { useConnectionReady } from 'connection/eagerlyConnect'
import { useFotAdjustmentsEnabled } from 'featureFlags/flags/fotAdjustments'
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 { useSwapTaxes } from 'hooks/useSwapTaxes'
@ -112,15 +111,14 @@ 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 fotAdjustmentsEnabled = useFotAdjustmentsEnabled()
const { inputTax, outputTax } = useSwapTaxes(
inputCurrency?.isToken && fotAdjustmentsEnabled ? inputCurrency.address : undefined,
outputCurrency?.isToken && fotAdjustmentsEnabled ? 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
const { inputTax, outputTax } = useSwapTaxes(
inputCurrency?.isToken ? inputCurrency.address : undefined,
outputCurrency?.isToken ? outputCurrency.address : undefined
)
const relevantTokenBalances = useCurrencyBalances( const relevantTokenBalances = useCurrencyBalances(
account ?? undefined, account ?? undefined,
useMemo(() => [inputCurrency ?? undefined, outputCurrency ?? undefined], [inputCurrency, outputCurrency]) useMemo(() => [inputCurrency ?? undefined, outputCurrency ?? undefined], [inputCurrency, outputCurrency])
@ -137,9 +135,7 @@ 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 { data: outputFeeFiatValue } = useUSDPrice( const { data: outputFeeFiatValue } = useUSDPrice(
@ -222,9 +218,9 @@ export function useDerivedSwapInfo(state: SwapState, chainId: ChainId | undefine
trade, trade,
autoSlippage, autoSlippage,
allowedSlippage, allowedSlippage,
outputFeeFiatValue,
inputTax, inputTax,
outputTax, outputTax,
outputFeeFiatValue,
}), }),
[ [
allowedSlippage, allowedSlippage,
@ -232,11 +228,11 @@ export function useDerivedSwapInfo(state: SwapState, chainId: ChainId | undefine
currencies, currencies,
currencyBalances, currencyBalances,
inputError, inputError,
inputTax,
outputFeeFiatValue, outputFeeFiatValue,
outputTax,
parsedAmount, parsedAmount,
trade, trade,
inputTax,
outputTax,
] ]
) )
} }

@ -3,7 +3,6 @@ import { ChainId, CurrencyAmount, Percent, Token, TradeType } from '@uniswap/sdk
// eslint-disable-next-line @typescript-eslint/no-restricted-imports // eslint-disable-next-line @typescript-eslint/no-restricted-imports
import { V3Route } from '@uniswap/smart-order-router' import { V3Route } from '@uniswap/smart-order-router'
import { FeeAmount, Pool } from '@uniswap/v3-sdk' import { FeeAmount, Pool } from '@uniswap/v3-sdk'
import { ZERO_PERCENT } from 'constants/misc'
import { nativeOnChain } from 'constants/tokens' import { nativeOnChain } from 'constants/tokens'
import { BigNumber } from 'ethers/lib/ethers' import { BigNumber } from 'ethers/lib/ethers'
import JSBI from 'jsbi' import JSBI from 'jsbi'
@ -49,8 +48,6 @@ export const TEST_TRADE_EXACT_INPUT = new ClassicTrade({
gasUseEstimateUSD: 1.0, gasUseEstimateUSD: 1.0,
approveInfo: { needsApprove: false }, approveInfo: { needsApprove: false },
quoteMethod: QuoteMethod.CLIENT_SIDE_FALLBACK, quoteMethod: QuoteMethod.CLIENT_SIDE_FALLBACK,
inputTax: ZERO_PERCENT,
outputTax: ZERO_PERCENT,
}) })
export const TEST_TRADE_EXACT_INPUT_API = new ClassicTrade({ export const TEST_TRADE_EXACT_INPUT_API = new ClassicTrade({
@ -66,8 +63,6 @@ export const TEST_TRADE_EXACT_INPUT_API = new ClassicTrade({
gasUseEstimateUSD: 1.0, gasUseEstimateUSD: 1.0,
approveInfo: { needsApprove: false }, approveInfo: { needsApprove: false },
quoteMethod: QuoteMethod.ROUTING_API, quoteMethod: QuoteMethod.ROUTING_API,
inputTax: ZERO_PERCENT,
outputTax: ZERO_PERCENT,
}) })
export const TEST_TRADE_EXACT_OUTPUT = new ClassicTrade({ export const TEST_TRADE_EXACT_OUTPUT = new ClassicTrade({
@ -82,8 +77,6 @@ export const TEST_TRADE_EXACT_OUTPUT = new ClassicTrade({
tradeType: TradeType.EXACT_OUTPUT, tradeType: TradeType.EXACT_OUTPUT,
quoteMethod: QuoteMethod.CLIENT_SIDE_FALLBACK, quoteMethod: QuoteMethod.CLIENT_SIDE_FALLBACK,
approveInfo: { needsApprove: false }, approveInfo: { needsApprove: false },
inputTax: ZERO_PERCENT,
outputTax: ZERO_PERCENT,
}) })
export const TEST_ALLOWED_SLIPPAGE = new Percent(2, 100) export const TEST_ALLOWED_SLIPPAGE = new Percent(2, 100)
@ -127,11 +120,29 @@ export const TEST_DUTCH_TRADE_ETH_INPUT = new DutchOrderTrade({
slippageTolerance: new Percent(5, 100), slippageTolerance: new Percent(5, 100),
}) })
const SELL_FEE_TOKEN = new Token(
1,
'0x0000000000000000000000000000000000000001',
18,
'ABC',
'Abc',
false,
undefined,
BigNumber.from(300)
)
const TEST_POOL_FOT_1 = new Pool(
SELL_FEE_TOKEN,
TEST_TOKEN_2,
FeeAmount.HIGH,
'2437312313659959819381354528',
'10272714736694327408',
-69633
)
export const TEST_TRADE_FEE_ON_SELL = new ClassicTrade({ export const TEST_TRADE_FEE_ON_SELL = new ClassicTrade({
v3Routes: [ v3Routes: [
{ {
routev3: new V3Route([TEST_POOL_12], TEST_TOKEN_1, TEST_TOKEN_2), routev3: new V3Route([TEST_POOL_FOT_1], SELL_FEE_TOKEN, TEST_TOKEN_2),
inputAmount: toCurrencyAmount(TEST_TOKEN_1, 1000), inputAmount: toCurrencyAmount(SELL_FEE_TOKEN, 1000),
outputAmount: toCurrencyAmount(TEST_TOKEN_2, 1000), outputAmount: toCurrencyAmount(TEST_TOKEN_2, 1000),
}, },
], ],
@ -140,16 +151,32 @@ export const TEST_TRADE_FEE_ON_SELL = new ClassicTrade({
gasUseEstimateUSD: 1.0, gasUseEstimateUSD: 1.0,
approveInfo: { needsApprove: false }, approveInfo: { needsApprove: false },
quoteMethod: QuoteMethod.ROUTING_API, quoteMethod: QuoteMethod.ROUTING_API,
inputTax: new Percent(3, 100),
outputTax: ZERO_PERCENT,
}) })
const BUY_FEE_TOKEN = new Token(
1,
'0x0000000000000000000000000000000000000002',
18,
'DEF',
'Def',
false,
BigNumber.from(300),
undefined
)
const TEST_POOL_FOT_2 = new Pool(
TEST_TOKEN_1,
BUY_FEE_TOKEN,
FeeAmount.HIGH,
'2437312313659959819381354528',
'10272714736694327408',
-69633
)
export const TEST_TRADE_FEE_ON_BUY = new ClassicTrade({ export const TEST_TRADE_FEE_ON_BUY = new ClassicTrade({
v3Routes: [ v3Routes: [
{ {
routev3: new V3Route([TEST_POOL_12], TEST_TOKEN_1, TEST_TOKEN_2), routev3: new V3Route([TEST_POOL_FOT_2], TEST_TOKEN_1, BUY_FEE_TOKEN),
inputAmount: toCurrencyAmount(TEST_TOKEN_1, 1000), inputAmount: toCurrencyAmount(TEST_TOKEN_1, 1000),
outputAmount: toCurrencyAmount(TEST_TOKEN_2, 1000), outputAmount: toCurrencyAmount(BUY_FEE_TOKEN, 1000),
}, },
], ],
v2Routes: [], v2Routes: [],
@ -157,14 +184,10 @@ export const TEST_TRADE_FEE_ON_BUY = new ClassicTrade({
gasUseEstimateUSD: 1.0, gasUseEstimateUSD: 1.0,
approveInfo: { needsApprove: false }, approveInfo: { needsApprove: false },
quoteMethod: QuoteMethod.ROUTING_API, quoteMethod: QuoteMethod.ROUTING_API,
inputTax: ZERO_PERCENT,
outputTax: new Percent(3, 100),
}) })
export const PREVIEW_EXACT_IN_TRADE = new PreviewTrade({ export const PREVIEW_EXACT_IN_TRADE = new PreviewTrade({
inputAmount: toCurrencyAmount(TEST_TOKEN_1, 1000), inputAmount: toCurrencyAmount(TEST_TOKEN_1, 1000),
outputAmount: toCurrencyAmount(TEST_TOKEN_2, 1000), outputAmount: toCurrencyAmount(TEST_TOKEN_2, 1000),
tradeType: TradeType.EXACT_INPUT, tradeType: TradeType.EXACT_INPUT,
inputTax: new Percent(0, 100),
outputTax: new Percent(0, 100),
}) })

@ -6112,18 +6112,18 @@
resolved "https://registry.yarnpkg.com/@uniswap/redux-multicall/-/redux-multicall-1.1.8.tgz#9cc5090305b10df68fb6162eb1ba7c2c762f5e7f" resolved "https://registry.yarnpkg.com/@uniswap/redux-multicall/-/redux-multicall-1.1.8.tgz#9cc5090305b10df68fb6162eb1ba7c2c762f5e7f"
integrity sha512-LttOBVJuoRNC6N4MHsb5dF2GszLsj1ddPKKccEw1XOX17bGrFdm2A6GwKgES+v+Hj3lluDbQL6atcQtymP21iw== integrity sha512-LttOBVJuoRNC6N4MHsb5dF2GszLsj1ddPKKccEw1XOX17bGrFdm2A6GwKgES+v+Hj3lluDbQL6atcQtymP21iw==
"@uniswap/router-sdk@^1.6.0": "@uniswap/router-sdk@^1.6.0", "@uniswap/router-sdk@^1.7.1":
version "1.6.0" version "1.7.1"
resolved "https://registry.yarnpkg.com/@uniswap/router-sdk/-/router-sdk-1.6.0.tgz#2f51dbba1b01467244b59500ed0da8aa84323f8c" resolved "https://registry.yarnpkg.com/@uniswap/router-sdk/-/router-sdk-1.7.1.tgz#642d5804299cd50b1a3ba2fa0a87963320fb7f93"
integrity sha512-onpAzcvEnrsm8tUtu49IrR9EP3n9j0IDpGc0Ee3FDDkVgXrp9cIrAADC+yb56vgLtJFnshbhyIdjXLMIzWe0Gw== integrity sha512-uBN9QX3t5lPLkxlkPoQPZpd0eN+GA0Ab9nq1pcPk/XDFuRnRxxVF629Ecz2SfTVm0gooOPO3aU3ETgyB3vuhYA==
dependencies: dependencies:
"@ethersproject/abi" "^5.5.0" "@ethersproject/abi" "^5.5.0"
"@uniswap/sdk-core" "^4" "@uniswap/sdk-core" "^4.0.7"
"@uniswap/swap-router-contracts" "1.1.0" "@uniswap/swap-router-contracts" "^1.1.0"
"@uniswap/v2-sdk" "^3.2.0" "@uniswap/v2-sdk" "^3.2.0"
"@uniswap/v3-sdk" "^3.10.0" "@uniswap/v3-sdk" "^3.10.0"
"@uniswap/sdk-core@4.0.7", "@uniswap/sdk-core@>= 3", "@uniswap/sdk-core@^4", "@uniswap/sdk-core@^4.0.0", "@uniswap/sdk-core@^4.0.2", "@uniswap/sdk-core@^4.0.3", "@uniswap/sdk-core@^4.0.6": "@uniswap/sdk-core@4.0.7", "@uniswap/sdk-core@>= 3", "@uniswap/sdk-core@^4", "@uniswap/sdk-core@^4.0.0", "@uniswap/sdk-core@^4.0.2", "@uniswap/sdk-core@^4.0.3", "@uniswap/sdk-core@^4.0.6", "@uniswap/sdk-core@^4.0.7":
version "4.0.7" version "4.0.7"
resolved "https://registry.npmjs.org/@uniswap/sdk-core/-/sdk-core-4.0.7.tgz#90dfd070d7e44494234618af398da158363ae827" resolved "https://registry.npmjs.org/@uniswap/sdk-core/-/sdk-core-4.0.7.tgz#90dfd070d7e44494234618af398da158363ae827"
integrity sha512-jscx7KUIWzQatcL5PHY6xy0gEL9IGQcL5h/obxzX9foP2KoNk9cq66Ia8I2Kvpa7zBcPOeW1hU0hJNBq6CzcIQ== integrity sha512-jscx7KUIWzQatcL5PHY6xy0gEL9IGQcL5h/obxzX9foP2KoNk9cq66Ia8I2Kvpa7zBcPOeW1hU0hJNBq6CzcIQ==
@ -6163,26 +6163,15 @@
node-cache "^5.1.2" node-cache "^5.1.2"
stats-lite "^2.2.0" stats-lite "^2.2.0"
"@uniswap/swap-router-contracts@1.1.0": "@uniswap/swap-router-contracts@^1.1.0", "@uniswap/swap-router-contracts@^1.2.1", "@uniswap/swap-router-contracts@^1.3.0":
version "1.1.0" version "1.3.1"
resolved "https://registry.yarnpkg.com/@uniswap/swap-router-contracts/-/swap-router-contracts-1.1.0.tgz#e027b14d4c172f231c53c48e1fd708a78d7d94d8" resolved "https://registry.yarnpkg.com/@uniswap/swap-router-contracts/-/swap-router-contracts-1.3.1.tgz#0ebbb93eb578625618ed9489872de381f9c66fb4"
integrity sha512-GPmpx1lvjXWloB95+YUabr3UHJYr3scnSS8EzaNXnNrIz9nYZ+XQcMaJxOKe85Yi7IfcUQpj0HzD2TW99dtolA== integrity sha512-mh/YNbwKb7Mut96VuEtL+Z5bRe0xVIbjjiryn+iMMrK2sFKhR4duk/86mEz0UO5gSx4pQIw9G5276P5heY/7Rg==
dependencies:
"@openzeppelin/contracts" "3.4.1-solc-0.7-2"
"@uniswap/v2-core" "1.0.1"
"@uniswap/v3-core" "1.0.0"
"@uniswap/v3-periphery" "1.3.0"
hardhat-watcher "^2.1.1"
"@uniswap/swap-router-contracts@^1.2.1", "@uniswap/swap-router-contracts@^1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@uniswap/swap-router-contracts/-/swap-router-contracts-1.3.0.tgz#8d555ca6d74b888d6e02a26ebb806ce315605f1f"
integrity sha512-iKvCuRkHXEe0EMjOf8HFUISTIhlxI57kKFllf3C3PUIE0HmwxrayyoflwAz5u/TRsFGYqJ9IjX2UgzLCsrNa5A==
dependencies: dependencies:
"@openzeppelin/contracts" "3.4.2-solc-0.7" "@openzeppelin/contracts" "3.4.2-solc-0.7"
"@uniswap/v2-core" "1.0.1" "@uniswap/v2-core" "^1.0.1"
"@uniswap/v3-core" "1.0.0" "@uniswap/v3-core" "^1.0.0"
"@uniswap/v3-periphery" "1.4.1" "@uniswap/v3-periphery" "^1.4.4"
dotenv "^14.2.0" dotenv "^14.2.0"
hardhat-watcher "^2.1.1" hardhat-watcher "^2.1.1"
@ -6259,34 +6248,21 @@
resolved "https://registry.npmjs.org/@uniswap/v3-core/-/v3-core-1.0.0.tgz" resolved "https://registry.npmjs.org/@uniswap/v3-core/-/v3-core-1.0.0.tgz"
integrity sha512-kSC4djMGKMHj7sLMYVnn61k9nu+lHjMIxgg9CDQT+s2QYLoA56GbSK9Oxr+qJXzzygbkrmuY6cwgP6cW2JXPFA== integrity sha512-kSC4djMGKMHj7sLMYVnn61k9nu+lHjMIxgg9CDQT+s2QYLoA56GbSK9Oxr+qJXzzygbkrmuY6cwgP6cW2JXPFA==
"@uniswap/v3-core@^1.0.1": "@uniswap/v3-core@^1.0.0", "@uniswap/v3-core@^1.0.1":
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/@uniswap/v3-core/-/v3-core-1.0.1.tgz#b6d2bdc6ba3c3fbd610bdc502395d86cd35264a0" resolved "https://registry.yarnpkg.com/@uniswap/v3-core/-/v3-core-1.0.1.tgz#b6d2bdc6ba3c3fbd610bdc502395d86cd35264a0"
integrity sha512-7pVk4hEm00j9tc71Y9+ssYpO6ytkeI0y7WE9P6UcmNzhxPePwyAxImuhVsTqWK9YFvzgtvzJHi64pBl4jUzKMQ== integrity sha512-7pVk4hEm00j9tc71Y9+ssYpO6ytkeI0y7WE9P6UcmNzhxPePwyAxImuhVsTqWK9YFvzgtvzJHi64pBl4jUzKMQ==
"@uniswap/v3-periphery@1.3.0": "@uniswap/v3-periphery@^1.0.1", "@uniswap/v3-periphery@^1.1.1", "@uniswap/v3-periphery@^1.4.4":
version "1.3.0" version "1.4.4"
resolved "https://registry.yarnpkg.com/@uniswap/v3-periphery/-/v3-periphery-1.3.0.tgz#37f0a1ef6025221722e50e9f3f2009c2d5d6e4ec" resolved "https://registry.yarnpkg.com/@uniswap/v3-periphery/-/v3-periphery-1.4.4.tgz#d2756c23b69718173c5874f37fd4ad57d2f021b7"
integrity sha512-HjHdI5RkjBl8zz3bqHShrbULFoZSrjbbrRHoO2vbzn+WRzTa6xY4PWphZv2Tlcb38YEKfKHp6NPl5hVedac8uw== integrity sha512-S4+m+wh8HbWSO3DKk4LwUCPZJTpCugIsHrWR86m/OrUyvSqGDTXKFfc2sMuGXCZrD1ZqO3rhQsKgdWg3Hbb2Kw==
dependencies:
"@openzeppelin/contracts" "3.4.1-solc-0.7-2"
"@uniswap/lib" "^4.0.1-alpha"
"@uniswap/v2-core" "1.0.1"
"@uniswap/v3-core" "1.0.0"
base64-sol "1.0.1"
hardhat-watcher "^2.1.1"
"@uniswap/v3-periphery@1.4.1", "@uniswap/v3-periphery@^1.0.1", "@uniswap/v3-periphery@^1.1.1":
version "1.4.1"
resolved "https://registry.yarnpkg.com/@uniswap/v3-periphery/-/v3-periphery-1.4.1.tgz#b90f08b7386163c0abfd7258831caef6339c7862"
integrity sha512-Ab0ZCKOQrQMKIcpBTezTsEhWfQjItd0TtkCG8mPhoQu+wC67nPaf4hYUhM6wGHeFUmDiYY5MpEQuokB0ENvoTg==
dependencies: dependencies:
"@openzeppelin/contracts" "3.4.2-solc-0.7" "@openzeppelin/contracts" "3.4.2-solc-0.7"
"@uniswap/lib" "^4.0.1-alpha" "@uniswap/lib" "^4.0.1-alpha"
"@uniswap/v2-core" "1.0.1" "@uniswap/v2-core" "^1.0.1"
"@uniswap/v3-core" "1.0.0" "@uniswap/v3-core" "^1.0.0"
base64-sol "1.0.1" base64-sol "1.0.1"
hardhat-watcher "^2.1.1"
"@uniswap/v3-sdk@^3.10.0": "@uniswap/v3-sdk@^3.10.0":
version "3.10.0" version "3.10.0"