From e01f30c0b4dd745106567f02a2342c7325e36ec3 Mon Sep 17 00:00:00 2001 From: lynn <41491154+lynnshaoyu@users.noreply.github.com> Date: Thu, 21 Jul 2022 17:44:34 -0400 Subject: [PATCH] feat: Web 214 implement the main submit swap event (#4061) * 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 * create helper function getPriceImpactPercentageNumber * 4 decimal points for percentages * change percentage to basis points units --- src/components/swap/AdvancedSwapDetails.tsx | 9 +- src/components/swap/ConfirmSwapModal.tsx | 4 +- src/components/swap/SwapModalFooter.tsx | 120 ++++++++++++++++++-- 3 files changed, 119 insertions(+), 14 deletions(-) diff --git a/src/components/swap/AdvancedSwapDetails.tsx b/src/components/swap/AdvancedSwapDetails.tsx index bd70c73f20..043d58e14c 100644 --- a/src/components/swap/AdvancedSwapDetails.tsx +++ b/src/components/swap/AdvancedSwapDetails.tsx @@ -45,6 +45,13 @@ function TextWithLoadingPlaceholder({ ) } +export function getPriceImpactPercent( + lpFeePercent: Percent, + trade: InterfaceTrade +): Percent { + return trade.priceImpact.subtract(lpFeePercent) +} + export function AdvancedSwapDetails({ trade, allowedSlippage, @@ -59,7 +66,7 @@ export function AdvancedSwapDetails({ if (!trade) return { expectedOutputAmount: undefined, priceImpact: undefined } const expectedOutputAmount = trade.outputAmount const realizedLpFeePercent = computeRealizedLPFeePercent(trade) - const priceImpact = trade.priceImpact.subtract(realizedLpFeePercent) + const priceImpact = getPriceImpactPercent(realizedLpFeePercent, trade) return { expectedOutputAmount, priceImpact } }, [trade]) diff --git a/src/components/swap/ConfirmSwapModal.tsx b/src/components/swap/ConfirmSwapModal.tsx index 764ef63e14..edd9578852 100644 --- a/src/components/swap/ConfirmSwapModal.tsx +++ b/src/components/swap/ConfirmSwapModal.tsx @@ -61,11 +61,13 @@ export default function ConfirmSwapModal({ ) : null - }, [onConfirm, showAcceptChanges, swapErrorMessage, trade]) + }, [onConfirm, showAcceptChanges, swapErrorMessage, trade, allowedSlippage, txHash]) // text to show while loading const pendingText = ( diff --git a/src/components/swap/SwapModalFooter.tsx b/src/components/swap/SwapModalFooter.tsx index 97dd995022..8b3d9edc45 100644 --- a/src/components/swap/SwapModalFooter.tsx +++ b/src/components/swap/SwapModalFooter.tsx @@ -1,36 +1,132 @@ import { Trans } from '@lingui/macro' -import { Trade } from '@uniswap/router-sdk' -import { Currency, TradeType } from '@uniswap/sdk-core' +import { Currency, CurrencyAmount, Percent, Token, TradeType } from '@uniswap/sdk-core' +import { ElementName, EventName } from 'components/AmplitudeAnalytics/constants' +import { Event } from 'components/AmplitudeAnalytics/constants' +import { TraceEvent } from 'components/AmplitudeAnalytics/TraceEvent' +import { useStablecoinValue } from 'hooks/useStablecoinPrice' +import useTransactionDeadline from 'hooks/useTransactionDeadline' import { ReactNode } from 'react' import { Text } from 'rebass' +import { InterfaceTrade } from 'state/routing/types' +import { useClientSideRouter, useUserSlippageTolerance } from 'state/user/hooks' +import { computeRealizedLPFeePercent } from 'utils/prices' import { ButtonError } from '../Button' import { AutoRow } from '../Row' +import { getPriceImpactPercent } from './AdvancedSwapDetails' import { SwapCallbackError } from './styleds' +function getDurationTillTimestampSinceEpoch(futureTimestampSinceEpoch?: number): number | undefined { + if (!futureTimestampSinceEpoch) return undefined + return futureTimestampSinceEpoch - new Date().getTime() / 1000 +} + +const getNumberFormattedToDecimalPlace = ( + intialNumberObject: Percent | CurrencyAmount, + decimalPlace: number +): number => parseFloat(intialNumberObject.toFixed(decimalPlace)) + +const formatPercentInBasisPointsNumber = (percent: Percent): number => parseFloat(percent.toFixed(2)) * 100 + +interface AnalyticsEventProps { + trade: InterfaceTrade + txHash: string | undefined + allowedSlippage: Percent + transactionDeadlineSecondsSinceEpoch: number | undefined + isAutoSlippage: boolean + isAutoRouterApi: boolean + tokenInAmountUsd: string | undefined + tokenOutAmountUsd: string | undefined + lpFeePercent: Percent +} + +const formatAnalyticsEventProperties = ({ + trade, + txHash, + allowedSlippage, + transactionDeadlineSecondsSinceEpoch, + isAutoSlippage, + isAutoRouterApi, + tokenInAmountUsd, + tokenOutAmountUsd, + lpFeePercent, +}: AnalyticsEventProps) => ({ + estimated_network_fee_usd: trade.gasUseEstimateUSD + ? getNumberFormattedToDecimalPlace(trade.gasUseEstimateUSD, 2) + : undefined, + transaction_hash: txHash, + transaction_deadline_seconds: getDurationTillTimestampSinceEpoch(transactionDeadlineSecondsSinceEpoch), + token_in_amount_usd: tokenInAmountUsd ? parseFloat(tokenInAmountUsd) : undefined, + token_out_amount_usd: tokenOutAmountUsd ? parseFloat(tokenOutAmountUsd) : undefined, + token_in_address: trade.inputAmount.currency.isToken ? trade.inputAmount.currency.address : undefined, + token_out_address: trade.outputAmount.currency.isToken ? trade.outputAmount.currency.address : undefined, + token_in_symbol: trade.inputAmount.currency.symbol, + token_out_symbol: trade.outputAmount.currency.symbol, + token_in_amount: getNumberFormattedToDecimalPlace(trade.inputAmount, trade.inputAmount.currency.decimals), + token_out_amount: getNumberFormattedToDecimalPlace(trade.outputAmount, trade.outputAmount.currency.decimals), + price_impact_basis_points: formatPercentInBasisPointsNumber(getPriceImpactPercent(lpFeePercent, trade)), + allowed_slippage_basis_points: formatPercentInBasisPointsNumber(allowedSlippage), + is_auto_router_api: isAutoRouterApi, + is_auto_slippage: isAutoSlippage, + chain_id: + trade.inputAmount.currency.chainId === trade.outputAmount.currency.chainId + ? trade.inputAmount.currency.chainId + : undefined, + // TODO(lynnshaoyu): implement duration_from_first_quote_to_swap_submission_seconds +}) + export default function SwapModalFooter({ + trade, + allowedSlippage, + txHash, onConfirm, swapErrorMessage, disabledConfirm, }: { - trade: Trade + trade: InterfaceTrade + txHash: string | undefined + allowedSlippage: Percent onConfirm: () => void swapErrorMessage: ReactNode | undefined disabledConfirm: boolean }) { + const transactionDeadlineSecondsSinceEpoch = useTransactionDeadline()?.toNumber() // in seconds since epoch + const isAutoSlippage = useUserSlippageTolerance() === 'auto' + const [clientSideRouter] = useClientSideRouter() + const tokenInAmountUsd = useStablecoinValue(trade.inputAmount)?.toFixed(2) + const tokenOutAmountUsd = useStablecoinValue(trade.outputAmount)?.toFixed(2) + const lpFeePercent = computeRealizedLPFeePercent(trade) + return ( <> - - - Confirm Swap - - + + + Confirm Swap + + + {swapErrorMessage ? : null}