feat: swap quote event (#7174)

* wip: more metrics

* wip: SWAP_INPUT_FIRST_USED

* feat: track elapsed times

* feat: add e2e test

* fix: order of logging

* feat: swap quote request logging

* feat: e2e test

* feat: another property

* test: test events separately

* fix: dont log for price quotes
This commit is contained in:
eddie 2023-08-18 11:47:45 -07:00 committed by GitHub
parent 3f05a88409
commit 877e000da6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 57 additions and 4 deletions

@ -3,8 +3,8 @@ import { SwapEventName } from '@uniswap/analytics-events'
import { USDC_MAINNET } from '../../../src/constants/tokens'
import { getTestSelector } from '../../utils'
describe('time-to-swap logging', () => {
it('completes two swaps and verifies the TTS logging for the first', () => {
describe('swap flow logging', () => {
it('completes two swaps and verifies the TTS logging for the first, plus all intermediate steps along the way', () => {
cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${USDC_MAINNET.address}`)
cy.hardhat()
@ -13,6 +13,24 @@ describe('time-to-swap logging', () => {
cy.get('#swap-currency-output .token-amount-input').type('1').should('have.value', '1')
cy.get('#swap-currency-input .token-amount-input').should('not.have.value', '')
// Verify first swap action
cy.waitForAmplitudeEvent(SwapEventName.SWAP_FIRST_ACTION).then((event: any) => {
cy.wrap(event.event_properties).should('have.property', 'time_to_first_swap_action')
cy.wrap(event.event_properties.time_to_first_swap_action).should('be.a', 'number')
cy.wrap(event.event_properties.time_to_first_swap_action).should('be.gte', 0)
})
// Verify Swap Quote
cy.waitForAmplitudeEvent(SwapEventName.SWAP_QUOTE_FETCH).then((event: any) => {
// Price quotes don't include these values, so we only verify the types if they exist
if (event.event_properties.time_to_first_quote_request) {
cy.wrap(event.event_properties.time_to_first_quote_request).should('be.a', 'number')
cy.wrap(event.event_properties.time_to_first_quote_request).should('be.gte', 0)
cy.wrap(event.event_properties.time_to_first_quote_request_since_first_input).should('be.a', 'number')
cy.wrap(event.event_properties.time_to_first_quote_request_since_first_input).should('be.gte', 0)
}
})
// Submit transaction
cy.get('#swap-button').click()
cy.contains('Confirm swap').click()
@ -31,10 +49,19 @@ describe('time-to-swap logging', () => {
})
// Second swap in the session:
// Enter amount to swap
cy.get('#swap-currency-output .token-amount-input').clear().type('1').should('have.value', '1')
// Enter amount to swap (different from first trade, to trigger a new quote request)
cy.get('#swap-currency-output .token-amount-input').clear().type('10').should('have.value', '10')
cy.get('#swap-currency-input .token-amount-input').should('not.have.value', '')
// Verify second Swap Quote
cy.waitForAmplitudeEvent(SwapEventName.SWAP_QUOTE_FETCH).then((event: any) => {
// Price quotes don't include these values, so we only verify the types if they exist
if (event.event_properties.time_to_first_quote_request) {
cy.wrap(event.event_properties.time_to_first_quote_request).should('be.undefined')
cy.wrap(event.event_properties.time_to_first_quote_request_since_first_input).should('be.undefined')
}
})
// Submit transaction
cy.get('#swap-button').click()
cy.contains('Confirm swap').click()

@ -4,6 +4,7 @@ import { TradeType } from '@uniswap/sdk-core'
import { isUniswapXSupportedChain } from 'constants/chains'
import { getClientSideQuote } from 'lib/hooks/routing/clientSideSmartOrderRouter'
import ms from 'ms'
import { logSwapQuoteRequest } from 'tracing/swapFlowLoggers'
import { trace } from 'tracing/trace'
import {
@ -119,6 +120,7 @@ export const routingApi = createApi({
},
async queryFn(args, _api, _extraOptions, fetch) {
let fellBack = false
logSwapQuoteRequest(args.tokenInChainId, args.routerPreference)
const quoteStartMark = performance.mark(`quote-fetch-start-${Date.now()}`)
if (shouldUseAPIRouter(args)) {
fellBack = true

@ -1,5 +1,6 @@
import { calculateElapsedTimeWithPerformanceMark } from './utils'
// These events should happen in this order.
export enum SwapEventType {
/**
* Full list of actions that can trigger the FIRST_SWAP_ACTION moment:

@ -1,6 +1,7 @@
import { SwapEventName } from '@uniswap/analytics-events'
import { ITraceContext } from 'analytics'
import { sendAnalyticsEvent } from 'analytics'
import { INTERNAL_ROUTER_PREFERENCE_PRICE, RouterPreference } from 'state/routing/types'
import { SwapEventTimestampTracker, SwapEventType } from './SwapEventTimestampTracker'
@ -32,3 +33,25 @@ export function maybeLogFirstSwapAction(analyticsContext: ITraceContext) {
})
}
}
export function logSwapQuoteRequest(
chainId: number,
routerPreference: RouterPreference | typeof INTERNAL_ROUTER_PREFERENCE_PRICE
) {
let performanceMetrics = {}
if (routerPreference !== INTERNAL_ROUTER_PREFERENCE_PRICE) {
const hasSetSwapQuote = tracker.hasTimestamp(SwapEventType.FIRST_QUOTE_FETCH_STARTED)
const elapsedTime = tracker.setElapsedTime(SwapEventType.FIRST_QUOTE_FETCH_STARTED)
performanceMetrics = {
// We only log the time_to_first_quote_request metric for the first quote request of a session.
time_to_first_quote_request: hasSetSwapQuote ? undefined : elapsedTime,
time_to_first_quote_request_since_first_input: hasSetSwapQuote
? undefined
: tracker.getElapsedTime(SwapEventType.FIRST_QUOTE_FETCH_STARTED, SwapEventType.FIRST_SWAP_ACTION),
}
}
sendAnalyticsEvent(SwapEventName.SWAP_QUOTE_FETCH, {
chainId,
...performanceMetrics,
})
}