>>
+type UniversalRouterFeeField = { feeOptions: FeeOptions } | { flatFeeOptions: FlatFeeOptions }
+
+function getUniversalRouterFeeFields(trade?: InterfaceTrade): UniversalRouterFeeField | undefined {
+ if (!isClassicTrade(trade)) return undefined
+ if (!trade.swapFee) return undefined
+
+ if (trade.tradeType === TradeType.EXACT_INPUT) {
+ return { feeOptions: { fee: trade.swapFee.percent, recipient: trade.swapFee.recipient } }
+ } else {
+ return { flatFeeOptions: { amount: BigNumber.from(trade.swapFee.amount), recipient: trade.swapFee.recipient } }
+ }
+}
+
// Returns a function that will execute a swap, if the parameters are all valid
// and the user has approved the slippage adjusted input amount for the trade
export function useSwapCallback(
trade: InterfaceTrade | undefined, // trade to execute, required
- fiatValues: { amountIn?: number; amountOut?: number }, // usd values for amount in and out, logged for analytics
+ fiatValues: { amountIn?: number; amountOut?: number; feeUsd?: number }, // usd values for amount in and out, and the fee value, logged for analytics
allowedSlippage: Percent, // in bips
permitSignature: PermitSignature | undefined
) {
@@ -47,6 +63,7 @@ export function useSwapCallback(
slippageTolerance: allowedSlippage,
deadline,
permit: permitSignature,
+ ...getUniversalRouterFeeFields(trade),
}
)
diff --git a/src/hooks/useUniswapXSwapCallback.ts b/src/hooks/useUniswapXSwapCallback.ts
index 044dfcd767..d4b892c7bd 100644
--- a/src/hooks/useUniswapXSwapCallback.ts
+++ b/src/hooks/useUniswapXSwapCallback.ts
@@ -5,6 +5,7 @@ import { Percent } from '@uniswap/sdk-core'
import { DutchOrder, DutchOrderBuilder } from '@uniswap/uniswapx-sdk'
import { useWeb3React } from '@web3-react/core'
import { sendAnalyticsEvent, useTrace } from 'analytics'
+import { useCachedPortfolioBalancesQuery } from 'components/PrefetchBalancesWrapper/PrefetchBalancesWrapper'
import { formatSwapSignedAnalyticsEventProperties } from 'lib/utils/analytics'
import { useCallback } from 'react'
import { DutchOrderTrade, TradeFillType } from 'state/routing/types'
@@ -50,12 +51,15 @@ export function useUniswapXSwapCallback({
fiatValues,
}: {
trade?: DutchOrderTrade
- fiatValues: { amountIn?: number; amountOut?: number }
+ fiatValues: { amountIn?: number; amountOut?: number; feeUsd?: number }
allowedSlippage: Percent
}) {
const { account, provider } = useWeb3React()
const analyticsContext = useTrace()
+ const { data } = useCachedPortfolioBalancesQuery({ account })
+ const portfolioBalanceUsd = data?.portfolios?.[0]?.tokensTotalDenominatedValue?.value
+
return useCallback(
async () =>
trace('swapx.send', async ({ setTraceData, setTraceStatus }) => {
@@ -82,7 +86,7 @@ export function useUniswapXSwapCallback({
.decayEndTime(endTime)
.deadline(deadline)
.swapper(account)
- .nonFeeRecipient(account)
+ .nonFeeRecipient(account, trade.swapFee?.recipient)
// if fetching the nonce fails for any reason, default to existing nonce from the Swap quote.
.nonce(updatedNonce ?? trade.order.info.nonce)
.build()
@@ -115,6 +119,7 @@ export function useUniswapXSwapCallback({
allowedSlippage,
fiatValues,
timeToSignSinceRequestMs: Date.now() - beforeSign,
+ portfolioBalanceUsd,
}),
...analyticsContext,
})
@@ -139,6 +144,7 @@ export function useUniswapXSwapCallback({
trade,
allowedSlippage,
fiatValues,
+ portfolioBalanceUsd,
}),
...analyticsContext,
errorCode: body.errorCode,
@@ -154,6 +160,6 @@ export function useUniswapXSwapCallback({
response: { orderHash: body.hash, deadline: updatedOrder.info.deadline },
}
}),
- [account, provider, trade, allowedSlippage, fiatValues, analyticsContext]
+ [account, provider, trade, allowedSlippage, fiatValues, analyticsContext, portfolioBalanceUsd]
)
}
diff --git a/src/hooks/useUniversalRouter.ts b/src/hooks/useUniversalRouter.ts
index 3ba976a34f..1eae988274 100644
--- a/src/hooks/useUniversalRouter.ts
+++ b/src/hooks/useUniversalRouter.ts
@@ -2,10 +2,11 @@ import { BigNumber } from '@ethersproject/bignumber'
import { t } from '@lingui/macro'
import { SwapEventName } from '@uniswap/analytics-events'
import { Percent } from '@uniswap/sdk-core'
-import { SwapRouter, UNIVERSAL_ROUTER_ADDRESS } from '@uniswap/universal-router-sdk'
+import { FlatFeeOptions, SwapRouter, UNIVERSAL_ROUTER_ADDRESS } from '@uniswap/universal-router-sdk'
import { FeeOptions, toHex } from '@uniswap/v3-sdk'
import { useWeb3React } from '@web3-react/core'
import { sendAnalyticsEvent, useTrace } from 'analytics'
+import { useCachedPortfolioBalancesQuery } from 'components/PrefetchBalancesWrapper/PrefetchBalancesWrapper'
import useBlockNumber from 'lib/hooks/useBlockNumber'
import { formatCommonPropertiesForTrade, formatSwapSignedAnalyticsEventProperties } from 'lib/utils/analytics'
import { useCallback } from 'react'
@@ -43,17 +44,20 @@ interface SwapOptions {
deadline?: BigNumber
permit?: PermitSignature
feeOptions?: FeeOptions
+ flatFeeOptions?: FlatFeeOptions
}
export function useUniversalRouterSwapCallback(
trade: ClassicTrade | undefined,
- fiatValues: { amountIn?: number; amountOut?: number },
+ fiatValues: { amountIn?: number; amountOut?: number; feeUsd?: number },
options: SwapOptions
) {
const { account, chainId, provider } = useWeb3React()
const analyticsContext = useTrace()
const blockNumber = useBlockNumber()
const isAutoSlippage = useUserSlippageTolerance()[0] === 'auto'
+ const { data } = useCachedPortfolioBalancesQuery({ account })
+ const portfolioBalanceUsd = data?.portfolios?.[0]?.tokensTotalDenominatedValue?.value
return useCallback(async () => {
return trace('swap.send', async ({ setTraceData, setTraceStatus, setTraceError }) => {
@@ -76,6 +80,7 @@ export function useUniversalRouterSwapCallback(
deadlineOrPreviousBlockhash: options.deadline?.toString(),
inputTokenPermit: options.permit,
fee: options.feeOptions,
+ flatFee: options.flatFeeOptions,
})
const tx = {
@@ -117,6 +122,7 @@ export function useUniversalRouterSwapCallback(
allowedSlippage: options.slippageTolerance,
fiatValues,
txHash: response.hash,
+ portfolioBalanceUsd,
}),
...analyticsContext,
})
@@ -154,16 +160,14 @@ export function useUniversalRouterSwapCallback(
})
}, [
account,
- analyticsContext,
- blockNumber,
chainId,
- fiatValues,
- options.deadline,
- options.feeOptions,
- options.permit,
- options.slippageTolerance,
provider,
trade,
+ options,
+ analyticsContext,
+ blockNumber,
isAutoSlippage,
+ fiatValues,
+ portfolioBalanceUsd,
])
}
diff --git a/src/lib/hooks/routing/useRoutingAPIArguments.ts b/src/lib/hooks/routing/useRoutingAPIArguments.ts
index 8f1c6df3c7..ca624530e3 100644
--- a/src/lib/hooks/routing/useRoutingAPIArguments.ts
+++ b/src/lib/hooks/routing/useRoutingAPIArguments.ts
@@ -4,6 +4,7 @@ import { useUniswapXDefaultEnabled } from 'featureFlags/flags/uniswapXDefault'
import { useUniswapXEthOutputEnabled } from 'featureFlags/flags/uniswapXEthOutput'
import { useUniswapXExactOutputEnabled } from 'featureFlags/flags/uniswapXExactOutput'
import { useUniswapXSyntheticQuoteEnabled } from 'featureFlags/flags/uniswapXUseSyntheticQuote'
+import { useFeesEnabled } from 'featureFlags/flags/useFees'
import { useMemo } from 'react'
import { GetQuoteArgs, INTERNAL_ROUTER_PREFERENCE_PRICE, RouterPreference } from 'state/routing/types'
import { currencyAddressForSwapQuote } from 'state/routing/utils'
@@ -40,6 +41,10 @@ export function useRoutingAPIArguments({
const uniswapXExactOutputEnabled = useUniswapXExactOutputEnabled()
const isUniswapXDefaultEnabled = useUniswapXDefaultEnabled()
+ const feesEnabled = useFeesEnabled()
+ // Don't enable fee logic if this is a quote for pricing
+ const sendPortionEnabled = routerPreference === INTERNAL_ROUTER_PREFERENCE_PRICE ? false : feesEnabled
+
return useMemo(
() =>
!tokenIn || !tokenOut || !amount || tokenIn.equals(tokenOut) || tokenIn.wrapped.equals(tokenOut.wrapped)
@@ -64,6 +69,7 @@ export function useRoutingAPIArguments({
uniswapXEthOutputEnabled,
uniswapXExactOutputEnabled,
isUniswapXDefaultEnabled,
+ sendPortionEnabled,
inputTax,
outputTax,
},
@@ -80,6 +86,7 @@ export function useRoutingAPIArguments({
userOptedOutOfUniswapX,
uniswapXEthOutputEnabled,
isUniswapXDefaultEnabled,
+ sendPortionEnabled,
inputTax,
outputTax,
]
diff --git a/src/lib/utils/analytics.ts b/src/lib/utils/analytics.ts
index ad26af2f08..ae3960e739 100644
--- a/src/lib/utils/analytics.ts
+++ b/src/lib/utils/analytics.ts
@@ -1,6 +1,6 @@
import { Currency, CurrencyAmount, Percent, Price, Token } from '@uniswap/sdk-core'
import { NATIVE_CHAIN_ID } from 'constants/tokens'
-import { InterfaceTrade, QuoteMethod } from 'state/routing/types'
+import { InterfaceTrade, QuoteMethod, SubmittableTrade } from 'state/routing/types'
import { isClassicTrade, isSubmittableTrade, isUniswapXTrade } from 'state/routing/utils'
import { computeRealizedPriceImpact } from 'utils/prices'
@@ -35,7 +35,11 @@ function getEstimatedNetworkFee(trade: InterfaceTrade) {
return undefined
}
-export function formatCommonPropertiesForTrade(trade: InterfaceTrade, allowedSlippage: Percent) {
+export function formatCommonPropertiesForTrade(
+ trade: InterfaceTrade,
+ allowedSlippage: Percent,
+ outputFeeFiatValue?: number
+) {
return {
routing: trade.fillType,
type: trade.tradeType,
@@ -47,7 +51,7 @@ export function formatCommonPropertiesForTrade(trade: InterfaceTrade, allowedSli
token_in_symbol: trade.inputAmount.currency.symbol,
token_out_symbol: trade.outputAmount.currency.symbol,
token_in_amount: formatToDecimal(trade.inputAmount, trade.inputAmount.currency.decimals),
- token_out_amount: formatToDecimal(trade.outputAmount, trade.outputAmount.currency.decimals),
+ token_out_amount: formatToDecimal(trade.postTaxOutputAmount, trade.outputAmount.currency.decimals),
price_impact_basis_points: isClassicTrade(trade)
? formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade))
: undefined,
@@ -59,6 +63,7 @@ export function formatCommonPropertiesForTrade(trade: InterfaceTrade, allowedSli
minimum_output_after_slippage: trade.minimumAmountOut(allowedSlippage).toSignificant(6),
allowed_slippage: formatPercentNumber(allowedSlippage),
method: getQuoteMethod(trade),
+ fee_usd: outputFeeFiatValue,
}
}
@@ -68,19 +73,22 @@ export const formatSwapSignedAnalyticsEventProperties = ({
fiatValues,
txHash,
timeToSignSinceRequestMs,
+ portfolioBalanceUsd,
}: {
- trade: InterfaceTrade
+ trade: SubmittableTrade
allowedSlippage: Percent
- fiatValues: { amountIn?: number; amountOut?: number }
+ fiatValues: { amountIn?: number; amountOut?: number; feeUsd?: number }
txHash?: string
timeToSignSinceRequestMs?: number
+ portfolioBalanceUsd?: number
}) => ({
+ total_balances_usd: portfolioBalanceUsd,
transaction_hash: txHash,
token_in_amount_usd: fiatValues.amountIn,
token_out_amount_usd: fiatValues.amountOut,
// measures the amount of time the user took to sign the permit message or swap tx in their wallet
time_to_sign_since_request_ms: timeToSignSinceRequestMs,
- ...formatCommonPropertiesForTrade(trade, allowedSlippage),
+ ...formatCommonPropertiesForTrade(trade, allowedSlippage, fiatValues.feeUsd),
})
function getQuoteMethod(trade: InterfaceTrade) {
@@ -94,10 +102,11 @@ export const formatSwapQuoteReceivedEventProperties = (
allowedSlippage: Percent,
swapQuoteLatencyMs: number | undefined,
inputTax: Percent,
- outputTax: Percent
+ outputTax: Percent,
+ outputFeeFiatValue: number | undefined
) => {
return {
- ...formatCommonPropertiesForTrade(trade, allowedSlippage),
+ ...formatCommonPropertiesForTrade(trade, allowedSlippage, outputFeeFiatValue),
swap_quote_block_number: isClassicTrade(trade) ? trade.blockNumber : undefined,
allowed_slippage_basis_points: formatPercentInBasisPointsNumber(allowedSlippage),
token_in_amount_max: trade.maximumAmountIn(allowedSlippage).toExact(),
diff --git a/src/pages/Swap/index.tsx b/src/pages/Swap/index.tsx
index 213986b153..1436c8d50d 100644
--- a/src/pages/Swap/index.tsx
+++ b/src/pages/Swap/index.tsx
@@ -294,6 +294,7 @@ export function Swap({
inputError: swapInputError,
inputTax,
outputTax,
+ outputFeeFiatValue,
} = swapInfo
const [inputTokenHasTax, outputTokenHasTax] = useMemo(
@@ -441,8 +442,8 @@ export function Swap({
)
const showMaxButton = Boolean(maxInputAmount?.greaterThan(0) && !parsedAmounts[Field.INPUT]?.equalTo(maxInputAmount))
const swapFiatValues = useMemo(() => {
- return { amountIn: fiatValueTradeInput.data, amountOut: fiatValueTradeOutput.data }
- }, [fiatValueTradeInput, fiatValueTradeOutput])
+ return { amountIn: fiatValueTradeInput.data, amountOut: fiatValueTradeOutput.data, feeUsd: outputFeeFiatValue }
+ }, [fiatValueTradeInput.data, fiatValueTradeOutput.data, outputFeeFiatValue])
// the callback to execute the swap
const swapCallback = useSwapCallback(
@@ -584,10 +585,17 @@ export function Swap({
if (!trade || prevTrade === trade) return // no new swap quote to log
sendAnalyticsEvent(SwapEventName.SWAP_QUOTE_RECEIVED, {
- ...formatSwapQuoteReceivedEventProperties(trade, allowedSlippage, swapQuoteLatency, inputTax, outputTax),
+ ...formatSwapQuoteReceivedEventProperties(
+ trade,
+ allowedSlippage,
+ swapQuoteLatency,
+ inputTax,
+ outputTax,
+ outputFeeFiatValue
+ ),
...trace,
})
- }, [prevTrade, trade, trace, allowedSlippage, swapQuoteLatency, inputTax, outputTax])
+ }, [prevTrade, trade, trace, allowedSlippage, swapQuoteLatency, inputTax, outputTax, outputFeeFiatValue])
const showDetailsDropdown = Boolean(
!showWrap && userHasSpecifiedInputOutput && (trade || routeIsLoading || routeIsSyncing)
diff --git a/src/state/routing/slice.ts b/src/state/routing/slice.ts
index c77d58446d..a0e16b2f40 100644
--- a/src/state/routing/slice.ts
+++ b/src/state/routing/slice.ts
@@ -35,6 +35,8 @@ const protocols: Protocol[] = [Protocol.V2, Protocol.V3, Protocol.MIXED]
// routing API quote query params: https://github.com/Uniswap/routing-api/blob/main/lib/handlers/quote/schema/quote-schema.ts
const DEFAULT_QUERY_PARAMS = {
protocols,
+ // this should be removed once BE fixes issue where enableUniversalRouter is required for fees to work
+ enableUniversalRouter: true,
}
function getQuoteLatencyMeasure(mark: PerformanceMark): PerformanceMeasure {
@@ -66,6 +68,7 @@ function getRoutingAPIConfig(args: GetQuoteArgs): RoutingConfig {
const classic = {
...DEFAULT_QUERY_PARAMS,
routingType: URAQuoteType.CLASSIC,
+ recipient: account,
}
const tokenOutIsNative = Object.values(SwapRouterNativeAssets).includes(tokenOutAddress as SwapRouterNativeAssets)
@@ -124,16 +127,24 @@ export const routingApi = createApi({
logSwapQuoteRequest(args.tokenInChainId, args.routerPreference, false)
const quoteStartMark = performance.mark(`quote-fetch-start-${Date.now()}`)
try {
- const { tokenInAddress, tokenInChainId, tokenOutAddress, tokenOutChainId, amount, tradeType } = args
- const type = isExactInput(tradeType) ? 'EXACT_INPUT' : 'EXACT_OUTPUT'
+ const {
+ tokenInAddress: tokenIn,
+ tokenInChainId,
+ tokenOutAddress: tokenOut,
+ tokenOutChainId,
+ amount,
+ tradeType,
+ sendPortionEnabled,
+ } = args
const requestBody = {
tokenInChainId,
- tokenIn: tokenInAddress,
+ tokenIn,
tokenOutChainId,
- tokenOut: tokenOutAddress,
+ tokenOut,
amount,
- type,
+ sendPortionEnabled,
+ type: isExactInput(tradeType) ? 'EXACT_INPUT' : 'EXACT_OUTPUT',
intent: args.routerPreference === INTERNAL_ROUTER_PREFERENCE_PRICE ? 'pricing' : undefined,
configs: getRoutingAPIConfig(args),
}
diff --git a/src/state/routing/types.ts b/src/state/routing/types.ts
index b1802a5a61..a7e0bb3650 100644
--- a/src/state/routing/types.ts
+++ b/src/state/routing/types.ts
@@ -50,6 +50,7 @@ export interface GetQuoteArgs {
// temporary field indicating the user disabled UniswapX during the transition to the opt-out model
userOptedOutOfUniswapX: boolean
isUniswapXDefaultEnabled: boolean
+ sendPortionEnabled: boolean
inputTax: Percent
outputTax: Percent
}
@@ -112,11 +113,11 @@ export interface ClassicQuoteData {
blockNumber: string
amount: string
amountDecimals: string
- gasPriceWei: string
- gasUseEstimate: string
- gasUseEstimateQuote: string
- gasUseEstimateQuoteDecimals: string
- gasUseEstimateUSD: string
+ gasPriceWei?: string
+ gasUseEstimate?: string
+ gasUseEstimateQuote?: string
+ gasUseEstimateQuoteDecimals?: string
+ gasUseEstimateUSD?: string
methodParameters?: { calldata: string; value: string }
quote: string
quoteDecimals: string
@@ -124,19 +125,30 @@ export interface ClassicQuoteData {
quoteGasAdjustedDecimals: string
route: Array<(V3PoolInRoute | V2PoolInRoute)[]>
routeString: string
+ portionBips?: number
+ portionRecipient?: string
+ portionAmount?: string
+ portionAmountDecimals?: string
+ quoteGasAndPortionAdjusted?: string
+ quoteGasAndPortionAdjustedDecimals?: string
+}
+
+export type URADutchOrderQuoteData = {
+ auctionPeriodSecs: number
+ deadlineBufferSecs: number
+ startTimeBufferSecs: number
+ orderInfo: DutchOrderInfoJSON
+ quoteId?: string
+ requestId?: string
+ slippageTolerance: string
+ portionBips?: number
+ portionRecipient?: string
+ portionAmount?: string
}
type URADutchOrderQuoteResponse = {
routing: URAQuoteType.DUTCH_LIMIT
- quote: {
- auctionPeriodSecs: number
- deadlineBufferSecs: number
- startTimeBufferSecs: number
- orderInfo: DutchOrderInfoJSON
- quoteId?: string
- requestId?: string
- slippageTolerance: string
- }
+ quote: URADutchOrderQuoteData
allQuotes: Array
}
type URAClassicQuoteResponse = {
@@ -179,6 +191,8 @@ export enum TradeFillType {
export type ApproveInfo = { needsApprove: true; approveGasEstimateUSD: number } | { needsApprove: false }
export type WrapInfo = { needsWrap: true; wrapGasEstimateUSD: number } | { needsWrap: false }
+export type SwapFeeInfo = { recipient: string; percent: Percent; amount: string /* raw amount of output token */ }
+
export class ClassicTrade extends Trade {
public readonly fillType = TradeFillType.Classic
approveInfo: ApproveInfo
@@ -189,6 +203,7 @@ export class ClassicTrade extends Trade {
quoteMethod: QuoteMethod
inputTax: Percent
outputTax: Percent
+ swapFee: SwapFeeInfo | undefined
constructor({
gasUseEstimateUSD,
@@ -199,6 +214,7 @@ export class ClassicTrade extends Trade {
approveInfo,
inputTax,
outputTax,
+ swapFee,
...routes
}: {
gasUseEstimateUSD?: number
@@ -210,6 +226,7 @@ export class ClassicTrade extends Trade {
approveInfo: ApproveInfo
inputTax: Percent
outputTax: Percent
+ swapFee?: SwapFeeInfo
v2Routes: {
routev2: V2Route
inputAmount: CurrencyAmount
@@ -236,17 +253,33 @@ export class ClassicTrade extends Trade {
this.approveInfo = approveInfo
this.inputTax = inputTax
this.outputTax = outputTax
+ this.swapFee = swapFee
+ }
+
+ public get executionPrice(): Price {
+ if (this.tradeType === TradeType.EXACT_INPUT || !this.swapFee) return super.executionPrice
+
+ // Fix inaccurate price calculation for exact output trades
+ return new Price({ baseAmount: this.inputAmount, quoteAmount: this.postSwapFeeOutputAmount })
}
public get totalTaxRate(): Percent {
return this.inputTax.add(this.outputTax)
}
+ public get postSwapFeeOutputAmount(): CurrencyAmount {
+ // Routing api already applies the swap fee to the output amount for exact-in
+ if (this.tradeType === TradeType.EXACT_INPUT) return this.outputAmount
+
+ const swapFeeAmount = CurrencyAmount.fromRawAmount(this.outputAmount.currency, this.swapFee?.amount ?? 0)
+ 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.outputAmount.multiply(new Fraction(ONE).subtract(this.totalTaxRate))
+ return this.postSwapFeeOutputAmount.multiply(new Fraction(ONE).subtract(this.totalTaxRate))
}
public minimumAmountOut(slippageTolerance: Percent, amountOut = this.outputAmount): CurrencyAmount {
@@ -281,6 +314,7 @@ export class DutchOrderTrade extends IDutchOrderTrade }
inputTax: Percent
outputTax: Percent
+ outputFeeFiatValue?: number
parsedAmount?: CurrencyAmount
inputError?: ReactNode
trade: {
@@ -140,6 +142,13 @@ export function useDerivedSwapInfo(state: SwapState, chainId: ChainId | undefine
outputTax
)
+ const { data: outputFeeFiatValue } = useUSDPrice(
+ isSubmittableTrade(trade.trade) && trade.trade.swapFee
+ ? CurrencyAmount.fromRawAmount(trade.trade.outputAmount.currency, trade.trade.swapFee.amount)
+ : undefined,
+ trade.trade?.outputAmount.currency
+ )
+
const currencyBalances = useMemo(
() => ({
[Field.INPUT]: relevantTokenBalances[0],
@@ -215,8 +224,20 @@ export function useDerivedSwapInfo(state: SwapState, chainId: ChainId | undefine
allowedSlippage,
inputTax,
outputTax,
+ outputFeeFiatValue,
}),
- [allowedSlippage, autoSlippage, currencies, currencyBalances, inputError, inputTax, outputTax, parsedAmount, trade]
+ [
+ allowedSlippage,
+ autoSlippage,
+ currencies,
+ currencyBalances,
+ inputError,
+ inputTax,
+ outputFeeFiatValue,
+ outputTax,
+ parsedAmount,
+ trade,
+ ]
)
}
diff --git a/src/utils/formatNumbers.test.ts b/src/utils/formatNumbers.test.ts
index a0712612b8..ed281d0a22 100644
--- a/src/utils/formatNumbers.test.ts
+++ b/src/utils/formatNumbers.test.ts
@@ -188,7 +188,7 @@ describe('formatNumber', () => {
expect(formatNumber({ input: 1234567.891, type: NumberType.FiatGasPrice })).toBe('$1.23M')
expect(formatNumber({ input: 18.448, type: NumberType.FiatGasPrice })).toBe('$18.45')
expect(formatNumber({ input: 0.0099, type: NumberType.FiatGasPrice })).toBe('<$0.01')
- expect(formatNumber({ input: 0, type: NumberType.FiatGasPrice })).toBe('$0.00')
+ expect(formatNumber({ input: 0, type: NumberType.FiatGasPrice })).toBe('$0')
})
it('formats gas prices correctly with portugese locale and thai baht currency', () => {
@@ -199,7 +199,7 @@ describe('formatNumber', () => {
expect(formatNumber({ input: 1234567.891, type: NumberType.FiatGasPrice })).toBe('฿\xa01,23\xa0mi')
expect(formatNumber({ input: 18.448, type: NumberType.FiatGasPrice })).toBe('฿\xa018,45')
expect(formatNumber({ input: 0.0099, type: NumberType.FiatGasPrice })).toBe('<฿\xa00,01')
- expect(formatNumber({ input: 0, type: NumberType.FiatGasPrice })).toBe('฿\xa00,00')
+ expect(formatNumber({ input: 0, type: NumberType.FiatGasPrice })).toBe('฿\xa00')
})
it('formats USD token quantities prices correctly', () => {
diff --git a/src/utils/formatNumbers.ts b/src/utils/formatNumbers.ts
index 22ff6290c4..33644aed42 100644
--- a/src/utils/formatNumbers.ts
+++ b/src/utils/formatNumbers.ts
@@ -39,6 +39,14 @@ const NO_DECIMALS: NumberFormatOptions = {
minimumFractionDigits: 0,
}
+const NO_DECIMALS_CURRENCY: NumberFormatOptions = {
+ notation: 'standard',
+ maximumFractionDigits: 0,
+ minimumFractionDigits: 0,
+ currency: 'USD',
+ style: 'currency',
+}
+
const THREE_DECIMALS_NO_TRAILING_ZEROS: NumberFormatOptions = {
notation: 'standard',
maximumFractionDigits: 3,
@@ -262,7 +270,7 @@ const fiatTokenStatsFormatter: FormatterRule[] = [
]
const fiatGasPriceFormatter: FormatterRule[] = [
- { exact: 0, formatterOptions: TWO_DECIMALS_CURRENCY },
+ { exact: 0, formatterOptions: NO_DECIMALS_CURRENCY },
{ upperBound: 0.01, hardCodedInput: { input: 0.01, prefix: '<' }, formatterOptions: TWO_DECIMALS_CURRENCY },
{ upperBound: 1e6, formatterOptions: TWO_DECIMALS_CURRENCY },
{ upperBound: Infinity, formatterOptions: SHORTHAND_CURRENCY_TWO_DECIMALS },
diff --git a/yarn.lock b/yarn.lock
index 50505ed186..9729a92206 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6217,10 +6217,10 @@
resolved "https://registry.yarnpkg.com/@uniswap/token-lists/-/token-lists-1.0.0-beta.33.tgz#966ba96c9ccc8f0e9e09809890b438203f2b1911"
integrity sha512-JQkXcpRI3jFG8y3/CGC4TS8NkDgcxXaOQuYW8Qdvd6DcDiIyg2vVYCG9igFEzF0G6UvxgHkBKC7cWCgzZNYvQg==
-"@uniswap/uniswapx-sdk@^1.3.0":
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/@uniswap/uniswapx-sdk/-/uniswapx-sdk-1.3.0.tgz#22867580c7f5d5ee35d669444d093e09203e1b47"
- integrity sha512-TXH0+3reXA/liY2IRbCRvPVyREDObKSVmd4vEtTD0sPM0NW6ndSowKDH0hWBj2d7lBnSNKz5fp7IOaFT7yHkug==
+"@uniswap/uniswapx-sdk@^1.4.1":
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/@uniswap/uniswapx-sdk/-/uniswapx-sdk-1.4.1.tgz#c5fc50000032aa714ff0cc4b9cd1957128a2a4ec"
+ integrity sha512-M7uuZdozWbXJq8J64KTJ9e0PkYcfe6lx7RBpIsvJaypkGgGDrmU1bAqr1j3sphHlzKTmJCuG7GZBFNcnvzxHLw==
dependencies:
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/providers" "^5.7.0"
@@ -6228,10 +6228,10 @@
"@uniswap/sdk-core" "^4.0.3"
ethers "^5.7.0"
-"@uniswap/universal-router-sdk@^1.5.4", "@uniswap/universal-router-sdk@^1.5.6":
- version "1.5.6"
- resolved "https://registry.yarnpkg.com/@uniswap/universal-router-sdk/-/universal-router-sdk-1.5.6.tgz#274a6ac5df032c34544005fe329aa9e2aac9ade6"
- integrity sha512-ZD27U+kugMRJRVEX0oWZsRCw1n5vBN3I17Q22IWE+w/WhOJSppUr6PLo9u4HRdqXTZET7gubnlRc0LOAEkkSkQ==
+"@uniswap/universal-router-sdk@^1.5.4", "@uniswap/universal-router-sdk@^1.5.8":
+ version "1.5.8"
+ resolved "https://registry.yarnpkg.com/@uniswap/universal-router-sdk/-/universal-router-sdk-1.5.8.tgz#16c62c3883e99073ba8b6e19188cf418b6551847"
+ integrity sha512-9tDDBTXarpdRfJStF5mDCNmsQrCfiIT6HCQN1EPq0tAm2b+JzjRkUzsLpbNpVef066FETc3YjPH6JDPB3CMyyA==
dependencies:
"@uniswap/permit2-sdk" "^1.2.0"
"@uniswap/router-sdk" "^1.6.0"