Skip to content

Commit 6cb6faa

Browse files
authored
feat: add time / duration based event properties to swap events (#4193)
* init commit * remove absolute value in date calc * all the events are now logged properly plus changed native token address to NATIVE * add documentation line * remove unnecessary prop * respond to vm comments * merge and rename util method * respond to vm comments again
1 parent 3c5cc21 commit 6cb6faa

File tree

8 files changed

+124
-79
lines changed

8 files changed

+124
-79
lines changed

src/components/AmplitudeAnalytics/constants.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ export enum WALLET_CONNECTION_RESULT {
4040
FAILED = 'Failed',
4141
}
4242

43+
export const NATIVE_CHAIN_ID = 'NATIVE'
44+
4345
export enum SWAP_PRICE_UPDATE_USER_RESPONSE {
4446
ACCEPTED = 'Accepted',
4547
REJECTED = 'Rejected',
@@ -89,7 +91,6 @@ export const enum ElementName {
8991
SWAP_BUTTON = 'swap-button',
9092
SWAP_DETAILS_DROPDOWN = 'swap-details-dropdown',
9193
SWAP_TOKENS_REVERSE_ARROW_BUTTON = 'swap-tokens-reverse-arrow-button',
92-
SWAP_TRADE_PRICE_ROW = 'swap-trade-price-row',
9394
TOKEN_SELECTOR_ROW = 'token-selector-row',
9495
WALLET_TYPE_OPTION = 'wallet-type-option',
9596
// alphabetize additional element names.
Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
22

3-
export const getDurationTillTimestampSinceEpoch = (futureTimestampSinceEpoch?: number): number | undefined => {
4-
if (!futureTimestampSinceEpoch) return undefined
5-
return futureTimestampSinceEpoch - new Date().getTime() / 1000
3+
import { NATIVE_CHAIN_ID } from './constants'
4+
5+
export const getDurationUntilTimestampSeconds = (futureTimestampInSecondsSinceEpoch?: number): number | undefined => {
6+
if (!futureTimestampInSecondsSinceEpoch) return undefined
7+
return futureTimestampInSecondsSinceEpoch - new Date().getTime() / 1000
8+
}
9+
10+
export const getDurationFromDateMilliseconds = (start: Date): number => {
11+
return new Date().getTime() - start.getTime()
612
}
713

8-
export const getNumberFormattedToDecimalPlace = (
14+
export const formatToDecimal = (
915
intialNumberObject: Percent | CurrencyAmount<Token | Currency>,
1016
decimalPlace: number
1117
): number => parseFloat(intialNumberObject.toFixed(decimalPlace))
1218

13-
export const formatPercentInBasisPointsNumber = (percent: Percent): number => parseFloat(percent.toFixed(6)) * 100
19+
export const getTokenAddress = (currency: Currency) => (currency.isNative ? NATIVE_CHAIN_ID : currency.address)
20+
21+
export const formatPercentInBasisPointsNumber = (percent: Percent): number => parseFloat(percent.toFixed(2)) * 100

src/components/SearchModal/CommonBases.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { Currency, Token } from '@uniswap/sdk-core'
1+
import { Currency } from '@uniswap/sdk-core'
22
import { ElementName, Event, EventName } from 'components/AmplitudeAnalytics/constants'
33
import { TraceEvent } from 'components/AmplitudeAnalytics/TraceEvent'
4+
import { getTokenAddress } from 'components/AmplitudeAnalytics/utils'
45
import { AutoColumn } from 'components/Column'
56
import CurrencyLogo from 'components/CurrencyLogo'
67
import { AutoRow } from 'components/Row'
@@ -33,15 +34,10 @@ const BaseWrapper = styled.div<{ disable?: boolean }>`
3334
filter: ${({ disable }) => disable && 'grayscale(1)'};
3435
`
3536

36-
const formatAnalyticsEventProperties = (
37-
currency: Currency,
38-
tokenAddress: string | undefined,
39-
searchQuery: string,
40-
isAddressSearch: string | false
41-
) => ({
37+
const formatAnalyticsEventProperties = (currency: Currency, searchQuery: string, isAddressSearch: string | false) => ({
4238
token_symbol: currency?.symbol,
4339
token_chain_id: currency?.chainId,
44-
...(tokenAddress ? { token_address: tokenAddress } : {}),
40+
token_address: getTokenAddress(currency),
4541
is_suggested_token: true,
4642
is_selected_from_list: false,
4743
is_imported_by_user: false,
@@ -70,13 +66,12 @@ export default function CommonBases({
7066
<AutoRow gap="4px">
7167
{bases.map((currency: Currency) => {
7268
const isSelected = selectedCurrency?.equals(currency)
73-
const tokenAddress = currency instanceof Token ? currency?.address : undefined
7469

7570
return (
7671
<TraceEvent
7772
events={[Event.onClick, Event.onKeyPress]}
7873
name={EventName.TOKEN_SELECTED}
79-
properties={formatAnalyticsEventProperties(currency, tokenAddress, searchQuery, isAddressSearch)}
74+
properties={formatAnalyticsEventProperties(currency, searchQuery, isAddressSearch)}
8075
element={ElementName.COMMON_BASES_CURRENCY_BUTTON}
8176
key={currencyId(currency)}
8277
>

src/components/SearchModal/CurrencySearch.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,8 @@ export function CurrencySearch({
191191
}, [])
192192

193193
return (
194-
<Trace name={EventName.TOKEN_SELECTOR_OPENED} modal={ModalName.TOKEN_SELECTOR} shouldLogImpression={true}>
195-
<ContentWrapper>
194+
<ContentWrapper>
195+
<Trace name={EventName.TOKEN_SELECTOR_OPENED} modal={ModalName.TOKEN_SELECTOR} shouldLogImpression>
196196
<PaddedColumn gap="16px">
197197
<RowBetween>
198198
<Text fontWeight={500} fontSize={16}>
@@ -270,7 +270,7 @@ export function CurrencySearch({
270270
</ButtonText>
271271
</Row>
272272
</Footer>
273-
</ContentWrapper>
274-
</Trace>
273+
</Trace>
274+
</ContentWrapper>
275275
)
276276
}

src/components/swap/ConfirmSwapModal.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export default function ConfirmSwapModal({
2626
isOpen,
2727
attemptingTxn,
2828
txHash,
29+
swapQuoteReceivedDate,
2930
}: {
3031
isOpen: boolean
3132
trade: InterfaceTrade<Currency, Currency, TradeType> | undefined
@@ -38,6 +39,7 @@ export default function ConfirmSwapModal({
3839
onConfirm: () => void
3940
swapErrorMessage: ReactNode | undefined
4041
onDismiss: () => void
42+
swapQuoteReceivedDate: Date | undefined
4143
}) {
4244
// shouldLogModalCloseEvent lets the child SwapModalHeader component know when modal has been closed
4345
// and an event triggered by modal closing should be logged.
@@ -75,9 +77,10 @@ export default function ConfirmSwapModal({
7577
allowedSlippage={allowedSlippage}
7678
disabledConfirm={showAcceptChanges}
7779
swapErrorMessage={swapErrorMessage}
80+
swapQuoteReceivedDate={swapQuoteReceivedDate}
7881
/>
7982
) : null
80-
}, [onConfirm, showAcceptChanges, swapErrorMessage, trade, allowedSlippage, txHash])
83+
}, [onConfirm, showAcceptChanges, swapErrorMessage, trade, allowedSlippage, txHash, swapQuoteReceivedDate])
8184

8285
// text to show while loading
8386
const pendingText = (

src/components/swap/SwapDetailsDropdown.tsx

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ import { Trans } from '@lingui/macro'
22
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
33
import { useWeb3React } from '@web3-react/core'
44
import { ElementName, Event, EventName } from 'components/AmplitudeAnalytics/constants'
5-
import { Trace } from 'components/AmplitudeAnalytics/Trace'
65
import { TraceEvent } from 'components/AmplitudeAnalytics/TraceEvent'
7-
import { formatPercentInBasisPointsNumber, getNumberFormattedToDecimalPlace } from 'components/AmplitudeAnalytics/utils'
86
import AnimatedDropdown from 'components/AnimatedDropdown'
97
import Card, { OutlineCard } from 'components/Card'
108
import { AutoColumn } from 'components/Column'
@@ -13,15 +11,13 @@ import Row, { RowBetween, RowFixed } from 'components/Row'
1311
import { MouseoverTooltipContent } from 'components/Tooltip'
1412
import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from 'constants/chains'
1513
import { darken } from 'polished'
16-
import { useEffect, useState } from 'react'
14+
import { useState } from 'react'
1715
import { ChevronDown, Info } from 'react-feather'
1816
import { InterfaceTrade } from 'state/routing/types'
1917
import styled, { keyframes, useTheme } from 'styled-components/macro'
2018
import { HideSmall, ThemedText } from 'theme'
21-
import { computeRealizedLPFeePercent } from 'utils/prices'
2219

2320
import { AdvancedSwapDetails } from './AdvancedSwapDetails'
24-
import { getPriceImpactPercent } from './AdvancedSwapDetails'
2521
import GasEstimateBadge from './GasEstimateBadge'
2622
import { ResponsiveTooltipContainer } from './styleds'
2723
import SwapRoute from './SwapRoute'
@@ -124,29 +120,6 @@ interface SwapDetailsInlineProps {
124120
allowedSlippage: Percent
125121
}
126122

127-
const formatAnalyticsEventProperties = (trade: InterfaceTrade<Currency, Currency, TradeType>) => {
128-
const lpFeePercent = trade ? computeRealizedLPFeePercent(trade) : undefined
129-
return {
130-
token_in_symbol: trade.inputAmount.currency.symbol,
131-
token_out_symbol: trade.outputAmount.currency.symbol,
132-
token_in_address: trade.inputAmount.currency.isToken ? trade.inputAmount.currency.address : undefined,
133-
token_out_address: trade.outputAmount.currency.isToken ? trade.outputAmount.currency.address : undefined,
134-
price_impact_basis_points: lpFeePercent
135-
? formatPercentInBasisPointsNumber(getPriceImpactPercent(lpFeePercent, trade))
136-
: undefined,
137-
estimated_network_fee_usd: trade.gasUseEstimateUSD
138-
? getNumberFormattedToDecimalPlace(trade.gasUseEstimateUSD, 2)
139-
: undefined,
140-
chain_id:
141-
trade.inputAmount.currency.chainId === trade.outputAmount.currency.chainId
142-
? trade.inputAmount.currency.chainId
143-
: undefined,
144-
token_in_amount: getNumberFormattedToDecimalPlace(trade.inputAmount, trade.inputAmount.currency.decimals),
145-
token_out_amount: getNumberFormattedToDecimalPlace(trade.outputAmount, trade.outputAmount.currency.decimals),
146-
// TODO(lynnshaoyu): Implement quote_latency_milliseconds.
147-
}
148-
}
149-
150123
export default function SwapDetailsDropdown({
151124
trade,
152125
syncing,
@@ -158,11 +131,6 @@ export default function SwapDetailsDropdown({
158131
const theme = useTheme()
159132
const { chainId } = useWeb3React()
160133
const [showDetails, setShowDetails] = useState(false)
161-
const [isFirstPriceFetch, setIsFirstPriceFetch] = useState(true)
162-
163-
useEffect(() => {
164-
if (isFirstPriceFetch && syncing) setIsFirstPriceFetch(false)
165-
}, [isFirstPriceFetch, syncing])
166134

167135
return (
168136
<Wrapper>
@@ -206,18 +174,11 @@ export default function SwapDetailsDropdown({
206174
)}
207175
{trade ? (
208176
<LoadingOpacityContainer $loading={syncing}>
209-
<Trace
210-
name={EventName.SWAP_QUOTE_RECEIVED}
211-
element={ElementName.SWAP_TRADE_PRICE_ROW}
212-
properties={formatAnalyticsEventProperties(trade)}
213-
shouldLogImpression={!loading && !syncing && isFirstPriceFetch}
214-
>
215-
<TradePrice
216-
price={trade.executionPrice}
217-
showInverted={showInverted}
218-
setShowInverted={setShowInverted}
219-
/>
220-
</Trace>
177+
<TradePrice
178+
price={trade.executionPrice}
179+
showInverted={showInverted}
180+
setShowInverted={setShowInverted}
181+
/>
221182
</LoadingOpacityContainer>
222183
) : loading || syncing ? (
223184
<ThemedText.DeprecatedMain fontSize={14}>

src/components/swap/SwapModalFooter.tsx

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import { Event } from 'components/AmplitudeAnalytics/constants'
55
import { TraceEvent } from 'components/AmplitudeAnalytics/TraceEvent'
66
import {
77
formatPercentInBasisPointsNumber,
8-
getDurationTillTimestampSinceEpoch,
9-
getNumberFormattedToDecimalPlace,
8+
formatToDecimal,
9+
getDurationFromDateMilliseconds,
10+
getDurationUntilTimestampSeconds,
11+
getTokenAddress,
1012
} from 'components/AmplitudeAnalytics/utils'
1113
import { useStablecoinValue } from 'hooks/useStablecoinPrice'
1214
import useTransactionDeadline from 'hooks/useTransactionDeadline'
@@ -31,6 +33,7 @@ interface AnalyticsEventProps {
3133
tokenInAmountUsd: string | undefined
3234
tokenOutAmountUsd: string | undefined
3335
lpFeePercent: Percent
36+
swapQuoteReceivedDate: Date | undefined
3437
}
3538

3639
const formatAnalyticsEventProperties = ({
@@ -43,20 +46,19 @@ const formatAnalyticsEventProperties = ({
4346
tokenInAmountUsd,
4447
tokenOutAmountUsd,
4548
lpFeePercent,
49+
swapQuoteReceivedDate,
4650
}: AnalyticsEventProps) => ({
47-
estimated_network_fee_usd: trade.gasUseEstimateUSD
48-
? getNumberFormattedToDecimalPlace(trade.gasUseEstimateUSD, 2)
49-
: undefined,
51+
estimated_network_fee_usd: trade.gasUseEstimateUSD ? formatToDecimal(trade.gasUseEstimateUSD, 2) : undefined,
5052
transaction_hash: txHash,
51-
transaction_deadline_seconds: getDurationTillTimestampSinceEpoch(transactionDeadlineSecondsSinceEpoch),
53+
transaction_deadline_seconds: getDurationUntilTimestampSeconds(transactionDeadlineSecondsSinceEpoch),
5254
token_in_amount_usd: tokenInAmountUsd ? parseFloat(tokenInAmountUsd) : undefined,
5355
token_out_amount_usd: tokenOutAmountUsd ? parseFloat(tokenOutAmountUsd) : undefined,
54-
token_in_address: trade.inputAmount.currency.isToken ? trade.inputAmount.currency.address : undefined,
55-
token_out_address: trade.outputAmount.currency.isToken ? trade.outputAmount.currency.address : undefined,
56+
token_in_address: getTokenAddress(trade.inputAmount.currency),
57+
token_out_address: getTokenAddress(trade.outputAmount.currency),
5658
token_in_symbol: trade.inputAmount.currency.symbol,
5759
token_out_symbol: trade.outputAmount.currency.symbol,
58-
token_in_amount: getNumberFormattedToDecimalPlace(trade.inputAmount, trade.inputAmount.currency.decimals),
59-
token_out_amount: getNumberFormattedToDecimalPlace(trade.outputAmount, trade.outputAmount.currency.decimals),
60+
token_in_amount: formatToDecimal(trade.inputAmount, trade.inputAmount.currency.decimals),
61+
token_out_amount: formatToDecimal(trade.outputAmount, trade.outputAmount.currency.decimals),
6062
price_impact_basis_points: formatPercentInBasisPointsNumber(getPriceImpactPercent(lpFeePercent, trade)),
6163
allowed_slippage_basis_points: formatPercentInBasisPointsNumber(allowedSlippage),
6264
is_auto_router_api: isAutoRouterApi,
@@ -65,7 +67,9 @@ const formatAnalyticsEventProperties = ({
6567
trade.inputAmount.currency.chainId === trade.outputAmount.currency.chainId
6668
? trade.inputAmount.currency.chainId
6769
: undefined,
68-
// TODO(lynnshaoyu): implement duration_from_first_quote_to_swap_submission_seconds
70+
duration_from_first_quote_to_swap_submission_milliseconds: swapQuoteReceivedDate
71+
? getDurationFromDateMilliseconds(swapQuoteReceivedDate)
72+
: undefined,
6973
})
7074

7175
export default function SwapModalFooter({
@@ -75,13 +79,15 @@ export default function SwapModalFooter({
7579
onConfirm,
7680
swapErrorMessage,
7781
disabledConfirm,
82+
swapQuoteReceivedDate,
7883
}: {
7984
trade: InterfaceTrade<Currency, Currency, TradeType>
8085
txHash: string | undefined
8186
allowedSlippage: Percent
8287
onConfirm: () => void
8388
swapErrorMessage: ReactNode | undefined
8489
disabledConfirm: boolean
90+
swapQuoteReceivedDate: Date | undefined
8591
}) {
8692
const transactionDeadlineSecondsSinceEpoch = useTransactionDeadline()?.toNumber() // in seconds since epoch
8793
const isAutoSlippage = useUserSlippageTolerance() === 'auto'
@@ -107,6 +113,7 @@ export default function SwapModalFooter({
107113
tokenInAmountUsd,
108114
tokenOutAmountUsd,
109115
lpFeePercent,
116+
swapQuoteReceivedDate,
110117
})}
111118
>
112119
<ButtonError

0 commit comments

Comments
 (0)