Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e8d3e8d2f | ||
|
|
d674f8500f | ||
|
|
686f24b98e | ||
|
|
40d8da42a5 | ||
|
|
2151ffbac7 |
1
CODEOWNERS
Normal file
1
CODEOWNERS
Normal file
@ -0,0 +1 @@
|
|||||||
|
@uniswap/web-admins
|
||||||
@ -90,7 +90,7 @@ const DescriptionText = styled(ThemedText.LabelMicro)`
|
|||||||
|
|
||||||
function useOrderAmounts(
|
function useOrderAmounts(
|
||||||
orderDetails?: UniswapXOrderDetails
|
orderDetails?: UniswapXOrderDetails
|
||||||
): Pick<InterfaceTrade, 'inputAmount' | 'outputAmount'> | undefined {
|
): Pick<InterfaceTrade, 'inputAmount' | 'postTaxOutputAmount'> | 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),
|
||||||
outputAmount: CurrencyAmount.fromRawAmount(
|
postTaxOutputAmount: 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),
|
||||||
outputAmount: CurrencyAmount.fromRawAmount(outputCurrency, swapInfo.outputCurrencyAmountRaw),
|
postTaxOutputAmount: CurrencyAmount.fromRawAmount(outputCurrency, swapInfo.outputCurrencyAmountRaw),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { BaseVariant, FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags'
|
import { BaseVariant, FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags'
|
||||||
import { useCurrencyConversionFlag } from 'featureFlags/flags/currencyConversion'
|
import { useCurrencyConversionFlag } from 'featureFlags/flags/currencyConversion'
|
||||||
import { useForceUniswapXOnFlag } from 'featureFlags/flags/forceUniswapXOn'
|
import { useForceUniswapXOnFlag } from 'featureFlags/flags/forceUniswapXOn'
|
||||||
|
import { useFotAdjustmentsFlag } from 'featureFlags/flags/fotAdjustments'
|
||||||
import { useMultichainUXFlag } from 'featureFlags/flags/multichainUx'
|
import { useMultichainUXFlag } from 'featureFlags/flags/multichainUx'
|
||||||
import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc'
|
import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc'
|
||||||
import { UniswapXVariant, useUniswapXFlag } from 'featureFlags/flags/uniswapx'
|
import { UniswapXVariant, useUniswapXFlag } from 'featureFlags/flags/uniswapx'
|
||||||
@ -242,6 +243,12 @@ 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"
|
||||||
|
/>
|
||||||
<FeatureFlagGroup name="Debug">
|
<FeatureFlagGroup name="Debug">
|
||||||
<FeatureFlagOption
|
<FeatureFlagOption
|
||||||
variant={TraceJsonRpcVariant}
|
variant={TraceJsonRpcVariant}
|
||||||
|
|||||||
@ -5,8 +5,9 @@ import { useWeb3React } from '@web3-react/core'
|
|||||||
import { sendAnalyticsEvent } from 'analytics'
|
import { sendAnalyticsEvent } from 'analytics'
|
||||||
import { LoadingRows } from 'components/Loader/styled'
|
import { LoadingRows } from 'components/Loader/styled'
|
||||||
import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from 'constants/chains'
|
import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from 'constants/chains'
|
||||||
|
import { ZERO_PERCENT } from 'constants/misc'
|
||||||
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
|
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
|
||||||
import { InterfaceTrade } from 'state/routing/types'
|
import { ClassicTrade, InterfaceTrade } from 'state/routing/types'
|
||||||
import { getTransactionCount, isClassicTrade } from 'state/routing/utils'
|
import { getTransactionCount, isClassicTrade } from 'state/routing/utils'
|
||||||
import { formatCurrencyAmount, formatNumber, formatPriceImpact, NumberType } from 'utils/formatNumbers'
|
import { formatCurrencyAmount, formatNumber, formatPriceImpact, NumberType } from 'utils/formatNumbers'
|
||||||
|
|
||||||
@ -82,16 +83,20 @@ export function AdvancedSwapDetails({ trade, allowedSlippage, syncing = false }:
|
|||||||
</RowBetween>
|
</RowBetween>
|
||||||
)}
|
)}
|
||||||
{isClassicTrade(trade) && (
|
{isClassicTrade(trade) && (
|
||||||
<RowBetween>
|
<>
|
||||||
<MouseoverTooltip text={<Trans>The impact your trade has on the market price of this pool.</Trans>}>
|
<TokenTaxLineItem trade={trade} type="input" />
|
||||||
<ThemedText.BodySmall color="textSecondary">
|
<TokenTaxLineItem trade={trade} type="output" />
|
||||||
<Trans>Price Impact</Trans>
|
<RowBetween>
|
||||||
</ThemedText.BodySmall>
|
<MouseoverTooltip text={<Trans>The impact your trade has on the market price of this pool.</Trans>}>
|
||||||
</MouseoverTooltip>
|
<ThemedText.BodySmall color="textSecondary">
|
||||||
<TextWithLoadingPlaceholder syncing={syncing} width={50}>
|
<Trans>Price Impact</Trans>
|
||||||
<ThemedText.BodySmall>{formatPriceImpact(trade.priceImpact)}</ThemedText.BodySmall>
|
</ThemedText.BodySmall>
|
||||||
</TextWithLoadingPlaceholder>
|
</MouseoverTooltip>
|
||||||
</RowBetween>
|
<TextWithLoadingPlaceholder syncing={syncing} width={50}>
|
||||||
|
<ThemedText.BodySmall>{formatPriceImpact(trade.priceImpact)}</ThemedText.BodySmall>
|
||||||
|
</TextWithLoadingPlaceholder>
|
||||||
|
</RowBetween>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<RowBetween>
|
<RowBetween>
|
||||||
<RowFixed>
|
<RowFixed>
|
||||||
@ -135,7 +140,7 @@ export function AdvancedSwapDetails({ trade, allowedSlippage, syncing = false }:
|
|||||||
</RowFixed>
|
</RowFixed>
|
||||||
<TextWithLoadingPlaceholder syncing={syncing} width={65}>
|
<TextWithLoadingPlaceholder syncing={syncing} width={65}>
|
||||||
<ThemedText.BodySmall>
|
<ThemedText.BodySmall>
|
||||||
{`${formatCurrencyAmount(trade.outputAmount, NumberType.SwapTradeAmount)} ${
|
{`${formatCurrencyAmount(trade.postTaxOutputAmount, NumberType.SwapTradeAmount)} ${
|
||||||
trade.outputAmount.currency.symbol
|
trade.outputAmount.currency.symbol
|
||||||
}`}
|
}`}
|
||||||
</ThemedText.BodySmall>
|
</ThemedText.BodySmall>
|
||||||
@ -176,3 +181,26 @@ export function AdvancedSwapDetails({ trade, allowedSlippage, syncing = false }:
|
|||||||
</Column>
|
</Column>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function TokenTaxLineItem({ trade, type }: { trade: ClassicTrade; type: 'input' | 'output' }) {
|
||||||
|
const [currency, percentage] =
|
||||||
|
type === 'input' ? [trade.inputAmount.currency, trade.inputTax] : [trade.outputAmount.currency, trade.outputTax]
|
||||||
|
|
||||||
|
if (percentage.equalTo(ZERO_PERCENT)) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RowBetween>
|
||||||
|
<MouseoverTooltip
|
||||||
|
text={
|
||||||
|
<Trans>
|
||||||
|
Some tokens take a fee when they are bought or sold, which is set by the token issuer. Uniswap does not
|
||||||
|
receive any of these fees.
|
||||||
|
</Trans>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ThemedText.BodySmall color="textSecondary">{`${currency.symbol} fee`}</ThemedText.BodySmall>
|
||||||
|
</MouseoverTooltip>
|
||||||
|
<ThemedText.BodySmall>{formatPriceImpact(percentage)}</ThemedText.BodySmall>
|
||||||
|
</RowBetween>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@ -303,6 +303,7 @@ export default function ConfirmSwapModal({
|
|||||||
// Swap failed locally and was not broadcast to the blockchain.
|
// Swap failed locally and was not broadcast to the blockchain.
|
||||||
const localSwapFailure = Boolean(swapError) && !didUserReject(swapError)
|
const localSwapFailure = Boolean(swapError) && !didUserReject(swapError)
|
||||||
const swapFailed = localSwapFailure || swapReverted
|
const swapFailed = localSwapFailure || swapReverted
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Reset the modal state if the user rejected the swap.
|
// Reset the modal state if the user rejected the swap.
|
||||||
if (swapError && !swapFailed) {
|
if (swapError && !swapFailed) {
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { useTheme } from 'styled-components'
|
|||||||
import { ThemedText } from 'theme'
|
import { ThemedText } from 'theme'
|
||||||
import { formatReviewSwapCurrencyAmount } from 'utils/formatNumbers'
|
import { formatReviewSwapCurrencyAmount } from 'utils/formatNumbers'
|
||||||
|
|
||||||
export function TradeSummary({ trade }: { trade: Pick<InterfaceTrade, 'inputAmount' | 'outputAmount'> }) {
|
export function TradeSummary({ trade }: { trade: Pick<InterfaceTrade, 'inputAmount' | 'postTaxOutputAmount'> }) {
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
return (
|
return (
|
||||||
<Row gap="sm" justify="center" align="center">
|
<Row gap="sm" justify="center" align="center">
|
||||||
@ -15,9 +15,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.textPrimary} size="12px" />
|
<ArrowRight color={theme.textPrimary} size="12px" />
|
||||||
<CurrencyLogo currency={trade.outputAmount.currency} size="16px" />
|
<CurrencyLogo currency={trade.postTaxOutputAmount.currency} size="16px" />
|
||||||
<ThemedText.LabelSmall color="textPrimary">
|
<ThemedText.LabelSmall color="textPrimary">
|
||||||
{formatReviewSwapCurrencyAmount(trade.outputAmount)} {trade.outputAmount.currency.symbol}
|
{formatReviewSwapCurrencyAmount(trade.postTaxOutputAmount)} {trade.postTaxOutputAmount.currency.symbol}
|
||||||
</ThemedText.LabelSmall>
|
</ThemedText.LabelSmall>
|
||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
import userEvent from '@testing-library/user-event'
|
import userEvent from '@testing-library/user-event'
|
||||||
import { TEST_ALLOWED_SLIPPAGE, TEST_TRADE_EXACT_INPUT } from 'test-utils/constants'
|
import {
|
||||||
|
TEST_ALLOWED_SLIPPAGE,
|
||||||
|
TEST_TOKEN_1,
|
||||||
|
TEST_TOKEN_2,
|
||||||
|
TEST_TRADE_EXACT_INPUT,
|
||||||
|
TEST_TRADE_FEE_ON_BUY,
|
||||||
|
TEST_TRADE_FEE_ON_SELL,
|
||||||
|
} from 'test-utils/constants'
|
||||||
import { act, render, screen } from 'test-utils/render'
|
import { act, render, screen } from 'test-utils/render'
|
||||||
|
|
||||||
import SwapDetailsDropdown from './SwapDetailsDropdown'
|
import SwapDetailsDropdown from './SwapDetailsDropdown'
|
||||||
@ -39,4 +46,42 @@ describe('SwapDetailsDropdown.tsx', () => {
|
|||||||
await act(() => userEvent.click(screen.getByTestId('swap-details-header-row')))
|
await act(() => userEvent.click(screen.getByTestId('swap-details-header-row')))
|
||||||
expect(screen.getByTestId('advanced-swap-details')).toBeInTheDocument()
|
expect(screen.getByTestId('advanced-swap-details')).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('renders fee on input transfer information', async () => {
|
||||||
|
render(
|
||||||
|
<SwapDetailsDropdown
|
||||||
|
trade={TEST_TRADE_FEE_ON_SELL}
|
||||||
|
syncing={true}
|
||||||
|
loading={true}
|
||||||
|
allowedSlippage={TEST_ALLOWED_SLIPPAGE}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
await act(() => userEvent.click(screen.getByTestId('swap-details-header-row')))
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.getByText(
|
||||||
|
'Some tokens take a fee when they are bought or sold, which is set by the token issuer. Uniswap does not receive any of these fees.'
|
||||||
|
)
|
||||||
|
).toBeInTheDocument()
|
||||||
|
expect(screen.getByText(`${TEST_TOKEN_1.symbol} fee`)).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders fee on ouput transfer information', async () => {
|
||||||
|
render(
|
||||||
|
<SwapDetailsDropdown
|
||||||
|
trade={TEST_TRADE_FEE_ON_BUY}
|
||||||
|
syncing={true}
|
||||||
|
loading={true}
|
||||||
|
allowedSlippage={TEST_ALLOWED_SLIPPAGE}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
await act(() => userEvent.click(screen.getByTestId('swap-details-header-row')))
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.getByText(
|
||||||
|
'Some tokens take a fee when they are bought or sold, which is set by the token issuer. Uniswap does not receive any of these fees.'
|
||||||
|
)
|
||||||
|
).toBeInTheDocument()
|
||||||
|
expect(screen.getByText(`${TEST_TOKEN_2.symbol} fee`)).toBeInTheDocument()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,4 +1,12 @@
|
|||||||
import { TEST_ALLOWED_SLIPPAGE, TEST_TRADE_EXACT_INPUT, TEST_TRADE_EXACT_OUTPUT } from 'test-utils/constants'
|
import {
|
||||||
|
TEST_ALLOWED_SLIPPAGE,
|
||||||
|
TEST_TOKEN_1,
|
||||||
|
TEST_TOKEN_2,
|
||||||
|
TEST_TRADE_EXACT_INPUT,
|
||||||
|
TEST_TRADE_EXACT_OUTPUT,
|
||||||
|
TEST_TRADE_FEE_ON_BUY,
|
||||||
|
TEST_TRADE_FEE_ON_SELL,
|
||||||
|
} from 'test-utils/constants'
|
||||||
import { render, screen, within } from 'test-utils/render'
|
import { render, screen, within } from 'test-utils/render'
|
||||||
|
|
||||||
import SwapModalFooter from './SwapModalFooter'
|
import SwapModalFooter from './SwapModalFooter'
|
||||||
@ -97,4 +105,62 @@ describe('SwapModalFooter.tsx', () => {
|
|||||||
).toBeInTheDocument()
|
).toBeInTheDocument()
|
||||||
expect(screen.getByText('The impact your trade has on the market price of this pool.')).toBeInTheDocument()
|
expect(screen.getByText('The impact your trade has on the market price of this pool.')).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('test trade fee on input token transfer', () => {
|
||||||
|
render(
|
||||||
|
<SwapModalFooter
|
||||||
|
trade={TEST_TRADE_FEE_ON_SELL}
|
||||||
|
allowedSlippage={TEST_ALLOWED_SLIPPAGE}
|
||||||
|
swapResult={undefined}
|
||||||
|
onConfirm={jest.fn()}
|
||||||
|
swapErrorMessage={undefined}
|
||||||
|
disabledConfirm={false}
|
||||||
|
fiatValueInput={{
|
||||||
|
data: undefined,
|
||||||
|
isLoading: false,
|
||||||
|
}}
|
||||||
|
fiatValueOutput={{
|
||||||
|
data: undefined,
|
||||||
|
isLoading: false,
|
||||||
|
}}
|
||||||
|
showAcceptChanges={true}
|
||||||
|
onAcceptChanges={jest.fn()}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
expect(
|
||||||
|
screen.getByText(
|
||||||
|
'Some tokens take a fee when they are bought or sold, which is set by the token issuer. Uniswap does not receive any of these fees.'
|
||||||
|
)
|
||||||
|
).toBeInTheDocument()
|
||||||
|
expect(screen.getByText(`${TEST_TOKEN_1.symbol} fee`)).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('test trade fee on output token transfer', () => {
|
||||||
|
render(
|
||||||
|
<SwapModalFooter
|
||||||
|
trade={TEST_TRADE_FEE_ON_BUY}
|
||||||
|
allowedSlippage={TEST_ALLOWED_SLIPPAGE}
|
||||||
|
swapResult={undefined}
|
||||||
|
onConfirm={jest.fn()}
|
||||||
|
swapErrorMessage={undefined}
|
||||||
|
disabledConfirm={false}
|
||||||
|
fiatValueInput={{
|
||||||
|
data: undefined,
|
||||||
|
isLoading: false,
|
||||||
|
}}
|
||||||
|
fiatValueOutput={{
|
||||||
|
data: undefined,
|
||||||
|
isLoading: false,
|
||||||
|
}}
|
||||||
|
showAcceptChanges={true}
|
||||||
|
onAcceptChanges={jest.fn()}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
expect(
|
||||||
|
screen.getByText(
|
||||||
|
'Some tokens take a fee when they are bought or sold, which is set by the token issuer. Uniswap does not receive any of these fees.'
|
||||||
|
)
|
||||||
|
).toBeInTheDocument()
|
||||||
|
expect(screen.getByText(`${TEST_TOKEN_2.symbol} fee`)).toBeInTheDocument()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,25 +1,26 @@
|
|||||||
import { Plural, Trans } from '@lingui/macro'
|
import { Plural, t, Trans } from '@lingui/macro'
|
||||||
import { BrowserEvent, InterfaceElementName, SwapEventName } from '@uniswap/analytics-events'
|
import { BrowserEvent, InterfaceElementName, SwapEventName } from '@uniswap/analytics-events'
|
||||||
import { Percent, TradeType } from '@uniswap/sdk-core'
|
import { Percent, TradeType } from '@uniswap/sdk-core'
|
||||||
import { useWeb3React } from '@web3-react/core'
|
import { useWeb3React } from '@web3-react/core'
|
||||||
import { TraceEvent } from 'analytics'
|
import { TraceEvent } from 'analytics'
|
||||||
import Column from 'components/Column'
|
import Column from 'components/Column'
|
||||||
import { MouseoverTooltip, TooltipSize } from 'components/Tooltip'
|
import { MouseoverTooltip, TooltipSize } from 'components/Tooltip'
|
||||||
|
import { ZERO_PERCENT } from 'constants/misc'
|
||||||
import { SwapResult } from 'hooks/useSwapCallback'
|
import { SwapResult } from 'hooks/useSwapCallback'
|
||||||
import useTransactionDeadline from 'hooks/useTransactionDeadline'
|
import useTransactionDeadline from 'hooks/useTransactionDeadline'
|
||||||
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
|
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
|
||||||
import { ReactNode } from 'react'
|
import { ReactNode } from 'react'
|
||||||
import { AlertTriangle } from 'react-feather'
|
import { AlertTriangle } from 'react-feather'
|
||||||
import { InterfaceTrade, RouterPreference } from 'state/routing/types'
|
import { ClassicTrade, InterfaceTrade, RouterPreference } from 'state/routing/types'
|
||||||
import { getTransactionCount, isClassicTrade } from 'state/routing/utils'
|
import { getTransactionCount, isClassicTrade } from 'state/routing/utils'
|
||||||
import { useRouterPreference, useUserSlippageTolerance } from 'state/user/hooks'
|
import { useRouterPreference, useUserSlippageTolerance } from 'state/user/hooks'
|
||||||
import styled, { useTheme } from 'styled-components'
|
import styled, { DefaultTheme, useTheme } from 'styled-components'
|
||||||
import { ThemedText } from 'theme'
|
import { ThemedText } from 'theme'
|
||||||
import { formatNumber, formatPriceImpact, NumberType } from 'utils/formatNumbers'
|
import { formatNumber, formatPriceImpact, NumberType } from 'utils/formatNumbers'
|
||||||
import { formatTransactionAmount, priceToPreciseFloat } from 'utils/formatNumbers'
|
import { formatTransactionAmount, priceToPreciseFloat } from 'utils/formatNumbers'
|
||||||
import getRoutingDiagramEntries from 'utils/getRoutingDiagramEntries'
|
import getRoutingDiagramEntries from 'utils/getRoutingDiagramEntries'
|
||||||
import { formatSwapButtonClickEventProperties } from 'utils/loggingFormatters'
|
import { formatSwapButtonClickEventProperties } from 'utils/loggingFormatters'
|
||||||
import { getPriceImpactWarning } from 'utils/prices'
|
import { getPriceImpactColor } from 'utils/prices'
|
||||||
|
|
||||||
import { ButtonError, SmallButtonPrimary } from '../Button'
|
import { ButtonError, SmallButtonPrimary } from '../Button'
|
||||||
import Row, { AutoRow, RowBetween, RowFixed } from '../Row'
|
import Row, { AutoRow, RowBetween, RowFixed } from '../Row'
|
||||||
@ -41,9 +42,10 @@ const ConfirmButton = styled(ButtonError)`
|
|||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
`
|
`
|
||||||
|
|
||||||
const DetailRowValue = styled(ThemedText.BodySmall)`
|
const DetailRowValue = styled(ThemedText.BodySmall)<{ warningColor?: keyof DefaultTheme }>`
|
||||||
text-align: right;
|
text-align: right;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
|
${({ warningColor, theme }) => warningColor && `color: ${theme[warningColor]};`};
|
||||||
`
|
`
|
||||||
|
|
||||||
export default function SwapModalFooter({
|
export default function SwapModalFooter({
|
||||||
@ -112,18 +114,22 @@ export default function SwapModalFooter({
|
|||||||
</Row>
|
</Row>
|
||||||
</ThemedText.BodySmall>
|
</ThemedText.BodySmall>
|
||||||
{isClassicTrade(trade) && (
|
{isClassicTrade(trade) && (
|
||||||
<ThemedText.BodySmall>
|
<>
|
||||||
<Row align="flex-start" justify="space-between" gap="sm">
|
<TokenTaxLineItem trade={trade} type="input" />
|
||||||
<MouseoverTooltip text={<Trans>The impact your trade has on the market price of this pool.</Trans>}>
|
<TokenTaxLineItem trade={trade} type="output" />
|
||||||
<Label cursor="help">
|
<ThemedText.BodySmall>
|
||||||
<Trans>Price impact</Trans>
|
<Row align="flex-start" justify="space-between" gap="sm">
|
||||||
</Label>
|
<MouseoverTooltip text={<Trans>The impact your trade has on the market price of this pool.</Trans>}>
|
||||||
</MouseoverTooltip>
|
<Label cursor="help">
|
||||||
<DetailRowValue color={getPriceImpactWarning(trade.priceImpact)}>
|
<Trans>Price impact</Trans>
|
||||||
{trade.priceImpact ? formatPriceImpact(trade.priceImpact) : '-'}
|
</Label>
|
||||||
</DetailRowValue>
|
</MouseoverTooltip>
|
||||||
</Row>
|
<DetailRowValue warningColor={getPriceImpactColor(trade.priceImpact)}>
|
||||||
</ThemedText.BodySmall>
|
{trade.priceImpact ? formatPriceImpact(trade.priceImpact) : '-'}
|
||||||
|
</DetailRowValue>
|
||||||
|
</Row>
|
||||||
|
</ThemedText.BodySmall>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<ThemedText.BodySmall>
|
<ThemedText.BodySmall>
|
||||||
<Row align="flex-start" justify="space-between" gap="sm">
|
<Row align="flex-start" justify="space-between" gap="sm">
|
||||||
@ -209,3 +215,28 @@ export default function SwapModalFooter({
|
|||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function TokenTaxLineItem({ trade, type }: { trade: ClassicTrade; type: 'input' | 'output' }) {
|
||||||
|
const [currency, percentage] =
|
||||||
|
type === 'input' ? [trade.inputAmount.currency, trade.inputTax] : [trade.outputAmount.currency, trade.outputTax]
|
||||||
|
|
||||||
|
if (percentage.equalTo(ZERO_PERCENT)) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemedText.BodySmall>
|
||||||
|
<Row align="flex-start" justify="space-between" gap="sm">
|
||||||
|
<MouseoverTooltip
|
||||||
|
text={
|
||||||
|
<Trans>
|
||||||
|
Some tokens take a fee when they are bought or sold, which is set by the token issuer. Uniswap does not
|
||||||
|
receive any of these fees.
|
||||||
|
</Trans>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Label cursor="help">{t`${currency.symbol} fee`}</Label>
|
||||||
|
</MouseoverTooltip>
|
||||||
|
<DetailRowValue warningColor={getPriceImpactColor(percentage)}>{formatPriceImpact(percentage)}</DetailRowValue>
|
||||||
|
</Row>
|
||||||
|
</ThemedText.BodySmall>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@ export default function SwapModalHeader({
|
|||||||
allowedSlippage: Percent
|
allowedSlippage: Percent
|
||||||
}) {
|
}) {
|
||||||
const fiatValueInput = useUSDPrice(trade.inputAmount)
|
const fiatValueInput = useUSDPrice(trade.inputAmount)
|
||||||
const fiatValueOutput = useUSDPrice(trade.outputAmount)
|
const fiatValueOutput = useUSDPrice(trade.postTaxOutputAmount)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HeaderContainer gap="sm">
|
<HeaderContainer gap="sm">
|
||||||
@ -42,7 +42,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.outputAmount}
|
amount={trade.postTaxOutputAmount}
|
||||||
currency={trade.outputAmount.currency}
|
currency={trade.outputAmount.currency}
|
||||||
usdAmount={fiatValueOutput.data}
|
usdAmount={fiatValueOutput.data}
|
||||||
tooltipText={
|
tooltipText={
|
||||||
|
|||||||
@ -141,7 +141,7 @@ exports[`SwapModalFooter.tsx matches base snapshot, test trade exact input 1`] =
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="c6 css-zhpkf8"
|
class="c2 c6 css-zhpkf8"
|
||||||
>
|
>
|
||||||
105566.373%
|
105566.373%
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
75
src/constants/tax.ts
Normal file
75
src/constants/tax.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@ -24,12 +24,12 @@ class TokenSafetyLookupTable {
|
|||||||
this.initialized = true
|
this.initialized = true
|
||||||
|
|
||||||
// Initialize extended tokens first
|
// Initialize extended tokens first
|
||||||
lists.byUrl[UNI_EXTENDED_LIST].current?.tokens.forEach((token) => {
|
lists.byUrl[UNI_EXTENDED_LIST]?.current?.tokens.forEach((token) => {
|
||||||
this.dict[token.address.toLowerCase()] = TOKEN_LIST_TYPES.UNI_EXTENDED
|
this.dict[token.address.toLowerCase()] = TOKEN_LIST_TYPES.UNI_EXTENDED
|
||||||
})
|
})
|
||||||
|
|
||||||
// Initialize default tokens second, so that any tokens on both default and extended will display as default (no warning)
|
// Initialize default tokens second, so that any tokens on both default and extended will display as default (no warning)
|
||||||
lists.byUrl[UNI_LIST].current?.tokens.forEach((token) => {
|
lists.byUrl[UNI_LIST]?.current?.tokens.forEach((token) => {
|
||||||
this.dict[token.address.toLowerCase()] = TOKEN_LIST_TYPES.UNI_DEFAULT
|
this.dict[token.address.toLowerCase()] = TOKEN_LIST_TYPES.UNI_DEFAULT
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ class TokenSafetyLookupTable {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Initialize blocked tokens from all urls included
|
// Initialize blocked tokens from all urls included
|
||||||
UNSUPPORTED_LIST_URLS.map((url) => lists.byUrl[url].current?.tokens)
|
UNSUPPORTED_LIST_URLS.map((url) => lists.byUrl[url]?.current?.tokens)
|
||||||
.filter((x): x is TokenInfo[] => !!x)
|
.filter((x): x is TokenInfo[] => !!x)
|
||||||
.flat(1)
|
.flat(1)
|
||||||
.forEach((token) => {
|
.forEach((token) => {
|
||||||
|
|||||||
9
src/featureFlags/flags/fotAdjustments.ts
Normal file
9
src/featureFlags/flags/fotAdjustments.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { BaseVariant, FeatureFlag, useBaseFlag } from '../index'
|
||||||
|
|
||||||
|
export function useFotAdjustmentsFlag(): BaseVariant {
|
||||||
|
return useBaseFlag(FeatureFlag.fotAdjustedmentsEnabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useFotAdjustmentsEnabled(): boolean {
|
||||||
|
return useFotAdjustmentsFlag() === BaseVariant.Enabled
|
||||||
|
}
|
||||||
@ -16,6 +16,7 @@ export enum FeatureFlag {
|
|||||||
uniswapXEthOutputEnabled = 'uniswapx_eth_output_enabled',
|
uniswapXEthOutputEnabled = 'uniswapx_eth_output_enabled',
|
||||||
multichainUX = 'multichain_ux',
|
multichainUX = 'multichain_ux',
|
||||||
currencyConversion = 'currency_conversion',
|
currencyConversion = 'currency_conversion',
|
||||||
|
fotAdjustedmentsEnabled = 'fot_adjustments_enabled',
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FeatureFlagsContextType {
|
interface FeatureFlagsContextType {
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import { useEffect } from 'react'
|
|||||||
import { useAppDispatch, useAppSelector } from 'state/hooks'
|
import { useAppDispatch, useAppSelector } from 'state/hooks'
|
||||||
import { updateSelectedWallet } from 'state/user/reducer'
|
import { updateSelectedWallet } from 'state/user/reducer'
|
||||||
|
|
||||||
|
import { useStateRehydrated } from './useStateRehydrated'
|
||||||
|
|
||||||
async function connect(connector: Connector) {
|
async function connect(connector: Connector) {
|
||||||
try {
|
try {
|
||||||
if (connector.connectEagerly) {
|
if (connector.connectEagerly) {
|
||||||
@ -21,7 +23,7 @@ export default function useEagerlyConnect() {
|
|||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
const selectedWallet = useAppSelector((state) => state.user.selectedWallet)
|
const selectedWallet = useAppSelector((state) => state.user.selectedWallet)
|
||||||
const rehydrated = useAppSelector((state) => state._persist.rehydrated)
|
const rehydrated = useStateRehydrated()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
5
src/hooks/useStateRehydrated.ts
Normal file
5
src/hooks/useStateRehydrated.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { useAppSelector } from 'state/hooks'
|
||||||
|
|
||||||
|
export function useStateRehydrated() {
|
||||||
|
return useAppSelector((state) => state._persist.rehydrated)
|
||||||
|
}
|
||||||
@ -67,13 +67,13 @@ export function useSwapCallback(
|
|||||||
? {
|
? {
|
||||||
tradeType: TradeType.EXACT_INPUT,
|
tradeType: TradeType.EXACT_INPUT,
|
||||||
inputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
|
inputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
|
||||||
expectedOutputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
|
expectedOutputCurrencyAmountRaw: trade.postTaxOutputAmount.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.outputAmount.quotient.toString(),
|
outputCurrencyAmountRaw: trade.postTaxOutputAmount.quotient.toString(),
|
||||||
expectedInputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
|
expectedInputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,8 +62,13 @@ export function useUniversalRouterSwapCallback(
|
|||||||
if (chainId !== connectedChainId) throw new Error('signer chainId does not match')
|
if (chainId !== connectedChainId) throw new Error('signer chainId does not match')
|
||||||
|
|
||||||
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: options.slippageTolerance,
|
slippageTolerance: taxAdjustedSlippageTolerance,
|
||||||
deadlineOrPreviousBlockhash: options.deadline?.toString(),
|
deadlineOrPreviousBlockhash: options.deadline?.toString(),
|
||||||
inputTokenPermit: options.permit,
|
inputTokenPermit: options.permit,
|
||||||
fee: options.feeOptions,
|
fee: options.feeOptions,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
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, TradeType } from '@uniswap/sdk-core'
|
||||||
import { useForceUniswapXOn } from 'featureFlags/flags/forceUniswapXOn'
|
import { useForceUniswapXOn } from 'featureFlags/flags/forceUniswapXOn'
|
||||||
|
import { useFotAdjustmentsEnabled } from 'featureFlags/flags/fotAdjustments'
|
||||||
import { useUniswapXEnabled } from 'featureFlags/flags/uniswapx'
|
import { useUniswapXEnabled } from 'featureFlags/flags/uniswapx'
|
||||||
import { useUniswapXEthOutputEnabled } from 'featureFlags/flags/uniswapXEthOutput'
|
import { useUniswapXEthOutputEnabled } from 'featureFlags/flags/uniswapXEthOutput'
|
||||||
import { useUniswapXSyntheticQuoteEnabled } from 'featureFlags/flags/uniswapXUseSyntheticQuote'
|
import { useUniswapXSyntheticQuoteEnabled } from 'featureFlags/flags/uniswapXUseSyntheticQuote'
|
||||||
@ -34,6 +35,7 @@ export function useRoutingAPIArguments({
|
|||||||
const forceUniswapXOn = useForceUniswapXOn()
|
const forceUniswapXOn = useForceUniswapXOn()
|
||||||
const userDisabledUniswapX = useUserDisabledUniswapX()
|
const userDisabledUniswapX = useUserDisabledUniswapX()
|
||||||
const uniswapXEthOutputEnabled = useUniswapXEthOutputEnabled()
|
const uniswapXEthOutputEnabled = useUniswapXEthOutputEnabled()
|
||||||
|
const fotAdjustmentsEnabled = useFotAdjustmentsEnabled()
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() =>
|
() =>
|
||||||
@ -58,6 +60,7 @@ export function useRoutingAPIArguments({
|
|||||||
forceUniswapXOn,
|
forceUniswapXOn,
|
||||||
userDisabledUniswapX,
|
userDisabledUniswapX,
|
||||||
uniswapXEthOutputEnabled,
|
uniswapXEthOutputEnabled,
|
||||||
|
fotAdjustmentsEnabled,
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
account,
|
account,
|
||||||
@ -71,6 +74,7 @@ export function useRoutingAPIArguments({
|
|||||||
forceUniswapXOn,
|
forceUniswapXOn,
|
||||||
userDisabledUniswapX,
|
userDisabledUniswapX,
|
||||||
uniswapXEthOutputEnabled,
|
uniswapXEthOutputEnabled,
|
||||||
|
fotAdjustmentsEnabled,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
3602
src/locales/af-ZA.po
Normal file
3602
src/locales/af-ZA.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/ar-SA.po
Normal file
3602
src/locales/ar-SA.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/ca-ES.po
Normal file
3602
src/locales/ca-ES.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/cs-CZ.po
Normal file
3602
src/locales/cs-CZ.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/da-DK.po
Normal file
3602
src/locales/da-DK.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/de-DE.po
Normal file
3602
src/locales/de-DE.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/el-GR.po
Normal file
3602
src/locales/el-GR.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/es-ES.po
Normal file
3602
src/locales/es-ES.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/fi-FI.po
Normal file
3602
src/locales/fi-FI.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/fr-FR.po
Normal file
3602
src/locales/fr-FR.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/he-IL.po
Normal file
3602
src/locales/he-IL.po
Normal file
File diff suppressed because it is too large
Load Diff
3603
src/locales/hu-HU.po
Normal file
3603
src/locales/hu-HU.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/id-ID.po
Normal file
3602
src/locales/id-ID.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/it-IT.po
Normal file
3602
src/locales/it-IT.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/ja-JP.po
Normal file
3602
src/locales/ja-JP.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/ko-KR.po
Normal file
3602
src/locales/ko-KR.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/nl-NL.po
Normal file
3602
src/locales/nl-NL.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/no-NO.po
Normal file
3602
src/locales/no-NO.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/pl-PL.po
Normal file
3602
src/locales/pl-PL.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/pt-BR.po
Normal file
3602
src/locales/pt-BR.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/pt-PT.po
Normal file
3602
src/locales/pt-PT.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/ro-RO.po
Normal file
3602
src/locales/ro-RO.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/ru-RU.po
Normal file
3602
src/locales/ru-RU.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/sl-SI.po
Normal file
3602
src/locales/sl-SI.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/sr-SP.po
Normal file
3602
src/locales/sr-SP.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/sv-SE.po
Normal file
3602
src/locales/sv-SE.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/sw-TZ.po
Normal file
3602
src/locales/sw-TZ.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/th-TH.po
Normal file
3602
src/locales/th-TH.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/tr-TR.po
Normal file
3602
src/locales/tr-TR.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/uk-UA.po
Normal file
3602
src/locales/uk-UA.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/vi-VN.po
Normal file
3602
src/locales/vi-VN.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/zh-CN.po
Normal file
3602
src/locales/zh-CN.po
Normal file
File diff suppressed because it is too large
Load Diff
3602
src/locales/zh-TW.po
Normal file
3602
src/locales/zh-TW.po
Normal file
File diff suppressed because it is too large
Load Diff
@ -294,7 +294,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?.outputAmount,
|
[Field.OUTPUT]: independentField === Field.OUTPUT ? parsedAmount : trade?.postTaxOutputAmount,
|
||||||
},
|
},
|
||||||
[independentField, parsedAmount, showWrap, trade]
|
[independentField, parsedAmount, showWrap, trade]
|
||||||
)
|
)
|
||||||
@ -314,13 +314,17 @@ export function Swap({
|
|||||||
)
|
)
|
||||||
|
|
||||||
const fiatValueTradeInput = useUSDPrice(trade?.inputAmount)
|
const fiatValueTradeInput = useUSDPrice(trade?.inputAmount)
|
||||||
const fiatValueTradeOutput = useUSDPrice(trade?.outputAmount)
|
const fiatValueTradeOutput = useUSDPrice(trade?.postTaxOutputAmount)
|
||||||
const stablecoinPriceImpact = useMemo(
|
const preTaxFiatValueTradeOutput = useUSDPrice(trade?.outputAmount)
|
||||||
|
const [stablecoinPriceImpact, preTaxStablecoinPriceImpact] = useMemo(
|
||||||
() =>
|
() =>
|
||||||
routeIsSyncing || !isClassicTrade(trade)
|
routeIsSyncing || !isClassicTrade(trade)
|
||||||
? undefined
|
? [undefined, undefined]
|
||||||
: computeFiatValuePriceImpact(fiatValueTradeInput.data, fiatValueTradeOutput.data),
|
: [
|
||||||
[fiatValueTradeInput, fiatValueTradeOutput, routeIsSyncing, trade]
|
computeFiatValuePriceImpact(fiatValueTradeInput.data, fiatValueTradeOutput.data),
|
||||||
|
computeFiatValuePriceImpact(fiatValueTradeInput.data, preTaxFiatValueTradeOutput.data),
|
||||||
|
],
|
||||||
|
[fiatValueTradeInput, fiatValueTradeOutput, preTaxFiatValueTradeOutput, routeIsSyncing, trade]
|
||||||
)
|
)
|
||||||
|
|
||||||
const { onSwitchTokens, onCurrencySelection, onUserInput, onChangeRecipient } = useSwapActionHandlers(dispatch)
|
const { onSwitchTokens, onCurrencySelection, onUserInput, onChangeRecipient } = useSwapActionHandlers(dispatch)
|
||||||
@ -417,7 +421,7 @@ export function Swap({
|
|||||||
if (!swapCallback) {
|
if (!swapCallback) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (stablecoinPriceImpact && !confirmPriceImpactWithoutFee(stablecoinPriceImpact)) {
|
if (preTaxStablecoinPriceImpact && !confirmPriceImpactWithoutFee(preTaxStablecoinPriceImpact)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setSwapState((currentState) => ({
|
setSwapState((currentState) => ({
|
||||||
@ -440,7 +444,7 @@ export function Swap({
|
|||||||
swapResult: undefined,
|
swapResult: undefined,
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
}, [swapCallback, stablecoinPriceImpact])
|
}, [swapCallback, preTaxStablecoinPriceImpact])
|
||||||
|
|
||||||
const handleOnWrap = useCallback(async () => {
|
const handleOnWrap = useCallback(async () => {
|
||||||
if (!onWrap) return
|
if (!onWrap) return
|
||||||
@ -476,9 +480,9 @@ export function Swap({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const marketPriceImpact = trade?.priceImpact ? computeRealizedPriceImpact(trade) : undefined
|
const marketPriceImpact = trade?.priceImpact ? computeRealizedPriceImpact(trade) : undefined
|
||||||
const largerPriceImpact = largerPercentValue(marketPriceImpact, stablecoinPriceImpact)
|
const largerPriceImpact = largerPercentValue(marketPriceImpact, preTaxStablecoinPriceImpact)
|
||||||
return { priceImpactSeverity: warningSeverity(largerPriceImpact), largerPriceImpact }
|
return { priceImpactSeverity: warningSeverity(largerPriceImpact), largerPriceImpact }
|
||||||
}, [stablecoinPriceImpact, trade])
|
}, [preTaxStablecoinPriceImpact, trade])
|
||||||
|
|
||||||
const handleConfirmDismiss = useCallback(() => {
|
const handleConfirmDismiss = useCallback(() => {
|
||||||
setSwapState((currentState) => ({ ...currentState, showConfirm: false }))
|
setSwapState((currentState) => ({ ...currentState, showConfirm: false }))
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { getVersionUpgrade, VersionUpgrade } from '@uniswap/token-lists'
|
|||||||
import { useWeb3React } from '@web3-react/core'
|
import { useWeb3React } from '@web3-react/core'
|
||||||
import { DEFAULT_LIST_OF_LISTS, UNSUPPORTED_LIST_URLS } from 'constants/lists'
|
import { DEFAULT_LIST_OF_LISTS, UNSUPPORTED_LIST_URLS } from 'constants/lists'
|
||||||
import TokenSafetyLookupTable from 'constants/tokenSafetyLookup'
|
import TokenSafetyLookupTable from 'constants/tokenSafetyLookup'
|
||||||
|
import { useStateRehydrated } from 'hooks/useStateRehydrated'
|
||||||
import useInterval from 'lib/hooks/useInterval'
|
import useInterval from 'lib/hooks/useInterval'
|
||||||
import ms from 'ms'
|
import ms from 'ms'
|
||||||
import { useCallback, useEffect } from 'react'
|
import { useCallback, useEffect } from 'react'
|
||||||
@ -21,10 +22,11 @@ export default function Updater(): null {
|
|||||||
// get all loaded lists, and the active urls
|
// get all loaded lists, and the active urls
|
||||||
const lists = useAllLists()
|
const lists = useAllLists()
|
||||||
const listsState = useAppSelector((state) => state.lists)
|
const listsState = useAppSelector((state) => state.lists)
|
||||||
|
const rehydrated = useStateRehydrated()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
TokenSafetyLookupTable.update(listsState)
|
if (rehydrated) TokenSafetyLookupTable.update(listsState)
|
||||||
}, [listsState])
|
}, [listsState, rehydrated])
|
||||||
|
|
||||||
const fetchList = useFetchListCallback()
|
const fetchList = useFetchListCallback()
|
||||||
const fetchAllListsCallback = useCallback(() => {
|
const fetchAllListsCallback = useCallback(() => {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { MixedRouteSDK, Protocol, Trade } from '@uniswap/router-sdk'
|
import { MixedRouteSDK, ONE, Protocol, Trade } from '@uniswap/router-sdk'
|
||||||
import { ChainId, Currency, CurrencyAmount, Percent, Token, TradeType } from '@uniswap/sdk-core'
|
import { ChainId, Currency, CurrencyAmount, Fraction, Percent, Token, TradeType } from '@uniswap/sdk-core'
|
||||||
import { DutchOrderInfo, DutchOrderInfoJSON, DutchOrderTrade as IDutchOrderTrade } from '@uniswap/uniswapx-sdk'
|
import { DutchOrderInfo, DutchOrderInfoJSON, DutchOrderTrade as IDutchOrderTrade } from '@uniswap/uniswapx-sdk'
|
||||||
import { Route as V2Route } from '@uniswap/v2-sdk'
|
import { Route as V2Route } from '@uniswap/v2-sdk'
|
||||||
import { Route as V3Route } from '@uniswap/v3-sdk'
|
import { Route as V3Route } from '@uniswap/v3-sdk'
|
||||||
@ -47,6 +47,7 @@ export interface GetQuoteArgs {
|
|||||||
uniswapXEthOutputEnabled: boolean
|
uniswapXEthOutputEnabled: boolean
|
||||||
forceUniswapXOn: boolean
|
forceUniswapXOn: boolean
|
||||||
userDisabledUniswapX: boolean
|
userDisabledUniswapX: boolean
|
||||||
|
fotAdjustmentsEnabled: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@ -146,6 +147,8 @@ 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
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
gasUseEstimateUSD,
|
gasUseEstimateUSD,
|
||||||
@ -154,6 +157,8 @@ export class ClassicTrade extends Trade<Currency, Currency, TradeType> {
|
|||||||
requestId,
|
requestId,
|
||||||
quoteMethod,
|
quoteMethod,
|
||||||
approveInfo,
|
approveInfo,
|
||||||
|
inputTax,
|
||||||
|
outputTax,
|
||||||
...routes
|
...routes
|
||||||
}: {
|
}: {
|
||||||
gasUseEstimateUSD?: number
|
gasUseEstimateUSD?: number
|
||||||
@ -163,6 +168,8 @@ export class ClassicTrade extends Trade<Currency, Currency, TradeType> {
|
|||||||
requestId?: string
|
requestId?: string
|
||||||
quoteMethod: QuoteMethod
|
quoteMethod: QuoteMethod
|
||||||
approveInfo: ApproveInfo
|
approveInfo: ApproveInfo
|
||||||
|
inputTax: Percent
|
||||||
|
outputTax: Percent
|
||||||
v2Routes: {
|
v2Routes: {
|
||||||
routev2: V2Route<Currency, Currency>
|
routev2: V2Route<Currency, Currency>
|
||||||
inputAmount: CurrencyAmount<Currency>
|
inputAmount: CurrencyAmount<Currency>
|
||||||
@ -187,6 +194,26 @@ 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
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
@ -259,6 +286,11 @@ 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 type InterfaceTrade = ClassicTrade | DutchOrderTrade
|
export type InterfaceTrade = ClassicTrade | DutchOrderTrade
|
||||||
|
|||||||
@ -6,7 +6,9 @@ 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'
|
||||||
|
|
||||||
@ -213,6 +215,9 @@ 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
|
||||||
@ -247,6 +252,8 @@ export async function transformRoutesToTrade(
|
|||||||
isUniswapXBetter,
|
isUniswapXBetter,
|
||||||
requestId: data.quote.requestId,
|
requestId: data.quote.requestId,
|
||||||
quoteMethod,
|
quoteMethod,
|
||||||
|
inputTax,
|
||||||
|
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,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { ChainId, CurrencyAmount, Percent, Token, TradeType } from '@uniswap/sdk-core'
|
import { ChainId, CurrencyAmount, Percent, Token, TradeType } from '@uniswap/sdk-core'
|
||||||
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'
|
||||||
@ -46,6 +47,8 @@ export const TEST_TRADE_EXACT_INPUT = new ClassicTrade({
|
|||||||
gasUseEstimateUSD: 1.0,
|
gasUseEstimateUSD: 1.0,
|
||||||
approveInfo: { needsApprove: false },
|
approveInfo: { needsApprove: false },
|
||||||
quoteMethod: QuoteMethod.CLIENT_SIDE,
|
quoteMethod: QuoteMethod.CLIENT_SIDE,
|
||||||
|
inputTax: ZERO_PERCENT,
|
||||||
|
outputTax: ZERO_PERCENT,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const TEST_TRADE_EXACT_INPUT_API = new ClassicTrade({
|
export const TEST_TRADE_EXACT_INPUT_API = new ClassicTrade({
|
||||||
@ -61,6 +64,8 @@ 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({
|
||||||
@ -75,6 +80,8 @@ export const TEST_TRADE_EXACT_OUTPUT = new ClassicTrade({
|
|||||||
tradeType: TradeType.EXACT_OUTPUT,
|
tradeType: TradeType.EXACT_OUTPUT,
|
||||||
quoteMethod: QuoteMethod.CLIENT_SIDE,
|
quoteMethod: QuoteMethod.CLIENT_SIDE,
|
||||||
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)
|
||||||
@ -116,3 +123,37 @@ export const TEST_DUTCH_TRADE_ETH_INPUT = new DutchOrderTrade({
|
|||||||
deadlineBufferSecs: 30,
|
deadlineBufferSecs: 30,
|
||||||
slippageTolerance: new Percent(5, 100),
|
slippageTolerance: new Percent(5, 100),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const TEST_TRADE_FEE_ON_SELL = new ClassicTrade({
|
||||||
|
v3Routes: [
|
||||||
|
{
|
||||||
|
routev3: new V3Route([TEST_POOL_12], TEST_TOKEN_1, TEST_TOKEN_2),
|
||||||
|
inputAmount: toCurrencyAmount(TEST_TOKEN_1, 1000),
|
||||||
|
outputAmount: toCurrencyAmount(TEST_TOKEN_2, 1000),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
v2Routes: [],
|
||||||
|
tradeType: TradeType.EXACT_INPUT,
|
||||||
|
gasUseEstimateUSD: 1.0,
|
||||||
|
approveInfo: { needsApprove: false },
|
||||||
|
quoteMethod: QuoteMethod.ROUTING_API,
|
||||||
|
inputTax: new Percent(3, 100),
|
||||||
|
outputTax: ZERO_PERCENT,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const TEST_TRADE_FEE_ON_BUY = new ClassicTrade({
|
||||||
|
v3Routes: [
|
||||||
|
{
|
||||||
|
routev3: new V3Route([TEST_POOL_12], TEST_TOKEN_1, TEST_TOKEN_2),
|
||||||
|
inputAmount: toCurrencyAmount(TEST_TOKEN_1, 1000),
|
||||||
|
outputAmount: toCurrencyAmount(TEST_TOKEN_2, 1000),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
v2Routes: [],
|
||||||
|
tradeType: TradeType.EXACT_INPUT,
|
||||||
|
gasUseEstimateUSD: 1.0,
|
||||||
|
approveInfo: { needsApprove: false },
|
||||||
|
quoteMethod: QuoteMethod.ROUTING_API,
|
||||||
|
inputTax: ZERO_PERCENT,
|
||||||
|
outputTax: new Percent(3, 100),
|
||||||
|
})
|
||||||
|
|||||||
@ -2,7 +2,8 @@
|
|||||||
* Returns the time elapsed between page load and now.
|
* Returns the time elapsed between page load and now.
|
||||||
* @param markName the identifier for the performance mark to be created and measured.
|
* @param markName the identifier for the performance mark to be created and measured.
|
||||||
*/
|
*/
|
||||||
export function calculateElapsedTimeWithPerformanceMark(markName: string): number {
|
export function calculateElapsedTimeWithPerformanceMark(markName: string): number | undefined {
|
||||||
const elapsedTime = performance.mark(markName)
|
const elapsedTime = performance.mark(markName)
|
||||||
|
if (!elapsedTime) return undefined
|
||||||
return elapsedTime.startTime
|
return elapsedTime.startTime
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { Currency, CurrencyAmount, Fraction, Percent, TradeType } from '@uniswap
|
|||||||
import { Pair } from '@uniswap/v2-sdk'
|
import { Pair } from '@uniswap/v2-sdk'
|
||||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||||
import JSBI from 'jsbi'
|
import JSBI from 'jsbi'
|
||||||
|
import { DefaultTheme } from 'styled-components'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ALLOWED_PRICE_IMPACT_HIGH,
|
ALLOWED_PRICE_IMPACT_HIGH,
|
||||||
@ -104,3 +105,14 @@ export function getPriceImpactWarning(priceImpact: Percent): 'warning' | 'error'
|
|||||||
if (priceImpact.greaterThan(ALLOWED_PRICE_IMPACT_MEDIUM)) return 'warning'
|
if (priceImpact.greaterThan(ALLOWED_PRICE_IMPACT_MEDIUM)) return 'warning'
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getPriceImpactColor(priceImpact: Percent): keyof DefaultTheme | undefined {
|
||||||
|
switch (getPriceImpactWarning(priceImpact)) {
|
||||||
|
case 'error':
|
||||||
|
return 'accentFailure'
|
||||||
|
case 'warning':
|
||||||
|
return 'accentWarning'
|
||||||
|
default:
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user