From 60bffb16bd89cb3aa612fe4e92546fdb78f32466 Mon Sep 17 00:00:00 2001 From: Matthew Wall Date: Thu, 6 Jun 2024 17:36:49 -0400 Subject: [PATCH 1/8] fix wrong chainname (#5823) --- src/__swaps__/screens/Swap/components/ReviewPanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__swaps__/screens/Swap/components/ReviewPanel.tsx b/src/__swaps__/screens/Swap/components/ReviewPanel.tsx index 8343d2f4170..1b804f6c208 100644 --- a/src/__swaps__/screens/Swap/components/ReviewPanel.tsx +++ b/src/__swaps__/screens/Swap/components/ReviewPanel.tsx @@ -145,7 +145,7 @@ export function ReviewPanel() { const unknown = i18n.t(i18n.l.swap.unknown); - const chainName = useDerivedValue(() => ChainNameDisplay[internalSelectedOutputAsset.value?.chainId ?? ChainId.mainnet]); + const chainName = useDerivedValue(() => ChainNameDisplay[internalSelectedInputAsset.value?.chainId ?? ChainId.mainnet]); const minimumReceived = useDerivedValue(() => { if (!SwapInputController.formattedOutputAmount.value || !internalSelectedOutputAsset.value?.symbol) { From 66422314d44cfe92c1fe9a5d67c90e6f10fec3bd Mon Sep 17 00:00:00 2001 From: Christian Baroni <7061887+christianbaroni@users.noreply.github.com> Date: Fri, 7 Jun 2024 11:32:54 -0400 Subject: [PATCH 2/8] Performance: limit Sentry tracking, fix NFT hooks (#5819) --- index.js | 3 + src/App.js | 15 +- src/components/DappBrowser/search/Search.tsx | 5 +- .../search/results/SearchResults.tsx | 218 ++++++++++-------- src/components/asset-list/AssetList.js | 1 - .../UniqueTokenExpandedState.tsx | 4 +- src/components/toasts/Toast.tsx | 6 +- src/handlers/ens.ts | 18 -- src/hooks/index.ts | 2 - src/hooks/useAccountEmptyState.ts | 25 -- src/hooks/useCollectible.ts | 23 +- src/hooks/useTrackENSProfile.ts | 63 ----- src/logger/sentry.ts | 28 +-- src/resources/nfts/index.ts | 105 +++++---- src/screens/WalletScreen/index.tsx | 42 +--- .../points/components/LeaderboardRow.tsx | 8 +- src/utils/logger.ts | 3 +- 17 files changed, 227 insertions(+), 342 deletions(-) delete mode 100644 src/hooks/useAccountEmptyState.ts delete mode 100644 src/hooks/useTrackENSProfile.ts diff --git a/index.js b/index.js index 5720753a824..74db2fc363f 100644 --- a/index.js +++ b/index.js @@ -5,11 +5,14 @@ It needs to be an import statement because otherwise it doesn't load properly likely because of typescript. */ import '@walletconnect/react-native-compat'; +import { initSentry } from '@/logger/sentry'; import { analytics } from './src/analytics'; import { StartTime } from './src/performance/start-time'; import { PerformanceTracking } from './src/performance/tracking'; import { PerformanceMetrics } from './src/performance/tracking/types/PerformanceMetrics'; +initSentry(); + analytics.track('Started executing JavaScript bundle'); PerformanceTracking.logDirectly(PerformanceMetrics.loadJSBundle, Date.now() - StartTime.START_TIME); PerformanceTracking.startMeasuring(PerformanceMetrics.loadRootAppComponent); diff --git a/src/App.js b/src/App.js index 6a8d8db8850..3825e243a8a 100644 --- a/src/App.js +++ b/src/App.js @@ -42,7 +42,6 @@ import { InitialRouteContext } from '@/navigation/initialRoute'; import Routes from '@/navigation/routesNames'; import { Portal } from '@/react-native-cool-modals/Portal'; import { NotificationsHandler } from '@/notifications/NotificationsHandler'; -import { initSentry, sentryRoutingInstrumentation } from '@/logger/sentry'; import { analyticsV2 } from '@/analytics'; import { getOrCreateDeviceId, securelyHashWalletAddress } from '@/analytics/utils'; import { logger, RainbowError } from '@/logger'; @@ -57,9 +56,10 @@ import { handleReviewPromptAction } from '@/utils/reviewAlert'; import { RemotePromoSheetProvider } from '@/components/remote-promo-sheet/RemotePromoSheetProvider'; import { RemoteCardProvider } from '@/components/cards/remote-cards'; import { initializeRemoteConfig } from '@/model/remoteConfig'; +import { IS_DEV } from './env'; import { checkIdentifierOnLaunch } from './model/backup'; -if (__DEV__) { +if (IS_DEV) { reactNativeDisableYellowBox && LogBox.ignoreAllLogs(); (showNetworkRequests || showNetworkResponses) && monitorNetwork(showNetworkRequests, showNetworkResponses); } @@ -218,10 +218,6 @@ class OldApp extends Component { updateBalancesAfter(isL2 ? 10000 : 5000, isL2, network); }; - handleSentryNavigationIntegration = () => { - sentryRoutingInstrumentation?.registerNavigationContainer(this.navigatorRef); - }; - render() { return ( @@ -230,7 +226,7 @@ class OldApp extends Component { - + @@ -257,9 +253,7 @@ function Root() { React.useEffect(() => { async function initializeApplication() { - await initSentry(); // must be set up immediately await initializeRemoteConfig(); - // must happen immediately, but after Sentry await migrate(); const isReturningUser = ls.device.get(['isReturningUser']); @@ -339,7 +333,7 @@ function Root() { // init complete, load the rest of the app setInitializing(false); }) - .catch(e => { + .catch(() => { logger.error(new RainbowError(`initializeApplication failed`)); // for failure, continue to rest of the app for now @@ -369,6 +363,7 @@ function Root() { ); } +/** Wrapping Root allows Sentry to accurately track startup times */ const RootWithSentry = Sentry.wrap(Root); const PlaygroundWithReduxStore = () => ( diff --git a/src/components/DappBrowser/search/Search.tsx b/src/components/DappBrowser/search/Search.tsx index 59a433604ab..df60db17000 100644 --- a/src/components/DappBrowser/search/Search.tsx +++ b/src/components/DappBrowser/search/Search.tsx @@ -17,7 +17,6 @@ import { useBrowserWorkletsContext } from '../BrowserWorkletsContext'; import { SearchResults } from './results/SearchResults'; import { useSearchContext } from './SearchContext'; import { useSyncSharedValue } from '@/hooks/reanimated/useSyncSharedValue'; -import { DappsContextProvider } from '@/resources/metadata/dapps'; import { useSharedValueState } from '@/hooks/reanimated/useSharedValueState'; export const Search = () => { @@ -125,9 +124,7 @@ export const Search = () => { <> - - - + diff --git a/src/components/DappBrowser/search/results/SearchResults.tsx b/src/components/DappBrowser/search/results/SearchResults.tsx index c4df358842a..e7bf159980f 100644 --- a/src/components/DappBrowser/search/results/SearchResults.tsx +++ b/src/components/DappBrowser/search/results/SearchResults.tsx @@ -3,7 +3,7 @@ import { ScrollView, StyleSheet } from 'react-native'; import Animated, { SharedValue, useAnimatedReaction, useAnimatedStyle, withSpring } from 'react-native-reanimated'; import { ButtonPressAnimation } from '@/components/animations'; import { Bleed, Box, Inline, Inset, Stack, Text, TextIcon, globalColors, useColorMode, useForegroundColor } from '@/design-system'; -import { Dapp, useDappsContext } from '@/resources/metadata/dapps'; +import { Dapp, DappsContextProvider, useDappsContext } from '@/resources/metadata/dapps'; import { useBrowserContext } from '../../BrowserContext'; import { SEARCH_BAR_HEIGHT } from '../../search-input/SearchInput'; import { useSearchContext } from '../SearchContext'; @@ -88,7 +88,6 @@ export const SearchResults = React.memo(function SearchResults({ const { isDarkMode } = useColorMode(); const { searchViewProgress } = useBrowserContext(); const { inputRef, keyboardHeight, searchQuery, searchResults } = useSearchContext(); - const { dapps } = useDappsContext(); const separatorSecondary = useForegroundColor('separatorSecondary'); const separatorTertiary = useForegroundColor('separatorTertiary'); @@ -102,20 +101,6 @@ export const SearchResults = React.memo(function SearchResults({ inputRef?.current?.blur(); }, [inputRef]); - useAnimatedReaction( - () => searchQuery.value, - (result, previous) => { - if (result !== previous && isFocused.value) { - searchResults.modify(value => { - const results = search(result, dapps, 4); - value.splice(0, value.length); - value.push(...results); - return value; - }); - } - } - ); - const allResultsAnimatedStyle = useAnimatedStyle(() => ({ display: searchQuery.value ? 'flex' : 'none', })); @@ -143,95 +128,128 @@ export const SearchResults = React.memo(function SearchResults({ })); return ( - - - - - 􀆄 - - - - - - - 􀊫 - - - {i18n.t(i18n.l.dapp_browser.search.find_apps_and_more)} - - - - - - - - - - - - - - - - - - - - - 􀊫 - - - {i18n.t(i18n.l.dapp_browser.search.more_results)} - - - - - - - - - - + <> + + + + + + + + 􀆄 + + + + + + + 􀊫 + + + {i18n.t(i18n.l.dapp_browser.search.find_apps_and_more)} + + + + + + + + + + + + + - - - - - + + + + + + + 􀊫 + + + {i18n.t(i18n.l.dapp_browser.search.more_results)} + + + + + + + + + + + + + + + + + - + ); }); +const DappsDataSync = ({ + isFocused, + searchQuery, + searchResults, +}: { + isFocused: SharedValue; + searchQuery: SharedValue; + searchResults: SharedValue; +}) => { + const { dapps } = useDappsContext(); + + useAnimatedReaction( + () => searchQuery.value, + (result, previous) => { + if (result !== previous && isFocused.value) { + searchResults.modify(value => { + const results = search(result, dapps, 4); + value.splice(0, value.length); + value.push(...results); + return value; + }); + } + } + ); + + return null; +}; + const styles = StyleSheet.create({ closeButton: { height: 32, diff --git a/src/components/asset-list/AssetList.js b/src/components/asset-list/AssetList.js index b96cecce68a..c221e4f8661 100644 --- a/src/components/asset-list/AssetList.js +++ b/src/components/asset-list/AssetList.js @@ -12,7 +12,6 @@ const FabSizeWithPadding = FloatingActionButtonSize + FabWrapperBottomPosition * const AssetList = ({ accentColor, hideHeader, - isEmpty, isLoading, isWalletEthZero, network, diff --git a/src/components/expanded-state/UniqueTokenExpandedState.tsx b/src/components/expanded-state/UniqueTokenExpandedState.tsx index 228d04107b3..a1c92d99192 100644 --- a/src/components/expanded-state/UniqueTokenExpandedState.tsx +++ b/src/components/expanded-state/UniqueTokenExpandedState.tsx @@ -226,8 +226,8 @@ const UniqueTokenExpandedState = ({ asset: passedAsset, external }: UniqueTokenE const { navigate, setOptions } = useNavigation(); const { colors, isDarkMode } = useTheme(); const { isReadOnlyWallet } = useWallets(); - const collecible = useCollectible(passedAsset?.uniqueId); - const asset = external ? passedAsset : collecible; + const collectible = useCollectible(passedAsset?.uniqueId); + const asset = external ? passedAsset : collectible; const { data: { nftOffers }, } = useNFTOffers({ diff --git a/src/components/toasts/Toast.tsx b/src/components/toasts/Toast.tsx index f6895711651..8b6852a5375 100644 --- a/src/components/toasts/Toast.tsx +++ b/src/components/toasts/Toast.tsx @@ -1,4 +1,4 @@ -import React, { Fragment, PropsWithChildren, useLayoutEffect } from 'react'; +import React, { Fragment, PropsWithChildren, memo, useLayoutEffect } from 'react'; import { Insets, ViewProps } from 'react-native'; import Animated, { interpolate, useAnimatedStyle, useSharedValue, withSpring, WithSpringConfig } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; @@ -53,7 +53,7 @@ type Props = PropsWithChildren<{ }> & Pick; -export default function Toast({ children, color, distance = 90, targetTranslate = 0, icon, isVisible, testID, text, textColor }: Props) { +function Toast({ children, color, distance = 90, targetTranslate = 0, icon, isVisible, testID, text, textColor }: Props) { const { colors, isDarkMode } = useTheme(); const { width: deviceWidth } = useDimensions(); const insets = useSafeAreaInsets(); @@ -89,3 +89,5 @@ export default function Toast({ children, color, distance = 90, targetTranslate ); } + +export default memo(Toast); diff --git a/src/handlers/ens.ts b/src/handlers/ens.ts index cfb31cf92c3..e29076a878f 100644 --- a/src/handlers/ens.ts +++ b/src/handlers/ens.ts @@ -463,24 +463,6 @@ export const fetchAccountPrimary = async (accountAddress: string) => { }; }; -export function prefetchENSIntroData() { - const ensMarqueeQueryData = queryClient.getQueryData<{ - ensMarquee: EnsMarqueeAccount[]; - }>([ENS_MARQUEE_QUERY_KEY]); - - if (ensMarqueeQueryData?.ensMarquee) { - const ensMarqueeAccounts = ensMarqueeQueryData.ensMarquee.map((account: EnsMarqueeAccount) => account.name); - - for (const name of ensMarqueeAccounts) { - prefetchENSAddress({ name }, { staleTime: Infinity }); - prefetchENSAvatar(name, { cacheFirst: true }); - prefetchENSCover(name, { cacheFirst: true }); - prefetchENSRecords(name, { cacheFirst: true }); - prefetchFirstTransactionTimestamp({ addressOrName: name }); - } - } -} - export const estimateENSCommitGasLimit = async ({ name, ownerAddress, duration, rentPrice, salt }: ENSActionParameters) => estimateENSTransactionGasLimit({ duration, diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 77a279b144a..048802245d6 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -2,7 +2,6 @@ export { useChartDataLabels, useChartThrottledPoints } from './charts'; export { default as useDelayedValueWithLayoutAnimation } from './useDelayedValueWithLayoutAnimation'; export { default as useAccountAsset } from './useAccountAsset'; export { default as useFrameDelayedValue } from './useFrameDelayedValue'; -export { default as useAccountEmptyState } from './useAccountEmptyState'; export { default as useAccountENSDomains, prefetchAccountENSDomains } from './useAccountENSDomains'; export { default as useAndroidScrollViewGestureHandler } from './useAndroidScrollViewGestureHandler'; export { default as useAccountProfile } from './useAccountProfile'; @@ -33,7 +32,6 @@ export { default as useENSResolver, prefetchENSResolver } from './useENSResolver export { default as useENSRegistrant, prefetchENSRegistrant } from './useENSRegistrant'; export { default as useENSRecords, prefetchENSRecords, ensRecordsQueryKey } from './useENSRecords'; export { default as useFadeImage } from './useFadeImage'; -export { default as useTrackENSProfile } from './useTrackENSProfile'; export { default as useENSRecordDisplayProperties } from './useENSRecordDisplayProperties'; export { default as useENSRegistration } from './useENSRegistration'; export { default as useENSModifiedRegistration } from './useENSModifiedRegistration'; diff --git a/src/hooks/useAccountEmptyState.ts b/src/hooks/useAccountEmptyState.ts deleted file mode 100644 index d86210480de..00000000000 --- a/src/hooks/useAccountEmptyState.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { useEffect, useMemo } from 'react'; -import { getAccountEmptyState, saveAccountEmptyState } from '../handlers/localstorage/accountLocal'; -import useAccountSettings from './useAccountSettings'; - -export default function useAccountEmptyState(isSectionsEmpty: boolean, isLoadingUserAssets: boolean) { - const { network, accountAddress } = useAccountSettings(); - const isAccountEmptyInStorage = useMemo(() => getAccountEmptyState(accountAddress, network), [accountAddress, network]); - const isEmpty: { [address: string]: boolean | undefined } = useMemo( - () => ({ - ...isEmpty, - [accountAddress]: isLoadingUserAssets ? isAccountEmptyInStorage : isSectionsEmpty, - }), - [accountAddress, isAccountEmptyInStorage, isLoadingUserAssets, isSectionsEmpty] - ); - - useEffect(() => { - if (!isLoadingUserAssets) { - saveAccountEmptyState(false, accountAddress, network); - } - }, [accountAddress, isLoadingUserAssets, isSectionsEmpty, network]); - - return { - isEmpty: isEmpty[accountAddress], - }; -} diff --git a/src/hooks/useCollectible.ts b/src/hooks/useCollectible.ts index 780d12ad619..30e4fd9af35 100644 --- a/src/hooks/useCollectible.ts +++ b/src/hooks/useCollectible.ts @@ -1,24 +1,19 @@ import { useMemo } from 'react'; -import { ParsedAddressAsset } from '@/entities'; import { useLegacyNFTs } from '@/resources/nfts'; import { useAccountSettings } from '.'; export default function useCollectible(uniqueId: string, externalAddress?: string) { const { accountAddress } = useAccountSettings(); - const { - data: { nftsMap: selfNFTsMap }, - } = useLegacyNFTs({ address: accountAddress }); - const { - data: { nftsMap: externalNFTsMap }, - } = useLegacyNFTs({ - address: externalAddress ?? '', - }); + const isExternal = Boolean(externalAddress); - // Use the appropriate tokens based on if the user is viewing the - // current accounts tokens, or external tokens (e.g. ProfileSheet) - const uniqueTokensMap = useMemo(() => (isExternal ? externalNFTsMap : selfNFTsMap), [externalNFTsMap, isExternal, selfNFTsMap]); + const address = isExternal ? externalAddress ?? '' : accountAddress; - const asset = uniqueTokensMap?.[uniqueId]; + const { data: asset } = useLegacyNFTs({ + address, + config: { + select: data => data.nftsMap[uniqueId], + }, + }); - return { ...asset, isExternal }; + return useMemo(() => ({ ...asset, isExternal }), [asset, isExternal]); } diff --git a/src/hooks/useTrackENSProfile.ts b/src/hooks/useTrackENSProfile.ts deleted file mode 100644 index 0a0345a0360..00000000000 --- a/src/hooks/useTrackENSProfile.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { useCallback, useMemo } from 'react'; -import { fetchENSRecords } from './useENSRecords'; -import useWallets from './useWallets'; -import { analyticsV2 } from '@/analytics'; -import { EthereumAddress } from '@/entities'; -import { fetchAccountDomains } from '@/handlers/ens'; -import { ENS_RECORDS } from '@/helpers/ens'; -import walletTypes from '@/helpers/walletTypes'; -import { RainbowWallet } from '@/model/wallet'; - -export default function useTrackENSProfile() { - const { walletNames, wallets } = useWallets(); - - const addresses = useMemo( - () => - Object.values(wallets || {}) - .filter(wallet => wallet?.type !== walletTypes.readOnly) - .reduce( - (addresses: EthereumAddress[], wallet: RainbowWallet | undefined) => - addresses.concat(wallet?.addresses.map(({ address }: { address: EthereumAddress }) => address)!), - [] - ), - [wallets] - ); - - const getTrackProfilesData = useCallback(async () => { - const data = { - numberOfENSOwned: 0, - numberOfENSWithAvatarOrCoverSet: 0, - numberOfENSWithOtherMetadataSet: 0, - numberOfENSWithPrimaryNameSet: 0, - }; - for (const i in addresses) { - const ens = walletNames[addresses[i]]; - if (ens) { - const { records } = await fetchENSRecords(ens); - const domains = await fetchAccountDomains(addresses[i]); - data.numberOfENSOwned += domains?.account?.registrations?.length || 0; - data.numberOfENSWithAvatarOrCoverSet += records?.avatar || records?.header ? 1 : 0; - - data.numberOfENSWithOtherMetadataSet = Object.keys(records ?? {}).some( - key => key !== ENS_RECORDS.header && key !== ENS_RECORDS.avatar - ) - ? 1 - : 0; - data.numberOfENSWithPrimaryNameSet += 1; - } - } - return data; - }, [addresses, walletNames]); - - const { data, isSuccess } = useQuery(['getTrackProfilesData', [addresses]], getTrackProfilesData, { - enabled: Boolean(addresses.length), - retry: 0, - }); - - const trackENSProfile = useCallback(() => { - isSuccess && data && analyticsV2.identify(data); - }, [isSuccess, data]); - - return { trackENSProfile }; -} diff --git a/src/logger/sentry.ts b/src/logger/sentry.ts index d236e9ae087..05b7ceaac05 100644 --- a/src/logger/sentry.ts +++ b/src/logger/sentry.ts @@ -6,27 +6,21 @@ import { IS_PROD, IS_TEST } from '@/env'; import { logger, RainbowError } from '@/logger'; import isTestFlight from '@/helpers/isTestFlight'; -/** - * We need to disable React Navigation instrumentation for E2E tests because - * detox doesn't like setTimeout calls that are used inside When enabled detox - * hangs and timeouts on all test cases - */ -export const sentryRoutingInstrumentation = IS_PROD ? new Sentry.ReactNavigationInstrumentation() : undefined; - -export const defaultOptions = { +export const defaultOptions: Sentry.ReactNativeOptions = { + attachStacktrace: true, + defaultIntegrations: false, dsn: SENTRY_ENDPOINT, - enableAutoSessionTracking: true, + enableAppHangTracking: false, + enableAutoPerformanceTracing: false, + enableAutoSessionTracking: false, + enableTracing: false, environment: isTestFlight ? 'Testflight' : SENTRY_ENVIRONMENT, - integrations: [ - new Sentry.ReactNativeTracing({ - routingInstrumentation: sentryRoutingInstrumentation, - tracingOrigins: ['localhost', /^\//], - }), - ], - tracesSampleRate: 0.2, + integrations: [], + maxBreadcrumbs: 5, + tracesSampleRate: 0, }; -export async function initSentry() { +export function initSentry() { if (IS_TEST) { logger.debug(`Sentry is disabled for test environment`); return; diff --git a/src/resources/nfts/index.ts b/src/resources/nfts/index.ts index e49337ef00b..d7b1c88c198 100644 --- a/src/resources/nfts/index.ts +++ b/src/resources/nfts/index.ts @@ -1,20 +1,20 @@ -import { useQuery } from '@tanstack/react-query'; -import { createQueryKey } from '@/react-query'; +import { QueryFunction, useQuery } from '@tanstack/react-query'; +import { QueryConfigWithSelect, createQueryKey } from '@/react-query'; import { NFT } from '@/resources/nfts/types'; import { fetchSimpleHashNFTListing } from '@/resources/nfts/simplehash'; -import { useMemo } from 'react'; import { simpleHashNFTToUniqueAsset } from '@/resources/nfts/simplehash/utils'; import { useSelector } from 'react-redux'; import { AppState } from '@/redux/store'; import { Network } from '@/helpers'; import { UniqueAsset } from '@/entities'; import { arcClient } from '@/graphql'; +import { createSelector } from 'reselect'; -const NFTS_STALE_TIME = 300000; // 5 minutes +const NFTS_STALE_TIME = 600000; // 10 minutes const NFTS_CACHE_TIME_EXTERNAL = 3600000; // 1 hour const NFTS_CACHE_TIME_INTERNAL = 604800000; // 1 week -export const nftsQueryKey = ({ address }: { address: string }) => createQueryKey('nfts', { address }, { persisterVersion: 2 }); +export const nftsQueryKey = ({ address }: { address: string }) => createQueryKey('nfts', { address }, { persisterVersion: 3 }); export const nftListingQueryKey = ({ contractAddress, @@ -26,53 +26,74 @@ export const nftListingQueryKey = ({ network: Omit; }) => createQueryKey('nftListing', { contractAddress, tokenId, network }); -export function useNFTs(): NFT[] { - // normal react query where we get new NFT formatted data - return []; +const walletsSelector = (state: AppState) => state.wallets?.wallets; + +const isImportedWalletSelector = createSelector( + walletsSelector, + (_: AppState, address: string) => address, + (wallets, address) => { + if (!wallets) { + return false; + } + for (const wallet of Object.values(wallets)) { + if (wallet.addresses.some(account => account.address === address)) { + return true; + } + } + return false; + } +); + +interface NFTData { + nfts: UniqueAsset[]; + nftsMap: Record; } -export function useLegacyNFTs({ address }: { address: string }) { - const { wallets } = useSelector((state: AppState) => state.wallets); +type NFTQueryKey = ReturnType; - const walletAddresses = useMemo( - () => (wallets ? Object.values(wallets).flatMap(wallet => wallet.addresses.map(account => account.address)) : []), - [wallets] - ); - const isImportedWallet = walletAddresses.includes(address); - - const { data, error, isFetching } = useQuery({ - queryKey: nftsQueryKey({ address }), - queryFn: async () => { - const queryResponse = await arcClient.getNFTs({ walletAddress: address }); - const nfts = queryResponse?.nfts?.map(nft => simpleHashNFTToUniqueAsset(nft, address)); - return nfts; +const fetchNFTData: QueryFunction = async ({ queryKey }) => { + const [{ address }] = queryKey; + const queryResponse = await arcClient.getNFTs({ walletAddress: address }); + + const nfts = queryResponse?.nfts?.map(nft => simpleHashNFTToUniqueAsset(nft, address)); + + // ⚠️ TODO: Delete this and rework the code that uses it + const nftsMap = nfts?.reduce( + (acc, nft) => { + // Track down why these both exist - we should not be doing this + acc[nft.uniqueId] = nft; + acc[nft.fullUniqueId] = nft; + return acc; }, - staleTime: NFTS_STALE_TIME, - retry: 3, + {} as Record + ); + + return { nfts: nfts ?? [], nftsMap: nftsMap ?? {} }; +}; + +const FALLBACK_DATA: NFTData = { nfts: [], nftsMap: {} }; + +export function useLegacyNFTs({ + address, + config, +}: { + address: string; + config?: QueryConfigWithSelect; +}) { + const isImportedWallet = useSelector((state: AppState) => isImportedWalletSelector(state, address)); + + const { data, error, isFetching } = useQuery(nftsQueryKey({ address }), fetchNFTData, { cacheTime: isImportedWallet ? NFTS_CACHE_TIME_INTERNAL : NFTS_CACHE_TIME_EXTERNAL, enabled: !!address, + retry: 3, + staleTime: NFTS_STALE_TIME, + ...config, }); - const nfts = useMemo(() => data ?? [], [data]); - - const nftsMap = useMemo( - () => - nfts.reduce( - (acc, nft) => { - // index by both uniqueId and fullUniqueId bc why not - acc[nft.uniqueId] = nft; - acc[nft.fullUniqueId] = nft; - return acc; - }, - {} as { [key: string]: UniqueAsset } - ), - [nfts] - ); - return { - data: { nfts, nftsMap }, + data: (config?.select ? data ?? config.select(FALLBACK_DATA) : data ?? FALLBACK_DATA) as TSelected, error, - isInitialLoading: !data?.length && isFetching, + isInitialLoading: !data && isFetching, }; } diff --git a/src/screens/WalletScreen/index.tsx b/src/screens/WalletScreen/index.tsx index 1672d5bd227..1b982d1cace 100644 --- a/src/screens/WalletScreen/index.tsx +++ b/src/screens/WalletScreen/index.tsx @@ -6,13 +6,10 @@ import { Page } from '../../components/layout'; import { Network } from '@/helpers'; import { useRemoveFirst } from '@/navigation/useRemoveFirst'; import { settingsUpdateNetwork } from '@/redux/settings'; -import useExperimentalFlag, { PROFILES } from '@/config/experimentalHooks'; -import { prefetchENSIntroData } from '@/handlers/ens'; import { navbarHeight } from '@/components/navbar/Navbar'; import { Box } from '@/design-system'; import { useAccountAccentColor, - useAccountEmptyState, useAccountSettings, useInitializeAccountData, useInitializeWallet, @@ -20,7 +17,6 @@ import { useLoadAccountLateData, useLoadGlobalLateData, useResetAccountState, - useTrackENSProfile, useWalletSectionsData, } from '@/hooks'; import Routes from '@rainbow-me/routes'; @@ -34,19 +30,20 @@ import { addressCopiedToastAtom } from '@/recoil/addressCopiedToastAtom'; import { usePositions } from '@/resources/defi/PositionsQuery'; import styled from '@/styled-thing'; import { UserAssetsSync } from '@/__swaps__/screens/Swap/components/UserAssetsSync'; +import { IS_ANDROID } from '@/env'; const WalletPage = styled(Page)({ ...position.sizeAsObject('100%'), flex: 1, }); +// eslint-disable-next-line @typescript-eslint/no-explicit-any const WalletScreen: React.FC = ({ navigation, route }) => { const { params } = route; const { setParams, getState: dangerouslyGetState, getParent: dangerouslyGetParent } = navigation; const removeFirst = useRemoveFirst(); const [initialized, setInitialized] = useState(!!params?.initialized); const initializeWallet = useInitializeWallet(); - const { trackENSProfile } = useTrackENSProfile(); const { network: currentNetwork, accountAddress, appIcon, nativeCurrency } = useAccountSettings(); usePositions({ address: accountAddress, currency: nativeCurrency }); @@ -75,17 +72,12 @@ const WalletScreen: React.FC = ({ navigation, route }) => { }, [currentNetwork, revertToMainnet]); const walletReady = useSelector(({ appState: { walletReady } }: AppState) => walletReady); - const { - isWalletEthZero, - isEmpty: isSectionsEmpty, - isLoadingUserAssets, - briefSectionsData: walletBriefSectionsData, - } = useWalletSectionsData(); + const { isWalletEthZero, isLoadingUserAssets, briefSectionsData: walletBriefSectionsData } = useWalletSectionsData(); useEffect(() => { // This is the fix for Android wallet creation problem. // We need to remove the welcome screen from the stack. - if (ios) { + if (!IS_ANDROID) { return; } const isWelcomeScreen = dangerouslyGetParent()?.getState().routes[0].name === Routes.WELCOME_SCREEN; @@ -94,14 +86,6 @@ const WalletScreen: React.FC = ({ navigation, route }) => { } }, [dangerouslyGetState, removeFirst]); - const { isEmpty: isAccountEmpty } = useAccountEmptyState(isSectionsEmpty, isLoadingUserAssets); - - const { addressSocket } = useSelector(({ explorer: { addressSocket } }: AppState) => ({ - addressSocket, - })); - - const profilesEnabled = useExperimentalFlag(PROFILES); - useEffect(() => { const initializeAndSetParams = async () => { // @ts-expect-error messed up initializeWallet types @@ -123,19 +107,6 @@ const WalletScreen: React.FC = ({ navigation, route }) => { } }, [loadAccountLateData, loadGlobalLateData, walletReady]); - useEffect(() => { - if (walletReady && profilesEnabled) { - InteractionManager.runAfterInteractions(() => { - // We are not prefetching intro profiles data on Android - // as the RPC call queue is considerably slower. - if (ios) { - prefetchENSIntroData(); - } - trackENSProfile(); - }); - } - }, [profilesEnabled, trackENSProfile, walletReady]); - // track current app icon useEffect(() => { analyticsV2.identify({ appIcon }); @@ -160,8 +131,7 @@ const WalletScreen: React.FC = ({ navigation, route }) => { = ({ navigation, route }) => { - {/* NOTE: The components below render null and are solely for keeping react-query and Zustand in sync */} + {/* NOTE: The component below renders null and is solely for keeping react-query and Zustand in sync */} diff --git a/src/screens/points/components/LeaderboardRow.tsx b/src/screens/points/components/LeaderboardRow.tsx index 6aac7b2e24f..6892a058fd0 100644 --- a/src/screens/points/components/LeaderboardRow.tsx +++ b/src/screens/points/components/LeaderboardRow.tsx @@ -1,5 +1,5 @@ import * as i18n from '@/languages'; -import React, { useCallback, useMemo } from 'react'; +import React, { memo, useCallback, useMemo } from 'react'; import { Keyboard, Share } from 'react-native'; import { MenuActionConfig } from 'react-native-ios-context-menu'; import ContextMenuButton from '@/components/native-context-menu/contextMenu'; @@ -30,7 +30,7 @@ const ACTIONS = { SHARE: 'share', }; -export const LeaderboardRow = ({ +export const LeaderboardRow = memo(function LeaderboardRow({ address, ens, avatarURL, @@ -42,7 +42,7 @@ export const LeaderboardRow = ({ avatarURL?: string; points: number; rank: number; -}) => { +}) { const { switchToWalletWithAddress, selectedWallet } = useWallets(); const { isWatching } = useWatchWallet({ address }); const { colors } = useTheme(); @@ -253,4 +253,4 @@ export const LeaderboardRow = ({ ); -}; +}); diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 955c36edff1..c69ff91a70f 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -72,8 +72,7 @@ const Logger = { console.log(...args); // eslint-disable-line no-console } if (args.length === 1 && typeof args[0] === 'string') { - // @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call. - sentryUtils.addInfoBreadcrumb.apply(null, args); + sentryUtils.addInfoBreadcrumb.apply(null, [args[0]]); } else { const safeData = safelyStringifyWithFormat(args[1]); sentryUtils.addDataBreadcrumb(args[0], safeData); From ca22bfd60f028b9aa508b4d37ec4b0ffb54a9080 Mon Sep 17 00:00:00 2001 From: Bruno Barbieri <1247834+brunobar79@users.noreply.github.com> Date: Fri, 7 Jun 2024 14:26:57 -0400 Subject: [PATCH 3/8] reorg ens related calls (#5828) --- src/components/cards/ENSCreateProfileCard.tsx | 22 +++++++++---------- src/hooks/useOnAvatarPress.ts | 14 +++++------- src/redux/wallets.ts | 2 -- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/components/cards/ENSCreateProfileCard.tsx b/src/components/cards/ENSCreateProfileCard.tsx index c04936b76ec..e905091f6de 100644 --- a/src/components/cards/ENSCreateProfileCard.tsx +++ b/src/components/cards/ENSCreateProfileCard.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useCallback, useEffect } from 'react'; import ENSAvatarGrid from '../../assets/ensAvatarGrid.png'; import ENSIcon from '../../assets/ensIcon.png'; import { useNavigation } from '../../navigation/Navigation'; @@ -33,8 +33,15 @@ export const ENSCreateProfileCard = () => { // 40 represents the horizontal padding outside the card const imageWidth = deviceWidth - 40; - const handlePress = () => { + const { uniqueDomain } = useAccountENSDomains(); + + const handlePress = useCallback(() => { if (!isReadOnlyWallet || enableActionsOnReadOnlyWallet) { + if (uniqueDomain?.name) { + prefetchENSAvatar(uniqueDomain.name); + prefetchENSRecords(uniqueDomain.name); + } + analyticsV2.track(analyticsV2.event.cardPressed, { cardName: 'ENSCreateProfileCard', routeName, @@ -46,16 +53,7 @@ export const ENSCreateProfileCard = () => { } else { watchingAlert(); } - }; - - const { uniqueDomain } = useAccountENSDomains(); - - useEffect(() => { - if (uniqueDomain?.name) { - prefetchENSAvatar(uniqueDomain.name); - prefetchENSRecords(uniqueDomain.name); - } - }, [uniqueDomain]); + }, [isReadOnlyWallet, navigate, routeName, uniqueDomain?.name]); return ( diff --git a/src/hooks/useOnAvatarPress.ts b/src/hooks/useOnAvatarPress.ts index cf47929b0ea..4c7323bcf24 100644 --- a/src/hooks/useOnAvatarPress.ts +++ b/src/hooks/useOnAvatarPress.ts @@ -54,14 +54,6 @@ export default ({ screenType = 'transaction' }: UseOnAvatarPressProps = {}) => { const { startRegistration } = useENSRegistration(); const { setNextEmoji } = useUpdateEmoji(); - useEffect(() => { - if (accountENS) { - prefetchENSAvatar(accountENS); - prefetchENSCover(accountENS); - prefetchENSRecords(accountENS); - } - }, [accountENS]); - const onAvatarRemovePhoto = useCallback(async () => { const newWallets: typeof wallets = { ...wallets, @@ -256,6 +248,12 @@ export default ({ screenType = 'transaction' }: UseOnAvatarPressProps = {}) => { }, [accountENS, navigate]); const onAvatarPress = useCallback(() => { + if (accountENS) { + prefetchENSAvatar(accountENS); + prefetchENSCover(accountENS); + prefetchENSRecords(accountENS); + } + if (hasENSAvatar && accountENS) { onAvatarPressProfile(); } else { diff --git a/src/redux/wallets.ts b/src/redux/wallets.ts index ea733c8706f..519223b54c3 100644 --- a/src/redux/wallets.ts +++ b/src/redux/wallets.ts @@ -207,8 +207,6 @@ export const walletsLoadState = type: WALLETS_LOAD, }); - dispatch(fetchWalletNames()); - profilesEnabled && dispatch(fetchWalletENSAvatars()); return wallets; } catch (error) { logger.error(new RainbowError('Exception during walletsLoadState'), { From 6f00408829b75a93f4a52922e394e1982d3281bc Mon Sep 17 00:00:00 2001 From: brdy <41711440+BrodyHughes@users.noreply.github.com> Date: Fri, 7 Jun 2024 16:44:47 -0400 Subject: [PATCH 4/8] [e2e] Add Malicious Dapp interaction test (#5764) * . * . * . * . * final * cleanup * fix discover test * rm comment * Update e2e/7_maliciousDappConnection.spec.ts Co-authored-by: Matthew Wall * . --------- Co-authored-by: Matthew Wall --- e2e/4_discoverSheetFlow.spec.ts | 7 +-- e2e/7_maliciousDappConnection.spec.ts | 56 +++++++++++++++++++ e2e/environment.js | 2 +- e2e/helpers.ts | 17 ++++-- e2e/init.js | 2 + src/components/DappBrowser/DappBrowser.tsx | 1 + .../DappBrowser/search-input/SearchInput.tsx | 1 + src/config/experimental.ts | 3 +- src/screens/WalletConnectApprovalSheet.js | 2 +- 9 files changed, 78 insertions(+), 13 deletions(-) create mode 100644 e2e/7_maliciousDappConnection.spec.ts diff --git a/e2e/4_discoverSheetFlow.spec.ts b/e2e/4_discoverSheetFlow.spec.ts index bc4a2b4ad7c..de0ad61f37e 100644 --- a/e2e/4_discoverSheetFlow.spec.ts +++ b/e2e/4_discoverSheetFlow.spec.ts @@ -37,13 +37,12 @@ describe('Discover Screen Flow', () => { }); it('Should navigate to the Points screen after swiping left', async () => { - await swipe('profile-screen', 'left', 'slow'); + await swipe('profile-screen', 'left', 'fast'); await checkIfVisible('points-screen'); }); - it('Should navigate back to Discover screen after swiping right twice', async () => { - await swipe('points-screen', 'right', 'slow'); - await swipe('profile-screen', 'right', 'slow'); + it('Should navigate back to Discover screen after tapping Discover icon', async () => { + await waitAndTap('tab-bar-icon-DiscoverScreen'); await checkIfVisible('discover-header'); }); diff --git a/e2e/7_maliciousDappConnection.spec.ts b/e2e/7_maliciousDappConnection.spec.ts new file mode 100644 index 00000000000..0b6f49fd0bf --- /dev/null +++ b/e2e/7_maliciousDappConnection.spec.ts @@ -0,0 +1,56 @@ +import { + beforeAllcleanApp, + afterAllcleanApp, + importWalletFlow, + waitAndTap, + swipe, + checkIfVisible, + checkIfExistsByText, + typeText, + delayTime, + tapAtPoint, + checkIfExists, +} from './helpers'; +import { WALLET_VARS } from './testVariables'; + +describe('Check malicious dapp warning', () => { + beforeAll(async () => { + await beforeAllcleanApp({ hardhat: false }); + }); + + afterAll(async () => { + await afterAllcleanApp({ hardhat: false }); + }); + + it('Should be able to watch a wallet and load the wallet screen', async () => { + await importWalletFlow(WALLET_VARS.SEED_WALLET.PK); + }); + + it('Should be able to navigate to the dapp browser', async () => { + await swipe('wallet-screen', 'left', 'fast'); + await swipe('discover-sheet', 'left', 'fast'); + await checkIfVisible('browser-screen'); + }); + + it('Should be able to type on search input and go to malicious dapp', async () => { + await waitAndTap('browser-search-input'); + await checkIfExistsByText('Find apps and more'); + await typeText('browser-search-input', 'https://test-dap-welps.vercel.app/', true, false, true); + // Waiting for webpage to load + await delayTime('long'); + }); + + it('Should attempt to connect to in browser dapp', async () => { + // Detox can't query elements within a WebView within our app + // Using tapAtPoint() to tap coordinates is a workaround for now + + // Tapping connect button + await tapAtPoint('browser-screen', { x: 275, y: 80 }); + // Waiting for rainbowkit sheet to load / animate in + await delayTime('medium'); + // Tapping Rainbow button + await tapAtPoint('browser-screen', { x: 50, y: 325 }); + + await checkIfExists('malicious-dapp-warning'); + }); +}); diff --git a/e2e/environment.js b/e2e/environment.js index 74d331de25b..3d9918b8551 100644 --- a/e2e/environment.js +++ b/e2e/environment.js @@ -5,7 +5,7 @@ class CustomDetoxEnvironment extends DetoxCircusEnvironment { constructor(config, context) { super(config, context); this.launchAppTimeout = 120_000; - this.initTimeout = 360_000; + this.initTimeout = 120_000; } } module.exports = CustomDetoxEnvironment; diff --git a/e2e/helpers.ts b/e2e/helpers.ts index 206c3a7ab84..55344f6052c 100644 --- a/e2e/helpers.ts +++ b/e2e/helpers.ts @@ -28,7 +28,7 @@ export async function killHardhat() { exec('kill $(lsof -t -i:8545)'); } -export async function importWalletFlow() { +export async function importWalletFlow(customSeed?: string) { await checkIfVisible('welcome-screen'); await waitAndTap('already-have-wallet-button'); await checkIfExists('add-wallet-sheet'); @@ -36,7 +36,7 @@ export async function importWalletFlow() { await checkIfExists('import-sheet'); await clearField('import-sheet-input'); await device.disableSynchronization(); - await typeText('import-sheet-input', process.env.TEST_SEEDS, false); + await typeText('import-sheet-input', customSeed ? customSeed : process.env.TEST_SEEDS, false); await checkIfElementHasString('import-sheet-button-label', 'Continue'); await waitAndTap('import-sheet-button'); await checkIfVisible('wallet-info-modal'); @@ -52,9 +52,6 @@ export async function importWalletFlow() { } export async function beforeAllcleanApp({ hardhat }: { hardhat?: boolean }) { - // sometimes i see tests failed from the get-go - // giving an extra 15 to let things set up - await delayTime('very-long'); jest.resetAllMocks(); hardhat && (await startHardhat()); } @@ -127,7 +124,13 @@ export async function startIosSimulator() { } } -export async function typeText(elementId: string | RegExp, text: string | undefined, focus = true, syncOnAndroid = false) { +export async function typeText( + elementId: string | RegExp, + text: string | undefined, + focus = true, + syncOnAndroid = false, + hitEnterAfterText = false +) { if (text === undefined) { throw new Error(`Cannot type 'undefined' into element with id ${elementId}`); } @@ -140,6 +143,7 @@ export async function typeText(elementId: string | RegExp, text: string | undefi await device.disableSynchronization(); } await element(by.id(elementId)).typeText(text); + hitEnterAfterText && (await typeText(elementId, '\n')); if (device.getPlatform() === 'android' && !syncOnAndroid) { await device.enableSynchronization(); } @@ -147,6 +151,7 @@ export async function typeText(elementId: string | RegExp, text: string | undefi throw new Error(`Error typing "${text}" at element with id ${elementId}}: ${error}`); } } + export async function typeNumbers(elementId: string | RegExp, text: string, submitLabel: string | RegExp) { try { await element(by.id(elementId)).replaceText(text.replace('\n', '')); diff --git a/e2e/init.js b/e2e/init.js index 0778641286a..a6d016ad6b6 100644 --- a/e2e/init.js +++ b/e2e/init.js @@ -27,5 +27,7 @@ beforeAll(async () => { '.*rainbowme-res.cloudinary.com*', '.*rainbow-proxy-rpc.rainbowdotme.workers.*', '.*localhost:8081/assets/src/assets*.', + '.*arc-graphql.rainbowdotme.workers.dev*.', + '.*googleapis.com*.', ]); }); diff --git a/src/components/DappBrowser/DappBrowser.tsx b/src/components/DappBrowser/DappBrowser.tsx index e6cdc74b076..a1c41ffe873 100644 --- a/src/components/DappBrowser/DappBrowser.tsx +++ b/src/components/DappBrowser/DappBrowser.tsx @@ -157,6 +157,7 @@ const TabViewScrollView = ({ children }: { children: React.ReactNode }) => { pinchGestureEnabled={false} ref={scrollViewRef} showsVerticalScrollIndicator={false} + testID={'browser-screen'} > {children} diff --git a/src/components/DappBrowser/search-input/SearchInput.tsx b/src/components/DappBrowser/search-input/SearchInput.tsx index 61d9133f455..6e527ae9c9b 100644 --- a/src/components/DappBrowser/search-input/SearchInput.tsx +++ b/src/components/DappBrowser/search-input/SearchInput.tsx @@ -393,6 +393,7 @@ const AddressBar = React.memo(function AddressBar({ = { [REMOTE_PROMO_SHEETS]: { settings: true, value: false }, [REMOTE_CARDS]: { settings: true, value: false }, [POINTS_NOTIFICATIONS_TOGGLE]: { settings: true, value: false }, - [DAPP_BROWSER]: { settings: true, value: false }, + [DAPP_BROWSER]: { settings: true, value: IS_TEST ? true : false }, [SWAPS_V2]: { settings: true, value: false }, }; diff --git a/src/screens/WalletConnectApprovalSheet.js b/src/screens/WalletConnectApprovalSheet.js index d4a9b1fd3c6..d708e37d162 100644 --- a/src/screens/WalletConnectApprovalSheet.js +++ b/src/screens/WalletConnectApprovalSheet.js @@ -430,7 +430,7 @@ export default function WalletConnectApprovalSheet() { {isScam && ( - + From a79ec4b5bff71fa533cd63357a38919554037bc1 Mon Sep 17 00:00:00 2001 From: Matthew Wall Date: Mon, 10 Jun 2024 10:37:40 -0400 Subject: [PATCH 5/8] remove info icon on max transaction fee row as we don't have that in spec (#5827) --- .../screens/Swap/components/GasPanel.tsx | 46 +++++++++++++------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/src/__swaps__/screens/Swap/components/GasPanel.tsx b/src/__swaps__/screens/Swap/components/GasPanel.tsx index 2402552e183..17224d285c7 100644 --- a/src/__swaps__/screens/Swap/components/GasPanel.tsx +++ b/src/__swaps__/screens/Swap/components/GasPanel.tsx @@ -16,7 +16,7 @@ import { import { add, subtract } from '@/__swaps__/utils/numbers'; import { opacity } from '@/__swaps__/utils/swaps'; import { ButtonPressAnimation } from '@/components/animations'; -import { Box, Inline, Separator, Stack, Text, globalColors, useColorMode, useForegroundColor } from '@/design-system'; +import { Bleed, Box, Inline, Separator, Stack, Text, globalColors, useColorMode, useForegroundColor } from '@/design-system'; import { IS_ANDROID } from '@/env'; import { lessThan } from '@/helpers/utilities'; import { useNavigation } from '@/navigation'; @@ -24,11 +24,14 @@ import Routes from '@/navigation/routesNames'; import { createRainbowStore } from '@/state/internal/createRainbowStore'; import { useSwapsStore } from '@/state/swaps/swapsStore'; import { upperFirst } from 'lodash'; +import { gasUtils } from '@/utils'; import { formatNumber } from '../hooks/formatNumber'; import { GasSettings, getCustomGasSettings, setCustomGasSettings, useCustomGasStore } from '../hooks/useCustomGas'; import { setSelectedGasSpeed, useSelectedGasSpeed } from '../hooks/useSelectedGas'; import { EstimatedSwapGasFee } from './EstimatedSwapGasFee'; +const { GAS_TRENDS } = gasUtils; + const MINER_TIP_TYPE = 'minerTip'; const MAX_BASE_FEE_TYPE = 'maxBaseFee'; const HIGH_ALERT = 'HIGH_ALERT'; @@ -136,9 +139,12 @@ function CurrentBaseFee() { const { isDarkMode } = useColorMode(); const { navigate } = useNavigation(); + const label = useForegroundColor('label'); + const labelSecondary = useForegroundColor('labelSecondary'); + const chainId = useSwapsStore(s => s.inputAsset?.chainId || ChainId.mainnet); const { data: baseFee } = useBaseFee({ chainId, select: selectWeiToGwei }); - const { data: gasTrend } = useGasTrend({ chainId }); + const { data: gasTrend = 'notrend' } = useGasTrend({ chainId }); const trendType = 'currentBaseFee' + upperFirst(gasTrend); @@ -160,15 +166,30 @@ function CurrentBaseFee() { > {i18n.t(i18n.l.gas.current_base_fee)} - - {formatNumber(baseFee || '0')} - + + + + {GAS_TRENDS[gasTrend].label} + + + {formatNumber(baseFee || '0')} + + + ); } @@ -277,9 +298,6 @@ function MaxTransactionFee() { {i18n.t(i18n.l.gas.max_transaction_fee)} - - 􀅴 - From 9b668f8acf56b1ad5295c7a2c0a429330ca79acd Mon Sep 17 00:00:00 2001 From: Matthew Wall Date: Mon, 10 Jun 2024 20:01:21 -0400 Subject: [PATCH 6/8] Add entry point for other networks (#5824) * add entry point from other available networks * actually remove the setting of the input filter for chain selector. doesn't make sense since they don't have another asset on that chain * Update src/components/expanded-state/AvailableNetworksv2.tsx * formatting fixes * Update src/__swaps__/screens/Swap/hooks/useSwapInputsController.ts Co-authored-by: gregs --------- Co-authored-by: gregs --- .../Swap/hooks/useSwapInputsController.ts | 2 +- .../screens/Swap/providers/swap-provider.tsx | 4 +- src/__swaps__/utils/swaps.ts | 3 + .../expanded-state/AvailableNetworksv2.tsx | 67 ++++++++++++++++++- .../asset/ChartExpandedState.js | 2 +- .../sheet-action-buttons/SwapActionButton.tsx | 7 +- 6 files changed, 79 insertions(+), 6 deletions(-) diff --git a/src/__swaps__/screens/Swap/hooks/useSwapInputsController.ts b/src/__swaps__/screens/Swap/hooks/useSwapInputsController.ts index 3bd64647059..cfb9daead9d 100644 --- a/src/__swaps__/screens/Swap/hooks/useSwapInputsController.ts +++ b/src/__swaps__/screens/Swap/hooks/useSwapInputsController.ts @@ -32,7 +32,7 @@ import { queryClient } from '@/react-query'; import { divWorklet, equalWorklet, greaterThanWorklet, mulWorklet, toFixedWorklet } from '@/__swaps__/safe-math/SafeMath'; function getInitialInputValues(initialSelectedInputAsset: ExtendedAnimatedAssetWithColors | null) { - const initialBalance = Number(initialSelectedInputAsset?.balance.amount) ?? 0; + const initialBalance = Number(initialSelectedInputAsset?.balance.amount) || 0; const initialNiceIncrement = findNiceIncrement(initialBalance); const initialDecimalPlaces = countDecimalPlaces(initialNiceIncrement); diff --git a/src/__swaps__/screens/Swap/providers/swap-provider.tsx b/src/__swaps__/screens/Swap/providers/swap-provider.tsx index b63615582da..f67193d98d6 100644 --- a/src/__swaps__/screens/Swap/providers/swap-provider.tsx +++ b/src/__swaps__/screens/Swap/providers/swap-provider.tsx @@ -127,7 +127,9 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { const selectedOutputChainId = useSharedValue(initialSelectedInputAsset?.chainId || ChainId.mainnet); const quote = useSharedValue(null); - const inputProgress = useSharedValue(NavigationSteps.INPUT_ELEMENT_FOCUSED); + const inputProgress = useSharedValue( + initialSelectedOutputAsset && !initialSelectedInputAsset ? NavigationSteps.TOKEN_LIST_FOCUSED : NavigationSteps.INPUT_ELEMENT_FOCUSED + ); const outputProgress = useSharedValue( initialSelectedOutputAsset ? NavigationSteps.INPUT_ELEMENT_FOCUSED : NavigationSteps.TOKEN_LIST_FOCUSED ); diff --git a/src/__swaps__/utils/swaps.ts b/src/__swaps__/utils/swaps.ts index dc57412ed99..501c15fd7a1 100644 --- a/src/__swaps__/utils/swaps.ts +++ b/src/__swaps__/utils/swaps.ts @@ -149,6 +149,9 @@ export const countDecimalPlaces = (number: number | string): number => { export const findNiceIncrement = (availableBalance: string | number) => { 'worklet'; + if (Number(availableBalance) === 0) { + return 0; + } // We'll use one of these factors to adjust the base increment // These factors are chosen to: diff --git a/src/components/expanded-state/AvailableNetworksv2.tsx b/src/components/expanded-state/AvailableNetworksv2.tsx index d15af381327..bc1967a0d96 100644 --- a/src/components/expanded-state/AvailableNetworksv2.tsx +++ b/src/components/expanded-state/AvailableNetworksv2.tsx @@ -17,6 +17,14 @@ import ContextMenuButton from '@/components/native-context-menu/contextMenu'; import { implementation } from '@/entities/dispersion'; import { RainbowNetworks, getNetworkObj } from '@/networks'; import { EthCoinIcon } from '../coin-icon/EthCoinIcon'; +import { SWAPS_V2, useExperimentalFlag } from '@/config'; +import { useRemoteConfig } from '@/model/remoteConfig'; +import { userAssetsStore } from '@/state/assets/userAssets'; +import { parseSearchAsset } from '@/__swaps__/utils/assets'; +import { AddressOrEth, AssetType } from '@/__swaps__/types/assets'; +import { chainNameFromChainId } from '@/__swaps__/utils/chains'; +import { swapsStore } from '@/state/swaps/swapsStore'; +import { InteractionManager } from 'react-native'; const NOOP = () => null; @@ -33,6 +41,8 @@ const AvailableNetworksv2 = ({ }) => { const { colors } = useTheme(); const { goBack, navigate } = useNavigation(); + const { swaps_v2 } = useRemoteConfig(); + const swapsV2Enabled = useExperimentalFlag(SWAPS_V2); const radialGradientProps = { center: [0, 1], @@ -62,10 +72,63 @@ const AvailableNetworksv2 = ({ // we need to convert the mainnet asset to the selected network's newAsset.mainnet_address = networks?.[ethereumUtils.getChainIdFromNetwork(Network.mainnet)]?.address ?? asset.address; newAsset.address = networks?.[ethereumUtils.getChainIdFromNetwork(chosenNetwork)].address; + newAsset.network = chosenNetwork; + + goBack(); + + if (swapsV2Enabled || swaps_v2) { + const chainId = ethereumUtils.getChainIdFromNetwork(newAsset.network); + const uniqueId = `${newAsset.address}_${chainId}`; + const userAsset = userAssetsStore.getState().userAssets.get(uniqueId); + + const parsedAsset = parseSearchAsset({ + assetWithPrice: { + ...newAsset, + uniqueId, + address: newAsset.address as AddressOrEth, + type: newAsset.type as AssetType, + chainId, + chainName: chainNameFromChainId(chainId), + isNativeAsset: false, + native: {}, + }, + searchAsset: { + ...newAsset, + uniqueId, + chainId, + chainName: chainNameFromChainId(chainId), + address: newAsset.address as AddressOrEth, + highLiquidity: newAsset.highLiquidity ?? false, + isRainbowCurated: newAsset.isRainbowCurated ?? false, + isVerified: newAsset.isVerified ?? false, + mainnetAddress: (newAsset.mainnet_address ?? '') as AddressOrEth, + networks: newAsset.networks ?? [], + type: newAsset.type as AssetType, + }, + userAsset, + }); + + const largestBalanceSameChainUserAsset = userAssetsStore + .getState() + .getUserAssets() + .find(userAsset => userAsset.chainId === chainId && userAsset.address !== newAsset.address); + if (largestBalanceSameChainUserAsset) { + swapsStore.setState({ inputAsset: largestBalanceSameChainUserAsset }); + } else { + swapsStore.setState({ inputAsset: null }); + } + swapsStore.setState({ outputAsset: parsedAsset }); + + InteractionManager.runAfterInteractions(() => { + navigate(Routes.SWAP); + }); + + return; + } + newAsset.uniqueId = `${asset.address}_${chosenNetwork}`; newAsset.type = chosenNetwork; - goBack(); navigate(Routes.EXCHANGE_MODAL, { params: { fromDiscover: true, @@ -81,7 +144,7 @@ const AvailableNetworksv2 = ({ screen: Routes.CURRENCY_SELECT_SCREEN, }); }, - [asset, goBack, navigate, networks, updateInputCurrency] + [asset, goBack, navigate, networks, swapsV2Enabled, swaps_v2, updateInputCurrency] ); const handlePressContextMenu = useCallback( diff --git a/src/components/expanded-state/asset/ChartExpandedState.js b/src/components/expanded-state/asset/ChartExpandedState.js index d947faf4254..e4db1c1c7b2 100644 --- a/src/components/expanded-state/asset/ChartExpandedState.js +++ b/src/components/expanded-state/asset/ChartExpandedState.js @@ -29,7 +29,7 @@ import { useNavigation } from '@/navigation'; import { ETH_ADDRESS } from '@/references'; import Routes from '@/navigation/routesNames'; import styled from '@/styled-thing'; -import { ethereumUtils, safeAreaInsetValues } from '@/utils'; +import { safeAreaInsetValues } from '@/utils'; import AvailableNetworksv2 from '@/components/expanded-state/AvailableNetworksv2'; import AvailableNetworksv1 from '@/components/expanded-state/AvailableNetworks'; import { Box } from '@/design-system'; diff --git a/src/components/sheet/sheet-action-buttons/SwapActionButton.tsx b/src/components/sheet/sheet-action-buttons/SwapActionButton.tsx index 8daef82baa5..8e5123b3394 100644 --- a/src/components/sheet/sheet-action-buttons/SwapActionButton.tsx +++ b/src/components/sheet/sheet-action-buttons/SwapActionButton.tsx @@ -38,11 +38,13 @@ function SwapActionButton({ asset, color: givenColor, inputType, label, fromDisc const goToSwap = useCallback(() => { if (swapsV2Enabled || swaps_v2) { const chainId = ethereumUtils.getChainIdFromNetwork(asset.network); - const userAsset = userAssetsStore.getState().userAssets.get(`${asset.address}_${chainId}`); + const uniqueId = `${asset.address}_${chainId}`; + const userAsset = userAssetsStore.getState().userAssets.get(uniqueId); const parsedAsset = parseSearchAsset({ assetWithPrice: { ...asset, + uniqueId, address: asset.address as AddressOrEth, type: asset.type as AssetType, chainId, @@ -52,6 +54,7 @@ function SwapActionButton({ asset, color: givenColor, inputType, label, fromDisc }, searchAsset: { ...asset, + uniqueId, chainId, chainName: chainNameFromChainId(chainId), address: asset.address as AddressOrEth, @@ -74,6 +77,8 @@ function SwapActionButton({ asset, color: givenColor, inputType, label, fromDisc .find(userAsset => userAsset.chainId === chainId && userAsset.address !== asset.address); if (largestBalanceSameChainUserAsset) { swapsStore.setState({ inputAsset: largestBalanceSameChainUserAsset }); + } else { + swapsStore.setState({ inputAsset: null }); } swapsStore.setState({ outputAsset: parsedAsset }); } From f2c7bfb0b5ee98bc3fb43dbb9ca5d0a62cad74fa Mon Sep 17 00:00:00 2001 From: Bruno Barbieri <1247834+brunobar79@users.noreply.github.com> Date: Mon, 10 Jun 2024 22:12:00 -0400 Subject: [PATCH 7/8] bump braces (#5831) --- package.json | 1 + yarn.lock | 94 ++++++---------------------------------------------- 2 files changed, 12 insertions(+), 83 deletions(-) diff --git a/package.json b/package.json index 4919b3a7e07..fee4475140a 100644 --- a/package.json +++ b/package.json @@ -374,6 +374,7 @@ "webpack-cli": "5.1.4" }, "resolutions": { + "**/braces": "3.0.3", "**/async": "2.6.4", "**/zod": "3.22.3", "**/file-type": "16.5.4", diff --git a/yarn.lock b/yarn.lock index a0348585153..d9ef9da7484 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6331,11 +6331,6 @@ arr-diff@^4.0.0: resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== -arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - arr-union@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" @@ -7087,28 +7082,12 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - -braces@^3.0.2, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== +braces@3.0.3, braces@^2.3.1, braces@^3.0.2, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: - fill-range "^7.0.1" + fill-range "^7.1.1" brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" @@ -10242,7 +10221,7 @@ extend-shallow@^2.0.1: dependencies: is-extendable "^0.1.0" -extend-shallow@^3.0.0, extend-shallow@^3.0.2: +extend-shallow@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== @@ -10452,20 +10431,10 @@ filing-cabinet@^3.0.1: tsconfig-paths "^3.10.1" typescript "^3.9.7" -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ== - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" @@ -13132,7 +13101,7 @@ kind-of@^2.0.1: dependencies: is-buffer "^1.0.2" -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: +kind-of@^3.0.2, kind-of@^3.0.3: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== @@ -17187,16 +17156,6 @@ remove-accents@0.4.2: resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5" integrity sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA== -repeat-element@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" - integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== - request@^2.79.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" @@ -17962,22 +17921,6 @@ smitter@1.1.1: resolved "https://registry.yarnpkg.com/smitter/-/smitter-1.1.1.tgz#cade535ccd3b2cc8ad274a9fe9b02937f50a316f" integrity sha512-6AwxCy1VfHVBpCljZb/QCGUcRmZKL6s3o5NRjJfJKAQxtiC8GCJUpy1OFs3RcJinykoj/p7jIkPrM3Z3bYmgZg== -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - snapdragon@^0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" @@ -18116,13 +18059,6 @@ split-on-first@^1.0.0: resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== -split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - split2@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" @@ -18835,14 +18771,6 @@ to-object-path@^0.3.0: dependencies: kind-of "^3.0.2" -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg== - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" From 7cdcc7ac985ab1a775353b017fdc507ac289190d Mon Sep 17 00:00:00 2001 From: Christian Baroni <7061887+christianbaroni@users.noreply.github.com> Date: Mon, 10 Jun 2024 23:15:05 -0400 Subject: [PATCH 8/8] Fix provider recursion bug, provider function types (#5829) --- .../screens/Swap/providers/swap-provider.tsx | 2 +- .../Swap/resources/assets/userAssets.ts | 24 +++--- src/handlers/web3.ts | 73 ++++++++++--------- src/hooks/useRefreshAccountData.ts | 11 +-- src/networks/arbitrum.ts | 4 +- src/networks/avalanche.ts | 4 +- src/networks/base.ts | 4 +- src/networks/blast.ts | 4 +- src/networks/bsc.ts | 4 +- src/networks/degen.ts | 4 +- src/networks/gnosis.ts | 4 +- src/networks/goerli.ts | 4 +- src/networks/index.ts | 6 +- src/networks/mainnet.ts | 4 +- src/networks/optimism.ts | 4 +- src/networks/polygon.ts | 6 +- src/networks/types.ts | 4 +- src/networks/zora.ts | 4 +- src/resources/assets/useSortedUserAssets.ts | 8 +- src/resources/assets/useUserAsset.ts | 8 +- src/resources/assets/useUserAssetCount.ts | 8 +- src/screens/NFTSingleOfferSheet/index.tsx | 4 +- src/screens/mints/MintSheet.tsx | 4 +- src/utils/ethereumUtils.ts | 9 +-- 24 files changed, 104 insertions(+), 107 deletions(-) diff --git a/src/__swaps__/screens/Swap/providers/swap-provider.tsx b/src/__swaps__/screens/Swap/providers/swap-provider.tsx index f67193d98d6..9ca5177abfc 100644 --- a/src/__swaps__/screens/Swap/providers/swap-provider.tsx +++ b/src/__swaps__/screens/Swap/providers/swap-provider.tsx @@ -174,7 +174,7 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { ? await getFlashbotsProvider() : getCachedProviderForNetwork(network); const providerUrl = provider?.connection?.url; - const connectedToHardhat = isHardHat(providerUrl); + const connectedToHardhat = !!providerUrl && isHardHat(providerUrl); const selectedGas = getSelectedGas(parameters.chainId); if (!selectedGas) { diff --git a/src/__swaps__/screens/Swap/resources/assets/userAssets.ts b/src/__swaps__/screens/Swap/resources/assets/userAssets.ts index 93c0c458b56..024392c1969 100644 --- a/src/__swaps__/screens/Swap/resources/assets/userAssets.ts +++ b/src/__swaps__/screens/Swap/resources/assets/userAssets.ts @@ -1,20 +1,19 @@ import { useQuery } from '@tanstack/react-query'; import { Address } from 'viem'; import { ADDYS_API_KEY } from 'react-native-dotenv'; - import { QueryConfigWithSelect, QueryFunctionArgs, QueryFunctionResult, createQueryKey, queryClient } from '@/react-query'; + +import { getIsHardhatConnected } from '@/handlers/web3'; +import { RainbowError, logger } from '@/logger'; +import { RainbowFetchClient } from '@/rainbow-fetch'; import { SupportedCurrencyKey, SUPPORTED_CHAIN_IDS } from '@/references'; import { ParsedAssetsDictByChain, ZerionAsset } from '@/__swaps__/types/assets'; import { ChainId } from '@/__swaps__/types/chains'; import { AddressAssetsReceivedMessage } from '@/__swaps__/types/refraction'; import { filterAsset, parseUserAsset } from '@/__swaps__/utils/assets'; import { greaterThan } from '@/__swaps__/utils/numbers'; -import { RainbowError, logger } from '@/logger'; import { fetchUserAssetsByChain } from './userAssetsByChain'; -import { RainbowFetchClient } from '@/rainbow-fetch'; -import { useAccountSettings } from '@/hooks'; -import { getCachedProviderForNetwork, isHardHat } from '@/handlers/web3'; const addysHttp = new RainbowFetchClient({ baseURL: 'https://addys.p.rainbow.me/v3', @@ -31,27 +30,27 @@ export const USER_ASSETS_STALE_INTERVAL = 30000; // Query Types export type UserAssetsArgs = { - address?: Address; + address: Address; currency: SupportedCurrencyKey; testnetMode?: boolean; }; type SetUserAssetsArgs = { - address?: Address; + address: Address; currency: SupportedCurrencyKey; userAssets?: UserAssetsResult; testnetMode?: boolean; }; type SetUserDefaultsArgs = { - address?: Address; + address: Address; currency: SupportedCurrencyKey; staleTime: number; testnetMode?: boolean; }; type FetchUserAssetsArgs = { - address?: Address; + address: Address; currency: SupportedCurrencyKey; testnetMode?: boolean; }; @@ -211,12 +210,9 @@ export function useUserAssets( { address, currency }: UserAssetsArgs, config: QueryConfigWithSelect = {} ) { - const { network: currentNetwork } = useAccountSettings(); - const provider = getCachedProviderForNetwork(currentNetwork); - const providerUrl = provider?.connection?.url; - const connectedToHardhat = isHardHat(providerUrl); + const isHardhatConnected = getIsHardhatConnected(); - return useQuery(userAssetsQueryKey({ address, currency, testnetMode: connectedToHardhat }), userAssetsQueryFunction, { + return useQuery(userAssetsQueryKey({ address, currency, testnetMode: isHardhatConnected }), userAssetsQueryFunction, { ...config, refetchInterval: USER_ASSETS_REFETCH_INTERVAL, staleTime: process.env.IS_TESTING === 'true' ? 0 : 1000, diff --git a/src/handlers/web3.ts b/src/handlers/web3.ts index 5b05dcd8775..72283159f67 100644 --- a/src/handlers/web3.ts +++ b/src/handlers/web3.ts @@ -37,15 +37,14 @@ import { ethereumUtils } from '@/utils'; import { logger, RainbowError } from '@/logger'; import { IS_IOS, RPC_PROXY_API_KEY, RPC_PROXY_BASE_URL } from '@/env'; import { getNetworkObj } from '@/networks'; +import store from '@/redux/store'; export enum TokenStandard { ERC1155 = 'ERC1155', ERC721 = 'ERC721', } -export const networkProviders: { - [network in Network]?: StaticJsonRpcProvider; -} = {}; +export const networkProviders = new Map(); /** * Creates an rpc endpoint for a given chain id using the Rainbow rpc proxy. @@ -207,8 +206,8 @@ export const getFlashbotsProvider = async () => { ); }; -export const getCachedProviderForNetwork = (network: Network = Network.mainnet) => { - return networkProviders[network]!; +export const getCachedProviderForNetwork = (network: Network = Network.mainnet): StaticJsonRpcProvider | undefined => { + return networkProviders.get(network); }; /** @@ -217,27 +216,37 @@ export const getCachedProviderForNetwork = (network: Network = Network.mainnet) * @return The provider for the network. */ export const getProviderForNetwork = async (network: Network | string = Network.mainnet): Promise => { - if (isNetworkEnum(network) && networkProviders[network]) { - return networkProviders[network]!; + const isSupportedNetwork = isNetworkEnum(network); + const cachedProvider = isSupportedNetwork ? networkProviders.get(network) : undefined; + + if (isSupportedNetwork && cachedProvider) { + return cachedProvider; } - if (!isNetworkEnum(network)) { + if (!isSupportedNetwork) { const provider = new StaticJsonRpcProvider(network, Network.mainnet); - networkProviders[Network.mainnet] = provider; + networkProviders.set(Network.mainnet, provider); return provider; } else { - const chainId = getNetworkObj(network).id; - const provider = new StaticJsonRpcProvider(getNetworkObj(network).rpc, chainId); - if (!networkProviders[network]) { - networkProviders[network] = provider; - } - await provider.ready; + const provider = new StaticJsonRpcProvider(getNetworkObj(network).rpc(), getNetworkObj(network).id); + networkProviders.set(network, provider); return provider; } }; /** - * @desc Sends an arbitrary RCP call using a given provider, or the default + * @desc Checks if the active network is Hardhat. + * @returns boolean: `true` if connected to Hardhat. + */ +export const getIsHardhatConnected = (): boolean => { + const currentNetwork = store.getState().settings.network; + const currentProviderUrl = getCachedProviderForNetwork(currentNetwork)?.connection?.url; + const connectedToHardhat = !!currentProviderUrl && isHardHat(currentProviderUrl); + return connectedToHardhat; +}; + +/** + * @desc Sends an arbitrary RPC call using a given provider, or the default * cached provider. * @param payload The payload, including a method and parameters, based on * the Ethers.js `StaticJsonRpcProvider.send` arguments. @@ -248,10 +257,10 @@ export const getProviderForNetwork = async (network: Network | string = Network. export const sendRpcCall = async ( payload: { method: string; - params: any[]; + params: unknown[]; }, provider: StaticJsonRpcProvider | null = null -): Promise => (provider || web3Provider)?.send(payload.method, payload.params); +): Promise => (provider || web3Provider)?.send(payload.method, payload.params); /** * @desc check if hex string @@ -359,9 +368,9 @@ export const estimateGas = async ( export async function estimateGasWithPadding( txPayload: TransactionRequest, contractCallEstimateGas: Contract['estimateGas'][string] | null = null, - callArguments: any[] | null = null, + callArguments: unknown[] | null = null, provider: StaticJsonRpcProvider | null = null, - paddingFactor: number = 1.1 + paddingFactor = 1.1 ): Promise { try { const p = provider || web3Provider; @@ -383,16 +392,16 @@ export async function estimateGasWithPadding( const code = to ? await p.getCode(to) : undefined; // 2 - if it's not a contract AND it doesn't have any data use the default gas limit if ((!contractCallEstimateGas && !to) || (to && !data && (!code || code === '0x'))) { - logger.info('⛽ Skipping estimates, using default', { + logger.debug('⛽ Skipping estimates, using default', { ethUnits: ethUnits.basic_tx.toString(), }); return ethUnits.basic_tx.toString(); } - logger.info('⛽ Calculating safer gas limit for last block'); + logger.debug('⛽ Calculating safer gas limit for last block'); // 3 - If it is a contract, call the RPC method `estimateGas` with a safe value const saferGasLimit = fraction(gasLimit.toString(), 19, 20); - logger.info('⛽ safer gas limit for last block is', { saferGasLimit }); + logger.debug('⛽ safer gas limit for last block is', { saferGasLimit }); txPayloadToEstimate[contractCallEstimateGas ? 'gasLimit' : 'gas'] = toHex(saferGasLimit); @@ -404,7 +413,7 @@ export async function estimateGasWithPadding( const lastBlockGasLimit = addBuffer(gasLimit.toString(), 0.9); const paddedGas = addBuffer(estimatedGas.toString(), paddingFactor.toString()); - logger.info('⛽ GAS CALCULATIONS!', { + logger.debug('⛽ GAS CALCULATIONS!', { estimatedGas: estimatedGas.toString(), gasLimit: gasLimit.toString(), lastBlockGasLimit: lastBlockGasLimit, @@ -413,26 +422,24 @@ export async function estimateGasWithPadding( // If the safe estimation is above the last block gas limit, use it if (greaterThan(estimatedGas.toString(), lastBlockGasLimit)) { - logger.info('⛽ returning orginal gas estimation', { + logger.debug('⛽ returning orginal gas estimation', { esimatedGas: estimatedGas.toString(), }); return estimatedGas.toString(); } // If the estimation is below the last block gas limit, use the padded estimate if (greaterThan(lastBlockGasLimit, paddedGas)) { - logger.info('⛽ returning padded gas estimation', { paddedGas }); + logger.debug('⛽ returning padded gas estimation', { paddedGas }); return paddedGas; } // otherwise default to the last block gas limit - logger.info('⛽ returning last block gas limit', { lastBlockGasLimit }); + logger.debug('⛽ returning last block gas limit', { lastBlockGasLimit }); return lastBlockGasLimit; - } catch (e: any) { + } catch (e) { /* * Reported ~400x per day, but if it's not actionable it might as well be a warning. */ - logger.warn('Error calculating gas limit with padding', { - message: e.message, - }); + logger.warn('Error calculating gas limit with padding', { message: e instanceof Error ? e.message : 'Unknown error' }); return null; } } @@ -522,7 +529,7 @@ export const resolveUnstoppableDomain = async (domain: string): Promise { return address; }) - .catch((error: any) => { + .catch(error => { logger.error(new RainbowError(`resolveUnstoppableDomain error`), { message: error.message, }); @@ -779,7 +786,7 @@ export const estimateGasLimit = async ( recipient: string; amount: number; }, - addPadding: boolean = false, + addPadding = false, provider: StaticJsonRpcProvider | null = null, network: Network = Network.mainnet ): Promise => { diff --git a/src/hooks/useRefreshAccountData.ts b/src/hooks/useRefreshAccountData.ts index 090ef7614dd..4de563b7108 100644 --- a/src/hooks/useRefreshAccountData.ts +++ b/src/hooks/useRefreshAccountData.ts @@ -2,8 +2,7 @@ import { captureException } from '@sentry/react-native'; import delay from 'delay'; import { useCallback, useState } from 'react'; import { useDispatch } from 'react-redux'; -import { getCachedProviderForNetwork, isHardHat } from '@/handlers/web3'; -import NetworkTypes from '../helpers/networkTypes'; +import { getIsHardhatConnected } from '@/handlers/web3'; import { walletConnectLoadState } from '../redux/walletconnect'; import { fetchWalletENSAvatars, fetchWalletNames } from '../redux/wallets'; import useAccountSettings from './useAccountSettings'; @@ -16,14 +15,12 @@ import { positionsQueryKey } from '@/resources/defi/PositionsQuery'; export default function useRefreshAccountData() { const dispatch = useDispatch(); - const { accountAddress, network, nativeCurrency } = useAccountSettings(); + const { accountAddress, nativeCurrency } = useAccountSettings(); const [isRefreshing, setIsRefreshing] = useState(false); const profilesEnabled = useExperimentalFlag(PROFILES); const fetchAccountData = useCallback(async () => { - const provider = getCachedProviderForNetwork(network); - const providerUrl = provider?.connection?.url; - const connectedToHardhat = isHardHat(providerUrl); + const connectedToHardhat = getIsHardhatConnected(); queryClient.invalidateQueries({ queryKey: nftsQueryKey({ address: accountAddress }), @@ -57,7 +54,7 @@ export default function useRefreshAccountData() { captureException(error); throw error; } - }, [accountAddress, dispatch, nativeCurrency, network, profilesEnabled]); + }, [accountAddress, dispatch, nativeCurrency, profilesEnabled]); const refresh = useCallback(async () => { if (isRefreshing) return; diff --git a/src/networks/arbitrum.ts b/src/networks/arbitrum.ts index 8a9ae182fe6..72c95c4cad5 100644 --- a/src/networks/arbitrum.ts +++ b/src/networks/arbitrum.ts @@ -24,8 +24,8 @@ export const getArbitrumNetworkObject = (): NetworkProperties => { address: ARBITRUM_ETH_ADDRESS, }, - rpc: proxyRpcEndpoint(arbitrum.id), - getProvider: getProviderForNetwork(Network.arbitrum), + rpc: () => proxyRpcEndpoint(arbitrum.id), + getProvider: () => getProviderForNetwork(Network.arbitrum), balanceCheckerAddress: '0x54A4E5800345c01455a7798E0D96438364e22723', // features diff --git a/src/networks/avalanche.ts b/src/networks/avalanche.ts index 813fce77c90..ecb8b628d99 100644 --- a/src/networks/avalanche.ts +++ b/src/networks/avalanche.ts @@ -25,8 +25,8 @@ export const getAvalancheNetworkObject = (): NetworkProperties => { address: AVAX_AVALANCHE_ADDRESS, }, - rpc: proxyRpcEndpoint(avalanche.id), - getProvider: getProviderForNetwork(Network.avalanche), + rpc: () => proxyRpcEndpoint(avalanche.id), + getProvider: () => getProviderForNetwork(Network.avalanche), // need to find balance checker address balanceCheckerAddress: '', diff --git a/src/networks/base.ts b/src/networks/base.ts index f483b210019..ea2b6163d5a 100644 --- a/src/networks/base.ts +++ b/src/networks/base.ts @@ -25,8 +25,8 @@ export const getBaseNetworkObject = (): NetworkProperties => { address: BASE_ETH_ADDRESS, }, - rpc: proxyRpcEndpoint(base.id), - getProvider: getProviderForNetwork(Network.base), + rpc: () => proxyRpcEndpoint(base.id), + getProvider: () => getProviderForNetwork(Network.base), balanceCheckerAddress: '0x1C8cFdE3Ba6eFc4FF8Dd5C93044B9A690b6CFf36', // features diff --git a/src/networks/blast.ts b/src/networks/blast.ts index a0a7ba98d44..44c63c24fb3 100644 --- a/src/networks/blast.ts +++ b/src/networks/blast.ts @@ -30,8 +30,8 @@ export const getBlastNetworkObject = (): NetworkProperties => { }, balanceCheckerAddress: '', - rpc: proxyRpcEndpoint(BLAST_CHAIN_ID), - getProvider: getProviderForNetwork(Network.blast), + rpc: () => proxyRpcEndpoint(BLAST_CHAIN_ID), + getProvider: () => getProviderForNetwork(Network.blast), // features features: { diff --git a/src/networks/bsc.ts b/src/networks/bsc.ts index f69d37a88b3..3e38d897969 100644 --- a/src/networks/bsc.ts +++ b/src/networks/bsc.ts @@ -27,8 +27,8 @@ export const getBSCNetworkObject = (): NetworkProperties => { }, // this should be refactored to have less deps - rpc: proxyRpcEndpoint(bsc.id), - getProvider: getProviderForNetwork(Network.bsc), + rpc: () => proxyRpcEndpoint(bsc.id), + getProvider: () => getProviderForNetwork(Network.bsc), balanceCheckerAddress: '0x400A9f1Bb1Db80643C33710C2232A0D74EF5CFf1', // features diff --git a/src/networks/degen.ts b/src/networks/degen.ts index de95637f4c3..bc7c43aad2d 100644 --- a/src/networks/degen.ts +++ b/src/networks/degen.ts @@ -27,8 +27,8 @@ export const getDegenNetworkObject = (): NetworkProperties => { address: DEGEN_CHAIN_DEGEN_ADDRESS, }, - rpc: proxyRpcEndpoint(degen.id), - getProvider: getProviderForNetwork(Network.degen), + rpc: () => proxyRpcEndpoint(degen.id), + getProvider: () => getProviderForNetwork(Network.degen), // need to find balance checker address balanceCheckerAddress: '', diff --git a/src/networks/gnosis.ts b/src/networks/gnosis.ts index ad90dbac2d0..f6e18c742b7 100644 --- a/src/networks/gnosis.ts +++ b/src/networks/gnosis.ts @@ -23,8 +23,8 @@ export const getGnosisNetworkObject = (): NetworkProperties => { address: ETH_ADDRESS, }, - rpc: '', - getProvider: getProviderForNetwork(Network.optimism), + rpc: () => '', + getProvider: () => getProviderForNetwork(Network.optimism), balanceCheckerAddress: '', // features diff --git a/src/networks/goerli.ts b/src/networks/goerli.ts index 82e9c501ee5..2d9423a7ac2 100644 --- a/src/networks/goerli.ts +++ b/src/networks/goerli.ts @@ -25,8 +25,8 @@ export const getGoerliNetworkObject = (): NetworkProperties => { }, // this should be refactored to have less deps - getProvider: getProviderForNetwork(Network.goerli), - rpc: proxyRpcEndpoint(goerli.id), + getProvider: () => getProviderForNetwork(Network.goerli), + rpc: () => proxyRpcEndpoint(goerli.id), balanceCheckerAddress: '0xf3352813b612a2d198e437691557069316b84ebe', // features diff --git a/src/networks/index.ts b/src/networks/index.ts index 6cf68c4e728..949a70ef828 100644 --- a/src/networks/index.ts +++ b/src/networks/index.ts @@ -38,6 +38,10 @@ export const RainbowNetworks = [ */ export function getNetworkObj(network: Network): NetworkProperties { switch (network) { + // Mainnet + case Network.mainnet: + return getMainnetNetworkObject(); + // L2s case Network.arbitrum: return getArbitrumNetworkObject(); @@ -63,7 +67,7 @@ export function getNetworkObj(network: Network): NetworkProperties { case Network.goerli: return getGoerliNetworkObject(); - // Mainnet + // Fallback default: return getMainnetNetworkObject(); } diff --git a/src/networks/mainnet.ts b/src/networks/mainnet.ts index caeab7da391..2c3644511ec 100644 --- a/src/networks/mainnet.ts +++ b/src/networks/mainnet.ts @@ -25,8 +25,8 @@ export const getMainnetNetworkObject = (): NetworkProperties => { }, // this should be refactored to have less deps - getProvider: getProviderForNetwork(Network.mainnet), - rpc: proxyRpcEndpoint(mainnet.id), + getProvider: () => getProviderForNetwork(Network.mainnet), + rpc: () => proxyRpcEndpoint(mainnet.id), balanceCheckerAddress: '0x4dcf4562268dd384fe814c00fad239f06c2a0c2b', // features diff --git a/src/networks/optimism.ts b/src/networks/optimism.ts index ade896684be..b2d6ce8c8a6 100644 --- a/src/networks/optimism.ts +++ b/src/networks/optimism.ts @@ -25,8 +25,8 @@ export const getOptimismNetworkObject = (): NetworkProperties => { address: OPTIMISM_ETH_ADDRESS, }, - rpc: proxyRpcEndpoint(optimism.id), - getProvider: getProviderForNetwork(Network.optimism), + rpc: () => proxyRpcEndpoint(optimism.id), + getProvider: () => getProviderForNetwork(Network.optimism), balanceCheckerAddress: '0x1C8cFdE3Ba6eFc4FF8Dd5C93044B9A690b6CFf36', // features diff --git a/src/networks/polygon.ts b/src/networks/polygon.ts index 91650074209..7866a77a4c4 100644 --- a/src/networks/polygon.ts +++ b/src/networks/polygon.ts @@ -26,9 +26,9 @@ export const getPolygonNetworkObject = (): NetworkProperties => { mainnetAddress: MATIC_MAINNET_ADDRESS, }, - rpc: proxyRpcEndpoint(polygon.id), - getProvider: getProviderForNetwork(Network.polygon), - balanceCheckerAddress: '0x54A4E5800345c01455a7798E0D96438364e22723', + rpc: () => proxyRpcEndpoint(polygon.id), + getProvider: () => getProviderForNetwork(Network.polygon), + balanceCheckerAddress: '0x54A4E5800345c01455a77798E0D96438364e22723', // features features: { diff --git a/src/networks/types.ts b/src/networks/types.ts index 77501e0f459..028e83450b6 100644 --- a/src/networks/types.ts +++ b/src/networks/types.ts @@ -37,8 +37,8 @@ export interface NetworkProperties extends Chain { mainnetAddress?: string; }; - rpc: string; - getProvider: Promise; + rpc: () => string; + getProvider: () => Promise; balanceCheckerAddress: EthereumAddress; // feature flags diff --git a/src/networks/zora.ts b/src/networks/zora.ts index 2bde888fd7c..e8c5da58ffd 100644 --- a/src/networks/zora.ts +++ b/src/networks/zora.ts @@ -25,8 +25,8 @@ export const getZoraNetworkObject = (): NetworkProperties => { address: ZORA_ETH_ADDRESS, }, - rpc: proxyRpcEndpoint(zora.id), - getProvider: getProviderForNetwork(Network.zora), + rpc: () => proxyRpcEndpoint(zora.id), + getProvider: () => getProviderForNetwork(Network.zora), balanceCheckerAddress: '0x1C8cFdE3Ba6eFc4FF8Dd5C93044B9A690b6CFf36', // features diff --git a/src/resources/assets/useSortedUserAssets.ts b/src/resources/assets/useSortedUserAssets.ts index ab917fa760d..96cd2e91490 100644 --- a/src/resources/assets/useSortedUserAssets.ts +++ b/src/resources/assets/useSortedUserAssets.ts @@ -1,13 +1,11 @@ -import { getCachedProviderForNetwork, isHardHat } from '@/handlers/web3'; +import { getIsHardhatConnected } from '@/handlers/web3'; import { useAccountSettings } from '@/hooks'; import { selectSortedUserAssets } from '@/resources/assets/assetSelectors'; import { useUserAssets } from '@/resources/assets/UserAssetsQuery'; export function useSortedUserAssets() { - const { accountAddress, nativeCurrency, network: currentNetwork } = useAccountSettings(); - const provider = getCachedProviderForNetwork(currentNetwork); - const providerUrl = provider?.connection?.url; - const connectedToHardhat = isHardHat(providerUrl); + const { accountAddress, nativeCurrency } = useAccountSettings(); + const connectedToHardhat = getIsHardhatConnected(); return useUserAssets( { diff --git a/src/resources/assets/useUserAsset.ts b/src/resources/assets/useUserAsset.ts index c9777a9fa1f..96c5b7a6b34 100644 --- a/src/resources/assets/useUserAsset.ts +++ b/src/resources/assets/useUserAsset.ts @@ -1,13 +1,11 @@ -import { getCachedProviderForNetwork, isHardHat } from '@/handlers/web3'; +import { getIsHardhatConnected } from '@/handlers/web3'; import { useAccountSettings } from '@/hooks'; import { selectUserAssetWithUniqueId } from '@/resources/assets/assetSelectors'; import { useUserAssets } from '@/resources/assets/UserAssetsQuery'; export function useUserAsset(uniqueId: string) { - const { accountAddress, nativeCurrency, network: currentNetwork } = useAccountSettings(); - const provider = getCachedProviderForNetwork(currentNetwork); - const providerUrl = provider?.connection?.url; - const connectedToHardhat = isHardHat(providerUrl); + const { accountAddress, nativeCurrency } = useAccountSettings(); + const connectedToHardhat = getIsHardhatConnected(); return useUserAssets( { diff --git a/src/resources/assets/useUserAssetCount.ts b/src/resources/assets/useUserAssetCount.ts index 04785d280da..bd414a8c651 100644 --- a/src/resources/assets/useUserAssetCount.ts +++ b/src/resources/assets/useUserAssetCount.ts @@ -1,4 +1,4 @@ -import { getCachedProviderForNetwork, isHardHat } from '@/handlers/web3'; +import { getIsHardhatConnected } from '@/handlers/web3'; import { useAccountSettings } from '@/hooks'; import { useUserAssets } from '@/resources/assets/UserAssetsQuery'; import { RainbowAddressAssets } from './types'; @@ -6,10 +6,8 @@ import { RainbowAddressAssets } from './types'; const countSelector = (accountAssets: RainbowAddressAssets) => accountAssets?.length; export function useUserAssetCount() { - const { accountAddress, nativeCurrency, network: currentNetwork } = useAccountSettings(); - const provider = getCachedProviderForNetwork(currentNetwork); - const providerUrl = provider?.connection?.url; - const connectedToHardhat = isHardHat(providerUrl); + const { accountAddress, nativeCurrency } = useAccountSettings(); + const connectedToHardhat = getIsHardhatConnected(); return useUserAssets( { diff --git a/src/screens/NFTSingleOfferSheet/index.tsx b/src/screens/NFTSingleOfferSheet/index.tsx index 05cce58e4f8..26901f51b5e 100644 --- a/src/screens/NFTSingleOfferSheet/index.tsx +++ b/src/screens/NFTSingleOfferSheet/index.tsx @@ -185,7 +185,7 @@ export function NFTSingleOfferSheet() { // @ts-ignore account: accountAddress, chain: networkObj, - transport: http(networkObj.rpc), + transport: http(networkObj.rpc()), }); getClient()?.actions.acceptOffer({ items: [ @@ -285,7 +285,7 @@ export function NFTSingleOfferSheet() { const signer = createWalletClient({ account, chain: networkObj, - transport: http(networkObj.rpc), + transport: http(networkObj.rpc()), }); const nonce = await getNextNonce({ address: accountAddress, network }); try { diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index ab26b3e892e..1a429d370db 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -251,7 +251,7 @@ const MintSheet = () => { const signer = createWalletClient({ account: accountAddress, chain: networkObj, - transport: http(networkObj.rpc), + transport: http(networkObj.rpc()), }); try { await getClient()?.actions.mintToken({ @@ -360,7 +360,7 @@ const MintSheet = () => { const signer = createWalletClient({ account, chain: networkObj, - transport: http(networkObj.rpc), + transport: http(networkObj.rpc()), }); const feeAddress = getRainbowFeeAddress(currentNetwork); diff --git a/src/utils/ethereumUtils.ts b/src/utils/ethereumUtils.ts index 44e766a5b38..cb150b637f6 100644 --- a/src/utils/ethereumUtils.ts +++ b/src/utils/ethereumUtils.ts @@ -24,7 +24,7 @@ import { SelectedGasFee, } from '@/entities'; import { getOnchainAssetBalance } from '@/handlers/assets'; -import { getCachedProviderForNetwork, getProviderForNetwork, isHardHat, isTestnetNetwork, toHex } from '@/handlers/web3'; +import { getIsHardhatConnected, getProviderForNetwork, isTestnetNetwork, toHex } from '@/handlers/web3'; import { Network } from '@/helpers/networkTypes'; import { convertRawAmountToDecimalFormat, fromWei, greaterThan, isZero, subtract, add } from '@/helpers/utilities'; import { Navigation } from '@/navigation'; @@ -112,12 +112,11 @@ const getAsset = (accountAssets: Record, uniqueId: E }; const getUserAssetFromCache = (uniqueId: string) => { - const { accountAddress, nativeCurrency, network } = store.getState().settings; + const { accountAddress, nativeCurrency } = store.getState().settings; + const connectedToHardhat = getIsHardhatConnected(); const cache = queryClient.getQueryCache(); - const provider = getCachedProviderForNetwork(network); - const providerUrl = provider?.connection?.url; - const connectedToHardhat = isHardHat(providerUrl); + const cachedAddressAssets = (cache.find( userAssetsQueryKey({ address: accountAddress,