Commit 3c5cc21e authored by lynn's avatar lynn Committed by GitHub

feat: remaining swap events (#4169)

* init commit

* add amplitude ts sdk to package.json

* add more comments and documentation

* respond to vm comments

* respond to cmcewen comments

* fix: remove unused constants

* init commit

* adapt to web

* add optional event properties to trace

* correct telemetry to analytics

* init commit

* change telemetry to analytics in doc

* init commit

* fix: respond to cmcewen comments + initialize analytics in app.tsx + add missing return statement

* add element name constant

* init commit

* correct price_impact calculation

* resolve vm comments

* fixes in response to comments

* respond to vm

* use ALL significant digits for token amounts

* init commit

* logged all properties

* create helper function getPriceImpactPercentageNumber

* 4 decimal points for percentages

* price percentage fn

* only log event on FIRST price fetch

* init commit

* add swap transaction completed event

* respond to cmcewen comments

* add two events

* remove console.logs

* move transaction completed logging to reducer

* simplify and remove unnecessary logic and constants

* respond to cmcewen comments

* respond to cmcewen comments

* respond to vm comment
parent 594dcb90
......@@ -10,9 +10,11 @@ export enum EventName {
SWAP_AUTOROUTER_VISUALIZATION_EXPANDED = 'Swap Autorouter Visualization Expanded',
SWAP_DETAILS_EXPANDED = 'Swap Details Expanded',
SWAP_MAX_TOKEN_AMOUNT_SELECTED = 'Swap Max Token Amount Selected',
SWAP_PRICE_UPDATE_ACKNOWLEDGED = 'Swap Price Update Acknowledged',
SWAP_QUOTE_RECEIVED = 'Swap Quote Received',
SWAP_SUBMITTED = 'Swap Submitted',
SWAP_TOKENS_REVERSED = 'Swap Tokens Reversed',
SWAP_TRANSACTION_COMPLETED = 'Swap Transaction Completed',
TOKEN_IMPORTED = 'Token Imported',
TOKEN_SELECTED = 'Token Selected',
TOKEN_SELECTOR_OPENED = 'Token Selector Opened',
......@@ -38,6 +40,11 @@ export enum WALLET_CONNECTION_RESULT {
FAILED = 'Failed',
}
export enum SWAP_PRICE_UPDATE_USER_RESPONSE {
ACCEPTED = 'Accepted',
REJECTED = 'Rejected',
}
/**
* Known pages in the app. Highest order context.
*/
......@@ -78,6 +85,7 @@ export const enum ElementName {
CONNECT_WALLET_BUTTON = 'connect-wallet-button',
IMPORT_TOKEN_BUTTON = 'import-token-button',
MAX_TOKEN_AMOUNT_BUTTON = 'max-token-amount-button',
PRICE_UPDATE_ACCEPT_BUTTON = 'price-update-accept-button',
SWAP_BUTTON = 'swap-button',
SWAP_DETAILS_DROPDOWN = 'swap-details-dropdown',
SWAP_TOKENS_REVERSE_ARROW_BUTTON = 'swap-tokens-reverse-arrow-button',
......
......@@ -10,4 +10,4 @@ export const getNumberFormattedToDecimalPlace = (
decimalPlace: number
): number => parseFloat(intialNumberObject.toFixed(decimalPlace))
export const formatPercentInBasisPointsNumber = (percent: Percent): number => parseFloat(percent.toFixed(2)) * 100
export const formatPercentInBasisPointsNumber = (percent: Percent): number => parseFloat(percent.toFixed(6)) * 100
......@@ -3,7 +3,7 @@ import { Trade } from '@uniswap/router-sdk'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { ModalName } from 'components/AmplitudeAnalytics/constants'
import { Trace } from 'components/AmplitudeAnalytics/Trace'
import { ReactNode, useCallback, useMemo } from 'react'
import { ReactNode, useCallback, useMemo, useState } from 'react'
import { InterfaceTrade } from 'state/routing/types'
import { tradeMeaningfullyDiffers } from 'utils/tradeMeaningFullyDiffer'
......@@ -39,22 +39,32 @@ export default function ConfirmSwapModal({
swapErrorMessage: ReactNode | undefined
onDismiss: () => void
}) {
// shouldLogModalCloseEvent lets the child SwapModalHeader component know when modal has been closed
// and an event triggered by modal closing should be logged.
const [shouldLogModalCloseEvent, setShouldLogModalCloseEvent] = useState(false)
const showAcceptChanges = useMemo(
() => Boolean(trade && originalTrade && tradeMeaningfullyDiffers(trade, originalTrade)),
[originalTrade, trade]
)
const onModalDismiss = useCallback(() => {
if (isOpen) setShouldLogModalCloseEvent(true)
onDismiss()
}, [isOpen, onDismiss])
const modalHeader = useCallback(() => {
return trade ? (
<SwapModalHeader
trade={trade}
shouldLogModalCloseEvent={shouldLogModalCloseEvent}
setShouldLogModalCloseEvent={setShouldLogModalCloseEvent}
allowedSlippage={allowedSlippage}
recipient={recipient}
showAcceptChanges={showAcceptChanges}
onAcceptChanges={onAcceptChanges}
/>
) : null
}, [allowedSlippage, onAcceptChanges, recipient, showAcceptChanges, trade])
}, [allowedSlippage, onAcceptChanges, recipient, showAcceptChanges, trade, shouldLogModalCloseEvent])
const modalBottom = useCallback(() => {
return trade ? (
......@@ -80,23 +90,23 @@ export default function ConfirmSwapModal({
const confirmationContent = useCallback(
() =>
swapErrorMessage ? (
<TransactionErrorContent onDismiss={onDismiss} message={swapErrorMessage} />
<TransactionErrorContent onDismiss={onModalDismiss} message={swapErrorMessage} />
) : (
<ConfirmationModalContent
title={<Trans>Confirm Swap</Trans>}
onDismiss={onDismiss}
onDismiss={onModalDismiss}
topContent={modalHeader}
bottomContent={modalBottom}
/>
),
[onDismiss, modalBottom, modalHeader, swapErrorMessage]
[onModalDismiss, modalBottom, modalHeader, swapErrorMessage]
)
return (
<Trace modal={ModalName.CONFIRM_SWAP} shouldLogImpression={isOpen}>
<TransactionConfirmationModal
isOpen={isOpen}
onDismiss={onDismiss}
onDismiss={onModalDismiss}
attemptingTxn={attemptingTxn}
hash={txHash}
content={confirmationContent}
......
import { Trans } from '@lingui/macro'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { useContext, useState } from 'react'
import { Price } from '@uniswap/sdk-core'
import { sendAnalyticsEvent } from 'components/AmplitudeAnalytics'
import { EventName, SWAP_PRICE_UPDATE_USER_RESPONSE } from 'components/AmplitudeAnalytics/constants'
import { formatPercentInBasisPointsNumber } from 'components/AmplitudeAnalytics/utils'
import { useContext, useEffect, useState } from 'react'
import { AlertTriangle, ArrowDown } from 'react-feather'
import { Text } from 'rebass'
import { InterfaceTrade } from 'state/routing/types'
......@@ -38,14 +42,42 @@ const ArrowWrapper = styled.div`
z-index: 2;
`
const formatAnalyticsEventProperties = (
trade: InterfaceTrade<Currency, Currency, TradeType>,
priceUpdate: number | undefined,
response: SWAP_PRICE_UPDATE_USER_RESPONSE
) => ({
chain_id:
trade.inputAmount.currency.chainId === trade.outputAmount.currency.chainId
? trade.inputAmount.currency.chainId
: undefined,
response,
token_in_symbol: trade.inputAmount.currency.symbol,
token_out_symbol: trade.outputAmount.currency.symbol,
price_update_basis_points: priceUpdate,
})
const getPriceUpdateBasisPoints = (
prevPrice: Price<Currency, Currency>,
newPrice: Price<Currency, Currency>
): 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,
setShouldLogModalCloseEvent,
allowedSlippage,
recipient,
showAcceptChanges,
onAcceptChanges,
}: {
trade: InterfaceTrade<Currency, Currency, TradeType>
shouldLogModalCloseEvent: boolean
setShouldLogModalCloseEvent: (shouldLog: boolean) => void
allowedSlippage: Percent
recipient: string | null
showAcceptChanges: boolean
......@@ -54,10 +86,28 @@ export default function SwapModalHeader({
const theme = useContext(ThemeContext)
const [showInverted, setShowInverted] = useState<boolean>(false)
const [lastExecutionPrice, setLastExecutionPrice] = useState(trade.executionPrice)
const [priceUpdate, setPriceUpdate] = useState<number | undefined>()
const fiatValueInput = useStablecoinValue(trade.inputAmount)
const fiatValueOutput = useStablecoinValue(trade.outputAmount)
useEffect(() => {
if (!trade.executionPrice.equalTo(lastExecutionPrice)) {
setPriceUpdate(getPriceUpdateBasisPoints(lastExecutionPrice, trade.executionPrice))
setLastExecutionPrice(trade.executionPrice)
}
}, [lastExecutionPrice, setLastExecutionPrice, trade.executionPrice])
useEffect(() => {
if (shouldLogModalCloseEvent && showAcceptChanges)
sendAnalyticsEvent(
EventName.SWAP_PRICE_UPDATE_ACKNOWLEDGED,
formatAnalyticsEventProperties(trade, priceUpdate, SWAP_PRICE_UPDATE_USER_RESPONSE.REJECTED)
)
setShouldLogModalCloseEvent(false)
}, [shouldLogModalCloseEvent, showAcceptChanges, setShouldLogModalCloseEvent, trade, priceUpdate])
return (
<AutoColumn gap={'4px'} style={{ marginTop: '1rem' }}>
<LightCard padding="0.75rem 1rem">
......
import { useWeb3React } from '@web3-react/core'
import { sendAnalyticsEvent } from 'components/AmplitudeAnalytics'
import { EventName } from 'components/AmplitudeAnalytics/constants'
import { DEFAULT_TXN_DISMISS_MS, L2_TXN_DISMISS_MS } from 'constants/misc'
import LibUpdater from 'lib/hooks/transactions/updater'
import { useCallback, useMemo } from 'react'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import { TransactionType } from 'state/transactions/types'
import { L2_CHAIN_IDS } from '../../constants/chains'
import { useAddPopup } from '../application/hooks'
......@@ -14,6 +17,7 @@ export default function Updater() {
const addPopup = useAddPopup()
// speed up popup dismisall time if on L2
const isL2 = Boolean(chainId && L2_CHAIN_IDS.includes(chainId))
const transactions = useAppSelector((state) => state.transactions)
const dispatch = useAppDispatch()
const onCheck = useCallback(
......@@ -39,6 +43,13 @@ export default function Updater() {
},
})
)
const tx = transactions[chainId]?.[hash]
if (tx.info.type === TransactionType.SWAP) {
sendAnalyticsEvent(EventName.SWAP_TRANSACTION_COMPLETED, {
transaction_hash: tx.hash,
succeeded: receipt.status === 1,
})
}
addPopup(
{
txn: { hash },
......@@ -47,11 +58,10 @@ export default function Updater() {
isL2 ? L2_TXN_DISMISS_MS : DEFAULT_TXN_DISMISS_MS
)
},
[addPopup, dispatch, isL2]
[addPopup, dispatch, isL2, transactions]
)
const state = useAppSelector((state) => state.transactions)
const pendingTransactions = useMemo(() => (chainId ? state[chainId] ?? {} : {}), [chainId, state])
const pendingTransactions = useMemo(() => (chainId ? transactions[chainId] ?? {} : {}), [chainId, transactions])
return <LibUpdater pendingTransactions={pendingTransactions} onCheck={onCheck} onReceipt={onReceipt} />
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment