Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
greg-schrammel committed May 28, 2024
1 parent 3661e36 commit d408dfe
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 37 deletions.
32 changes: 17 additions & 15 deletions src/__swaps__/screens/Swap/components/GasButton.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@
import { ChainId } from '@/__swaps__/types/chains';
import { weiToGwei } from '@/__swaps__/utils/ethereum';
import { useMeteorologySuggestions } from '@/__swaps__/utils/meteorology';
import { getCachedCurrentBaseFee, useMeteorologySuggestions } from '@/__swaps__/utils/meteorology';
import { add } from '@/__swaps__/utils/numbers';
import { ButtonPressAnimation } from '@/components/animations';
import { ContextMenu } from '@/components/context-menu';
import { Centered } from '@/components/layout';
import ContextMenuButton from '@/components/native-context-menu/contextMenu';
import { Box, Inline, Stack, Text, TextIcon, useColorMode, useForegroundColor } from '@/design-system';
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 styled from '@/styled-thing';
import { gasUtils } from '@/utils';
import React, { ReactNode, useCallback, useMemo } from 'react';
import { StyleSheet } from 'react-native';
import { 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 { useSwapEstimatedGasFee } from '../hooks/useEstimatedGasFee';
import { GasSpeed, setSelectedGasSpeed, useSelectedGas, useSelectedGasSpeed } from '../hooks/useSelectedGas';
import { useSwapContext } from '../providers/swap-provider';
import { StyleSheet } from 'react-native';

const { GAS_ICONS } = gasUtils;

function EstimatedGasFee() {
const chainId = useSwapsStore(s => s.inputAsset?.chainId || ChainId.mainnet);
const gasSettings = useSelectedGas(chainId);
const estimatedGasFee = useSwapEstimatedGasFee(gasSettings);
const { data: estimatedGasFee, isLoading } = useSwapEstimatedGasFee(gasSettings);

return (
<Inline alignVertical="center" space="4px">
<TextIcon color="labelQuaternary" height={10} size="icon 11px" weight="heavy" width={16}>
􀵟
</TextIcon>
<Text color="labelTertiary" size="15pt" weight="bold">
<Text color={isLoading ? 'red' : 'labelTertiary'} size="15pt" weight="bold">
{estimatedGasFee}
</Text>
</Inline>
Expand Down Expand Up @@ -66,17 +66,19 @@ const GasSpeedPagerCentered = styled(Centered).attrs(() => ({
marginHorizontal: 8,
}))({});

function getEstimatedFeeRangeInGwei(gasSettings: GasSettings | undefined, currentBaseFee?: string | undefined) {
function getEstimatedFeeRangeInGwei(gasSettings: GasSettings | undefined, currentBaseFee: string | undefined) {
if (!gasSettings) return undefined;

if (!gasSettings.isEIP1559) return `${formatNumber(weiToGwei(gasSettings.gasPrice))} Gwei`;

const { maxBaseFee, maxPriorityFee } = gasSettings;
return `${formatNumber(weiToGwei(add(maxBaseFee, maxPriorityFee)))} Gwei`;
const maxFee = formatNumber(weiToGwei(add(maxBaseFee, maxPriorityFee)));

if (!currentBaseFee) return `${maxFee} Gwei`;

const minFee = formatNumber(weiToGwei(add(currentBaseFee, maxPriorityFee)));

// return `${formatNumber(weiToGwei(add(baseFee, maxPriorityFee)))} - ${formatNumber(
// weiToGwei(add(maxBaseFee, maxPriorityFee))
// )} Gwei`;
return `${minFee} - ${maxFee} Gwei`;
}

function keys<const T extends string>(obj: Record<T, any> | undefined) {
Expand Down Expand Up @@ -118,9 +120,9 @@ const GasMenu = ({ children }: { children: ReactNode }) => {
const menuItems = menuOptions.map(gasOption => {
if (IS_ANDROID) return gasOption;

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

return {
actionKey: gasOption,
Expand All @@ -130,7 +132,7 @@ const GasMenu = ({ children }: { children: ReactNode }) => {
};
});
return { menuItems, menuTitle: '' };
}, [customGasSettings, menuOptions, metereologySuggestions.data]);
}, [customGasSettings, menuOptions, metereologySuggestions.data, chainId]);

if (metereologySuggestions.isLoading) return children;

Expand Down Expand Up @@ -211,10 +213,10 @@ export function ReviewGasButton() {
export const GasButton = () => {
return (
<GasMenu>
<Stack space="12px">
<Box gap={12}>
<SelectedGas />
<EstimatedGasFee />
</Stack>
</Box>
</GasMenu>
);
};
Expand Down
15 changes: 13 additions & 2 deletions src/__swaps__/screens/Swap/components/GasPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ function CurrentBaseFee() {

// loading state?

const isEIP1559 = useIsChainEIP1559(chainId);
if (!isEIP1559) return null;

return (
<Inline horizontalSpace="10px" alignVertical="center" alignHorizontal="justify">
<PressableLabel
Expand Down Expand Up @@ -184,7 +187,13 @@ function useGasPanelState<
const currentGasSettings = useCustomGasStore(s => select(s?.[chainId]));

const speed = useSelectedGasSpeed(chainId);
const { data: suggestion } = useMeteorologySuggestion({ chainId, speed, select, enabled: !!state });
const { data: suggestion } = useMeteorologySuggestion({
chainId,
speed,
select,
enabled: !!state,
notifyOnChangeProps: !!state && speed !== 'custom' ? ['data'] : [],
});

return useMemo(() => state ?? currentGasSettings ?? suggestion, [currentGasSettings, state, suggestion]);
}
Expand Down Expand Up @@ -259,7 +268,7 @@ function MaxTransactionFee() {

const gasPanelState = useGasPanelState();
const gasSettings = useMemo(() => stateToGasSettings(gasPanelState), [gasPanelState]);
const maxTransactionFee = useSwapEstimatedGasFee(gasSettings);
const { data: maxTransactionFee } = useSwapEstimatedGasFee(gasSettings);

return (
<Inline horizontalSpace="10px" alignVertical="center" alignHorizontal="justify">
Expand All @@ -286,7 +295,9 @@ function MaxTransactionFee() {
function EditableGasSettings() {
const chainId = useSwapsStore(s => s.inputAsset?.chainId || ChainId.mainnet);
const isEIP1559 = useIsChainEIP1559(chainId);

if (!isEIP1559) return <EditGasPrice />;

return (
<>
<EditMaxBaseFee />
Expand Down
2 changes: 1 addition & 1 deletion src/__swaps__/screens/Swap/components/ReviewPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const RainbowFee = () => {
function EstimatedGasFee() {
const chainId = useSwapsStore(s => s.inputAsset?.chainId || ChainId.mainnet);
const gasSettings = useSelectedGas(chainId);
const estimatedGasFee = useSwapEstimatedGasFee(gasSettings);
const { data: estimatedGasFee } = useSwapEstimatedGasFee(gasSettings);

return (
<Text align="left" color={'label'} size="15pt" weight="heavy">
Expand Down
44 changes: 30 additions & 14 deletions src/__swaps__/screens/Swap/hooks/useEstimatedGasFee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { weiToGwei } from '@/__swaps__/utils/ethereum';
import { add, multiply } from '@/__swaps__/utils/numbers';
import { useSwapsStore } from '@/state/swaps/swapsStore';
import ethereumUtils, { useNativeAssetForNetwork } from '@/utils/ethereumUtils';
import { useMemo } from 'react';
import { formatUnits } from 'viem';
import { formatCurrency, formatNumber } from './formatNumber';
import { GasSettings } from './useCustomGas';
import { useDebounce } from './useDebounce';
import { useSwapEstimatedGasLimit } from './useSwapEstimatedGasLimit';

function safeBigInt(value: string) {
Expand All @@ -29,31 +29,47 @@ export function useEstimatedGasFee({
const network = ethereumUtils.getNetworkFromChainId(chainId);
const nativeNetworkAsset = useNativeAssetForNetwork(network);

if (!gasLimit || !gasSettings || !nativeNetworkAsset) return 'Loading...'; // TODO: loading state
const amount = gasSettings.isEIP1559 ? add(gasSettings.maxBaseFee, gasSettings.maxPriorityFee) : gasSettings.gasPrice;
return useMemo(() => {
if (!gasLimit || !gasSettings || !nativeNetworkAsset?.price) return;

const totalWei = multiply(gasLimit, amount);
const networkAssetPrice = nativeNetworkAsset.price.value?.toString();
const amount = gasSettings.isEIP1559 ? add(gasSettings.maxBaseFee, gasSettings.maxPriorityFee) : gasSettings.gasPrice;

if (!networkAssetPrice) return `${formatNumber(weiToGwei(totalWei))} Gwei`;
const totalWei = multiply(gasLimit, amount);
const networkAssetPrice = nativeNetworkAsset.price.value?.toString();

const gasAmount = formatUnits(safeBigInt(totalWei), nativeNetworkAsset.decimals).toString();
const feeInUserCurrency = multiply(networkAssetPrice, gasAmount);
if (!networkAssetPrice) return `${formatNumber(weiToGwei(totalWei))} Gwei`;

return formatCurrency(feeInUserCurrency);
const gasAmount = formatUnits(safeBigInt(totalWei), nativeNetworkAsset.decimals).toString();
const feeInUserCurrency = multiply(networkAssetPrice, gasAmount);

return formatCurrency(feeInUserCurrency);
}, [gasLimit, gasSettings, nativeNetworkAsset]);
}

const isSameAddress = (a: string, b: string) => a.toLowerCase() === b.toLowerCase();
export function useSwapEstimatedGasFee(gasSettings: GasSettings | undefined) {
const chainId = useSwapsStore(s => s.inputAsset?.chainId || ChainId.mainnet);

const assetToSell = useSwapsStore(s => s.inputAsset);
const assetToBuy = useSwapsStore(s => s.outputAsset);
const quote = useSwapsStore(s => s.quote);

const debouncedQuote = useDebounce(quote, 200);

const { data: gasLimit } = useSwapEstimatedGasLimit({ chainId, quote: debouncedQuote, assetToSell }, { enabled: !!debouncedQuote });
const { data: gasLimit, isFetching } = useSwapEstimatedGasLimit(
{ chainId, quote, assetToSell },
{
enabled:
!!quote &&
!!assetToSell &&
!!assetToBuy &&
!('error' in quote) &&
// the quote and the input/output assets are not updated together,
// we shouldn't try to estimate if the assets are not the same as the quote (probably still fetching a quote)
isSameAddress(quote.sellTokenAddress, assetToSell.address) &&
isSameAddress(quote.buyTokenAddress, assetToBuy.address),
}
);

// useWhyDidYouUpdate('useSwapEstimatedGasFee', { chainId, gasLimit, gasSettings, assetToSell, quote });
const estimatedFee = useEstimatedGasFee({ chainId, gasLimit, gasSettings });

return useEstimatedGasFee({ chainId, gasLimit, gasSettings });
return useMemo(() => ({ isLoading: isFetching, data: estimatedFee }), [estimatedFee, isFetching]);
}
9 changes: 5 additions & 4 deletions src/__swaps__/screens/Swap/hooks/useSwapEstimatedGasLimit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export type EstimateSwapGasLimitArgs = {
// Query Key

const estimateSwapGasLimitQueryKey = ({ chainId, quote, assetToSell }: EstimateSwapGasLimitArgs) =>
createQueryKey('estimateSwapGasLimit', { chainId, quote, assetToSell }, { persisterVersion: 1 });
createQueryKey('estimateSwapGasLimit', { chainId, quote, assetToSell });

type EstimateSwapGasLimitQueryKey = ReturnType<typeof estimateSwapGasLimitQueryKey>;

Expand Down Expand Up @@ -94,10 +94,11 @@ export function useSwapEstimatedGasLimit(
}),
estimateSwapGasLimitQueryFunction,
{
staleTime: 30 * 1000, // 30s
cacheTime: 60 * 1000, // 1min
notifyOnChangeProps: ['data', 'isFetching'],
keepPreviousData: true,
staleTime: 12000,
cacheTime: Infinity,
notifyOnChangeProps: ['data'],
placeholderData: gasUnits.basic_swap[chainId],
...config,
}
);
Expand Down
4 changes: 3 additions & 1 deletion src/__swaps__/utils/meteorology.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,13 @@ export function useMeteorologySuggestion<Selected = GasSuggestions[keyof GasSugg
speed,
enabled,
select = s => s as Selected,
notifyOnChangeProps = ['data'],
}: {
chainId: ChainId;
speed: GasSpeed;
enabled?: boolean;
select?: (d: GasSuggestions[keyof GasSuggestions] | undefined) => Selected;
notifyOnChangeProps?: ['data'] | [];
}) {
return useMeteorology(
{ chainId },
Expand All @@ -241,7 +243,7 @@ export function useMeteorologySuggestion<Selected = GasSuggestions[keyof GasSugg
[select, speed]
),
enabled: enabled && speed !== 'custom',
notifyOnChangeProps: enabled && speed !== 'custom' ? ['data'] : [],
notifyOnChangeProps,
}
);
}
Expand Down

0 comments on commit d408dfe

Please sign in to comment.