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

Review button states #5854

Merged
merged 52 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
46162ee
wip
greg-schrammel May 29, 2024
3a3c341
fix gesture button states
walmat May 29, 2024
b8635be
Safemath pt 2 (#5778)
brunobar79 May 29, 2024
251f247
fix dynamic island overlap on recieve modal (#5672)
BrodyHughes May 29, 2024
ecb04e1
Gas optimizations (#5779)
greg-schrammel May 29, 2024
7e367e1
fix other networks section (#5784)
walmat May 29, 2024
f3307cb
Swaps: fix favorite button press (#5782)
benisgold May 29, 2024
009f887
todo
greg-schrammel May 29, 2024
6fd56f7
Merge remote-tracking branch 'origin/develop' into review-button-states
greg-schrammel May 29, 2024
2ce9e5f
remove console logs
greg-schrammel May 29, 2024
3c86b3b
Merge branch 'develop' into review-button-states
greg-schrammel May 30, 2024
a5799fb
Insufficient Funds
greg-schrammel May 30, 2024
9d816a8
remove todo
greg-schrammel May 30, 2024
a17464e
move cache getter closer to fetcher implentation
greg-schrammel Jun 2, 2024
c60dc66
Merge remote-tracking branch 'origin/develop' into review-button-states
greg-schrammel Jun 3, 2024
93cf94c
Merge branch 'develop' into review-button-states
greg-schrammel Jun 4, 2024
0af8ff4
fix
greg-schrammel Jun 5, 2024
0187af5
Merge remote-tracking branch 'origin/develop' into review-button-states
greg-schrammel Jun 5, 2024
40c2055
:)
greg-schrammel Jun 5, 2024
adf3b51
Merge branch 'develop' into review-button-states
walmat Jun 7, 2024
c7c0102
🍕
greg-schrammel Jun 12, 2024
cba4aed
or equal 🤌
greg-schrammel Jun 12, 2024
2ea366e
remove unused isSameAddress util
greg-schrammel Jun 12, 2024
e6d4382
just reordering declarations
greg-schrammel Jun 12, 2024
31d36b6
error i18n
greg-schrammel Jun 12, 2024
508a13b
fix merge conflicts with import sorting
walmat Jun 13, 2024
76df24c
useGasSharedValues
greg-schrammel Jun 13, 2024
f93b7ad
remove estimating
greg-schrammel Jun 13, 2024
c9bd12f
Merge branch 'develop' into review-button-states
greg-schrammel Jun 13, 2024
23c29c8
Merge branch 'develop' into review-button-states
greg-schrammel Jun 13, 2024
606866f
Merge branch 'develop' into review-button-states
walmat Jun 13, 2024
7697803
fix label flickering
greg-schrammel Jun 14, 2024
da8d964
fix review panel not prompting
walmat Jun 14, 2024
2819c72
Merge branch 'develop' into review-button-states
walmat Jun 14, 2024
fea8f65
Revert "Lint on pre-commit (#5836)"
walmat Jun 14, 2024
955e223
fix a bunch of shit
walmat Jun 14, 2024
8cfad78
Merge branch 'develop' of https://github.com/rainbow-me/rainbow into …
walmat Jun 14, 2024
988541d
Merge branch 'develop' into review-button-states
walmat Jun 14, 2024
e4cc776
fix?
greg-schrammel Jun 17, 2024
395dc38
Merge remote-tracking branch 'origin/develop' into review-button-states
greg-schrammel Jun 17, 2024
18da203
opacity
greg-schrammel Jun 17, 2024
ac80365
on review panel we should show fetching status and quote errors too
walmat Jun 18, 2024
6fcef75
merge
walmat Jun 18, 2024
50f51f0
remove error
greg-schrammel Jun 18, 2024
86026b3
useUserNativeNetworkAsset
greg-schrammel Jun 18, 2024
2ab06b5
Merge remote-tracking branch 'origin/develop' into review-button-states
greg-schrammel Jun 18, 2024
eafe6cc
less or equal
greg-schrammel Jun 18, 2024
c809659
Fixes for review button states (#5873)
jinchung Jun 20, 2024
e9f78e4
Fixup: missed spot for previous hasEnoughFundsForGas revert
jinchung Jun 21, 2024
58a8d79
Merge branch 'develop' into review-button-states
jinchung Jun 21, 2024
8ce503f
fix formatting
brunobar79 Jun 21, 2024
89d80e7
Merge branch 'develop' of github.com:rainbow-me/rainbow into review-b…
brunobar79 Jun 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ButtonPressAnimation } from '@/components/animations';
import { IS_IOS } from '@/env';
import ConditionalWrap from 'conditional-wrap';
import React from 'react';
import { StyleProp, ViewProps, ViewStyle } from 'react-native';
import { TapGestureHandler, TapGestureHandlerGestureEvent } from 'react-native-gesture-handler';
import Animated, { AnimatedStyle, runOnJS, useAnimatedGestureHandler } from 'react-native-reanimated';
import { ButtonPressAnimation } from '@/components/animations';
import { IS_IOS } from '@/env';

export type GestureHandlerButtonProps = {
buttonPressWrapperStyleIOS?: StyleProp<ViewStyle>;
Expand Down
34 changes: 23 additions & 11 deletions src/__swaps__/screens/Swap/components/SwapActionButton.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
/* eslint-disable no-nested-ternary */
import React from 'react';
import { StyleProp, StyleSheet, TextStyle, ViewStyle } from 'react-native';
import Animated, { DerivedValue, useAnimatedStyle, useDerivedValue } from 'react-native-reanimated';
import { StyleProp, StyleSheet, TextStyle, ViewProps, ViewStyle } from 'react-native';
import Animated, { DerivedValue, useAnimatedProps, useAnimatedStyle, useDerivedValue } from 'react-native-reanimated';

import { AnimatedText, Box, Column, Columns, globalColors, useColorMode, useForegroundColor } from '@/design-system';
import { ExtendedAnimatedAssetWithColors } from '@/__swaps__/types/assets';
import { getColorValueForThemeWorklet } from '@/__swaps__/utils/swaps';
import { AnimatedText, Box, Column, Columns, globalColors, useColorMode, useForegroundColor } from '@/design-system';
import { GestureHandlerV1Button } from './GestureHandlerV1Button';

const AnimatedGestureHandlerV1Button = Animated.createAnimatedComponent(GestureHandlerV1Button);

export const SwapActionButton = ({
asset,
borderRadius,
Expand All @@ -23,6 +25,8 @@ export const SwapActionButton = ({
scaleTo,
small,
style,
disabled,
opacity,
}: {
asset: DerivedValue<ExtendedAnimatedAssetWithColors | null>;
borderRadius?: number;
Expand All @@ -38,6 +42,8 @@ export const SwapActionButton = ({
scaleTo?: number;
small?: boolean;
style?: ViewStyle;
disabled?: DerivedValue<boolean | undefined>;
opacity?: DerivedValue<number | undefined>;
}) => {
const { isDarkMode } = useColorMode();
const fallbackColor = useForegroundColor('label');
Expand Down Expand Up @@ -80,6 +86,7 @@ export const SwapActionButton = ({
},
shadowOpacity: isDarkMode ? 0.2 : small ? 0.2 : 0.36,
shadowRadius: isDarkMode ? 26 : small ? 9 : 15,
opacity: opacity?.value ?? (disabled?.value ? 0.6 : 1),
};
});

Expand All @@ -97,15 +104,20 @@ export const SwapActionButton = ({
return rightIcon;
});

const buttonAnimatedProps = useAnimatedProps(() => {
return {
pointerEvents: (disabled?.value ? 'none' : 'box-only') as ViewProps['pointerEvents'],
disableButtonPressWrapper: disabled?.value,
scaleTo: scaleTo || (hugContent ? undefined : 0.925),
};
});

return (
<GestureHandlerV1Button
onPressJS={onPressJS}
<AnimatedGestureHandlerV1Button
animatedProps={buttonAnimatedProps}
onPressWorklet={onPressWorklet}
scaleTo={scaleTo || (hugContent ? undefined : 0.925)}
style={{
...(hugContent && feedActionButtonStyles.buttonWrapper),
...(style || {}),
}}
onPressJS={onPressJS}
style={[hugContent && feedActionButtonStyles.buttonWrapper, style]}
>
<Box
as={Animated.View}
Expand Down Expand Up @@ -138,7 +150,7 @@ export const SwapActionButton = ({
)}
</Columns>
</Box>
</GestureHandlerV1Button>
</AnimatedGestureHandlerV1Button>
);
};

Expand Down
26 changes: 13 additions & 13 deletions src/__swaps__/screens/Swap/components/SwapBottomPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { StyleSheet } from 'react-native';
import { getSoftMenuBarHeight } from 'react-native-extra-dimensions-android';
import { PanGestureHandler } from 'react-native-gesture-handler';
import Animated, { useAnimatedStyle, withSpring } from 'react-native-reanimated';
import Animated, { useAnimatedStyle, useDerivedValue, withSpring } from 'react-native-reanimated';

import { Box, Column, Columns, Separator, globalColors, useColorMode } from '@/design-system';
import { safeAreaInsetValues } from '@/utils';
Expand All @@ -21,15 +21,8 @@ import { SwapActionButton } from './SwapActionButton';

export function SwapBottomPanel() {
const { isDarkMode } = useColorMode();
const {
confirmButtonIcon,
confirmButtonIconStyle,
confirmButtonLabel,
internalSelectedOutputAsset,
AnimatedSwapStyles,
SwapNavigation,
configProgress,
} = useSwapContext();
const { confirmButtonIconStyle, confirmButtonProps, internalSelectedOutputAsset, AnimatedSwapStyles, SwapNavigation, configProgress } =
useSwapContext();

const { swipeToDismissGestureHandler, gestureY } = useBottomPanelGestureHandler();

Expand All @@ -50,6 +43,11 @@ export function SwapBottomPanel() {
};
});

const icon = useDerivedValue(() => confirmButtonProps.value.icon);
const label = useDerivedValue(() => confirmButtonProps.value.label);
const disabled = useDerivedValue(() => confirmButtonProps.value.disabled);
const opacity = useDerivedValue(() => confirmButtonProps.value.opacity);

return (
// @ts-expect-error Property 'children' does not exist on type
<PanGestureHandler maxPointers={1} onGestureEvent={swipeToDismissGestureHandler}>
Expand Down Expand Up @@ -80,11 +78,13 @@ export function SwapBottomPanel() {
</Box>
</Column>
<SwapActionButton
onPressWorklet={SwapNavigation.handleSwapAction}
asset={internalSelectedOutputAsset}
icon={confirmButtonIcon}
icon={icon}
iconStyle={confirmButtonIconStyle}
label={confirmButtonLabel}
onPressWorklet={SwapNavigation.handleSwapAction}
label={label}
disabled={disabled}
opacity={opacity}
scaleTo={0.9}
/>
</Columns>
Expand Down
8 changes: 4 additions & 4 deletions src/__swaps__/screens/Swap/components/SwapInputAsset.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import MaskedView from '@react-native-masked-view/masked-view';
import React from 'react';
import { StyleSheet, StatusBar } from 'react-native';
import { StatusBar, StyleSheet } from 'react-native';
import Animated, { useDerivedValue } from 'react-native-reanimated';
import { ScreenCornerRadius } from 'react-native-screen-corner-radius';

import { AnimatedText, Box, Column, Columns, Stack, useColorMode } from '@/design-system';

import { BalanceBadge } from '@/__swaps__/screens/Swap/components/BalanceBadge';
import { FadeMask } from '@/__swaps__/screens/Swap/components/FadeMask';
import { GestureHandlerV1Button } from '@/__swaps__/screens/Swap/components/GestureHandlerV1Button';
import { SwapActionButton } from '@/__swaps__/screens/Swap/components/SwapActionButton';
import { FadeMask } from '@/__swaps__/screens/Swap/components/FadeMask';
import { SwapInput } from '@/__swaps__/screens/Swap/components/SwapInput';
import { BalanceBadge } from '@/__swaps__/screens/Swap/components/BalanceBadge';
import { TokenList } from '@/__swaps__/screens/Swap/components/TokenList/TokenList';
import { BASE_INPUT_WIDTH, INPUT_INNER_WIDTH, INPUT_PADDING, THICK_BORDER_WIDTH } from '@/__swaps__/screens/Swap/constants';
import { IS_ANDROID } from '@/env';
import { useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider';
import { IS_ANDROID } from '@/env';
import { AnimatedSwapCoinIcon } from './AnimatedSwapCoinIcon';
import * as i18n from '@/languages';

Expand Down
17 changes: 9 additions & 8 deletions src/__swaps__/screens/Swap/components/SwapOutputAsset.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import { AnimatedText, Box, Column, Columns, Stack, useColorMode } from '@/design-system';
import MaskedView from '@react-native-masked-view/masked-view';
import React, { useCallback } from 'react';
import { StyleSheet, StatusBar } from 'react-native';
import { StatusBar, StyleSheet } from 'react-native';
import Animated, { runOnJS, useDerivedValue } from 'react-native-reanimated';
import { ScreenCornerRadius } from 'react-native-screen-corner-radius';
import { AnimatedText, Box, Column, Columns, Stack, useColorMode } from '@/design-system';

import { AnimatedSwapCoinIcon } from '@/__swaps__/screens/Swap/components/AnimatedSwapCoinIcon';
import { BalanceBadge } from '@/__swaps__/screens/Swap/components/BalanceBadge';
import { FadeMask } from '@/__swaps__/screens/Swap/components/FadeMask';
import { GestureHandlerV1Button } from '@/__swaps__/screens/Swap/components/GestureHandlerV1Button';
import { SwapActionButton } from '@/__swaps__/screens/Swap/components/SwapActionButton';
import { FadeMask } from '@/__swaps__/screens/Swap/components/FadeMask';
import { SwapInput } from '@/__swaps__/screens/Swap/components/SwapInput';
import { BalanceBadge } from '@/__swaps__/screens/Swap/components/BalanceBadge';
import { AnimatedSwapCoinIcon } from '@/__swaps__/screens/Swap/components/AnimatedSwapCoinIcon';
import { TokenList } from '@/__swaps__/screens/Swap/components/TokenList/TokenList';
import { BASE_INPUT_WIDTH, INPUT_INNER_WIDTH, INPUT_PADDING, THICK_BORDER_WIDTH } from '@/__swaps__/screens/Swap/constants';
import { IS_ANDROID } from '@/env';
import { useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider';
import { ChainId } from '@/__swaps__/types/chains';
import { IS_ANDROID } from '@/env';
import * as i18n from '@/languages';
import { useNavigation } from '@/navigation';
import Routes from '@/navigation/routesNames';
import { useSwapsStore } from '@/state/swaps/swapsStore';
import { ethereumUtils } from '@/utils';
import { ChainId } from '@/__swaps__/types/chains';
import * as i18n from '@/languages';

const SELECT_LABEL = i18n.t(i18n.l.swap.select);
const NO_BALANCE_LABEL = i18n.t(i18n.l.swap.no_balance);
Expand Down
70 changes: 15 additions & 55 deletions src/__swaps__/screens/Swap/hooks/useEstimatedGasFee.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { greaterThanWorklet, toScaledIntegerWorklet } from '@/__swaps__/safe-math/SafeMath';
import { ChainId } from '@/__swaps__/types/chains';
import { weiToGwei } from '@/__swaps__/utils/ethereum';
import { add, convertAmountToNativeDisplayWorklet, formatNumber, multiply } from '@/__swaps__/utils/numbers';
import ethereumUtils, { useNativeAssetForNetwork } from '@/utils/ethereumUtils';
import { useMemo, useState } from 'react';
import { runOnJS, useAnimatedReaction } from 'react-native-reanimated';
import { useDebouncedCallback } from 'use-debounce';
import { useMemo } from 'react';
import { formatUnits } from 'viem';
import { useSwapContext } from '../providers/swap-provider';

import { useSyncedSwapQuoteStore } from '../providers/SyncSwapStateAndSharedValues';
import { GasSettings } from './useCustomGas';
import { useSwapEstimatedGasLimit } from './useSwapEstimatedGasLimit';
import { useAccountSettings } from '@/hooks';
Expand All @@ -20,6 +18,11 @@ function safeBigInt(value: string) {
}
}

export function calculateGasFee(gasSettings: GasSettings, gasLimit: string) {
const amount = gasSettings.isEIP1559 ? add(gasSettings.maxBaseFee, gasSettings.maxPriorityFee) : gasSettings.gasPrice;
return multiply(gasLimit, amount);
}

export function useEstimatedGasFee({
chainId,
gasLimit,
Expand All @@ -36,66 +39,23 @@ export function useEstimatedGasFee({
return useMemo(() => {
if (!gasLimit || !gasSettings || !nativeNetworkAsset?.price) return;

const amount = gasSettings.isEIP1559 ? add(gasSettings.maxBaseFee, gasSettings.maxPriorityFee) : gasSettings.gasPrice;

const totalWei = multiply(gasLimit, amount);
const fee = calculateGasFee(gasSettings, gasLimit);
const networkAssetPrice = nativeNetworkAsset.price.value?.toString();

if (!networkAssetPrice) return `${formatNumber(weiToGwei(totalWei))} Gwei`;
if (!networkAssetPrice) return `${formatNumber(weiToGwei(fee))} Gwei`;

const gasAmount = formatUnits(safeBigInt(totalWei), nativeNetworkAsset.decimals).toString();
const feeInUserCurrency = multiply(networkAssetPrice, gasAmount);
const feeFormatted = formatUnits(safeBigInt(fee), nativeNetworkAsset.decimals).toString();
const feeInUserCurrency = multiply(networkAssetPrice, feeFormatted);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a theory that the reason we see 0 sometimes as the gas price is because there are decimals in the fee and the safeBigInt returns 0. IMO it's preferably to round up

Copy link
Contributor

@greg-schrammel greg-schrammel Jun 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should be using wei for everything here
but makes sense gonna test it


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

export function useSwapEstimatedGasFee(gasSettings: GasSettings | undefined) {
const { internalSelectedInputAsset: assetToSell, internalSelectedOutputAsset: assetToBuy, quote } = useSwapContext();

const [state, setState] = useState({
assetToBuy: assetToBuy.value,
assetToSell: assetToSell.value,
chainId: assetToSell.value?.chainId ?? ChainId.mainnet,
quote: quote.value,
});

const debouncedStateSet = useDebouncedCallback(setState, 100, { leading: false, trailing: true });

// Updates the state as a single block in response to quote changes to ensure the gas fee is cleanly updated once
useAnimatedReaction(
() => quote.value,
(current, previous) => {
if (!assetToSell.value || !assetToBuy.value || !current || !previous || 'error' in current) return;

const isSwappingMoreThanAvailableBalance = greaterThanWorklet(
current.sellAmount.toString(),
toScaledIntegerWorklet(assetToSell.value.balance.amount, assetToSell.value.decimals)
);

// Skip gas fee recalculation if the user is trying to swap more than their available balance, as it isn't
// needed and was previously resulting in errors in useEstimatedGasFee.
if (isSwappingMoreThanAvailableBalance) return;

if (current !== previous) {
runOnJS(debouncedStateSet)({
assetToBuy: assetToBuy.value,
assetToSell: assetToSell.value,
chainId: assetToSell.value?.chainId ?? ChainId.mainnet,
quote: current,
});
}
}
);

const { data: gasLimit, isFetching } = useSwapEstimatedGasLimit(
{ chainId: state.chainId, quote: state.quote, assetToSell: state.assetToSell },
{
enabled: !!state.quote && !!state.assetToSell && !!state.assetToBuy && !('error' in quote),
}
);
const { assetToSell, chainId = ChainId.mainnet, quote } = useSyncedSwapQuoteStore();
const { data: estimatedGasLimit, isFetching } = useSwapEstimatedGasLimit({ chainId, assetToSell, quote });

const estimatedFee = useEstimatedGasFee({ chainId: state.chainId, gasLimit, gasSettings });
const estimatedFee = useEstimatedGasFee({ chainId, gasLimit: estimatedGasLimit, gasSettings });

return useMemo(() => ({ isLoading: isFetching, data: estimatedFee }), [estimatedFee, isFetching]);
}
Loading
Loading