Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add analytics #5805

Merged
merged 13 commits into from
Jun 11, 2024
11 changes: 9 additions & 2 deletions src/__swaps__/screens/Swap/components/FlipButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,27 @@ import { AnimatedBlurView } from '@/__swaps__/screens/Swap/components/AnimatedBl
import { useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider';
import { TIMING_CONFIGS } from '@/components/animations/animationConfigs';
import { SwapAssetType } from '@/__swaps__/types/swap';
import { analyticsV2 } from '@/analytics';

export const FlipButton = () => {
const { isDarkMode } = useColorMode();

const { AnimatedSwapStyles, internalSelectedInputAsset, internalSelectedOutputAsset, setAsset } = useSwapContext();
const { AnimatedSwapStyles, internalSelectedInputAsset, internalSelectedOutputAsset, setAsset, SwapInputController } = useSwapContext();

const handleSwapAssets = useCallback(() => {
if (internalSelectedInputAsset.value && internalSelectedOutputAsset.value) {
const assetTypeToSet = SwapAssetType.outputAsset;
const assetToSet = internalSelectedInputAsset.value;

analyticsV2.track(analyticsV2.event.swapsFlippedAssets, {
inputAmount: SwapInputController.inputValues.value.inputAmount,
previousInputAsset: internalSelectedInputAsset.value,
previousOutputAsset: internalSelectedOutputAsset.value,
});

setAsset({ type: assetTypeToSet, asset: assetToSet });
}
}, [internalSelectedInputAsset, internalSelectedOutputAsset, /* lastTypedInput, */ setAsset]);
}, [SwapInputController.inputValues.value, internalSelectedInputAsset.value, internalSelectedOutputAsset.value, setAsset]);
walmat marked this conversation as resolved.
Show resolved Hide resolved

const flipButtonInnerStyles = useAnimatedStyle(() => {
return {
Expand Down
31 changes: 22 additions & 9 deletions src/__swaps__/screens/Swap/components/GasButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ChainId } from '@/__swaps__/types/chains';
import { weiToGwei } from '@/__swaps__/utils/ethereum';
import { OnPressMenuItemEventObject } from 'react-native-ios-context-menu';
import { getCachedCurrentBaseFee, useMeteorologySuggestions } from '@/__swaps__/utils/meteorology';
import { add } from '@/__swaps__/utils/numbers';
import { ContextMenu } from '@/components/context-menu';
Expand All @@ -8,19 +9,21 @@ import ContextMenuButton from '@/components/native-context-menu/contextMenu';
import { Box, Inline, Text, TextIcon, useColorMode, useForegroundColor } from '@/design-system';
import { IS_ANDROID } from '@/env';
import * as i18n from '@/languages';
import { useSwapsStore } from '@/state/swaps/swapsStore';
import { swapsStore, useSwapsStore } from '@/state/swaps/swapsStore';
walmat marked this conversation as resolved.
Show resolved Hide resolved
import { gasUtils } from '@/utils';
import React, { ReactNode, useCallback, useMemo } from 'react';
import { StyleSheet } from 'react-native';
import { runOnJS, runOnUI } from 'react-native-reanimated';
import { ETH_COLOR, ETH_COLOR_DARK, THICK_BORDER_WIDTH } from '../constants';
import { formatNumber } from '../hooks/formatNumber';
import { GasSettings, useCustomGasSettings } from '../hooks/useCustomGas';
import { GasSpeed, setSelectedGasSpeed, useSelectedGas, useSelectedGasSpeed } from '../hooks/useSelectedGas';
import { GasSpeed } from '@/__swaps__/types/gas';
import { setSelectedGasSpeed, useSelectedGas, useSelectedGasSpeed } from '../hooks/useSelectedGas';
import { useSwapContext } from '../providers/swap-provider';
import { EstimatedSwapGasFee } from './EstimatedSwapGasFee';
import { GestureHandlerV1Button } from './GestureHandlerV1Button';
import { ButtonPressAnimation } from '@/components/animations';
import { analyticsV2 } from '@/analytics';

const { GAS_ICONS } = gasUtils;
const GAS_BUTTON_HIT_SLOP = 16;
Expand Down Expand Up @@ -87,26 +90,36 @@ const GasMenu = ({ backToReview = false, children }: { backToReview?: boolean; c
const metereologySuggestions = useMeteorologySuggestions({ chainId });
const customGasSettings = useCustomGasSettings(chainId);

const menuOptions = useMemo(() => [...keys(metereologySuggestions.data), 'custom'] as const, [metereologySuggestions.data]);
const menuOptions = useMemo(() => [...keys(metereologySuggestions.data), GasSpeed.CUSTOM] as GasSpeed[], [metereologySuggestions.data]);

const handlePressSpeedOption = useCallback(
(selectedGasSpeed: GasSpeed) => {
setSelectedGasSpeed(chainId, selectedGasSpeed);
const { inputAsset, outputAsset, quote } = swapsStore.getState();
analyticsV2.track(analyticsV2.event.swapsChangedSelectedGasSpeed, {
walmat marked this conversation as resolved.
Show resolved Hide resolved
inputAsset,
outputAsset,
selectedGasSpeed,
quote,
});

if (selectedGasSpeed === 'custom') {
setSelectedGasSpeed(chainId, selectedGasSpeed);
if (selectedGasSpeed === GasSpeed.CUSTOM) {
runOnUI(SwapNavigation.handleShowGas)({ backToReview });
}
},
[SwapNavigation.handleShowGas, backToReview, chainId]
);

const handlePressMenuItem = useCallback(
({ nativeEvent: { actionKey } }: any) => handlePressSpeedOption(actionKey),
({ nativeEvent: { actionKey } }: OnPressMenuItemEventObject) => handlePressSpeedOption(actionKey as GasSpeed),
[handlePressSpeedOption]
);

const handlePressActionSheet = useCallback(
(buttonIndex: number) => handlePressSpeedOption(menuOptions[buttonIndex]),
(buttonIndex: number) => {
if (buttonIndex < 0) return;
handlePressSpeedOption(menuOptions[buttonIndex]);
},
[handlePressSpeedOption, menuOptions]
);

Expand All @@ -115,7 +128,7 @@ const GasMenu = ({ backToReview = false, children }: { backToReview?: boolean; c
if (IS_ANDROID) return gasOption;

const currentBaseFee = getCachedCurrentBaseFee(chainId);
const gasSettings = gasOption === 'custom' ? customGasSettings : metereologySuggestions.data?.[gasOption];
const gasSettings = gasOption === GasSpeed.CUSTOM ? customGasSettings : metereologySuggestions.data?.[gasOption];
const subtitle = getEstimatedFeeRangeInGwei(gasSettings, currentBaseFee);

return {
Expand Down Expand Up @@ -176,7 +189,7 @@ export function ReviewGasButton() {
const handleShowCustomGas = () => {
'worklet';

runOnJS(setSelectedGasSpeed)(internalSelectedInputAsset.value?.chainId || ChainId.mainnet, 'custom');
runOnJS(setSelectedGasSpeed)(internalSelectedInputAsset.value?.chainId || ChainId.mainnet, GasSpeed.CUSTOM);
SwapNavigation.handleShowGas({ backToReview: true });
};

Expand Down
5 changes: 3 additions & 2 deletions src/__swaps__/screens/Swap/components/GasPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { formatNumber } from '../hooks/formatNumber';
import { GasSettings, getCustomGasSettings, setCustomGasSettings, useCustomGasStore } from '../hooks/useCustomGas';
import { setSelectedGasSpeed, useSelectedGasSpeed } from '../hooks/useSelectedGas';
import { EstimatedSwapGasFee } from './EstimatedSwapGasFee';
import { GasSpeed } from '@/__swaps__/types/gas';

const MINER_TIP_TYPE = 'minerTip';
const MAX_BASE_FEE_TYPE = 'maxBaseFee';
Expand Down Expand Up @@ -316,12 +317,12 @@ function saveCustomGasSettings() {
const { inputAsset } = useSwapsStore.getState();
const chainId = inputAsset?.chainId || ChainId.mainnet;
if (!unsaved) {
if (getCustomGasSettings(chainId)) setSelectedGasSpeed(chainId, 'custom');
if (getCustomGasSettings(chainId)) setSelectedGasSpeed(chainId, GasSpeed.CUSTOM);
return;
}

setCustomGasSettings(chainId, unsaved);
setSelectedGasSpeed(chainId, 'custom');
setSelectedGasSpeed(chainId, GasSpeed.CUSTOM);
useGasPanelStore.setState(undefined);
}

Expand Down
9 changes: 9 additions & 0 deletions src/__swaps__/screens/Swap/components/SearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { ExtendedAnimatedAssetWithColors } from '@/__swaps__/types/assets';
import { GestureHandlerV1Button } from './GestureHandlerV1Button';
import { useDebouncedCallback } from 'use-debounce';
import { useSwapsStore } from '@/state/swaps/swapsStore';
import { analyticsV2 } from '@/analytics';
import * as i18n from '@/languages';

const AnimatedInput = Animated.createAnimatedComponent(Input);
Expand Down Expand Up @@ -80,13 +81,21 @@ export const SearchInput = ({
const onInputSearchQueryChange = useDebouncedCallback(
(text: string) => {
userAssetsStore.getState().setSearchQuery(text);
analyticsV2.track(analyticsV2.event.swapsSearchedForToken, {
walmat marked this conversation as resolved.
Show resolved Hide resolved
query: text,
type: 'input',
});
},
50,
{ leading: true, trailing: true }
);

const onOutputSearchQueryChange = useDebouncedCallback(
(text: string) => {
analyticsV2.track(analyticsV2.event.swapsSearchedForToken, {
query: text,
type: 'output',
});
useSwapsStore.setState({ outputSearchQuery: text });
},
100,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { OnPressMenuItemEventObject } from 'react-native-ios-context-menu';
import { userAssetsStore } from '@/state/assets/userAssets';
import { useSharedValueState } from '@/hooks/reanimated/useSharedValueState';
import { chainNameForChainIdWithMainnetSubstitution } from '@/__swaps__/utils/chains';
import { analyticsV2 } from '@/analytics';
import { swapsStore } from '@/state/swaps/swapsStore';

type ChainSelectionProps = {
allText?: string;
Expand Down Expand Up @@ -50,6 +52,12 @@ export const ChainSelection = memo(function ChainSelection({ allText, output }:

const handleSelectChain = useCallback(
({ nativeEvent: { actionKey } }: Omit<OnPressMenuItemEventObject, 'isUsingActionSheetFallback'>) => {
analyticsV2.track(analyticsV2.event.swapsChangedChainId, {
inputAsset: swapsStore.getState().inputAsset,
type: output ? 'output' : 'input',
chainId: Number(actionKey) as ChainId,
});

if (output) {
setSelectedOutputChainId(Number(actionKey) as ChainId);
} else {
Expand Down
16 changes: 8 additions & 8 deletions src/__swaps__/screens/Swap/hooks/useSelectedGas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,30 @@ import { getCachedGasSuggestions, useMeteorologySuggestions } from '@/__swaps__/
import { createRainbowStore } from '@/state/internal/createRainbowStore';
import { useMemo } from 'react';
import { getCustomGasSettings, useCustomGasSettings } from './useCustomGas';
import { GasSpeed } from '@/__swaps__/types/gas';

export type GasSpeed = 'custom' | 'urgent' | 'fast' | 'normal';
const useSelectedGasSpeedStore = createRainbowStore<{ [c in ChainId]?: GasSpeed }>(() => ({}), {
version: 0,
storageKey: 'preferred gas speed',
});
export const useSelectedGasSpeed = (chainId: ChainId) =>
useSelectedGasSpeedStore(s => {
const speed = s[chainId] || 'fast';
if (speed === 'custom' && getCustomGasSettings(chainId) === undefined) return 'fast';
const speed = s[chainId] || GasSpeed.FAST;
if (speed === GasSpeed.CUSTOM && getCustomGasSettings(chainId) === undefined) return GasSpeed.FAST;
return speed;
});
export const setSelectedGasSpeed = (chainId: ChainId, speed: GasSpeed) => useSelectedGasSpeedStore.setState({ [chainId]: speed });
export const getSelectedGasSpeed = (chainId: ChainId) => useSelectedGasSpeedStore.getState()[chainId] || 'fast';
export const getSelectedGasSpeed = (chainId: ChainId) => useSelectedGasSpeedStore.getState()[chainId] || GasSpeed.FAST;

export function useGasSettings(chainId: ChainId, speed: GasSpeed) {
const userCustomGasSettings = useCustomGasSettings(chainId);
const { data: metereologySuggestions } = useMeteorologySuggestions({
chainId,
enabled: speed !== 'custom',
enabled: speed !== GasSpeed.CUSTOM,
});

return useMemo(() => {
if (speed === 'custom') return userCustomGasSettings;
if (speed === GasSpeed.CUSTOM) return userCustomGasSettings;
return metereologySuggestions?.[speed];
}, [speed, userCustomGasSettings, metereologySuggestions]);
}
Expand All @@ -45,11 +45,11 @@ export function getGasSettingsBySpeed(chainId: ChainId) {
}

export function getGasSettings(speed: GasSpeed, chainId: ChainId) {
if (speed === 'custom') return getCustomGasSettings(chainId);
if (speed === GasSpeed.CUSTOM) return getCustomGasSettings(chainId);
return getCachedGasSuggestions(chainId)?.[speed];
}

export function getSelectedGas(chainId: ChainId) {
const selectedGasSpeed = useSelectedGasSpeedStore.getState()[chainId] || 'fast';
const selectedGasSpeed = useSelectedGasSpeedStore.getState()[chainId] || GasSpeed.FAST;
return getGasSettings(selectedGasSpeed, chainId);
}
8 changes: 8 additions & 0 deletions src/__swaps__/screens/Swap/hooks/useSwapInputsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import {
} from '@/resources/assets/externalAssetsQuery';
import { ethereumUtils } from '@/utils';
import { queryClient } from '@/react-query';
import { userAssetsStore } from '@/state/assets/userAssets';
import { analyticsV2 } from '@/analytics';
import { divWorklet, equalWorklet, greaterThanWorklet, mulWorklet, toFixedWorklet } from '@/__swaps__/safe-math/SafeMath';

function getInitialInputValues(initialSelectedInputAsset: ExtendedAnimatedAssetWithColors | null) {
Expand Down Expand Up @@ -485,6 +487,12 @@ export function useSwapInputsController({
)
: undefined;

analyticsV2.track(analyticsV2.event.swapsReceivedQuote, {
inputAsset: internalSelectedInputAsset.value,
outputAsset: internalSelectedOutputAsset.value,
quote: quoteResponse,
});

runOnUI(() => {
setQuote({
data: quoteResponse,
Expand Down
20 changes: 19 additions & 1 deletion src/__swaps__/screens/Swap/hooks/useSwapSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { slippageStep } from '@/__swaps__/screens/Swap/constants';
import { getDefaultSlippageWorklet } from '@/__swaps__/utils/swaps';
import { ChainId } from '@/__swaps__/types/chains';
import { DEFAULT_CONFIG } from '@/model/remoteConfig';
import { useCallback } from 'react';
import { analyticsV2 } from '@/analytics';

export const useSwapSettings = ({ inputAsset }: { inputAsset: SharedValue<ExtendedAnimatedAssetWithColors | null> }) => {
const flashbots = useSharedValue(swapsStore.getState().flashbots);
Expand All @@ -22,6 +24,22 @@ export const useSwapSettings = ({ inputAsset }: { inputAsset: SharedValue<Extend
runOnJS(setFlashbots)(!current);
};

const handleTrackAndUpdateSlippage = useCallback(
(value: string) => {
const { inputAsset, outputAsset, quote } = swapsStore.getState();

setSlippage(value);

analyticsV2.track(analyticsV2.event.swapsChangedMaximumSlippage, {
walmat marked this conversation as resolved.
Show resolved Hide resolved
slippage: value,
inputAsset,
outputAsset,
quote,
});
},
[setSlippage]
);

const onUpdateSlippage = (operation: 'plus' | 'minus') => {
'worklet';

Expand All @@ -34,7 +52,7 @@ export const useSwapSettings = ({ inputAsset }: { inputAsset: SharedValue<Extend
slippage.value = (Number(slippage.value) + value).toFixed(1).toString();
}

runOnJS(setSlippage)(slippage.value);
runOnJS(handleTrackAndUpdateSlippage)(slippage.value);
};

return {
Expand Down
41 changes: 37 additions & 4 deletions src/__swaps__/screens/Swap/providers/swap-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { useSwapSettings } from '../hooks/useSwapSettings';
import { useSwapOutputQuotesDisabled } from '../hooks/useSwapOutputQuotesDisabled';
import { getNetworkObj } from '@/networks';
import { userAssetsStore } from '@/state/assets/userAssets';
import { analyticsV2 } from '@/analytics';

const swapping = i18n.t(i18n.l.swap.actions.swapping);
const tapToSwap = i18n.t(i18n.l.swap.actions.tap_to_swap);
Expand Down Expand Up @@ -174,6 +175,9 @@ export const SwapProvider = ({ children }: SwapProviderProps) => {
const providerUrl = provider?.connection?.url;
const connectedToHardhat = isHardHat(providerUrl);

const isBridge = swapsStore.getState().inputAsset?.mainnetAddress === swapsStore.getState().outputAsset?.mainnetAddress;
const slippage = swapsStore.getState().slippage;

const selectedGas = getSelectedGas(parameters.chainId);
if (!selectedGas) {
runOnUI(resetSwappingStatus)();
Expand Down Expand Up @@ -208,14 +212,27 @@ export const SwapProvider = ({ children }: SwapProviderProps) => {
const { errorMessage } = await walletExecuteRap(wallet, type, {
...parameters,
gasParams,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
gasFeeParamsBySpeed: gasFeeParamsBySpeed as any,
// @ts-expect-error - collision between old gas types and new
gasFeeParamsBySpeed: gasFeeParamsBySpeed,
});
runOnUI(resetSwappingStatus)();

if (errorMessage) {
SwapInputController.quoteFetchingInterval.start();

analyticsV2.track(analyticsV2.event.swapsFailed, {
createdAt: Date.now(),
type,
parameters,
selectedGas,
slippage,
bridge: isBridge,
errorMessage,
// TODO: Does this work from the JS thread?
inputNativeValue: SwapInputController.inputValues.value.inputNativeValue,
outputNativeValue: SwapInputController.inputValues.value.outputNativeValue,
});

if (errorMessage !== 'handled') {
logger.error(new RainbowError(`[getNonceAndPerformSwap]: Error executing swap: ${errorMessage}`));
const extractedError = errorMessage.split('[')[0];
Expand All @@ -232,15 +249,25 @@ export const SwapProvider = ({ children }: SwapProviderProps) => {
}),
});

// TODO: Analytics
analyticsV2.track(analyticsV2.event.swapsSubmitted, {
createdAt: Date.now(),
type,
parameters,
selectedGas,
slippage,
bridge: isBridge,
// TODO: Does this work from the JS thread?
walmat marked this conversation as resolved.
Show resolved Hide resolved
inputNativeValue: SwapInputController.inputValues.value.inputNativeValue,
outputNativeValue: SwapInputController.inputValues.value.outputNativeValue,
});

NotificationManager?.postNotification('rapCompleted');
Navigation.handleAction(Routes.PROFILE_SCREEN, {});
};

const executeSwap = () => {
'worklet';

// TODO: Analytics
if (configProgress.value !== NavigationSteps.SHOW_REVIEW) return;

const inputAsset = internalSelectedInputAsset.value;
Expand Down Expand Up @@ -400,6 +427,12 @@ export const SwapProvider = ({ children }: SwapProviderProps) => {
? internalSelectedInputAsset.value?.uniqueId !== extendedAsset?.uniqueId
: internalSelectedOutputAsset.value?.uniqueId !== extendedAsset?.uniqueId;

analyticsV2.track(analyticsV2.event.swapsSelectedAsset, {
walmat marked this conversation as resolved.
Show resolved Hide resolved
asset,
otherAsset: otherSelectedAsset,
type,
});

runOnUI(() => {
const didSelectedAssetChange =
type === SwapAssetType.inputAsset
Expand Down
Loading
Loading