From a5c5567936655dd6c4b07c403c9ca66e22043072 Mon Sep 17 00:00:00 2001 From: lynn <41491154+lynnshaoyu@users.noreply.github.com> Date: Thu, 13 Oct 2022 12:20:59 -0400 Subject: [PATCH] feat: widget analytics (#4869) * chore: todos for analytics * example * another skeleton logging event * feat: onSwapApprove * feat: widget tracing * feat: better useTrace * feat: max * feat: switch * add trace amd remove onreviewswapclick * onExpandSwapDetails * feat: initial quote * add event properties for wrap * feat: update ack * SWAP_SIGNED * feat: submit * fix: wrap type * chore: tracing * move format fn to utils * fix: remove old background-color flash (#4890) remove old background-color * revert: add back phase0 bug fixes (#4888) * Revert "revert: removing phase0 bug fixes temporarily (#4886)" This reverts commit 06291a15a6af77b8493e14e2cb2c84cea5c10709. * use token amount * Revert "use token amount" This reverts commit f47c00358b29cfa9886bf4e490df4bf70b88e648. * dont render if empty * fix: upgrade pkg to eliminate compile error (#4898) * upgrade pkg * dedup * fix: Remove token selector flash of old ui (#4896) remove token selector flash of old view * fix: Web 1561 logging event for clicking on explore banner toast + WEB-1543 [Explore Banner] String should be sentence case (#4899) * explore banner changes * remove console log * oops * test: run tests on all PRs (#4905) * fix: use correct optimism icon in explore (#4893) * fix: click area should match button effect (#4887) * fix: update font-weight values to match spec (#4863) * fixes. working now verified on console. * fix: mobile tweaks (#4910) * update manifest theme colors to magenta * text spacing and right positioning on very small screens * feat: load token from query data (#4904) * fix: do not fetch balances cross-chain * build: updgrade widget * feat: cleanly load and switch chains from widget * fix: load token from query data * build: trigger checks * fix: do not override native token from query * fix: catch error on switch chain * refactor: useTokenFromActiveNetwork * refactor: defaultToken behavior clarification Co-authored-by: Zach Pomerantz Co-authored-by: Jordan Frankfurt Co-authored-by: vignesh mohankumar Co-authored-by: cartcrom <39385577+cartcrom@users.noreply.github.com> --- src/analytics/Trace.tsx | 22 ++++-- src/analytics/constants.ts | 1 + src/analytics/utils.ts | 62 ++++++++++++++++- src/components/Widget/index.tsx | 85 +++++++++++++++++++++++- src/components/Widget/inputs.tsx | 22 ++++-- src/components/Widget/transactions.ts | 36 +++++++++- src/components/swap/ConfirmSwapModal.tsx | 26 +------- src/components/swap/SwapModalHeader.tsx | 12 +--- src/pages/Swap/index.tsx | 32 +-------- 9 files changed, 218 insertions(+), 80 deletions(-) diff --git a/src/analytics/Trace.tsx b/src/analytics/Trace.tsx index 4e20f99987..d395ee1f2c 100644 --- a/src/analytics/Trace.tsx +++ b/src/analytics/Trace.tsx @@ -20,6 +20,11 @@ export interface ITraceContext { export const TraceContext = createContext({}) +export function useTrace(trace?: ITraceContext): ITraceContext { + const parentTrace = useContext(TraceContext) + return useMemo(() => ({ ...parentTrace, ...trace }), [parentTrace, trace]) +} + type TraceProps = { shouldLogImpression?: boolean // whether to log impression on mount name?: EventName @@ -31,15 +36,24 @@ type TraceProps = { * and propagates the context to child traces. */ export const Trace = memo( - ({ shouldLogImpression, name, children, page, section, element, properties }: PropsWithChildren) => { - const parentTrace = useContext(TraceContext) + ({ + shouldLogImpression, + name, + children, + page, + section, + modal, + element, + properties, + }: PropsWithChildren) => { + const parentTrace = useTrace() const combinedProps = useMemo( () => ({ ...parentTrace, - ...Object.fromEntries(Object.entries({ page, section, element }).filter(([_, v]) => v !== undefined)), + ...Object.fromEntries(Object.entries({ page, section, modal, element }).filter(([_, v]) => v !== undefined)), }), - [element, parentTrace, page, section] + [element, parentTrace, page, modal, section] ) useEffect(() => { diff --git a/src/analytics/constants.ts b/src/analytics/constants.ts index 5e68554390..615641e558 100644 --- a/src/analytics/constants.ts +++ b/src/analytics/constants.ts @@ -89,6 +89,7 @@ export enum PageName { export enum SectionName { CURRENCY_INPUT_PANEL = 'swap-currency-input', CURRENCY_OUTPUT_PANEL = 'swap-currency-output', + WIDGET = 'widget', // alphabetize additional section names. } diff --git a/src/analytics/utils.ts b/src/analytics/utils.ts index e8ad970178..ce94ed7a45 100644 --- a/src/analytics/utils.ts +++ b/src/analytics/utils.ts @@ -1,12 +1,16 @@ -import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core' +import { Trade } from '@uniswap/router-sdk' +import { Currency, CurrencyAmount, Percent, Price, Token, TradeType } from '@uniswap/sdk-core' import { NATIVE_CHAIN_ID } from 'constants/tokens' +import { InterfaceTrade } from 'state/routing/types' +import { computeRealizedPriceImpact } from 'utils/prices' export const getDurationUntilTimestampSeconds = (futureTimestampInSecondsSinceEpoch?: number): number | undefined => { if (!futureTimestampInSecondsSinceEpoch) return undefined return futureTimestampInSecondsSinceEpoch - new Date().getTime() / 1000 } -export const getDurationFromDateMilliseconds = (start: Date): number => { +export const getDurationFromDateMilliseconds = (start?: Date): number | undefined => { + if (!start) return undefined return new Date().getTime() - start.getTime() } @@ -20,3 +24,57 @@ export const getTokenAddress = (currency: Currency) => (currency.isNative ? NATI export const formatPercentInBasisPointsNumber = (percent: Percent): number => parseFloat(percent.toFixed(2)) * 100 export const formatPercentNumber = (percent: Percent): number => parseFloat(percent.toFixed(2)) + +export const getPriceUpdateBasisPoints = ( + prevPrice: Price, + newPrice: Price +): number => { + const changeFraction = newPrice.subtract(prevPrice).divide(prevPrice) + const changePercentage = new Percent(changeFraction.numerator, changeFraction.denominator) + return formatPercentInBasisPointsNumber(changePercentage) +} + +export const formatSwapSignedAnalyticsEventProperties = ({ + trade, + txHash, +}: { + trade: InterfaceTrade | Trade + txHash: string +}) => ({ + transaction_hash: txHash, + token_in_address: getTokenAddress(trade.inputAmount.currency), + token_out_address: getTokenAddress(trade.outputAmount.currency), + 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), + price_impact_basis_points: formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade)), + chain_id: + trade.inputAmount.currency.chainId === trade.outputAmount.currency.chainId + ? trade.inputAmount.currency.chainId + : undefined, +}) + +export const formatSwapQuoteReceivedEventProperties = ( + trade: Trade, + gasUseEstimateUSD?: CurrencyAmount, + fetchingSwapQuoteStartTime?: Date +) => { + return { + token_in_symbol: trade.inputAmount.currency.symbol, + token_out_symbol: trade.outputAmount.currency.symbol, + token_in_address: getTokenAddress(trade.inputAmount.currency), + token_out_address: getTokenAddress(trade.outputAmount.currency), + price_impact_basis_points: trade ? formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade)) : undefined, + estimated_network_fee_usd: gasUseEstimateUSD ? formatToDecimal(gasUseEstimateUSD, 2) : undefined, + chain_id: + trade.inputAmount.currency.chainId === trade.outputAmount.currency.chainId + ? trade.inputAmount.currency.chainId + : undefined, + token_in_amount: formatToDecimal(trade.inputAmount, trade.inputAmount.currency.decimals), + token_out_amount: formatToDecimal(trade.outputAmount, trade.outputAmount.currency.decimals), + quote_latency_milliseconds: fetchingSwapQuoteStartTime + ? getDurationFromDateMilliseconds(fetchingSwapQuoteStartTime) + : undefined, + } +} diff --git a/src/components/Widget/index.tsx b/src/components/Widget/index.tsx index b8ccc8ff37..fbd20dcd2e 100644 --- a/src/components/Widget/index.tsx +++ b/src/components/Widget/index.tsx @@ -2,19 +2,31 @@ // eslint-disable-next-line no-restricted-imports import '@uniswap/widgets/dist/fonts.css' +import { Trade } from '@uniswap/router-sdk' +import { Currency, TradeType } from '@uniswap/sdk-core' import { AddEthereumChainParameter, - Currency, EMPTY_TOKEN_LIST, OnReviewSwapClick, SwapWidget, SwapWidgetSkeleton, } from '@uniswap/widgets' import { useWeb3React } from '@web3-react/core' +import { sendAnalyticsEvent } from 'analytics' +import { EventName, SectionName } from 'analytics/constants' +import { SWAP_PRICE_UPDATE_USER_RESPONSE } from 'analytics/constants' +import { useTrace } from 'analytics/Trace' +import { + formatPercentInBasisPointsNumber, + formatToDecimal, + getPriceUpdateBasisPoints, + getTokenAddress, +} from 'analytics/utils' import { useActiveLocale } from 'hooks/useActiveLocale' import { useCallback } from 'react' import { useIsDarkMode } from 'state/user/hooks' import { DARK_THEME, LIGHT_THEME } from 'theme/widget' +import { computeRealizedPriceImpact } from 'utils/prices' import { switchChain } from 'utils/switchChain' import { useSyncWidgetInputs } from './inputs' @@ -39,6 +51,71 @@ export default function Widget({ defaultToken, onReviewSwapClick }: WidgetProps) const { settings } = useSyncWidgetSettings() const { transactions } = useSyncWidgetTransactions() + const trace = useTrace({ section: SectionName.WIDGET }) + + // TODO(lynnshaoyu): add back onInitialSwapQuote logging once widget side logic is fixed + // in onInitialSwapQuote handler. + + const onApproveToken = useCallback(() => { + const input = inputs.value.INPUT + if (!input) return + const eventProperties = { + chain_id: input.chainId, + token_symbol: input.symbol, + token_address: getTokenAddress(input), + ...trace, + } + sendAnalyticsEvent(EventName.APPROVE_TOKEN_TXN_SUBMITTED, eventProperties) + }, [inputs.value.INPUT, trace]) + + const onExpandSwapDetails = useCallback(() => { + sendAnalyticsEvent(EventName.SWAP_DETAILS_EXPANDED, { ...trace }) + }, [trace]) + + const onSwapPriceUpdateAck = useCallback( + (stale: Trade, update: Trade) => { + const eventProperties = { + chain_id: update.inputAmount.currency.chainId, + response: SWAP_PRICE_UPDATE_USER_RESPONSE.ACCEPTED, + token_in_symbol: update.inputAmount.currency.symbol, + token_out_symbol: update.outputAmount.currency.symbol, + price_update_basis_points: getPriceUpdateBasisPoints(stale.executionPrice, update.executionPrice), + ...trace, + } + sendAnalyticsEvent(EventName.SWAP_PRICE_UPDATE_ACKNOWLEDGED, eventProperties) + }, + [trace] + ) + + const onSubmitSwapClick = useCallback( + (trade: Trade) => { + const eventProperties = { + // TODO(1416): Include undefined values. + estimated_network_fee_usd: undefined, + transaction_deadline_seconds: undefined, + token_in_address: getTokenAddress(trade.inputAmount.currency), + token_out_address: getTokenAddress(trade.outputAmount.currency), + 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_in_amount_usd: undefined, + token_out_amount_usd: undefined, + price_impact_basis_points: formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade)), + allowed_slippage_basis_points: undefined, + is_auto_router_api: undefined, + is_auto_slippage: undefined, + chain_id: trade.inputAmount.currency.chainId, + // duration should be getDurationFromDateMilliseconds(initialQuoteDate) once initialQuoteDate + // is made available from TODO above for onInitialSwapQuote logging. + duration_from_first_quote_to_swap_submission_milliseconds: undefined, + swap_quote_block_number: undefined, + ...trace, + } + sendAnalyticsEvent(EventName.SWAP_SUBMITTED_BUTTON_CLICKED, eventProperties) + }, + [trace] + ) const onSwitchChain = useCallback( // TODO: Widget should not break if this rejects - upstream the catch to ignore it. ({ chainId }: AddEthereumChainParameter) => switchChain(connector, Number(chainId)).catch(() => undefined), @@ -58,7 +135,6 @@ export default function Widget({ defaultToken, onReviewSwapClick }: WidgetProps) width={WIDGET_WIDTH} locale={locale} theme={theme} - onReviewSwapClick={onReviewSwapClick} // defaultChainId is excluded - it is always inferred from the passed provider provider={provider} onSwitchChain={onSwitchChain} @@ -66,6 +142,11 @@ export default function Widget({ defaultToken, onReviewSwapClick }: WidgetProps) {...inputs} {...settings} {...transactions} + onExpandSwapDetails={onExpandSwapDetails} + onReviewSwapClick={onReviewSwapClick} + onSubmitSwapClick={onSubmitSwapClick} + onSwapApprove={onApproveToken} + onSwapPriceUpdateAck={onSwapPriceUpdateAck} /> {tokenSelector} diff --git a/src/components/Widget/inputs.tsx b/src/components/Widget/inputs.tsx index f4ae416d0f..57442d9aaf 100644 --- a/src/components/Widget/inputs.tsx +++ b/src/components/Widget/inputs.tsx @@ -1,4 +1,7 @@ import { Currency, Field, SwapController, SwapEventHandlers, TradeType } from '@uniswap/widgets' +import { sendAnalyticsEvent } from 'analytics' +import { EventName, SectionName } from 'analytics/constants' +import { useTrace } from 'analytics/Trace' import CurrencySearchModal from 'components/SearchModal/CurrencySearchModal' import { useCallback, useEffect, useMemo, useState } from 'react' @@ -9,12 +12,20 @@ const EMPTY_AMOUNT = '' * Treats the Widget as a controlled component, using the app's own token selector for selection. */ export function useSyncWidgetInputs(defaultToken?: Currency) { + const trace = useTrace({ section: SectionName.WIDGET }) + const [type, setType] = useState(TradeType.EXACT_INPUT) const [amount, setAmount] = useState(EMPTY_AMOUNT) - const onAmountChange = useCallback((field: Field, amount: string) => { - setType(toTradeType(field)) - setAmount(amount) - }, []) + const onAmountChange = useCallback( + (field: Field, amount: string, origin?: 'max') => { + if (origin === 'max') { + sendAnalyticsEvent(EventName.SWAP_MAX_TOKEN_AMOUNT_SELECTED, { ...trace }) + } + setType(toTradeType(field)) + setAmount(amount) + }, + [trace] + ) const [tokens, setTokens] = useState<{ [Field.INPUT]?: Currency; [Field.OUTPUT]?: Currency }>({ [Field.OUTPUT]: defaultToken, @@ -30,12 +41,13 @@ export function useSyncWidgetInputs(defaultToken?: Currency) { }, [defaultToken]) const onSwitchTokens = useCallback(() => { + sendAnalyticsEvent(EventName.SWAP_TOKENS_REVERSED, { ...trace }) setType((type) => invertTradeType(type)) setTokens((tokens) => ({ [Field.INPUT]: tokens[Field.OUTPUT], [Field.OUTPUT]: tokens[Field.INPUT], })) - }, []) + }, [trace]) const [selectingField, setSelectingField] = useState() const otherField = useMemo(() => (selectingField === Field.INPUT ? Field.OUTPUT : Field.INPUT), [selectingField]) diff --git a/src/components/Widget/transactions.ts b/src/components/Widget/transactions.ts index 7f84aa1b29..8f2d4c7d22 100644 --- a/src/components/Widget/transactions.ts +++ b/src/components/Widget/transactions.ts @@ -6,6 +6,12 @@ import { TransactionType as WidgetTransactionType, } from '@uniswap/widgets' import { useWeb3React } from '@web3-react/core' +import { sendAnalyticsEvent } from 'analytics' +import { EventName, SectionName } from 'analytics/constants' +import { useTrace } from 'analytics/Trace' +import { formatToDecimal, getTokenAddress } from 'analytics/utils' +import { formatSwapSignedAnalyticsEventProperties } from 'analytics/utils' +import { WrapType } from 'hooks/useWrapCallback' import { useCallback, useMemo } from 'react' import { useTransactionAdder } from 'state/transactions/hooks' import { @@ -18,6 +24,8 @@ import { currencyId } from 'utils/currencyId' /** Integrates the Widget's transactions, showing the widget's transactions in the app. */ export function useSyncWidgetTransactions() { + const trace = useTrace({ section: SectionName.WIDGET }) + const { chainId } = useWeb3React() const addTransaction = useTransactionAdder() @@ -28,8 +36,23 @@ export function useSyncWidgetTransactions() { if (!type || !response) { return } else if (type === WidgetTransactionType.WRAP || type === WidgetTransactionType.UNWRAP) { - const { amount } = transaction.info + const { type, amount: transactionAmount } = transaction.info + const eventProperties = { + // get this info from widget handlers + token_in_address: getTokenAddress(transactionAmount.currency), + token_out_address: getTokenAddress(transactionAmount.currency.wrapped), + token_in_symbol: transactionAmount.currency.symbol, + token_out_symbol: transactionAmount.currency.wrapped.symbol, + chain_id: transactionAmount.currency.chainId, + amount: transactionAmount + ? formatToDecimal(transactionAmount, transactionAmount?.currency.decimals) + : undefined, + type: type === WidgetTransactionType.WRAP ? WrapType.WRAP : WrapType.UNWRAP, + ...trace, + } + sendAnalyticsEvent(EventName.WRAP_TOKEN_TXN_SUBMITTED, eventProperties) + const { amount } = transaction.info addTransaction(response, { type: AppTransactionType.WRAP, unwrapped: type === WidgetTransactionType.UNWRAP, @@ -38,6 +61,15 @@ export function useSyncWidgetTransactions() { } as WrapTransactionInfo) } else if (type === WidgetTransactionType.SWAP) { const { slippageTolerance, trade, tradeType } = transaction.info + + const eventProperties = { + ...formatSwapSignedAnalyticsEventProperties({ + trade, + txHash: transaction.receipt?.transactionHash ?? '', + }), + ...trace, + } + sendAnalyticsEvent(EventName.SWAP_SIGNED, eventProperties) const baseTxInfo = { type: AppTransactionType.SWAP, tradeType, @@ -61,7 +93,7 @@ export function useSyncWidgetTransactions() { } } }, - [addTransaction, chainId] + [addTransaction, chainId, trace] ) const txHandlers: TransactionEventHandlers = useMemo(() => ({ onTxSubmit }), [onTxSubmit]) diff --git a/src/components/swap/ConfirmSwapModal.tsx b/src/components/swap/ConfirmSwapModal.tsx index 037c2dd36a..4272d7e5ee 100644 --- a/src/components/swap/ConfirmSwapModal.tsx +++ b/src/components/swap/ConfirmSwapModal.tsx @@ -5,10 +5,9 @@ import { sendAnalyticsEvent } from 'analytics' import { ModalName } from 'analytics/constants' import { EventName } from 'analytics/constants' import { Trace } from 'analytics/Trace' -import { formatPercentInBasisPointsNumber, formatToDecimal, getTokenAddress } from 'analytics/utils' +import { formatSwapSignedAnalyticsEventProperties } from 'analytics/utils' import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react' import { InterfaceTrade } from 'state/routing/types' -import { computeRealizedPriceImpact } from 'utils/prices' import { tradeMeaningfullyDiffers } from 'utils/tradeMeaningFullyDiffer' import TransactionConfirmationModal, { @@ -18,27 +17,6 @@ import TransactionConfirmationModal, { import SwapModalFooter from './SwapModalFooter' import SwapModalHeader from './SwapModalHeader' -const formatAnalyticsEventProperties = ({ - trade, - txHash, -}: { - trade: InterfaceTrade - txHash: string -}) => ({ - transaction_hash: txHash, - token_in_address: getTokenAddress(trade.inputAmount.currency), - token_out_address: getTokenAddress(trade.outputAmount.currency), - 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), - price_impact_basis_points: formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade)), - chain_id: - trade.inputAmount.currency.chainId === trade.outputAmount.currency.chainId - ? trade.inputAmount.currency.chainId - : undefined, -}) - export default function ConfirmSwapModal({ trade, originalTrade, @@ -149,7 +127,7 @@ export default function ConfirmSwapModal({ useEffect(() => { if (!attemptingTxn && isOpen && txHash && trade && txHash !== lastTxnHashLogged) { - sendAnalyticsEvent(EventName.SWAP_SIGNED, formatAnalyticsEventProperties({ trade, txHash })) + sendAnalyticsEvent(EventName.SWAP_SIGNED, formatSwapSignedAnalyticsEventProperties({ trade, txHash })) setLastTxnHashLogged(txHash) } }, [attemptingTxn, isOpen, txHash, trade, lastTxnHashLogged]) diff --git a/src/components/swap/SwapModalHeader.tsx b/src/components/swap/SwapModalHeader.tsx index 72f1d61ad8..2be5ac901a 100644 --- a/src/components/swap/SwapModalHeader.tsx +++ b/src/components/swap/SwapModalHeader.tsx @@ -1,9 +1,8 @@ import { Trans } from '@lingui/macro' import { Currency, Percent, TradeType } from '@uniswap/sdk-core' -import { Price } from '@uniswap/sdk-core' import { sendAnalyticsEvent } from 'analytics' import { EventName, SWAP_PRICE_UPDATE_USER_RESPONSE } from 'analytics/constants' -import { formatPercentInBasisPointsNumber } from 'analytics/utils' +import { getPriceUpdateBasisPoints } from 'analytics/utils' import { RedesignVariant, useRedesignFlag } from 'featureFlags/flags/redesign' import { useEffect, useState } from 'react' import { AlertTriangle, ArrowDown } from 'react-feather' @@ -58,15 +57,6 @@ const formatAnalyticsEventProperties = ( price_update_basis_points: priceUpdate, }) -const getPriceUpdateBasisPoints = ( - prevPrice: Price, - newPrice: Price -): number => { - const changeFraction = newPrice.subtract(prevPrice).divide(prevPrice) - const changePercentage = new Percent(changeFraction.numerator, changeFraction.denominator) - return formatPercentInBasisPointsNumber(changePercentage) -} - export default function SwapModalHeader({ trade, shouldLogModalCloseEvent, diff --git a/src/pages/Swap/index.tsx b/src/pages/Swap/index.tsx index 065f32278f..07f5bd121c 100644 --- a/src/pages/Swap/index.tsx +++ b/src/pages/Swap/index.tsx @@ -6,12 +6,7 @@ import { sendAnalyticsEvent } from 'analytics' import { ElementName, Event, EventName, PageName, SectionName } from 'analytics/constants' import { Trace } from 'analytics/Trace' import { TraceEvent } from 'analytics/TraceEvent' -import { - formatPercentInBasisPointsNumber, - formatToDecimal, - getDurationFromDateMilliseconds, - getTokenAddress, -} from 'analytics/utils' +import { formatSwapQuoteReceivedEventProperties } from 'analytics/utils' import { sendEvent } from 'components/analytics' import { NetworkAlert } from 'components/NetworkAlert/NetworkAlert' import PriceImpactWarning from 'components/swap/PriceImpactWarning' @@ -153,29 +148,6 @@ function largerPercentValue(a?: Percent, b?: Percent) { return undefined } -const formatSwapQuoteReceivedEventProperties = ( - trade: InterfaceTrade, - fetchingSwapQuoteStartTime: Date | undefined -) => { - return { - token_in_symbol: trade.inputAmount.currency.symbol, - token_out_symbol: trade.outputAmount.currency.symbol, - token_in_address: getTokenAddress(trade.inputAmount.currency), - token_out_address: getTokenAddress(trade.outputAmount.currency), - price_impact_basis_points: trade ? formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade)) : undefined, - estimated_network_fee_usd: trade.gasUseEstimateUSD ? formatToDecimal(trade.gasUseEstimateUSD, 2) : undefined, - chain_id: - trade.inputAmount.currency.chainId === trade.outputAmount.currency.chainId - ? trade.inputAmount.currency.chainId - : undefined, - token_in_amount: formatToDecimal(trade.inputAmount, trade.inputAmount.currency.decimals), - token_out_amount: formatToDecimal(trade.outputAmount, trade.outputAmount.currency.decimals), - quote_latency_milliseconds: fetchingSwapQuoteStartTime - ? getDurationFromDateMilliseconds(fetchingSwapQuoteStartTime) - : undefined, - } -} - const TRADE_STRING = 'SwapRouter' export default function Swap() { @@ -510,7 +482,7 @@ export default function Swap() { // Log swap quote. sendAnalyticsEvent( EventName.SWAP_QUOTE_RECEIVED, - formatSwapQuoteReceivedEventProperties(trade, fetchingSwapQuoteStartTime) + formatSwapQuoteReceivedEventProperties(trade, trade.gasUseEstimateUSD ?? undefined, fetchingSwapQuoteStartTime) ) // Latest swap quote has just been logged, so we don't need to log the current trade anymore // unless user inputs change again and a new trade is in the process of being generated.