diff --git a/src/__swaps__/screens/Swap/components/GasPanel.tsx b/src/__swaps__/screens/Swap/components/GasPanel.tsx index 8bd0251fbeb..69cf7c117c6 100644 --- a/src/__swaps__/screens/Swap/components/GasPanel.tsx +++ b/src/__swaps__/screens/Swap/components/GasPanel.tsx @@ -1,5 +1,5 @@ import * as i18n from '@/languages'; -import React, { PropsWithChildren, ReactNode, useMemo } from 'react'; +import React, { PropsWithChildren, ReactNode, useCallback, useMemo } from 'react'; import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated'; import { fadeConfig } from '@/__swaps__/screens/Swap/constants'; @@ -8,13 +8,16 @@ import { ChainId } from '@/__swaps__/types/chains'; import { GasSpeed } from '@/__swaps__/types/gas'; import { gweiToWei, weiToGwei } from '@/__swaps__/utils/ethereum'; import { + GasSuggestion, + getCachedCurrentBaseFee, getSelectedSpeedSuggestion, useBaseFee, useGasTrend, useIsChainEIP1559, useMeteorologySuggestion, + useMeteorologySuggestions, } from '@/__swaps__/utils/meteorology'; -import { add, formatNumber, subtract } from '@/__swaps__/utils/numbers'; +import { add, formatNumber, greaterThan, multiply, subtract } from '@/__swaps__/utils/numbers'; import { opacity } from '@/__swaps__/utils/swaps'; import { ButtonPressAnimation } from '@/components/animations'; import { Bleed, Box, Inline, Separator, Stack, Text, globalColors, useColorMode, useForegroundColor } from '@/design-system'; @@ -23,11 +26,11 @@ import { lessThan } from '@/helpers/utilities'; import { useNavigation } from '@/navigation'; import Routes from '@/navigation/routesNames'; import { createRainbowStore } from '@/state/internal/createRainbowStore'; -import { useSwapsStore } from '@/state/swaps/swapsStore'; +import { swapsStore, useSwapsStore } from '@/state/swaps/swapsStore'; import { gasUtils } from '@/utils'; import { upperFirst } from 'lodash'; import { GasSettings, getCustomGasSettings, setCustomGasSettings, useCustomGasStore } from '../hooks/useCustomGas'; -import { setSelectedGasSpeed, useSelectedGasSpeed } from '../hooks/useSelectedGas'; +import { getSelectedGas, setSelectedGasSpeed, useSelectedGasSpeed } from '../hooks/useSelectedGas'; import { EstimatedSwapGasFee, EstimatedSwapGasFeeSlot } from './EstimatedSwapGasFee'; import { UnmountOnAnimatedReaction } from './UnmountOnAnimatedReaction'; @@ -35,13 +38,6 @@ const { GAS_TRENDS } = gasUtils; const MINER_TIP_TYPE = 'minerTip'; const MAX_BASE_FEE_TYPE = 'maxBaseFee'; -const HIGH_ALERT = 'HIGH_ALERT'; -const LOW_ALERT = 'LOW_ALERT'; - -type AlertInfo = { - type: typeof LOW_ALERT | typeof HIGH_ALERT; - message: string; -} | null; function UnmountWhenGasPanelIsClosed({ placeholder, children }: PropsWithChildren<{ placeholder: ReactNode }>) { const { configProgress } = useSwapContext(); @@ -94,15 +90,16 @@ function NumericInputButton({ children, onPress }: PropsWithChildren<{ onPress: style={{ justifyContent: 'center', alignItems: 'center', + paddingTop: 1, + paddingLeft: 1, borderWidth: 1, - borderColor: isDarkMode ? globalColors.white10 : globalColors.grey100, + borderColor: isDarkMode ? globalColors.white10 : globalColors.grey20, backgroundColor: opacity(fillSecondary, 0.12), }} height={{ custom: 16 }} width={{ custom: 20 }} borderRadius={100} paddingVertical="1px (Deprecated)" - gap={10} > {children} @@ -112,7 +109,17 @@ function NumericInputButton({ children, onPress }: PropsWithChildren<{ onPress: ); } -const INPUT_STEP = gweiToWei('0.1'); +const minStep = gweiToWei('0.0001'); +const getStep = () => { + const chainId = useSwapsStore.getState().inputAsset?.chainId; + if (!chainId) return minStep; + + const baseFee = getCachedCurrentBaseFee(chainId); + if (!baseFee) return minStep; + + const step = 10 ** (baseFee.length - 2); + return step; +}; function GasSettingInput({ onChange, min = '0', @@ -129,18 +136,26 @@ function GasSettingInput({ { - const newValue = subtract(value, INPUT_STEP); - onChange(lessThan(newValue, min) ? min : newValue); + const step = getStep(); + const newValue = subtract(value, step); + onChange(lessThan(newValue, min) || lessThan(newValue, minStep) ? min : newValue); }} > 􀅽 - + {formatNumber(weiToGwei(value))} - onChange(add(value, INPUT_STEP))}>􀅼 + { + const step = getStep(); + onChange(add(value, step)); + }} + > + 􀅼 + @@ -150,7 +165,7 @@ function GasSettingInput({ ); } -const selectWeiToGwei = (s: string | undefined) => s && weiToGwei(s); +const selectBaseFee = (s: string | undefined = '0') => formatNumber(weiToGwei(s)); function CurrentBaseFeeSlot({ baseFee, gasTrend = 'notrend' }: { baseFee?: string; gasTrend?: keyof typeof GAS_TRENDS }) { const { isDarkMode } = useColorMode(); @@ -159,18 +174,12 @@ function CurrentBaseFeeSlot({ baseFee, gasTrend = 'notrend' }: { baseFee?: strin const label = useForegroundColor('label'); const labelSecondary = useForegroundColor('labelSecondary'); - const chainId = useSwapsStore(s => s.inputAsset?.chainId || ChainId.mainnet); - const trendType = 'currentBaseFee' + upperFirst(gasTrend); - - const isEIP1559 = useIsChainEIP1559(chainId); - if (!isEIP1559) return null; - const onPressLabel = () => { if (!baseFee || !gasTrend) return; navigate(Routes.EXPLAIN_SHEET, { currentBaseFee: baseFee, currentGasTrend: gasTrend, - type: trendType, + type: 'currentBaseFee' + upperFirst(gasTrend), }); }; @@ -197,7 +206,7 @@ function CurrentBaseFeeSlot({ baseFee, gasTrend = 'notrend' }: { baseFee?: strin weight="heavy" style={{ textTransform: 'capitalize' }} > - {formatNumber(baseFee || '0')} + {baseFee} @@ -207,7 +216,7 @@ function CurrentBaseFeeSlot({ baseFee, gasTrend = 'notrend' }: { baseFee?: strin function CurrentBaseFee() { const chainId = useSwapsStore(s => s.inputAsset?.chainId || ChainId.mainnet); - const { data: baseFee } = useBaseFee({ chainId, select: selectWeiToGwei }); + const { data: baseFee } = useBaseFee({ chainId, select: selectBaseFee }); const { data: gasTrend } = useGasTrend({ chainId }); return ; } @@ -216,25 +225,15 @@ type GasPanelState = { gasPrice?: string; maxBaseFee?: string; maxPriorityFee?: const useGasPanelStore = createRainbowStore(() => undefined); function useGasPanelState< - Key extends 'maxBaseFee' | 'maxPriorityFee' | 'gasPrice' | undefined = undefined, - Selected = Key extends string ? string : GasPanelState, ->(key?: Key, select: (s: GasPanelState | undefined) => Selected = s => (key ? s?.[key] : s) as Selected) { + Option extends 'maxBaseFee' | 'maxPriorityFee' | 'gasPrice' | undefined = undefined, + Selected = Option extends string ? string : GasPanelState, +>(opt?: Option, select: (s: GasPanelState | undefined) => Selected = s => (opt ? s?.[opt] : s) as Selected) { const state = useGasPanelStore(select); const chainId = useSwapsStore(s => s.inputAsset?.chainId || ChainId.mainnet); - const currentGasSettings = useCustomGasStore(s => select(s?.[chainId])); - const speed = useSelectedGasSpeed(chainId); - const { data: suggestion } = useMeteorologySuggestion({ - chainId, - speed, - select, - enabled: !!state, - notifyOnChangeProps: !!state && speed !== 'custom' ? ['data'] : [], - }); - - return useMemo(() => state ?? currentGasSettings ?? suggestion, [currentGasSettings, state, suggestion]); + return useMemo(() => state ?? currentGasSettings, [currentGasSettings, state]); } const setGasPanelState = (update: Partial) => { @@ -247,58 +246,120 @@ const setGasPanelState = (update: Partial) => { useGasPanelStore.setState({ ...suggestion, ...update }); }; +function useMetereologySuggested