From d7bfcacbbc5a16ee49f52622ffd947a346f6a7b7 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Fri, 26 Jan 2024 13:57:17 -0500 Subject: [PATCH 01/18] save --- .../asset/ChartExpandedState.js | 11 ++- src/graphql/queries/metadata.graphql | 79 ++++++++++++++++ src/resources/assets/externalAssetsQuery.ts | 89 +++++++++++++++++++ 3 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 src/resources/assets/externalAssetsQuery.ts diff --git a/src/components/expanded-state/asset/ChartExpandedState.js b/src/components/expanded-state/asset/ChartExpandedState.js index 580c34dc066..895f7a49603 100644 --- a/src/components/expanded-state/asset/ChartExpandedState.js +++ b/src/components/expanded-state/asset/ChartExpandedState.js @@ -44,7 +44,6 @@ import { useChartThrottledPoints, useDelayedValueWithLayoutAnimation, useDimensions, - useGenericAsset, } from '@/hooks'; import { useRemoteConfig } from '@/model/remoteConfig'; import { useNavigation } from '@/navigation'; @@ -56,6 +55,7 @@ import AvailableNetworksv2 from '@/components/expanded-state/AvailableNetworksv2 import AvailableNetworksv1 from '@/components/expanded-state/AvailableNetworks'; import { Box } from '@/design-system'; import { getNetworkObj } from '@/networks'; +import { useExternalToken } from '@/resources/assets/externalAssetsQuery'; const defaultCarouselHeight = 60; const baseHeight = @@ -183,13 +183,18 @@ function Description({ text = '' }) { } export default function ChartExpandedState({ asset }) { - const genericAsset = useGenericAsset(asset?.address); + const { nativeCurrency, network: currentNetwork } = useAccountSettings(); + + const { data: genericAsset } = useExternalToken({ + address: asset?.address, + chainId: getNetworkObj(ethereumUtils.getChainIdFromType(asset.type)).id, + currency: nativeCurrency, + }); const { params: { fromDiscover = false }, } = useRoute(); const [carouselHeight, setCarouselHeight] = useState(defaultCarouselHeight); - const { nativeCurrency, network: currentNetwork } = useAccountSettings(); const [additionalContentHeight, setAdditionalContentHeight] = useState(0); // If we don't have a balance for this asset diff --git a/src/graphql/queries/metadata.graphql b/src/graphql/queries/metadata.graphql index 6ef3d6fe5ab..be0491a3a5e 100644 --- a/src/graphql/queries/metadata.graphql +++ b/src/graphql/queries/metadata.graphql @@ -400,3 +400,82 @@ mutation redeemCodeForPoints($address: String!, $redemptionCode: String!) { } } } + +fragment TokenAllTimeFragment on TokenAllTime { + highDate + highValue + lowDate + lowValue +} + +fragment TokenColorsFragment on TokenColors { + fallback + primary + shadow +} + +fragment TokenLinkFragment on TokenLink { + url +} + +fragment TokenLinksFragment on TokenLinks { + facebook { + ...TokenLinkFragment + } + homepage { + ...TokenLinkFragment + } + reddit { + ...TokenLinkFragment + } + telegram { + ...TokenLinkFragment + } + twitter { + ...TokenLinkFragment + } +} + +fragment TokenPriceChartFragment on TokenPriceChart { + points + timeEnd + timeStart +} + +fragment TokenPriceChartsFragment on TokenPriceCharts { + day { + ...TokenPriceChartFragment + } + hour { + ...TokenPriceChartFragment + } + max { + ...TokenPriceChartFragment + } + month { + ...TokenPriceChartFragment + } + week { + ...TokenPriceChartFragment + } + year { + ...TokenPriceChartFragment + } +} + +query externalToken($address: String!, $chainId: Int!, $currency: String) { + token(address: $address, chainID: $chainId, currency: $currency) { + colors { + ...TokenColorsFragment + } + decimals + iconUrl + name + networks + price { + relativeChange24h + value + } + symbol + } +} diff --git a/src/resources/assets/externalAssetsQuery.ts b/src/resources/assets/externalAssetsQuery.ts new file mode 100644 index 00000000000..8f137946a23 --- /dev/null +++ b/src/resources/assets/externalAssetsQuery.ts @@ -0,0 +1,89 @@ +import { useQuery } from '@tanstack/react-query'; +import { metadataClient } from '@/graphql'; +import { QueryFunctionArgs, createQueryKey, queryClient } from '@/react-query'; +import { convertAmountToNativeDisplay } from '@/helpers/utilities'; +import { NativeCurrencyKey } from '@/entities'; + +// Query Types for External Token +type ExternalTokenArgs = { + address: string; + chainId: number; + currency: NativeCurrencyKey; +}; + +// Query Key for Token Price +const TokenPriceQueryKey = ({ + address, + chainId, + currency, +}: ExternalTokenArgs) => + createQueryKey( + 'externalToken', + { address, chainId, currency }, + { persisterVersion: 1 } + ); + +type TokenPriceQueryKey = ReturnType; + +// Query Function for Token Price +export async function fetchExternalToken({ + address, + chainId, + currency, +}: ExternalTokenArgs) { + console.log('fetchingExternalToken: ', { address, chainId, currency }); + const response = await metadataClient.externalToken({ + address, + chainId, + currency, + }); + console.log('res: ', response.token); + + return { + ...response.token, + ...(response?.token?.price?.value && { + native: convertAmountToNativeDisplay( + response?.token?.price?.value, + currency + ), + }), + }; +} + +export async function externalTokenQueryFunction({ + queryKey: [{ address, chainId, currency }], +}: QueryFunctionArgs): Promise { + if (!address || !chainId) return null; + return await fetchExternalToken({ address, chainId, currency }); +} + +// Prefetch function for Token Price +export async function prefetchExternalToken({ + address, + chainId, + currency, +}: ExternalTokenArgs) { + queryClient.prefetchQuery( + TokenPriceQueryKey({ address, chainId, currency }), + async () => fetchExternalToken({ address, chainId, currency }), + { + staleTime: 60000, + } + ); +} + +// Query Hook for Token Price +export function useExternalToken({ + address, + chainId, + currency, +}: ExternalTokenArgs) { + return useQuery( + TokenPriceQueryKey({ address, chainId, currency }), + externalTokenQueryFunction, + { + cacheTime: 1000 * 60 * 60 * 24, // 24 hours + enabled: !!address && !!chainId, + } + ); +} From 17e14300526e46c26a9365216f10cbac17578dee Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Fri, 26 Jan 2024 14:56:39 -0500 Subject: [PATCH 02/18] ethCard and query clean up --- src/components/cards/EthCard.tsx | 56 +++++++++---------- src/resources/assets/externalAssetsQuery.ts | 62 ++++++++++++++++----- 2 files changed, 75 insertions(+), 43 deletions(-) diff --git a/src/components/cards/EthCard.tsx b/src/components/cards/EthCard.tsx index fda5f1c4013..5182e624d99 100644 --- a/src/components/cards/EthCard.tsx +++ b/src/components/cards/EthCard.tsx @@ -18,17 +18,16 @@ import { useWallets, } from '@/hooks'; import { useRemoteConfig } from '@/model/remoteConfig'; -import { deviceUtils, ethereumUtils } from '@/utils'; +import { deviceUtils } from '@/utils'; import { useNavigation } from '@/navigation'; import Routes from '@/navigation/routesNames'; import { analyticsV2 } from '@/analytics'; -import { ETH_ADDRESS, ETH_SYMBOL } from '@/references'; +import { ETH_ADDRESS } from '@/references'; import { ChartPath, ChartPathProvider, } from '@/react-native-animated-charts/src'; import { CoinIcon } from '../coin-icon'; -import { AssetType } from '@/entities'; import Labels from '../value-chart/ExtremeLabels'; import showWalletErrorAlert from '@/helpers/support'; import { IS_IOS } from '@/env'; @@ -40,6 +39,9 @@ import { useAccountAccentColor } from '@/hooks/useAccountAccentColor'; import { useRoute } from '@react-navigation/native'; import * as i18n from '@/languages'; import { ButtonPressAnimationTouchEvent } from '@/components/animations/ButtonPressAnimation/types'; +import { useExternalToken } from '@/resources/assets/externalAssetsQuery'; +import { getMainnetNetworkObject } from '@/networks/mainnet'; +import assetTypes from '@/entities/assetTypes'; export const ETH_CARD_HEIGHT = 284.3; @@ -48,7 +50,11 @@ export const EthCard = () => { const { colors, isDarkMode } = useTheme(); const { navigate } = useNavigation(); const { isDamaged } = useWallets(); - const genericAsset = useGenericAsset(ETH_ADDRESS); + const { data: ethAsset } = useExternalToken({ + address: ETH_ADDRESS, + chainId: getMainnetNetworkObject().id, + currency: nativeCurrency, + }); const { loaded: accentColorLoaded } = useAccountAccentColor(); const { name: routeName } = useRoute(); const cardType = 'stretch'; @@ -78,17 +84,9 @@ export const EthCard = () => { [accountAddress, isDamaged, navigate, routeName] ); - const assetWithPrice = useMemo(() => { - return { - ...ethereumUtils.formatGenericAsset(genericAsset, nativeCurrency), - address: ETH_ADDRESS, - symbol: ETH_SYMBOL, - }; - }, [genericAsset, nativeCurrency]); - const handleAssetPress = useCallback(() => { navigate(Routes.EXPANDED_ASSET_SHEET, { - asset: assetWithPrice, + asset: ethAsset, type: 'token', }); analyticsV2.track(analyticsV2.event.cardPressed, { @@ -96,41 +94,39 @@ export const EthCard = () => { routeName, cardType, }); - }, [assetWithPrice, navigate, routeName]); + }, [ethAsset, navigate, routeName]); let colorForAsset = useColorForAsset( { - address: assetWithPrice.address, - mainnet_address: assetWithPrice?.mainnet_address, - type: assetWithPrice?.mainnet_address - ? AssetType.token - : assetWithPrice.type, + address: ETH_ADDRESS, + mainnet_address: ETH_ADDRESS, + type: assetTypes.token, }, - assetWithPrice?.address ? undefined : colors.appleBlue + colors.appleBlue ); - if (isDarkMode && assetWithPrice?.address === ETH_ADDRESS) { + if (isDarkMode) { colorForAsset = colors.whiteLabel; } const { throttledData } = useChartThrottledPoints({ - asset: assetWithPrice, + asset: ethAsset, }); const CHART_WIDTH = deviceUtils.dimensions.width - 80; const CHART_HEIGHT = 80; let isNegativePriceChange = false; - if (assetWithPrice.native.change[0] === '-') { + if (ethAsset?.native.change[0] === '-') { isNegativePriceChange = true; } const priceChangeDisplay = isNegativePriceChange - ? assetWithPrice.native.change.substring(1) - : assetWithPrice.native.change; + ? ethAsset?.native.change.substring(1) + : ethAsset?.native.change; const priceChangeColor = isNegativePriceChange ? colors.red : colors.green; - const loadedPrice = accentColorLoaded && assetWithPrice.native.change; + const loadedPrice = accentColorLoaded && ethAsset?.native.change; const loadedChart = throttledData?.points.length && loadedPrice; const [noChartData, setNoChartData] = useState(false); @@ -176,16 +172,16 @@ export const EthCard = () => { ) : ( <> - {assetWithPrice.name} + {ethAsset?.name} )} @@ -233,7 +229,7 @@ export const EthCard = () => { ) : ( - {assetWithPrice.native.price.display} + {ethAsset?.native.price.display} )} diff --git a/src/resources/assets/externalAssetsQuery.ts b/src/resources/assets/externalAssetsQuery.ts index 8f137946a23..8255b84beb9 100644 --- a/src/resources/assets/externalAssetsQuery.ts +++ b/src/resources/assets/externalAssetsQuery.ts @@ -1,8 +1,28 @@ import { useQuery } from '@tanstack/react-query'; import { metadataClient } from '@/graphql'; import { QueryFunctionArgs, createQueryKey, queryClient } from '@/react-query'; -import { convertAmountToNativeDisplay } from '@/helpers/utilities'; +import { + convertAmountAndPriceToNativeDisplay, + convertAmountToNativeDisplay, + convertAmountToPercentageDisplay, +} from '@/helpers/utilities'; import { NativeCurrencyKey } from '@/entities'; +import { Token } from '@/graphql/__generated__/metadata'; + +// Types +type ExternalToken = Pick< + Token, + 'decimals' | 'iconUrl' | 'name' | 'networks' | 'symbol' | 'colors' | 'price' +>; +type FormattedExternalAsset = ExternalToken & { + native: { + change: string; + price: { + amount: string; + display: string; + }; + }; +}; // Query Types for External Token type ExternalTokenArgs = { @@ -25,34 +45,50 @@ const TokenPriceQueryKey = ({ type TokenPriceQueryKey = ReturnType; +// Helpers +const formatExternalAsset = ( + asset: ExternalToken, + nativeCurrency: NativeCurrencyKey +): FormattedExternalAsset => { + return { + ...asset, + native: { + change: asset?.price?.relativeChange24h + ? convertAmountToPercentageDisplay(`${asset?.price?.relativeChange24h}`) + : '', + price: convertAmountAndPriceToNativeDisplay( + 1, + asset?.price?.value || 0, + nativeCurrency + ), + }, + }; +}; + // Query Function for Token Price export async function fetchExternalToken({ address, chainId, currency, }: ExternalTokenArgs) { - console.log('fetchingExternalToken: ', { address, chainId, currency }); const response = await metadataClient.externalToken({ address, chainId, currency, }); console.log('res: ', response.token); - - return { - ...response.token, - ...(response?.token?.price?.value && { - native: convertAmountToNativeDisplay( - response?.token?.price?.value, - currency - ), - }), - }; + if (response.token) { + return formatExternalAsset(response.token, currency); + } else { + return null; + } } export async function externalTokenQueryFunction({ queryKey: [{ address, chainId, currency }], -}: QueryFunctionArgs): Promise { +}: QueryFunctionArgs< + typeof TokenPriceQueryKey +>): Promise { if (!address || !chainId) return null; return await fetchExternalToken({ address, chainId, currency }); } From 256e9163a04c00423f75cc15940b22e84c09bb64 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Fri, 26 Jan 2024 17:04:39 -0500 Subject: [PATCH 03/18] remove instances --- .../RecyclerAssetList2/WrappedNFT.tsx | 2 +- .../UniqueTokenExpandedState.tsx | 9 +- .../asset/ChartExpandedState.js | 3 +- .../sheet-action-buttons/BuyActionButton.js | 4 +- .../sheet-action-buttons/SendActionButton.js | 2 +- .../sheet-action-buttons/SwapActionButton.js | 3 +- src/hooks/index.ts | 1 - src/hooks/useAsset.ts | 50 ++++--- src/hooks/useCollectible.ts | 6 +- src/hooks/useExpandedStateNavigation.ts | 12 +- src/hooks/useGenericAsset.ts | 40 ------ src/redux/data.ts | 90 +----------- .../helpers/debouncedUpdateGenericAssets.ts | 47 ------- src/resources/assets/externalAssetsQuery.ts | 4 +- src/screens/ExpandedAssetSheet.js | 9 +- src/utils/ethereumUtils.ts | 132 ++++++++++-------- 16 files changed, 135 insertions(+), 279 deletions(-) delete mode 100644 src/hooks/useGenericAsset.ts delete mode 100644 src/redux/helpers/debouncedUpdateGenericAssets.ts diff --git a/src/components/asset-list/RecyclerAssetList2/WrappedNFT.tsx b/src/components/asset-list/RecyclerAssetList2/WrappedNFT.tsx index 60e65b303ab..fad24ba57be 100644 --- a/src/components/asset-list/RecyclerAssetList2/WrappedNFT.tsx +++ b/src/components/asset-list/RecyclerAssetList2/WrappedNFT.tsx @@ -21,7 +21,7 @@ export default React.memo(function WrappedNFT({ placement: 'left' | 'right'; externalAddress?: string; }) { - const assetCollectible = useCollectible({ uniqueId }, externalAddress); + const assetCollectible = useCollectible(uniqueId, externalAddress); const asset = useMemo( () => ({ diff --git a/src/components/expanded-state/UniqueTokenExpandedState.tsx b/src/components/expanded-state/UniqueTokenExpandedState.tsx index 74b87d8a255..7bebad8f702 100644 --- a/src/components/expanded-state/UniqueTokenExpandedState.tsx +++ b/src/components/expanded-state/UniqueTokenExpandedState.tsx @@ -66,6 +66,7 @@ import { ENS_RECORDS, REGISTRATION_MODES } from '@/helpers/ens'; import { useAccountProfile, useBooleanState, + useCollectible, useDimensions, useENSProfile, useENSRegistration, @@ -242,16 +243,18 @@ const getIsSupportedOnRainbowWeb = (network: Network) => { }; const UniqueTokenExpandedState = ({ - asset, + asset: passedAsset, external, }: UniqueTokenExpandedStateProps) => { - const isSupportedOnRainbowWeb = getIsSupportedOnRainbowWeb(asset.network); - const { accountAddress } = useAccountProfile(); const { height: deviceHeight, width: deviceWidth } = useDimensions(); const { navigate, setOptions } = useNavigation(); const { colors, isDarkMode } = useTheme(); const { isReadOnlyWallet } = useWallets(); + const collecible = useCollectible(passedAsset?.uniqueId); + const asset = external ? passedAsset : collecible; + + const isSupportedOnRainbowWeb = getIsSupportedOnRainbowWeb(asset.network); const [ isRefreshMetadataToastActive, diff --git a/src/components/expanded-state/asset/ChartExpandedState.js b/src/components/expanded-state/asset/ChartExpandedState.js index 4ee8f7a4d29..569880bfdf8 100644 --- a/src/components/expanded-state/asset/ChartExpandedState.js +++ b/src/components/expanded-state/asset/ChartExpandedState.js @@ -259,6 +259,7 @@ export default function ChartExpandedState({ asset }) { }; }, [assetWithPrice, isL2, asset?.address, networks]); + console.log(assetWithPrice?.uniqueId); const { height: screenHeight } = useDimensions(); const delayedDescriptions = useDelayedValueWithLayoutAnimation( @@ -423,7 +424,7 @@ export default function ChartExpandedState({ asset }) { ) : addCashEnabled ? ( - + ) : null} {!networks && isL2 && ( diff --git a/src/components/sheet/sheet-action-buttons/BuyActionButton.js b/src/components/sheet/sheet-action-buttons/BuyActionButton.js index 0d95f8596e1..050376f6322 100644 --- a/src/components/sheet/sheet-action-buttons/BuyActionButton.js +++ b/src/components/sheet/sheet-action-buttons/BuyActionButton.js @@ -12,10 +12,10 @@ import { import Routes from '@/navigation/routesNames'; import { useRoute } from '@react-navigation/native'; -function BuyActionButton({ color: givenColor, ...props }) { +function BuyActionButton({ color: givenColor, asset, ...props }) { const { colors } = useTheme(); const color = givenColor || colors.paleBlue; - const navigate = useExpandedStateNavigation(); + const navigate = useExpandedStateNavigation(null, true, asset); const { isDamaged } = useWallets(); const { accountAddress } = useAccountSettings(); const { name: routeName } = useRoute(); diff --git a/src/components/sheet/sheet-action-buttons/SendActionButton.js b/src/components/sheet/sheet-action-buttons/SendActionButton.js index fa70f042d27..6d21a14226a 100644 --- a/src/components/sheet/sheet-action-buttons/SendActionButton.js +++ b/src/components/sheet/sheet-action-buttons/SendActionButton.js @@ -8,7 +8,7 @@ import { IS_IOS } from '@/env'; function SendActionButton({ asset, color: givenColor, ...props }) { const { colors } = useTheme(); const color = givenColor || colors.paleBlue; - const navigate = useExpandedStateNavigation(); + const navigate = useExpandedStateNavigation(null, false, asset); const handlePress = useCallback( () => navigate(Routes.SEND_FLOW, params => { diff --git a/src/components/sheet/sheet-action-buttons/SwapActionButton.js b/src/components/sheet/sheet-action-buttons/SwapActionButton.js index 0bdc0bd14b2..ff7ca933857 100644 --- a/src/components/sheet/sheet-action-buttons/SwapActionButton.js +++ b/src/components/sheet/sheet-action-buttons/SwapActionButton.js @@ -12,6 +12,7 @@ function SwapActionButton({ color: givenColor, inputType, label, + fromDiscover, weight = 'heavy', ...props }) { @@ -27,7 +28,7 @@ function SwapActionButton({ } ); - const navigate = useExpandedStateNavigation(inputType); + const navigate = useExpandedStateNavigation(inputType, fromDiscover, asset); const goToSwap = useCallback(() => { navigate(Routes.EXCHANGE_MODAL, params => { if (params.outputAsset) { diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 2f06a884c74..d8ad3ca52ef 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -67,7 +67,6 @@ export { default as useExpandedStateNavigation } from './useExpandedStateNavigat export { default as useExternalWalletSectionsData } from './useExternalWalletSectionsData'; export { default as useFetchHiddenTokens } from './useFetchHiddenTokens'; export { default as useGas } from './useGas'; -export { default as useGenericAsset } from './useGenericAsset'; export { default as useHeight } from './useHeight'; export { default as useHideSplashScreen } from './useHideSplashScreen'; export { default as useImageMetadata } from './useImageMetadata'; diff --git a/src/hooks/useAsset.ts b/src/hooks/useAsset.ts index 40f85f20278..c00a4815caa 100644 --- a/src/hooks/useAsset.ts +++ b/src/hooks/useAsset.ts @@ -1,31 +1,39 @@ import { useMemo } from 'react'; import useAccountAsset from './useAccountAsset'; -import useCollectible from './useCollectible'; -import { AssetTypes, ParsedAddressAsset } from '@/entities'; -import useGenericAsset from './useGenericAsset'; +import { Network } from '@/networks/types'; +import ethereumUtils, { getUniqueId } from '@/utils/ethereumUtils'; +import { useExternalToken } from '@/resources/assets/externalAssetsQuery'; +import { useSelector } from 'react-redux'; +import { AppState } from '@/redux/store'; // To fetch an asset from account assets, // generic assets, and uniqueTokens -export default function useAsset(asset: ParsedAddressAsset) { - const accountAsset = useAccountAsset( - asset?.uniqueId || asset?.mainnet_address || asset?.address +export default function useAsset({ + address, + network, +}: { + address: string; + network: Network; +}) { + const nativeCurrency = useSelector( + (state: AppState) => state.settings.nativeCurrency ); - const genericAsset = useGenericAsset( - asset?.uniqueId || asset?.mainnet_address || asset?.address - ); - const uniqueToken = useCollectible(asset); - return useMemo(() => { - if (!asset) return null; + const uniqueId = getUniqueId(address, network); + const accountAsset = useAccountAsset(uniqueId); + const chainId = ethereumUtils.getChainIdFromNetwork(network); + const externalAsset = useExternalToken({ + address, + chainId, + currency: nativeCurrency, + }); - let matched = null; - if (asset.type === AssetTypes.nft) { - matched = uniqueToken; - } else if (accountAsset) { - matched = accountAsset; - } else if (genericAsset) { - matched = genericAsset; + return useMemo(() => { + if (accountAsset) { + return accountAsset; + } else if (externalAsset) { + return externalAsset; } - return matched || asset; - }, [accountAsset, asset, genericAsset, uniqueToken]); + return null; + }, [accountAsset, externalAsset]); } diff --git a/src/hooks/useCollectible.ts b/src/hooks/useCollectible.ts index 7f3d9c18cfa..bcfb8289dd2 100644 --- a/src/hooks/useCollectible.ts +++ b/src/hooks/useCollectible.ts @@ -4,7 +4,7 @@ import { useLegacyNFTs } from '@/resources/nfts'; import { useAccountSettings } from '.'; export default function useCollectible( - initialAsset: Partial, + uniqueId: string, externalAddress?: string ) { const { accountAddress } = useAccountSettings(); @@ -24,9 +24,7 @@ export default function useCollectible( [externalNFTsMap, isExternal, selfNFTsMap] ); - const asset = initialAsset?.uniqueId - ? uniqueTokensMap[initialAsset.uniqueId] || initialAsset - : initialAsset; + const asset = uniqueTokensMap?.[uniqueId]; return { ...asset, isExternal }; } diff --git a/src/hooks/useExpandedStateNavigation.ts b/src/hooks/useExpandedStateNavigation.ts index b70260d51bc..5c757fbb8bd 100644 --- a/src/hooks/useExpandedStateNavigation.ts +++ b/src/hooks/useExpandedStateNavigation.ts @@ -1,24 +1,22 @@ import { useRoute } from '@react-navigation/native'; import { useCallback, useMemo } from 'react'; import { InteractionManager } from 'react-native'; -import useAsset from './useAsset'; + import useWallets from './useWallets'; import { enableActionsOnReadOnlyWallet } from '@/config'; import AssetInputTypes from '@/helpers/assetInputTypes'; import { useNavigation } from '@/navigation'; import { watchingAlert } from '@/utils'; +import { RainbowToken } from '@/entities'; export default function useExpandedStateNavigation( - inputType: typeof AssetInputTypes[keyof typeof AssetInputTypes], - fromDiscover = false + inputType: typeof AssetInputTypes[keyof typeof AssetInputTypes] | null, + fromDiscover = false, + asset: RainbowToken ) { const { goBack, navigate } = useNavigation(); - const { params } = useRoute(); const { isReadOnlyWallet } = useWallets(); - // @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'. - const asset = useAsset(params.asset); - const navigationPayload = useMemo(() => { switch (inputType) { case AssetInputTypes.in: diff --git a/src/hooks/useGenericAsset.ts b/src/hooks/useGenericAsset.ts deleted file mode 100644 index 4a42949f63c..00000000000 --- a/src/hooks/useGenericAsset.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { useMemo } from 'react'; -import { useSelector } from 'react-redux'; -import { createSelector } from 'reselect'; -import { AppState } from '@/redux/store'; - -const genericAssetsSelector = (state: AppState) => state.data.genericAssets; -const addressSelector = (_: any, address: string) => address; -const addressesSelector = (_: any, addresses: string[]) => addresses; - -const makeGenericAssetSelector = () => - createSelector( - genericAssetsSelector, - addressSelector, - (genericAssets, address) => genericAssets?.[address] - ); - -const genericManyAssetsSelector = createSelector( - genericAssetsSelector, - addressesSelector, - (genericAssets, addresses) => - addresses?.reduce((acc: any, address: any) => { - acc[address] = genericAssets?.[address]; - return acc; - }, {}) -); - -export default function useGenericAsset(address: any) { - const selectGenericAsset = useMemo(makeGenericAssetSelector, []); - const asset = useSelector((state: AppState) => - selectGenericAsset(state, address) - ); - return asset; -} - -export function useGenericAssets(addresses: any) { - const assets = useSelector((state: AppState) => - genericManyAssetsSelector(state, addresses) - ); - return assets; -} diff --git a/src/redux/data.ts b/src/redux/data.ts index 6b4f9da73f4..ce053b79b76 100644 --- a/src/redux/data.ts +++ b/src/redux/data.ts @@ -5,10 +5,6 @@ import { import { isEmpty, isNil, mapValues, partition } from 'lodash'; import { Dispatch } from 'redux'; import { ThunkDispatch } from 'redux-thunk'; -import { - cancelDebouncedUpdateGenericAssets, - debouncedUpdateGenericAssets, -} from './helpers/debouncedUpdateGenericAssets'; import { decrementNonce, incrementNonce } from './nonceManager'; import { AppGetState, AppState } from './store'; import { @@ -60,7 +56,6 @@ const TXN_WATCHER_POLL_INTERVAL = 5000; // 5 seconds // -- Constants --------------------------------------- // -const DATA_UPDATE_GENERIC_ASSETS = 'data/DATA_UPDATE_GENERIC_ASSETS'; const DATA_UPDATE_ETH_USD = 'data/DATA_UPDATE_ETH_USD'; const DATA_UPDATE_PORTFOLIOS = 'data/DATA_UPDATE_PORTFOLIOS'; @@ -84,13 +79,6 @@ export interface DataState { */ ethUSDPrice: number | undefined | null; - /** - * Parsed asset information for generic loaded assets. - */ - genericAssets: { - [assetAddress: string]: ParsedAddressAsset; - }; - /** * Whether or not transactions are currently being loaded. */ @@ -118,7 +106,6 @@ export interface DataState { * An action for the `data` reducer. */ type DataAction = - | DataUpdateGenericAssetsAction | DataUpdatePortfoliosAction | DataUpdateEthUsdAction | DataLoadTransactionsRequestAction @@ -127,14 +114,6 @@ type DataAction = | DataUpdatePendingTransactionSuccessAction | DataClearStateAction; -/** - * The action to update `genericAssets`. - */ -export interface DataUpdateGenericAssetsAction { - type: typeof DATA_UPDATE_GENERIC_ASSETS; - payload: DataState['genericAssets']; -} - /** * The action to update `portfolios`. */ @@ -184,7 +163,7 @@ interface DataUpdatePendingTransactionSuccessAction { } /** - * The action used to clear the state while maintaining generic asset data. + * The action used to clear the state. */ interface DataClearStateAction { type: typeof DATA_CLEAR_STATE; @@ -346,17 +325,13 @@ export const dataLoadState = () => async ( }; /** - * Resets state, with the exception of generic asset prices, and unsubscribes + * Resets state and unsubscribes * from listeners and timeouts. */ export const dataResetState = () => ( dispatch: Dispatch ) => { - // cancel any debounced updates so we won't override any new data with stale debounced ones - cancelDebouncedUpdateGenericAssets(); - pendingTransactionsHandle && clearTimeout(pendingTransactionsHandle); - dispatch({ type: DATA_CLEAR_STATE }); }; @@ -584,39 +559,10 @@ export function scheduleActionOnAssetReceived( */ export const assetPricesReceived = ( message: AssetPricesReceivedMessage | undefined -) => ( - dispatch: Dispatch, - getState: AppGetState -) => { +) => (dispatch: Dispatch, getState: AppGetState) => { const newAssetPrices = message?.payload?.prices ?? {}; const { nativeCurrency } = getState().settings; - if (nativeCurrency.toLowerCase() === message?.meta?.currency) { - if (isEmpty(newAssetPrices)) return; - const parsedAssets = mapValues(newAssetPrices, asset => - parseAsset(asset) - ) as { - [id: string]: ParsedAddressAsset; - }; - const { genericAssets } = getState().data; - - const updatedAssets = { - ...genericAssets, - ...parsedAssets, - }; - - const assetAddresses = Object.keys(parsedAssets); - - for (const address of assetAddresses) { - callbacksOnAssetReceived[address.toLowerCase()]?.(parsedAssets[address]); - callbacksOnAssetReceived[address.toLowerCase()] = undefined; - } - - dispatch({ - payload: updatedAssets, - type: DATA_UPDATE_GENERIC_ASSETS, - }); - } if ( message?.meta?.currency?.toLowerCase() === NativeCurrencyKeys.USD.toLowerCase() && @@ -637,37 +583,13 @@ export const assetPricesReceived = ( */ export const assetPricesChanged = ( message: AssetPricesChangedMessage | undefined -) => ( - dispatch: Dispatch, - getState: AppGetState -) => { +) => (dispatch: Dispatch, getState: AppGetState) => { const { nativeCurrency } = getState().settings; const price = message?.payload?.prices?.[0]?.price; const assetAddress = message?.meta?.asset_code; if (isNil(price) || isNil(assetAddress)) return; - if (nativeCurrency?.toLowerCase() === message?.meta?.currency) { - const { genericAssets } = getState().data; - const genericAsset = { - ...genericAssets?.[assetAddress], - price, - }; - const updatedAssets = { - ...genericAssets, - [assetAddress]: genericAsset, - } as { - [address: string]: ParsedAddressAsset; - }; - - debouncedUpdateGenericAssets( - { - payload: updatedAssets, - type: DATA_UPDATE_GENERIC_ASSETS, - }, - dispatch - ); - } if ( message?.meta?.currency?.toLowerCase() === NativeCurrencyKeys.USD.toLowerCase() && @@ -1092,7 +1014,6 @@ export const watchPendingTransactions = ( // -- Reducer ----------------------------------------- // const INITIAL_STATE: DataState = { ethUSDPrice: null, - genericAssets: {}, isLoadingTransactions: true, pendingTransactions: [], portfolios: {}, @@ -1101,8 +1022,6 @@ const INITIAL_STATE: DataState = { export default (state: DataState = INITIAL_STATE, action: DataAction) => { switch (action.type) { - case DATA_UPDATE_GENERIC_ASSETS: - return { ...state, genericAssets: action.payload }; case DATA_UPDATE_PORTFOLIOS: return { ...state, @@ -1138,7 +1057,6 @@ export default (state: DataState = INITIAL_STATE, action: DataAction) => { return { ...state, ...INITIAL_STATE, - genericAssets: state.genericAssets, }; default: return state; diff --git a/src/redux/helpers/debouncedUpdateGenericAssets.ts b/src/redux/helpers/debouncedUpdateGenericAssets.ts deleted file mode 100644 index de67c391c3f..00000000000 --- a/src/redux/helpers/debouncedUpdateGenericAssets.ts +++ /dev/null @@ -1,47 +0,0 @@ -import debounce from 'lodash/debounce'; -import { Dispatch } from 'redux'; -import { DataUpdateGenericAssetsAction } from '../data'; - -let payload: DataUpdateGenericAssetsAction['payload'] = {}; - -// we debounce the dispatching of the action in order to "batch" the events -// since we receive data from websockets -// so we might receive lots of events in a small period of time -// each dispatch will basically trigger random rerenders -// so in order to not rerender everything and have all the data -// we store the previous data inside `payload` and dispatch all the data -// from all the events at once -const debounced = debounce( - ( - action: DataUpdateGenericAssetsAction, - dispatch: Dispatch - ) => { - dispatch({ - payload: payload, - type: action.type, - }); - - payload = {}; - }, - 500, - { - // failsafe if zerion decides to bomb us with events - maxWait: 5000, - } -); - -export const cancelDebouncedUpdateGenericAssets = () => { - debounced.cancel(); -}; - -export const debouncedUpdateGenericAssets = ( - action: DataUpdateGenericAssetsAction, - dispatch: Dispatch -) => { - payload = { - ...payload, - ...action.payload, - }; - - debounced(action, dispatch); -}; diff --git a/src/resources/assets/externalAssetsQuery.ts b/src/resources/assets/externalAssetsQuery.ts index 8255b84beb9..4a2875c74ed 100644 --- a/src/resources/assets/externalAssetsQuery.ts +++ b/src/resources/assets/externalAssetsQuery.ts @@ -14,7 +14,7 @@ type ExternalToken = Pick< Token, 'decimals' | 'iconUrl' | 'name' | 'networks' | 'symbol' | 'colors' | 'price' >; -type FormattedExternalAsset = ExternalToken & { +export type FormattedExternalAsset = ExternalToken & { native: { change: string; price: { @@ -32,7 +32,7 @@ type ExternalTokenArgs = { }; // Query Key for Token Price -const TokenPriceQueryKey = ({ +export const TokenPriceQueryKey = ({ address, chainId, currency, diff --git a/src/screens/ExpandedAssetSheet.js b/src/screens/ExpandedAssetSheet.js index e60d7d2f374..72963413d71 100644 --- a/src/screens/ExpandedAssetSheet.js +++ b/src/screens/ExpandedAssetSheet.js @@ -11,7 +11,7 @@ import { UniqueTokenExpandedState, } from '../components/expanded-state'; import { Centered } from '../components/layout'; -import { useAsset, useDimensions } from '@/hooks'; +import { useDimensions } from '@/hooks'; import { useNavigation } from '@/navigation'; import styled from '@/styled-thing'; import { position } from '@/styles'; @@ -44,10 +44,6 @@ export default function ExpandedAssetSheet(props) { const { goBack } = useNavigation(); const { params } = useRoute(); - // We want to revalidate (ie. refresh OpenSea metadata) collectibles - // to ensure the user can get the latest metadata of their collectible. - const selectedAsset = useAsset(params.asset); - return ( diff --git a/src/utils/ethereumUtils.ts b/src/utils/ethereumUtils.ts index adb02658974..9b42114070d 100644 --- a/src/utils/ethereumUtils.ts +++ b/src/utils/ethereumUtils.ts @@ -70,6 +70,14 @@ import Routes from '@/navigation/routesNames'; import { logger, RainbowError } from '@/logger'; import { IS_IOS } from '@/env'; import { RainbowNetworks, getNetworkObj } from '@/networks'; +import { Token } from '@/graphql/__generated__/metadata'; +import { + ExternalTokenQueryKey, + FormattedExternalAsset, + fetchExternalToken, + useExternalToken, +} from '@/resources/assets/externalAssetsQuery'; +import { getMainnetNetworkObject } from '@/networks/mainnet'; // TODO: https://linear.app/rainbow/issue/APP-631/remove-networks-from-assettype const getNetworkNativeAsset = ( @@ -89,7 +97,7 @@ export const getNativeAssetForNetwork = async ( address: EthereumAddress ): Promise => { const networkNativeAsset = getNetworkNativeAsset(network); - const { accountAddress } = store.getState().settings; + const { accountAddress, nativeCurrency } = store.getState().settings; const differentWallet = address?.toLowerCase() !== accountAddress?.toLowerCase(); let nativeAsset = differentWallet ? undefined : networkNativeAsset; @@ -99,7 +107,28 @@ export const getNativeAssetForNetwork = async ( const mainnetAddress = getNetworkObj(network)?.nativeCurrency?.mainnetAddress || ETH_ADDRESS; - nativeAsset = store.getState().data?.genericAssets?.[mainnetAddress]; + const chainId = getNetworkObj(network).id; + const externalAsset = await queryClient.fetchQuery( + ExternalTokenQueryKey({ address, chainId, currency: nativeCurrency }), + async () => + fetchExternalToken({ address, chainId, currency: nativeCurrency }), + { + staleTime: 60000, + } + ); + + if (externalAsset) { + // @ts-ignore + nativeAsset = { + ...externalAsset, + type: getAssetTypeFromNetwork(network), + uniqueId: getUniqueId( + getNetworkObj(network).nativeCurrency.address, + network + ), + address: getNetworkObj(network).nativeCurrency.address, + }; + } const provider = await getProviderForNetwork(network); if (nativeAsset) { @@ -150,11 +179,27 @@ const getUserAssetFromCache = (uniqueId: string) => { return cachedAddressAssets?.[uniqueId]; }; +const getExternalAssetFromCache = (uniqueId: string) => { + const { nativeCurrency } = store.getState().settings; + const { network, address } = getAddressAndNetworkFromUniqueId(uniqueId); + const chainId = getNetworkObj(network).id; + + const cachedExternalAsset = queryClient.getQueryData( + ExternalTokenQueryKey({ + address, + currency: nativeCurrency, + chainId, + }) + ); + + return cachedExternalAsset; +}; + const getAssetFromAllAssets = (uniqueId: EthereumAddress | undefined) => { const loweredUniqueId = uniqueId?.toLowerCase() ?? ''; const accountAsset = getUserAssetFromCache(loweredUniqueId); - const genericAsset = store.getState().data?.genericAssets?.[loweredUniqueId]; - return accountAsset ?? genericAsset; + const externalAsset = getExternalAssetFromCache(loweredUniqueId); + return accountAsset ?? externalAsset; }; const getAccountAsset = ( @@ -166,41 +211,23 @@ const getAccountAsset = ( }; const getAssetPrice = (address: EthereumAddress = ETH_ADDRESS): number => { - const genericAsset = store.getState().data?.genericAssets?.[address]; - const genericPrice = genericAsset?.price?.value; + const externalAsset = getExternalAssetFromCache(address); + const genericPrice = externalAsset?.price?.value; return genericPrice || getAccountAsset(address)?.price?.value || 0; }; -export const useEth = (): ParsedAddressAsset => { - return useSelector( - ({ - // @ts-expect-error ts-migrate(2339) FIXME: Property 'data' does not exist on type 'DefaultRoo... Remove this comment to see the full error message - data: { - genericAssets: { [ETH_ADDRESS]: asset }, - }, - }) => asset - ); -}; - -export const useNativeAssetForNetwork = ( - network: Network -): ParsedAddressAsset => { +export const useNativeAssetForNetwork = (network: Network) => { const address = getNetworkObj(network).nativeCurrency?.mainnetAddress || ETH_ADDRESS; + const { nativeCurrency } = store.getState().settings; - return useSelector( - ({ - // @ts-expect-error ts-migrate(2339) FIXME: Property 'data' does not exist on type 'DefaultRoo... Remove this comment to see the full error message - data: { - genericAssets: { [address]: asset }, - }, - }) => asset - ); -}; + const { data: nativeAsset } = useExternalToken({ + address, + chainId: getMainnetNetworkObject().id, + currency: nativeCurrency, + }); -export const useEthUSDPrice = (): number => { - // @ts-expect-error ts-migrate(2339) FIXME: Property 'data' does not exist on type 'DefaultRoo... Remove this comment to see the full error message - return useSelector(({ data: { ethUSDPrice } }) => ethUSDPrice); + return nativeAsset; }; // anotha 1 @@ -245,27 +272,6 @@ const getBalanceAmount = ( const getHash = (txn: RainbowTransaction) => txn.hash?.split('-').shift(); -const formatGenericAsset = ( - asset: ParsedAddressAsset, - nativeCurrency: NativeCurrencyKey -) => { - return { - ...asset, - native: { - change: asset?.price?.relative_change_24h - ? convertAmountToPercentageDisplay( - `${asset?.price?.relative_change_24h}` - ) - : '', - price: convertAmountAndPriceToNativeDisplay( - 1, - asset?.price?.value || 0, - nativeCurrency - ), - }, - }; -}; - export const checkWalletEthZero = () => { const ethAsset = getAccountAsset(ETH_ADDRESS); const amount = ethAsset?.balance?.amount ?? 0; @@ -554,9 +560,26 @@ async function parseEthereumUrl(data: string) { }); } -const getUniqueId = (address: EthereumAddress, network: Network) => +export const getUniqueId = (address: EthereumAddress, network: Network) => network === Network.mainnet ? address : `${address}_${network}`; +export const getAddressAndNetworkFromUniqueId = ( + uniqueId: string +): { address: EthereumAddress; network: Network } => { + const parts = uniqueId.split('_'); + + // If the unique ID does not contain '_', it's a mainnet address + if (parts.length === 1) { + return { address: parts[0], network: Network.mainnet }; + } + + // If the unique ID contains '_', the last part is the network and the rest is the address + const network = parts.pop() as Network; // Assuming the last part is a valid Network enum value + const address = parts.join('_'); // Joining back in case the address itself contained '_' + + return { address, network }; +}; + const calculateL1FeeOptimism = async ( tx: RainbowTransaction, provider: Provider @@ -662,7 +685,6 @@ const getBasicSwapGasLimit = (chainId: number) => { export default { calculateL1FeeOptimism, - formatGenericAsset, getAssetFromAllAssets, getAccountAsset, getAsset, From d0e63ea4e957223e74df0ad53703ce47883174cb Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Sun, 28 Jan 2024 15:37:18 -0500 Subject: [PATCH 04/18] almost there --- src/components/cards/EthCard.tsx | 11 +++--- src/components/exchange/ExchangeTokenRow.tsx | 13 ++++--- .../asset/ChartExpandedState.js | 11 +++--- src/hooks/useAsset.ts | 5 ++- src/hooks/useOnAvatarPress.ts | 2 +- src/hooks/usePriceImpactDetails.ts | 9 +++-- src/resources/assets/externalAssetsQuery.ts | 36 ++++++++++--------- src/screens/CurrencySelectModal.tsx | 17 +++++++-- src/utils/ethereumUtils.ts | 30 ++++++++-------- 9 files changed, 73 insertions(+), 61 deletions(-) diff --git a/src/components/cards/EthCard.tsx b/src/components/cards/EthCard.tsx index 5182e624d99..330230e07a3 100644 --- a/src/components/cards/EthCard.tsx +++ b/src/components/cards/EthCard.tsx @@ -7,14 +7,13 @@ import { Bleed, } from '@/design-system'; import { useTheme } from '@/theme'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { GenericCard } from './GenericCard'; import { ButtonPressAnimation } from '../animations'; import { useAccountSettings, useChartThrottledPoints, useColorForAsset, - useGenericAsset, useWallets, } from '@/hooks'; import { useRemoteConfig } from '@/model/remoteConfig'; @@ -40,19 +39,19 @@ import { useRoute } from '@react-navigation/native'; import * as i18n from '@/languages'; import { ButtonPressAnimationTouchEvent } from '@/components/animations/ButtonPressAnimation/types'; import { useExternalToken } from '@/resources/assets/externalAssetsQuery'; -import { getMainnetNetworkObject } from '@/networks/mainnet'; import assetTypes from '@/entities/assetTypes'; +import { Network } from '@/networks/types'; export const ETH_CARD_HEIGHT = 284.3; export const EthCard = () => { - const { accountAddress, nativeCurrency } = useAccountSettings(); + const { nativeCurrency } = useAccountSettings(); const { colors, isDarkMode } = useTheme(); const { navigate } = useNavigation(); const { isDamaged } = useWallets(); const { data: ethAsset } = useExternalToken({ address: ETH_ADDRESS, - chainId: getMainnetNetworkObject().id, + network: Network.mainnet, currency: nativeCurrency, }); const { loaded: accentColorLoaded } = useAccountAccentColor(); @@ -81,7 +80,7 @@ export const EthCard = () => { routeName, }); }, - [accountAddress, isDamaged, navigate, routeName] + [isDamaged, navigate, routeName] ); const handleAssetPress = useCallback(() => { diff --git a/src/components/exchange/ExchangeTokenRow.tsx b/src/components/exchange/ExchangeTokenRow.tsx index 51d146fe14b..5553a529bdf 100644 --- a/src/components/exchange/ExchangeTokenRow.tsx +++ b/src/components/exchange/ExchangeTokenRow.tsx @@ -40,16 +40,15 @@ export default React.memo(function ExchangeTokenRow({ }, }: ExchangeTokenRowProps) { const { width: deviceWidth } = useDimensions(); + + const network = ethereumUtils.getNetworkFromType(type) ?? Network.mainnet; + + // ideally we pass all info in upfront and dont need to call here; const item = useAsset({ - uniqueId, - mainnet_address, - symbol, - type, address, - name, - decimals, + network, }); - const network = ethereumUtils.getNetworkFromType(type) ?? Network.mainnet; + const rowTestID = `${testID}-exchange-coin-row-${ symbol ?? item?.symbol ?? '' }-${type || 'token'}`; diff --git a/src/components/expanded-state/asset/ChartExpandedState.js b/src/components/expanded-state/asset/ChartExpandedState.js index 569880bfdf8..70074e17c59 100644 --- a/src/components/expanded-state/asset/ChartExpandedState.js +++ b/src/components/expanded-state/asset/ChartExpandedState.js @@ -47,7 +47,7 @@ import { } from '@/hooks'; import { useRemoteConfig } from '@/model/remoteConfig'; import { useNavigation } from '@/navigation'; -import { DOG_ADDRESS, ETH_ADDRESS } from '@/references'; +import { ETH_ADDRESS } from '@/references'; import Routes from '@/navigation/routesNames'; import styled from '@/styled-thing'; import { ethereumUtils, safeAreaInsetValues } from '@/utils'; @@ -187,7 +187,7 @@ export default function ChartExpandedState({ asset }) { const { data: genericAsset } = useExternalToken({ address: asset?.address, - chainId: getNetworkObj(ethereumUtils.getChainIdFromType(asset.type)).id, + network: ethereumUtils.getNetworkFromType(asset.type), currency: nativeCurrency, }); const { @@ -206,13 +206,13 @@ export default function ChartExpandedState({ asset }) { : genericAsset ? asset?.networks ? { - ...ethereumUtils.formatGenericAsset(genericAsset, nativeCurrency), + genericAsset, type: asset.type, colors: asset?.colors, } - : ethereumUtils.formatGenericAsset(genericAsset, nativeCurrency) + : genericAsset : { ...asset }; - }, [asset, genericAsset, hasBalance, nativeCurrency]); + }, [asset, genericAsset, hasBalance]); if (assetWithPrice?.mainnet_address) { assetWithPrice.l2Address = asset?.address; @@ -259,7 +259,6 @@ export default function ChartExpandedState({ asset }) { }; }, [assetWithPrice, isL2, asset?.address, networks]); - console.log(assetWithPrice?.uniqueId); const { height: screenHeight } = useDimensions(); const delayedDescriptions = useDelayedValueWithLayoutAnimation( diff --git a/src/hooks/useAsset.ts b/src/hooks/useAsset.ts index c00a4815caa..a0bab9880c9 100644 --- a/src/hooks/useAsset.ts +++ b/src/hooks/useAsset.ts @@ -20,10 +20,9 @@ export default function useAsset({ ); const uniqueId = getUniqueId(address, network); const accountAsset = useAccountAsset(uniqueId); - const chainId = ethereumUtils.getChainIdFromNetwork(network); - const externalAsset = useExternalToken({ + const { data: externalAsset } = useExternalToken({ address, - chainId, + network, currency: nativeCurrency, }); diff --git a/src/hooks/useOnAvatarPress.ts b/src/hooks/useOnAvatarPress.ts index 735d8e8d9f3..f4f05fedf11 100644 --- a/src/hooks/useOnAvatarPress.ts +++ b/src/hooks/useOnAvatarPress.ts @@ -172,7 +172,7 @@ export default ({ screenType = 'transaction' }: UseOnAvatarPressProps = {}) => { const isReadOnly = isReadOnlyWallet && !enableActionsOnReadOnlyWallet; const isENSProfile = profilesEnabled && profileEnabled && isOwner; - const isZeroETH = isZero(accountAsset.balance.amount); + const isZeroETH = isZero(accountAsset?.balance?.amount); const callback = useCallback( async (buttonIndex: number) => { diff --git a/src/hooks/usePriceImpactDetails.ts b/src/hooks/usePriceImpactDetails.ts index 1bd4f947fe0..2886da8cae9 100644 --- a/src/hooks/usePriceImpactDetails.ts +++ b/src/hooks/usePriceImpactDetails.ts @@ -65,8 +65,9 @@ export default function usePriceImpactDetails( const inputNativeAmount = useMemo(() => { if (isWrapOrUnwrap) { - if (!tradeDetails?.sellAmount || !inputCurrency?.price?.value) - return null; + if (!tradeDetails?.sellAmount || !inputCurrency?.price?.value) { + return ''; + } return convertRawAmountToNativeDisplay( tradeDetails?.sellAmount?.toString(), @@ -95,7 +96,9 @@ export default function usePriceImpactDetails( const outputNativeAmount = useMemo(() => { if (isWrapOrUnwrap) { - if (!tradeDetails?.buyAmount || !inputCurrency?.price?.value) return null; + if (!tradeDetails?.buyAmount || !inputCurrency?.price?.value) { + return ''; + } return convertRawAmountToNativeDisplay( tradeDetails?.buyAmount?.toString(), inputCurrency?.decimals || 18, diff --git a/src/resources/assets/externalAssetsQuery.ts b/src/resources/assets/externalAssetsQuery.ts index 4a2875c74ed..545d1318b5a 100644 --- a/src/resources/assets/externalAssetsQuery.ts +++ b/src/resources/assets/externalAssetsQuery.ts @@ -8,6 +8,8 @@ import { } from '@/helpers/utilities'; import { NativeCurrencyKey } from '@/entities'; import { Token } from '@/graphql/__generated__/metadata'; +import { ethereumUtils } from '@/utils'; +import { Network } from '@/networks/types'; // Types type ExternalToken = Pick< @@ -27,23 +29,23 @@ export type FormattedExternalAsset = ExternalToken & { // Query Types for External Token type ExternalTokenArgs = { address: string; - chainId: number; + network: Network; currency: NativeCurrencyKey; }; // Query Key for Token Price -export const TokenPriceQueryKey = ({ +export const ExternalTokenQueryKey = ({ address, - chainId, + network, currency, }: ExternalTokenArgs) => createQueryKey( 'externalToken', - { address, chainId, currency }, + { address, network, currency }, { persisterVersion: 1 } ); -type TokenPriceQueryKey = ReturnType; +type ExternalTokenQueryKey = ReturnType; // Helpers const formatExternalAsset = ( @@ -68,15 +70,15 @@ const formatExternalAsset = ( // Query Function for Token Price export async function fetchExternalToken({ address, - chainId, + network, currency, }: ExternalTokenArgs) { + const chainId = ethereumUtils.getChainIdFromNetwork(network); const response = await metadataClient.externalToken({ address, chainId, currency, }); - console.log('res: ', response.token); if (response.token) { return formatExternalAsset(response.token, currency); } else { @@ -85,23 +87,23 @@ export async function fetchExternalToken({ } export async function externalTokenQueryFunction({ - queryKey: [{ address, chainId, currency }], + queryKey: [{ address, network, currency }], }: QueryFunctionArgs< - typeof TokenPriceQueryKey + typeof ExternalTokenQueryKey >): Promise { - if (!address || !chainId) return null; - return await fetchExternalToken({ address, chainId, currency }); + if (!address || !network) return null; + return await fetchExternalToken({ address, network, currency }); } // Prefetch function for Token Price export async function prefetchExternalToken({ address, - chainId, + network, currency, }: ExternalTokenArgs) { queryClient.prefetchQuery( - TokenPriceQueryKey({ address, chainId, currency }), - async () => fetchExternalToken({ address, chainId, currency }), + ExternalTokenQueryKey({ address, network, currency }), + async () => fetchExternalToken({ address, network, currency }), { staleTime: 60000, } @@ -111,15 +113,15 @@ export async function prefetchExternalToken({ // Query Hook for Token Price export function useExternalToken({ address, - chainId, + network, currency, }: ExternalTokenArgs) { return useQuery( - TokenPriceQueryKey({ address, chainId, currency }), + ExternalTokenQueryKey({ address, network, currency }), externalTokenQueryFunction, { cacheTime: 1000 * 60 * 60 * 24, // 24 hours - enabled: !!address && !!chainId, + enabled: !!address && !!network, } ); } diff --git a/src/screens/CurrencySelectModal.tsx b/src/screens/CurrencySelectModal.tsx index 1c90cf54980..9f0f0e46484 100644 --- a/src/screens/CurrencySelectModal.tsx +++ b/src/screens/CurrencySelectModal.tsx @@ -38,7 +38,7 @@ import { analytics } from '@/analytics'; import { addHexPrefix, isL2Network } from '@/handlers/web3'; import { CurrencySelectionTypes, Network, TokenSectionTypes } from '@/helpers'; import { - useCoinListEditOptions, + useAccountSettings, useInteraction, useMagicAutofocus, usePrevious, @@ -48,7 +48,7 @@ import { } from '@/hooks'; import { delayNext } from '@/hooks/useMagicAutofocus'; import { getActiveRoute, useNavigation } from '@/navigation/Navigation'; -import { emitAssetRequest, emitChartsRequest } from '@/redux/explorer'; +import { emitChartsRequest } from '@/redux/explorer'; import Routes from '@/navigation/routesNames'; import { ethereumUtils, filterList } from '@/utils'; import NetworkSwitcherv2 from '@/components/exchange/NetworkSwitcherv2'; @@ -59,6 +59,8 @@ import { useTheme } from '@/theme'; import { IS_TEST } from '@/env'; import { useSortedUserAssets } from '@/resources/assets/useSortedUserAssets'; import DiscoverSearchInput from '@/components/discover/DiscoverSearchInput'; +import { prefetchExternalToken } from '@/resources/assets/externalAssetsQuery'; +import { getNetworkFromChainId } from '@/utils/ethereumUtils'; export interface EnrichedExchangeAsset extends SwappableAsset { ens: boolean; @@ -122,6 +124,7 @@ export default function CurrencySelectModal() { const isFocused = useIsFocused(); const prevIsFocused = usePrevious(isFocused); const { goBack, navigate, getState: dangerouslyGetState } = useNavigation(); + const { nativeCurrency } = useAccountSettings(); const { colors } = useTheme(); const dispatch = useDispatch(); const { @@ -435,8 +438,15 @@ export default function CurrencySelectModal() { }; const selectAsset = () => { + if (!item?.balance) { + const network = getNetworkFromChainId(currentChainId); + prefetchExternalToken({ + address: item.address, + network, + currency: nativeCurrency, + }); + } dispatch(emitChartsRequest(item.mainnet_address || item.address)); - dispatch(emitAssetRequest(item.mainnet_address || item.address)); setIsTransitioning(true); // continue to display list during transition callback?.(); onSelectCurrency(assetWithType, handleNavigate); @@ -461,6 +471,7 @@ export default function CurrencySelectModal() { currentChainId, type, checkForSameNetwork, + nativeCurrency, dispatch, callback, onSelectCurrency, diff --git a/src/utils/ethereumUtils.ts b/src/utils/ethereumUtils.ts index 9b42114070d..1eef2d73c8e 100644 --- a/src/utils/ethereumUtils.ts +++ b/src/utils/ethereumUtils.ts @@ -70,14 +70,12 @@ import Routes from '@/navigation/routesNames'; import { logger, RainbowError } from '@/logger'; import { IS_IOS } from '@/env'; import { RainbowNetworks, getNetworkObj } from '@/networks'; -import { Token } from '@/graphql/__generated__/metadata'; import { ExternalTokenQueryKey, FormattedExternalAsset, fetchExternalToken, useExternalToken, } from '@/resources/assets/externalAssetsQuery'; -import { getMainnetNetworkObject } from '@/networks/mainnet'; // TODO: https://linear.app/rainbow/issue/APP-631/remove-networks-from-assettype const getNetworkNativeAsset = ( @@ -107,11 +105,10 @@ export const getNativeAssetForNetwork = async ( const mainnetAddress = getNetworkObj(network)?.nativeCurrency?.mainnetAddress || ETH_ADDRESS; - const chainId = getNetworkObj(network).id; const externalAsset = await queryClient.fetchQuery( - ExternalTokenQueryKey({ address, chainId, currency: nativeCurrency }), + ExternalTokenQueryKey({ address, network, currency: nativeCurrency }), async () => - fetchExternalToken({ address, chainId, currency: nativeCurrency }), + fetchExternalToken({ address, network, currency: nativeCurrency }), { staleTime: 60000, } @@ -182,17 +179,20 @@ const getUserAssetFromCache = (uniqueId: string) => { const getExternalAssetFromCache = (uniqueId: string) => { const { nativeCurrency } = store.getState().settings; const { network, address } = getAddressAndNetworkFromUniqueId(uniqueId); - const chainId = getNetworkObj(network).id; - const cachedExternalAsset = queryClient.getQueryData( - ExternalTokenQueryKey({ - address, - currency: nativeCurrency, - chainId, - }) - ); + try { + const cachedExternalAsset = queryClient.getQueryData( + ExternalTokenQueryKey({ + address, + currency: nativeCurrency, + network, + }) + ); - return cachedExternalAsset; + return cachedExternalAsset; + } catch (e) { + console.log(e); + } }; const getAssetFromAllAssets = (uniqueId: EthereumAddress | undefined) => { @@ -223,7 +223,7 @@ export const useNativeAssetForNetwork = (network: Network) => { const { data: nativeAsset } = useExternalToken({ address, - chainId: getMainnetNetworkObject().id, + network: Network.mainnet, currency: nativeCurrency, }); From a58e7eaa2ce745f8af3ba8cf6bd3e54c6122934d Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Sun, 28 Jan 2024 16:38:11 -0500 Subject: [PATCH 05/18] use graphql for token metdata --- .../asset/ChartExpandedState.js | 72 ++++++---- src/graphql/queries/metadata.graphql | 24 ++++ src/hooks/useAdditionalAssetData.ts | 126 ++++++++++-------- src/hooks/useNativeCurrencyToUSD.ts | 7 - 4 files changed, 138 insertions(+), 91 deletions(-) delete mode 100644 src/hooks/useNativeCurrencyToUSD.ts diff --git a/src/components/expanded-state/asset/ChartExpandedState.js b/src/components/expanded-state/asset/ChartExpandedState.js index 70074e17c59..5a567e76eb1 100644 --- a/src/components/expanded-state/asset/ChartExpandedState.js +++ b/src/components/expanded-state/asset/ChartExpandedState.js @@ -56,6 +56,8 @@ import AvailableNetworksv1 from '@/components/expanded-state/AvailableNetworks'; import { Box } from '@/design-system'; import { getNetworkObj } from '@/networks'; import { useExternalToken } from '@/resources/assets/externalAssetsQuery'; +import { bigNumberFormat } from '@/helpers/bigNumberFormat'; +import { greaterThanOrEqualTo } from '@/helpers/utilities'; const defaultCarouselHeight = 60; const baseHeight = @@ -224,29 +226,29 @@ export default function ChartExpandedState({ asset }) { ]); const isTestnet = isTestnetNetwork(currentNetwork); - const { - description, - marketCap, - totalLiquidity, - totalVolume, - loading: additionalAssetDataLoading, - links, - networks, - } = useAdditionalAssetData( + console.log( asset?.address, - assetWithPrice?.price?.value, - ethereumUtils.getChainIdFromNetwork(assetWithPrice?.type) + ethereumUtils.getNetworkFromType(assetWithPrice.type), + nativeCurrency ); + const { + data, + isLoading: additionalAssetDataLoading, + } = useAdditionalAssetData({ + address: asset?.address, + network: ethereumUtils.getNetworkFromType(assetWithPrice.type), + currency: nativeCurrency, + }); // This one includes the original l2 address if exists const ogAsset = useMemo(() => { - if (networks) { + if (data?.networks) { const mappedNetworks = {}; - Object.keys(networks).forEach( + Object.keys(data?.networks).forEach( chainId => (mappedNetworks[ ethereumUtils.getNetworkFromChainId(Number(chainId)) - ] = networks[chainId]) + ] = data?.networks[chainId]) ); assetWithPrice.implementations = mappedNetworks; } @@ -257,17 +259,15 @@ export default function ChartExpandedState({ asset }) { ? assetWithPrice.l2Address || asset?.address : assetWithPrice.address, }; - }, [assetWithPrice, isL2, asset?.address, networks]); + }, [assetWithPrice, isL2, asset?.address, data?.networks]); const { height: screenHeight } = useDimensions(); const delayedDescriptions = useDelayedValueWithLayoutAnimation( - description?.replace(/\s+/g, '') + data?.description?.replace(/\s+/g, '') ); - const scrollableContentHeight = - !!totalVolume || !!marketCap || !!totalLiquidity ? 68 : 0; - + const scrollableContentHeight = true; const { chart, chartData, @@ -345,6 +345,20 @@ export default function ChartExpandedState({ asset }) { swagg_enabled && getNetworkObj(assetNetwork).features.swaps; const addCashEnabled = f2c_enabled; + const format = useCallback( + value => { + const test = bigNumberFormat( + value, + nativeCurrency, + greaterThanOrEqualTo(value, 10000) + ); + console.log(test); + return test; + }, + [nativeCurrency] + ); + + console.log(data); return ( ) : null} - {!networks && isL2 && ( + {!data?.networks && isL2 && ( )} - {networks && !hasBalance && ( + {data?.networks && !hasBalance && ( - + )} @@ -451,7 +465,7 @@ export default function ChartExpandedState({ asset }) { title={lang.t('expanded_state.asset.volume_24_hours')} weight="bold" > - {totalVolume} + {format(data?.volume1d)} - {totalLiquidity} + {data?.totalLiquidity} - {marketCap} + {format(data?.marketCap)} @@ -481,21 +495,21 @@ export default function ChartExpandedState({ asset }) { layout?.(); }} > - {!!delayedDescriptions && ( + {data?.description && ( - + )} diff --git a/src/graphql/queries/metadata.graphql b/src/graphql/queries/metadata.graphql index be0491a3a5e..baee88fd08b 100644 --- a/src/graphql/queries/metadata.graphql +++ b/src/graphql/queries/metadata.graphql @@ -479,3 +479,27 @@ query externalToken($address: String!, $chainId: Int!, $currency: String) { symbol } } + +query tokenMetadata($address: String!, $chainId: Int!, $currency: String) { + token(address: $address, chainID: $chainId, currency: $currency) { + circulatingSupply + colors { + ...TokenColorsFragment + } + description + fullyDilutedValuation + iconUrl + links { + ...TokenLinksFragment + } + marketCap + name + networks + price { + relativeChange24h + value + } + totalSupply + volume1d + } +} diff --git a/src/hooks/useAdditionalAssetData.ts b/src/hooks/useAdditionalAssetData.ts index 01b01142f00..97508f4c607 100644 --- a/src/hooks/useAdditionalAssetData.ts +++ b/src/hooks/useAdditionalAssetData.ts @@ -1,59 +1,75 @@ import { useQuery } from '@tanstack/react-query'; -import { useCallback } from 'react'; -import useNativeCurrencyToUSD from './useNativeCurrencyToUSD'; -import { useAccountSettings } from './index'; -import { EthereumAddress } from '@/entities'; -import { getAdditionalAssetData } from '@/handlers/dispersion'; -import { bigNumberFormat } from '@/helpers/bigNumberFormat'; -import { greaterThanOrEqualTo, multiply } from '@/helpers/utilities'; -import { ETH_ADDRESS, WETH_ADDRESS } from '@/references'; -import { implementation } from '@/entities/dispersion'; -import { Network } from '@/helpers'; +import { NativeCurrencyKey } from '@/entities'; +import { Network } from '@/networks/types'; +import { metadataClient } from '@/graphql'; +import { ethereumUtils } from '@/utils'; +import { Token } from '@/graphql/__generated__/metadata'; -export default function useAdditionalAssetData( - rawAddress: EthereumAddress, - tokenPrice = 0, - chainId = 1 -): { - description?: string; - loading: boolean; - totalVolume: string | null; - totalLiquidity: string | null; - marketCap: string | null; - links: Record; - networks: Record; -} { - const { data } = useQuery(['additionalAssetData', rawAddress], () => - getAdditionalAssetData(rawAddress, chainId) - ); - const { nativeCurrency } = useAccountSettings(); - const format = useCallback( - (value: string) => - bigNumberFormat( - value, - nativeCurrency, - greaterThanOrEqualTo(value, 10000) - ), - [nativeCurrency] +// Types +type TokenMetadata = Pick< + Token, + | 'description' + | 'volume1d' + | 'marketCap' + | 'totalSupply' + | 'circulatingSupply' + | 'fullyDilutedValuation' + | 'links' +>; + +// Types for the query arguments +type AdditionalAssetDataArgs = { + address: string; + network: Network; + currency: NativeCurrencyKey; +}; + +// Query Key function +const createAdditionalAssetDataQueryKey = ({ + address, + network, + currency, +}: AdditionalAssetDataArgs) => [ + 'additionalAssetData', + address, + network, + currency, +]; + +// Refactor the getAdditionalAssetData function to accept the new parameters +async function getAdditionalAssetData({ + address, + network, + currency, +}: AdditionalAssetDataArgs): Promise { + console.log('fetching'); + const chainId = ethereumUtils.getChainIdFromNetwork(network); + const data = await metadataClient.tokenMetadata({ + address, + chainId, + currency, + }); + + if (data.token) { + console.log({ res: data.token }); + return data.token as TokenMetadata; + } else { + console.log(data); + } + return null; +} + +// Usage of the useQuery hook +export default function useAdditionalAssetData({ + address, + network, + currency, +}: AdditionalAssetDataArgs) { + return useQuery( + createAdditionalAssetDataQueryKey({ address, network, currency }), + () => getAdditionalAssetData({ address, network, currency }), + { + enabled: !!address && !!network && !!currency, // Ensure all parameters are provided + } ); - const rate = useNativeCurrencyToUSD(); - const loading = !data; - const marketCap = data?.circulatingSupply - ? format(multiply(data?.circulatingSupply, tokenPrice)) - : null; - const totalLiquidity = data?.totalLiquidity - ? format(multiply(data?.totalLiquidity, tokenPrice)) - : null; - const totalVolume = data?.oneDayVolumeUSD - ? format(multiply(data?.oneDayVolumeUSD, rate)) - : null; - return { - description: data?.description, - links: data?.links, - loading, - marketCap, - networks: data?.networks, - totalLiquidity, - totalVolume, - }; } diff --git a/src/hooks/useNativeCurrencyToUSD.ts b/src/hooks/useNativeCurrencyToUSD.ts deleted file mode 100644 index cc2a58be75d..00000000000 --- a/src/hooks/useNativeCurrencyToUSD.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { useEth, useEthUSDPrice } from '../utils/ethereumUtils'; - -export default function useNativeCurrencyToUSD() { - const { price: { value: ethNative = 0 } = {} } = useEth() || {}; - const ethUSD = useEthUSDPrice() || Infinity; - return ethNative / ethUSD; -} From e944546cab09f3c01e3c97f30a5cd0b9ff5cc471 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Sun, 28 Jan 2024 16:39:32 -0500 Subject: [PATCH 06/18] clean logs --- src/components/expanded-state/asset/ChartExpandedState.js | 8 +------- src/hooks/useAdditionalAssetData.ts | 4 ---- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/components/expanded-state/asset/ChartExpandedState.js b/src/components/expanded-state/asset/ChartExpandedState.js index 5a567e76eb1..892d8f5d413 100644 --- a/src/components/expanded-state/asset/ChartExpandedState.js +++ b/src/components/expanded-state/asset/ChartExpandedState.js @@ -226,11 +226,6 @@ export default function ChartExpandedState({ asset }) { ]); const isTestnet = isTestnetNetwork(currentNetwork); - console.log( - asset?.address, - ethereumUtils.getNetworkFromType(assetWithPrice.type), - nativeCurrency - ); const { data, isLoading: additionalAssetDataLoading, @@ -352,13 +347,12 @@ export default function ChartExpandedState({ asset }) { nativeCurrency, greaterThanOrEqualTo(value, 10000) ); - console.log(test); + return test; }, [nativeCurrency] ); - console.log(data); return ( { - console.log('fetching'); const chainId = ethereumUtils.getChainIdFromNetwork(network); const data = await metadataClient.tokenMetadata({ address, @@ -51,10 +50,7 @@ async function getAdditionalAssetData({ }); if (data.token) { - console.log({ res: data.token }); return data.token as TokenMetadata; - } else { - console.log(data); } return null; } From 476ea3ae700ac615832abb4c6db98ce57de87cc3 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Mon, 29 Jan 2024 11:35:44 -0500 Subject: [PATCH 07/18] fix social links --- .../expanded-state/asset/SocialLinks.js | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/components/expanded-state/asset/SocialLinks.js b/src/components/expanded-state/asset/SocialLinks.js index 575efc9c448..cd22651e829 100644 --- a/src/components/expanded-state/asset/SocialLinks.js +++ b/src/components/expanded-state/asset/SocialLinks.js @@ -6,10 +6,6 @@ import EdgeFade from '../../EdgeFade'; import styled from '@/styled-thing'; import { ethereumUtils } from '@/utils'; -const TWITTER_URL = 'https://twitter.com/'; -const TELEGRAM_URL = 'https://t.me/'; -const FACEBOOK_URL = 'https://www.facebook.com/'; - const Carousel = styled.ScrollView.attrs({ contentContainerStyle: { paddingHorizontal: 13, @@ -41,6 +37,7 @@ export default function SocialLinks({ }) { const etherscanURL = ethereumUtils.getEtherscanHostForNetwork(type); const blockExplorerName = ethereumUtils.getBlockExplorer(type); + console.log(JSON.stringify(links)); return ( <> @@ -53,33 +50,33 @@ export default function SocialLinks({ url={`${etherscanURL}/token/${address}`} /> )} - {!!links?.twitter_screen_name && ( + {!!links?.twitter?.url && ( )} - {!!links?.homepage?.[0] && ( + {!!links?.homepage?.url && ( )} - {!!links?.telegram_channel_identifier && ( + {!!links?.telegram?.url && ( )} - {!!links?.subreddit_url && ( + {!!links?.reddit?.url && ( )} - {!!links?.facebook_username && ( + {!!links?.facebook?.url && ( )} From 3d5a54ff998216f3eab1992d189524c67f52dfd7 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Mon, 29 Jan 2024 11:36:23 -0500 Subject: [PATCH 08/18] oopsie --- src/components/expanded-state/asset/SocialLinks.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/expanded-state/asset/SocialLinks.js b/src/components/expanded-state/asset/SocialLinks.js index cd22651e829..0f3a74f3ea6 100644 --- a/src/components/expanded-state/asset/SocialLinks.js +++ b/src/components/expanded-state/asset/SocialLinks.js @@ -37,7 +37,6 @@ export default function SocialLinks({ }) { const etherscanURL = ethereumUtils.getEtherscanHostForNetwork(type); const blockExplorerName = ethereumUtils.getBlockExplorer(type); - console.log(JSON.stringify(links)); return ( <> From 5aa8d61ddaf32520acd2a1f78b5f4bcb30d6824a Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Wed, 31 Jan 2024 11:41:06 -0500 Subject: [PATCH 09/18] review --- src/resources/assets/externalAssetsQuery.ts | 24 ++++++++++++--------- src/resources/defi/PositionsQuery.ts | 2 +- src/utils/ethereumUtils.ts | 10 ++++----- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/resources/assets/externalAssetsQuery.ts b/src/resources/assets/externalAssetsQuery.ts index 545d1318b5a..3b6e07e157b 100644 --- a/src/resources/assets/externalAssetsQuery.ts +++ b/src/resources/assets/externalAssetsQuery.ts @@ -3,7 +3,6 @@ import { metadataClient } from '@/graphql'; import { QueryFunctionArgs, createQueryKey, queryClient } from '@/react-query'; import { convertAmountAndPriceToNativeDisplay, - convertAmountToNativeDisplay, convertAmountToPercentageDisplay, } from '@/helpers/utilities'; import { NativeCurrencyKey } from '@/entities'; @@ -11,6 +10,9 @@ import { Token } from '@/graphql/__generated__/metadata'; import { ethereumUtils } from '@/utils'; import { Network } from '@/networks/types'; +const EXTERNAL_TOKEN_CACHE_TIME = 1000 * 60 * 60 * 24; // 24 hours +const EXTERNAL_TOKEN_STALE_TIME = 1000 * 60; // 1 minute + // Types type ExternalToken = Pick< Token, @@ -34,7 +36,7 @@ type ExternalTokenArgs = { }; // Query Key for Token Price -export const ExternalTokenQueryKey = ({ +export const externalTokenQueryKey = ({ address, network, currency, @@ -45,7 +47,7 @@ export const ExternalTokenQueryKey = ({ { persisterVersion: 1 } ); -type ExternalTokenQueryKey = ReturnType; +type externalTokenQueryKey = ReturnType; // Helpers const formatExternalAsset = ( @@ -89,7 +91,7 @@ export async function fetchExternalToken({ export async function externalTokenQueryFunction({ queryKey: [{ address, network, currency }], }: QueryFunctionArgs< - typeof ExternalTokenQueryKey + typeof externalTokenQueryKey >): Promise { if (!address || !network) return null; return await fetchExternalToken({ address, network, currency }); @@ -101,11 +103,12 @@ export async function prefetchExternalToken({ network, currency, }: ExternalTokenArgs) { - queryClient.prefetchQuery( - ExternalTokenQueryKey({ address, network, currency }), - async () => fetchExternalToken({ address, network, currency }), + await queryClient.prefetchQuery( + externalTokenQueryKey({ address, network, currency }), + async () => await fetchExternalToken({ address, network, currency }), { - staleTime: 60000, + staleTime: EXTERNAL_TOKEN_STALE_TIME, + cacheTime: EXTERNAL_TOKEN_CACHE_TIME, } ); } @@ -117,10 +120,11 @@ export function useExternalToken({ currency, }: ExternalTokenArgs) { return useQuery( - ExternalTokenQueryKey({ address, network, currency }), + externalTokenQueryKey({ address, network, currency }), externalTokenQueryFunction, { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + staleTime: EXTERNAL_TOKEN_STALE_TIME, + cacheTime: EXTERNAL_TOKEN_CACHE_TIME, enabled: !!address && !!network, } ); diff --git a/src/resources/defi/PositionsQuery.ts b/src/resources/defi/PositionsQuery.ts index 1545ed43286..c98b9854283 100644 --- a/src/resources/defi/PositionsQuery.ts +++ b/src/resources/defi/PositionsQuery.ts @@ -8,7 +8,7 @@ import { QueryFunctionResult, } from '@/react-query'; -import { NativeCurrencyKey, ZerionAsset } from '@/entities'; +import { NativeCurrencyKey } from '@/entities'; import { rainbowFetch } from '@/rainbow-fetch'; import { ADDYS_API_KEY } from 'react-native-dotenv'; import { RainbowNetworks } from '@/networks'; diff --git a/src/utils/ethereumUtils.ts b/src/utils/ethereumUtils.ts index 1eef2d73c8e..9cfad7dd7c3 100644 --- a/src/utils/ethereumUtils.ts +++ b/src/utils/ethereumUtils.ts @@ -71,7 +71,7 @@ import { logger, RainbowError } from '@/logger'; import { IS_IOS } from '@/env'; import { RainbowNetworks, getNetworkObj } from '@/networks'; import { - ExternalTokenQueryKey, + externalTokenQueryKey, FormattedExternalAsset, fetchExternalToken, useExternalToken, @@ -106,7 +106,7 @@ export const getNativeAssetForNetwork = async ( getNetworkObj(network)?.nativeCurrency?.mainnetAddress || ETH_ADDRESS; const externalAsset = await queryClient.fetchQuery( - ExternalTokenQueryKey({ address, network, currency: nativeCurrency }), + externalTokenQueryKey({ address, network, currency: nativeCurrency }), async () => fetchExternalToken({ address, network, currency: nativeCurrency }), { @@ -182,7 +182,7 @@ const getExternalAssetFromCache = (uniqueId: string) => { try { const cachedExternalAsset = queryClient.getQueryData( - ExternalTokenQueryKey({ + externalTokenQueryKey({ address, currency: nativeCurrency, network, @@ -574,8 +574,8 @@ export const getAddressAndNetworkFromUniqueId = ( } // If the unique ID contains '_', the last part is the network and the rest is the address - const network = parts.pop() as Network; // Assuming the last part is a valid Network enum value - const address = parts.join('_'); // Joining back in case the address itself contained '_' + const network = parts[1] as Network; // Assuming the last part is a valid Network enum value + const address = parts[0]; return { address, network }; }; From d97e591e3b5fa87deccfc95890956d3f33989caa Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Thu, 1 Feb 2024 16:56:25 -0500 Subject: [PATCH 10/18] bunch of removing --- src/handlers/__tests__/deeplinks.test.ts.skip | 3 - src/handlers/deeplinks.ts | 3 +- src/hooks/useSwapCurrencyHandlers.ts | 31 ++- src/redux/explorer.ts | 189 +----------------- src/resources/assets/externalAssetsQuery.ts | 6 + .../discover/components/DiscoverSearch.js | 3 +- 6 files changed, 37 insertions(+), 198 deletions(-) diff --git a/src/handlers/__tests__/deeplinks.test.ts.skip b/src/handlers/__tests__/deeplinks.test.ts.skip index 7626ad378b5..bf108c13949 100644 --- a/src/handlers/__tests__/deeplinks.test.ts.skip +++ b/src/handlers/__tests__/deeplinks.test.ts.skip @@ -23,9 +23,6 @@ jest.mock('@/redux/walletconnect'); jest.mock('@/redux/data', () => ({ scheduleActionOnAssetReceived: jest.fn(), })); -jest.mock('@/redux/explorer', () => ({ - emitAssetRequest: jest.fn(), -})); jest.mock('@/utils/profileUtils', () => ({ fetchReverseRecordWithRetry: jest.fn(), })); diff --git a/src/handlers/deeplinks.ts b/src/handlers/deeplinks.ts index 49bbdb3f7dd..19236dc7b00 100644 --- a/src/handlers/deeplinks.ts +++ b/src/handlers/deeplinks.ts @@ -8,7 +8,7 @@ import { walletConnectSetPendingRedirect, } from '@/redux/walletconnect'; import { scheduleActionOnAssetReceived } from '@/redux/data'; -import { emitAssetRequest } from '@/redux/explorer'; + import { fetchReverseRecordWithRetry } from '@/utils/profileUtils'; import { defaultConfig } from '@/config/experimental'; import { PROFILES } from '@/config/experimentalHooks'; @@ -129,7 +129,6 @@ export default async function handleDeeplink( if (asset) { _action(asset); } else { - dispatch(emitAssetRequest(address)); scheduleActionOnAssetReceived(address, _action); } }, 50); diff --git a/src/hooks/useSwapCurrencyHandlers.ts b/src/hooks/useSwapCurrencyHandlers.ts index bb0f7f42b63..fc61ccdd7e7 100644 --- a/src/hooks/useSwapCurrencyHandlers.ts +++ b/src/hooks/useSwapCurrencyHandlers.ts @@ -4,9 +4,12 @@ import { useDispatch } from 'react-redux'; import { delayNext } from './useMagicAutofocus'; import { CurrencySelectionTypes, ExchangeModalTypes } from '@/helpers'; import { updatePrecisionToDisplay } from '@/helpers/utilities'; -import { useSwapDerivedValues, useSwapInputHandlers } from '@/hooks'; +import { + useAccountSettings, + useSwapDerivedValues, + useSwapInputHandlers, +} from '@/hooks'; import { useNavigation } from '@/navigation'; -import { emitAssetRequest } from '@/redux/explorer'; import { flipSwapCurrencies, updateSwapInputAmount, @@ -15,6 +18,8 @@ import { } from '@/redux/swap'; import Routes from '@/navigation/routesNames'; import { CROSSCHAIN_SWAPS, useExperimentalFlag } from '@/config'; +import { queryClient } from '@/react-query'; +import { prefetchExternalToken } from '@/resources/assets/externalAssetsQuery'; const { currentlyFocusedInput, focusTextInput } = TextInput.State; @@ -34,6 +39,7 @@ export default function useSwapCurrencyHandlers({ title, type, }: any = {}) { + const { nativeCurrency } = useAccountSettings(); const dispatch = useDispatch(); const crosschainSwapsEnabled = useExperimentalFlag(CROSSCHAIN_SWAPS); const { @@ -176,14 +182,21 @@ export default function useSwapCurrencyHandlers({ } : null; - dispatch(emitAssetRequest(newInputCurrency.mainnet_address)); + // prefetchExternalToken({address: newInputCurrency.address, network: newInputCurrency.network, currency: nativeCurrency}) + dispatch( updateSwapInputCurrency(newInputCurrency, crosschainSwapsEnabled) ); setLastFocusedInputHandle?.(inputFieldRef); handleNavigate?.(newInputCurrency); }, - [crosschainSwapsEnabled, dispatch, inputFieldRef, setLastFocusedInputHandle] + [ + crosschainSwapsEnabled, + dispatch, + inputFieldRef, + nativeCurrency, + setLastFocusedInputHandle, + ] ); const updateOutputCurrency = useCallback( @@ -194,14 +207,20 @@ export default function useSwapCurrencyHandlers({ } : null; - dispatch(emitAssetRequest(newOutputCurrency.mainnet_address)); + // prefetchExternalToken({address: newOutputCurrency.address, network: newOutputCurrency.network, currency: nativeCurrency}) dispatch( updateSwapOutputCurrency(newOutputCurrency, crosschainSwapsEnabled) ); setLastFocusedInputHandle?.(inputFieldRef); handleNavigate?.(newOutputCurrency); }, - [crosschainSwapsEnabled, dispatch, inputFieldRef, setLastFocusedInputHandle] + [ + crosschainSwapsEnabled, + dispatch, + inputFieldRef, + nativeCurrency, + setLastFocusedInputHandle, + ] ); const navigateToSelectInputCurrency = useCallback( diff --git a/src/redux/explorer.ts b/src/redux/explorer.ts index aa6648fb292..4019541bc18 100644 --- a/src/redux/explorer.ts +++ b/src/redux/explorer.ts @@ -53,13 +53,6 @@ const messages = { RECEIVED_ZORA: 'received address zora-transactions', RECEIVED_BASE: 'received address base-transactions', }, - ASSET_CHARTS: { - RECEIVED: 'received assets charts', - }, - ASSETS: { - CHANGED: 'changed assets prices', - RECEIVED: 'received assets prices', - }, CONNECT: 'connect', DISCONNECT: 'disconnect', ERROR: 'error', @@ -75,9 +68,6 @@ interface ExplorerState { // The address subscribed to on the address socket. addressSubscribed: string | null; - - // A socket for the assets endpoint. - assetsSocket: Socket | null; } /** @@ -85,10 +75,7 @@ interface ExplorerState { */ interface ExplorerUpdateSocketsAction { type: typeof EXPLORER_UPDATE_SOCKETS; - payload: Pick< - ExplorerState, - 'addressSocket' | 'addressSubscribed' | 'assetsSocket' - >; + payload: Pick; } /** @@ -212,52 +199,6 @@ export const notificationsSubscription = (address: string) => ( addressSocket?.emit(...payload); }; -/** - * Configures an asset price subscription. - * - * @param tokenAddresses The token addresses to watch. - * @param currency The currency to use. - * @param action The subscription action. - * @returns The arguments for an `emit` function call. - */ -const assetPricesSubscription = ( - tokenAddresses: string[], - currency: string, - action: SocketSubscriptionActionType = 'subscribe' -): SocketEmitArguments => { - const assetCodes = concat( - tokenAddresses, - ETH_ADDRESS, - MATIC_MAINNET_ADDRESS, - BNB_MAINNET_ADDRESS, - OP_ADDRESS - ); - return [ - action, - { - payload: { - asset_codes: assetCodes, - currency: toLower(currency), - }, - scope: ['prices'], - }, - ]; -}; - -/** - * Arguments to `emit` for an ETH-USD price subscription. - */ -const ethUSDSubscription: SocketEmitArguments = [ - 'subscribe', - { - payload: { - asset_codes: [ETH_ADDRESS], - currency: currencyTypes.usd, - }, - scope: ['prices'], - }, -]; - /** * Configures a layer-2 transaction history request for a given address. * @@ -287,59 +228,18 @@ const l2AddressTransactionHistoryRequest = ( }, ]; -/** - * Emits an asset price request. The result is handled by a listener in - * `listenOnAssetMessages`. - * - * @param assetAddress The address or addresses to fetch. - */ -export const fetchAssetPrices = (assetAddress: string | string[]) => ( - _: Dispatch, - getState: AppGetState -) => { - const { assetsSocket } = getState().explorer; - const { nativeCurrency } = getState().settings; - - const assetCodes = Array.isArray(assetAddress) - ? assetAddress - : [assetAddress]; - - const payload: SocketEmitArguments = [ - 'get', - { - payload: { - asset_codes: assetCodes, - currency: toLower(nativeCurrency), - }, - scope: ['prices'], - }, - ]; - assetsSocket?.emit(...payload); -}; - /** * Unsubscribes from existing asset subscriptions. */ const explorerUnsubscribe = () => (_: Dispatch, getState: AppGetState) => { - const { - addressSocket, - addressSubscribed, - assetsSocket, - } = getState().explorer; + const { addressSocket, addressSubscribed } = getState().explorer; const { nativeCurrency } = getState().settings; - const pairs = rainbowTokenList.CURATED_TOKENS; if (!isNil(addressSocket)) { addressSocket.emit( ...addressSubscription(addressSubscribed!, nativeCurrency, 'unsubscribe') ); addressSocket.close(); } - if (!isNil(assetsSocket)) { - assetsSocket.emit( - ...assetPricesSubscription(keys(pairs), nativeCurrency, 'unsubscribe') - ); - assetsSocket.close(); - } }; /** @@ -360,11 +260,10 @@ export const explorerInit = () => async ( getState: AppGetState ) => { const { network, accountAddress, nativeCurrency } = getState().settings; - const pairs = rainbowTokenList.CURATED_TOKENS; - const { addressSocket, assetsSocket } = getState().explorer; + const { addressSocket } = getState().explorer; // if there is another socket unsubscribe first - if (addressSocket || assetsSocket) { + if (addressSocket) { dispatch(explorerUnsubscribe()); } @@ -375,12 +274,10 @@ export const explorerInit = () => async ( } const newAddressSocket = createSocket('address'); - const newAssetsSocket = createSocket('assets'); dispatch({ payload: { addressSocket: newAddressSocket, addressSubscribed: accountAddress, - assetsSocket: newAssetsSocket, }, type: EXPLORER_UPDATE_SOCKETS, }); @@ -392,15 +289,6 @@ export const explorerInit = () => async ( ...addressSubscription(accountAddress, nativeCurrency) ); }); - - dispatch(listenOnAssetMessages(newAssetsSocket)); - - newAssetsSocket.on(messages.CONNECT, () => { - dispatch(emitAssetRequest(keys(pairs))); - - // we want to get ETH info ASAP - dispatch(emitAssetRequest(ETH_ADDRESS)); - }); }; /** @@ -420,48 +308,6 @@ export const emitPortfolioRequest = (address: string, currency?: string) => ( addressSocket?.emit(...portfolioSubscription(address, nativeCurrency)); }; -/** - * Subscribes to asset price information. The result is handled by a listener - * in `listenOnAssetMessages`. - * - * @param assetAddress The asset address or addresses to request. - */ -export const emitAssetRequest = (assetAddress: string | string[]) => ( - _: Dispatch, - getState: AppGetState -) => { - const { nativeCurrency } = getState().settings; - const { assetsSocket } = getState().explorer; - - const assetCodes = Array.isArray(assetAddress) - ? assetAddress - : [assetAddress]; - - const newAssetsCodes = assetCodes.filter( - code => !TokensListenedCache?.[nativeCurrency]?.[code] - ); - - newAssetsCodes.forEach(code => { - if (!TokensListenedCache?.[nativeCurrency]) { - TokensListenedCache[nativeCurrency] = {}; - } - assetsSocket && (TokensListenedCache[nativeCurrency][code] = true); - }); - - if (assetsSocket) { - if (newAssetsCodes.length > 0) { - assetsSocket.emit( - ...assetPricesSubscription(newAssetsCodes, nativeCurrency) - ); - assetsSocket.emit(...ethUSDSubscription); - return true; - } - } else { - setTimeout(() => emitAssetRequest(assetAddress), 100); - } - return false; -}; - /** * Emits a layer-2 transaction history request for the current address. The * result is handled by a listener in `listenOnAddressMessages`. @@ -477,31 +323,6 @@ export const emitL2TransactionHistoryRequest = () => ( ); }; -/** - * Adds asset message listeners to a given socket. - * - * @param socket The socket to add listeners to. - */ -const listenOnAssetMessages = (socket: Socket) => ( - dispatch: ThunkDispatch -) => { - socket.on(messages.ASSETS.RECEIVED, (message: AssetPricesReceivedMessage) => { - dispatch(assetPricesReceived(message)); - }); - - socket.on(messages.ASSETS.CHANGED, (message: AssetPricesChangedMessage) => { - dispatch(assetPricesChanged(message)); - }); - - socket.on( - messages.ASSET_CHARTS.RECEIVED, - (message: ChartsReceivedMessage) => { - // logger.log('charts received', message?.payload?.charts); - dispatch(assetChartsReceived(message)); - } - ); -}; - /** * Adds listeners for address information messages to a given socket. * @@ -588,7 +409,6 @@ const listenOnAddressMessages = (socket: Socket) => ( const INITIAL_STATE: ExplorerState = { addressSocket: null, addressSubscribed: null, - assetsSocket: null, }; export default ( @@ -601,7 +421,6 @@ export default ( ...state, addressSocket: action.payload.addressSocket, addressSubscribed: action.payload.addressSubscribed, - assetsSocket: action.payload.assetsSocket, }; case EXPLORER_CLEAR_STATE: return { diff --git a/src/resources/assets/externalAssetsQuery.ts b/src/resources/assets/externalAssetsQuery.ts index 3b6e07e157b..fae87a56289 100644 --- a/src/resources/assets/externalAssetsQuery.ts +++ b/src/resources/assets/externalAssetsQuery.ts @@ -13,6 +13,12 @@ import { Network } from '@/networks/types'; const EXTERNAL_TOKEN_CACHE_TIME = 1000 * 60 * 60 * 24; // 24 hours const EXTERNAL_TOKEN_STALE_TIME = 1000 * 60; // 1 minute +// need to keep these queried tokens up to date +// ETH_ADDRESS, +// MATIC_MAINNET_ADDRESS, +// BNB_MAINNET_ADDRESS, +// OP_ADDRESS + // Types type ExternalToken = Pick< Token, diff --git a/src/screens/discover/components/DiscoverSearch.js b/src/screens/discover/components/DiscoverSearch.js index 39e371e4f06..48f06982cbd 100644 --- a/src/screens/discover/components/DiscoverSearch.js +++ b/src/screens/discover/components/DiscoverSearch.js @@ -13,7 +13,7 @@ import { IS_TESTING } from 'react-native-dotenv'; import { useDispatch } from 'react-redux'; import { useDebounce } from 'use-debounce/lib'; import CurrencySelectionTypes from '@/helpers/currencySelectionTypes'; -import { emitAssetRequest } from '@/redux/explorer'; + import deviceUtils from '@/utils/deviceUtils'; import { CurrencySelectionList } from '@/components/exchange'; import { Row } from '@/components/layout'; @@ -193,7 +193,6 @@ export default function DiscoverSearch() { }); } else { const asset = ethereumUtils.getAccountAsset(item.uniqueId); - dispatch(emitAssetRequest(item.address)); navigate(Routes.EXPANDED_ASSET_SHEET, { asset: asset || item, fromDiscover: true, From d58a87e457770dce2b6698c1a55bd330636ee045 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Thu, 1 Feb 2024 17:49:02 -0500 Subject: [PATCH 11/18] clean up --- .../asset/ChartExpandedState.js | 20 +++++++------------ src/hooks/charts/useChartDataLabels.ts | 6 ++++-- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/components/expanded-state/asset/ChartExpandedState.js b/src/components/expanded-state/asset/ChartExpandedState.js index 542f3f65735..3ecad5058e9 100644 --- a/src/components/expanded-state/asset/ChartExpandedState.js +++ b/src/components/expanded-state/asset/ChartExpandedState.js @@ -189,7 +189,7 @@ export default function ChartExpandedState({ asset }) { const { data: genericAsset } = useExternalToken({ address: asset?.address, - network: ethereumUtils.getNetworkFromType(asset.type), + network: asset?.network, currency: nativeCurrency, }); const { @@ -206,20 +206,14 @@ export default function ChartExpandedState({ asset }) { return hasBalance ? { ...asset } : genericAsset - ? asset?.networks - ? { - genericAsset, - network: asset.network, - } - : genericAsset + ? { + ...genericAsset, + network: asset.network, + address: asset.address, + } : { ...asset }; }, [asset, genericAsset, hasBalance]); - if (assetWithPrice?.mainnet_address) { - assetWithPrice.l2Address = asset?.address; - assetWithPrice.address = assetWithPrice.mainnet_address; - } - const isL2 = useMemo(() => isL2Network(assetWithPrice.network), [ assetWithPrice.network, ]); @@ -230,7 +224,7 @@ export default function ChartExpandedState({ asset }) { isLoading: additionalAssetDataLoading, } = useAdditionalAssetData({ address: asset?.address, - network: ethereumUtils.getNetworkFromType(assetWithPrice.type), + network: asset?.network, currency: nativeCurrency, }); diff --git a/src/hooks/charts/useChartDataLabels.ts b/src/hooks/charts/useChartDataLabels.ts index b6bde1e65f8..1ff57f3845f 100644 --- a/src/hooks/charts/useChartDataLabels.ts +++ b/src/hooks/charts/useChartDataLabels.ts @@ -13,13 +13,15 @@ export default function useChartDataLabels({ asset, chartType, points }: any) { const percent = ((endPrice - startPrice) / startPrice) * 100; return formatPercentChange(percent); }, - [latestPrice, points] + [points] ); const latestChange = useMemo( () => !points || chartType === ChartTypes.day - ? formatPercentChange(asset?.price?.relative_change_24h) + ? formatPercentChange( + asset?.price?.relative_change_24h || asset?.price?.relativeChange24h + ) : getPercentChangeForPrice(points[0]?.y ?? 0), [asset, chartType, getPercentChangeForPrice, points] ); From 9cb56aee14cd3ff11f2b28d12e7e85e256b8e0b1 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Thu, 1 Feb 2024 17:49:56 -0500 Subject: [PATCH 12/18] clean + natives --- src/screens/WalletScreen/index.tsx | 33 +++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/screens/WalletScreen/index.tsx b/src/screens/WalletScreen/index.tsx index f0c8d49b3fc..874b5486b31 100644 --- a/src/screens/WalletScreen/index.tsx +++ b/src/screens/WalletScreen/index.tsx @@ -37,6 +37,12 @@ import { AppState } from '@/redux/store'; import { addressCopiedToastAtom } from '@/recoil/addressCopiedToastAtom'; import { usePositions } from '@/resources/defi/PositionsQuery'; import styled from '@/styled-thing'; +import { useExternalToken } from '@/resources/assets/externalAssetsQuery'; +import { + BNB_MAINNET_ADDRESS, + ETH_ADDRESS, + MATIC_MAINNET_ADDRESS, +} from '@/references'; const WalletPage = styled(Page)({ ...position.sizeAsObject('100%'), @@ -82,6 +88,24 @@ const WalletScreen: React.FC = ({ navigation, route }) => { }); }, [dispatch, initializeAccountData, loadAccountData, resetAccountState]); + // keep native assets up to date + + useExternalToken({ + address: BNB_MAINNET_ADDRESS, + network: Network.mainnet, + currency: nativeCurrency, + }); + useExternalToken({ + address: ETH_ADDRESS, + network: Network.mainnet, + currency: nativeCurrency, + }); + useExternalToken({ + address: MATIC_MAINNET_ADDRESS, + network: Network.mainnet, + currency: nativeCurrency, + }); + useEffect(() => { const supportedNetworks = [Network.mainnet]; if (!supportedNetworks.includes(currentNetwork)) { @@ -118,10 +142,9 @@ const WalletScreen: React.FC = ({ navigation, route }) => { isLoadingUserAssets ); - const { addressSocket, assetsSocket } = useSelector( - ({ explorer: { addressSocket, assetsSocket } }: AppState) => ({ + const { addressSocket } = useSelector( + ({ explorer: { addressSocket } }: AppState) => ({ addressSocket, - assetsSocket, }) ); @@ -173,11 +196,11 @@ const WalletScreen: React.FC = ({ navigation, route }) => { }, [portfolios, portfoliosFetched, trackPortfolios, userAccounts.length]); useEffect(() => { - if (walletReady && assetsSocket) { + if (walletReady) { loadAccountLateData(); loadGlobalLateData(); } - }, [assetsSocket, loadAccountLateData, loadGlobalLateData, walletReady]); + }, [loadAccountLateData, loadGlobalLateData, walletReady]); useEffect(() => { if (walletReady && profilesEnabled) { From 7291c2f1344e3277f3b17bb29fa12c240e28966a Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Thu, 1 Feb 2024 19:01:01 -0500 Subject: [PATCH 13/18] oop --- src/redux/swap.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/redux/swap.ts b/src/redux/swap.ts index 3f0299c3c01..29c469d6d1a 100644 --- a/src/redux/swap.ts +++ b/src/redux/swap.ts @@ -1,6 +1,6 @@ import { CrosschainQuote, Quote, QuoteError } from '@rainbow-me/swaps'; import { AnyAction } from 'redux'; -import { fetchAssetPrices } from './explorer'; +//import { fetchAssetPrices } from './explorer'; import { SwappableAsset } from '@/entities'; import { ExchangeModalTypes } from '@/helpers'; import { AppDispatch, AppGetState } from '@/redux/store'; @@ -140,7 +140,7 @@ export const updateSwapInputCurrency = ( } if (newInputCurrency) { - dispatch(fetchAssetPrices(newInputCurrency.address)); + // dispatch(fetchAssetPrices(newInputCurrency.address)); } if (independentField === SwapModalField.input) { dispatch(updateSwapInputAmount(null)); @@ -170,7 +170,7 @@ export const updateSwapOutputCurrency = ( dispatch({ payload: newOutputCurrency, type: SWAP_UPDATE_OUTPUT_CURRENCY }); if (newOutputCurrency) { - dispatch(fetchAssetPrices(newOutputCurrency.address)); + //dispatch(fetchAssetPrices(newOutputCurrency.address)); } if ( independentField === SwapModalField.output || From 99f7d9339faceb7516ea97601d42c1f9810676da Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Fri, 2 Feb 2024 12:20:00 -0500 Subject: [PATCH 14/18] move native asset hooks --- src/hooks/useGas.ts | 25 +++++++++++++++++++++++++ src/screens/WalletScreen/index.tsx | 24 ------------------------ 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/hooks/useGas.ts b/src/hooks/useGas.ts index 4e38fe7ffa0..518a81a1297 100644 --- a/src/hooks/useGas.ts +++ b/src/hooks/useGas.ts @@ -29,6 +29,13 @@ import { } from '@/redux/gas'; import { ethereumUtils } from '@/utils'; import { getNetworkObj } from '@/networks'; +import { useExternalToken } from '@/resources/assets/externalAssetsQuery'; +import { + BNB_MAINNET_ADDRESS, + ETH_ADDRESS, + MATIC_MAINNET_ADDRESS, +} from '@/references'; +import useAccountSettings from './useAccountSettings'; const checkSufficientGas = ( txFee: LegacyGasFee | GasFee, @@ -79,6 +86,24 @@ export default function useGas({ nativeAsset, }: { nativeAsset?: ParsedAddressAsset } = {}) { const dispatch = useDispatch(); + const { nativeCurrency } = useAccountSettings(); + + // keep native assets up to date + useExternalToken({ + address: BNB_MAINNET_ADDRESS, + network: Network.mainnet, + currency: nativeCurrency, + }); + useExternalToken({ + address: ETH_ADDRESS, + network: Network.mainnet, + currency: nativeCurrency, + }); + useExternalToken({ + address: MATIC_MAINNET_ADDRESS, + network: Network.mainnet, + currency: nativeCurrency, + }); const gasData: { currentBlockParams: CurrentBlockParams; diff --git a/src/screens/WalletScreen/index.tsx b/src/screens/WalletScreen/index.tsx index 874b5486b31..91dded80e2c 100644 --- a/src/screens/WalletScreen/index.tsx +++ b/src/screens/WalletScreen/index.tsx @@ -37,12 +37,6 @@ import { AppState } from '@/redux/store'; import { addressCopiedToastAtom } from '@/recoil/addressCopiedToastAtom'; import { usePositions } from '@/resources/defi/PositionsQuery'; import styled from '@/styled-thing'; -import { useExternalToken } from '@/resources/assets/externalAssetsQuery'; -import { - BNB_MAINNET_ADDRESS, - ETH_ADDRESS, - MATIC_MAINNET_ADDRESS, -} from '@/references'; const WalletPage = styled(Page)({ ...position.sizeAsObject('100%'), @@ -88,24 +82,6 @@ const WalletScreen: React.FC = ({ navigation, route }) => { }); }, [dispatch, initializeAccountData, loadAccountData, resetAccountState]); - // keep native assets up to date - - useExternalToken({ - address: BNB_MAINNET_ADDRESS, - network: Network.mainnet, - currency: nativeCurrency, - }); - useExternalToken({ - address: ETH_ADDRESS, - network: Network.mainnet, - currency: nativeCurrency, - }); - useExternalToken({ - address: MATIC_MAINNET_ADDRESS, - network: Network.mainnet, - currency: nativeCurrency, - }); - useEffect(() => { const supportedNetworks = [Network.mainnet]; if (!supportedNetworks.includes(currentNetwork)) { From 7a55171713d8caa6e81b50839b5af2dcb5677fc5 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Fri, 2 Feb 2024 12:51:05 -0500 Subject: [PATCH 15/18] clean --- src/components/cards/MintsCard/CollectionCell.tsx | 2 ++ src/components/coin-icon/CoinIcon.tsx | 2 +- src/components/expanded-state/asset/ChartExpandedState.js | 8 ++++++-- .../expanded-state/chart/ChartExpandedStateHeader.js | 2 +- src/hooks/charts/useChartDataLabels.ts | 2 +- src/screens/NFTOffersSheet/OfferRow.tsx | 1 + src/screens/discover/components/DiscoverSearch.js | 3 +++ 7 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/components/cards/MintsCard/CollectionCell.tsx b/src/components/cards/MintsCard/CollectionCell.tsx index 8eb393a77bc..466a5407726 100644 --- a/src/components/cards/MintsCard/CollectionCell.tsx +++ b/src/components/cards/MintsCard/CollectionCell.tsx @@ -193,6 +193,8 @@ export function CollectionCell({ symbol={currency.symbol} style={{ marginRight: 4, marginVertical: -4 }} network={getNetworkFromChainId(collection.chainId)} + mainnet_address={currency?.mainnetAddress} + ignoreBadge /> )} diff --git a/src/components/coin-icon/CoinIcon.tsx b/src/components/coin-icon/CoinIcon.tsx index d7c083f6bd2..6384d7ee5dd 100644 --- a/src/components/coin-icon/CoinIcon.tsx +++ b/src/components/coin-icon/CoinIcon.tsx @@ -83,7 +83,7 @@ const CoinIcon: React.FC = ({ } size={size} symbol={symbol} - network={network} + network={mainnet_address ? Network.mainnet : network} theme={theme} /> ) : ( diff --git a/src/components/expanded-state/asset/ChartExpandedState.js b/src/components/expanded-state/asset/ChartExpandedState.js index 3ecad5058e9..fd39e3eacea 100644 --- a/src/components/expanded-state/asset/ChartExpandedState.js +++ b/src/components/expanded-state/asset/ChartExpandedState.js @@ -58,6 +58,7 @@ import { getNetworkObj } from '@/networks'; import { useExternalToken } from '@/resources/assets/externalAssetsQuery'; import { bigNumberFormat } from '@/helpers/bigNumberFormat'; import { greaterThanOrEqualTo } from '@/helpers/utilities'; +import { Network } from '@/networks/types'; const defaultCarouselHeight = 60; const baseHeight = @@ -204,16 +205,19 @@ export default function ChartExpandedState({ asset }) { const hasBalance = asset?.balance; const assetWithPrice = useMemo(() => { return hasBalance - ? { ...asset } + ? asset : genericAsset ? { ...genericAsset, network: asset.network, address: asset.address, + mainnetAddress: + asset?.networks?.[getNetworkObj(Network.mainnet)]?.address, } - : { ...asset }; + : asset; }, [asset, genericAsset, hasBalance]); + console.log({ assetWithPrice }); const isL2 = useMemo(() => isL2Network(assetWithPrice.network), [ assetWithPrice.network, ]); diff --git a/src/components/expanded-state/chart/ChartExpandedStateHeader.js b/src/components/expanded-state/chart/ChartExpandedStateHeader.js index f4fbf4cfbab..ebc0a9f8b32 100644 --- a/src/components/expanded-state/chart/ChartExpandedStateHeader.js +++ b/src/components/expanded-state/chart/ChartExpandedStateHeader.js @@ -132,7 +132,7 @@ export default function ChartExpandedStateHeader({ badgeXPosition={-7} badgeYPosition={0} address={asset?.address} - mainnetAddress={asset?.mainnetAddress} + mainnetAddress={asset?.mainnet_address || asset?.mainnetAddress} network={asset?.network || Network.mainnet} symbol={asset?.symbol} theme={theme} diff --git a/src/hooks/charts/useChartDataLabels.ts b/src/hooks/charts/useChartDataLabels.ts index 1ff57f3845f..1bfb0c0966c 100644 --- a/src/hooks/charts/useChartDataLabels.ts +++ b/src/hooks/charts/useChartDataLabels.ts @@ -5,7 +5,7 @@ import { toFixedDecimals } from '@/helpers/utilities'; const formatPercentChange = (change = 0) => toFixedDecimals(change, 2); export default function useChartDataLabels({ asset, chartType, points }: any) { - const latestPrice = asset?.native?.price?.amount; + const latestPrice = asset?.price?.value; const getPercentChangeForPrice = useCallback( (startPrice: number) => { diff --git a/src/screens/NFTOffersSheet/OfferRow.tsx b/src/screens/NFTOffersSheet/OfferRow.tsx index 2061017c8ea..6ee07512766 100644 --- a/src/screens/NFTOffersSheet/OfferRow.tsx +++ b/src/screens/NFTOffersSheet/OfferRow.tsx @@ -243,6 +243,7 @@ export const OfferRow = ({ offer }: { offer: NftOffer }) => { size={COIN_ICON_SIZE} symbol={offer.paymentToken.symbol} network={offer.network} + ignoreBadge /> diff --git a/src/screens/discover/components/DiscoverSearch.js b/src/screens/discover/components/DiscoverSearch.js index 48f06982cbd..155a08dfd23 100644 --- a/src/screens/discover/components/DiscoverSearch.js +++ b/src/screens/discover/components/DiscoverSearch.js @@ -193,6 +193,9 @@ export default function DiscoverSearch() { }); } else { const asset = ethereumUtils.getAccountAsset(item.uniqueId); + if (item.favorite) { + item.network = Network.mainnet; + } navigate(Routes.EXPANDED_ASSET_SHEET, { asset: asset || item, fromDiscover: true, From 6dc8f9d61ed39110005fff9c76ff2790b75c3c38 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Fri, 2 Feb 2024 13:06:56 -0500 Subject: [PATCH 16/18] rm swap fetching --- src/components/expanded-state/asset/ChartExpandedState.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/expanded-state/asset/ChartExpandedState.js b/src/components/expanded-state/asset/ChartExpandedState.js index fd39e3eacea..b57df5a7fb0 100644 --- a/src/components/expanded-state/asset/ChartExpandedState.js +++ b/src/components/expanded-state/asset/ChartExpandedState.js @@ -217,7 +217,6 @@ export default function ChartExpandedState({ asset }) { : asset; }, [asset, genericAsset, hasBalance]); - console.log({ assetWithPrice }); const isL2 = useMemo(() => isL2Network(assetWithPrice.network), [ assetWithPrice.network, ]); From 986412a6294b82e0a58b6e77ad38e5f2ab74db82 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Fri, 2 Feb 2024 13:08:26 -0500 Subject: [PATCH 17/18] clean swap fetching --- src/redux/swap.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/redux/swap.ts b/src/redux/swap.ts index 29c469d6d1a..6aa4b8878c6 100644 --- a/src/redux/swap.ts +++ b/src/redux/swap.ts @@ -1,6 +1,5 @@ import { CrosschainQuote, Quote, QuoteError } from '@rainbow-me/swaps'; import { AnyAction } from 'redux'; -//import { fetchAssetPrices } from './explorer'; import { SwappableAsset } from '@/entities'; import { ExchangeModalTypes } from '@/helpers'; import { AppDispatch, AppGetState } from '@/redux/store'; @@ -139,9 +138,6 @@ export const updateSwapInputCurrency = ( dispatch(updateSwapOutputCurrency(null, true)); } - if (newInputCurrency) { - // dispatch(fetchAssetPrices(newInputCurrency.address)); - } if (independentField === SwapModalField.input) { dispatch(updateSwapInputAmount(null)); } @@ -169,9 +165,6 @@ export const updateSwapOutputCurrency = ( } dispatch({ payload: newOutputCurrency, type: SWAP_UPDATE_OUTPUT_CURRENCY }); - if (newOutputCurrency) { - //dispatch(fetchAssetPrices(newOutputCurrency.address)); - } if ( independentField === SwapModalField.output || newOutputCurrency === null From 0291bc79d299e5db2da249af7d9189ff7877cad8 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Fri, 2 Feb 2024 14:25:50 -0500 Subject: [PATCH 18/18] add xtra types --- src/components/cards/EthCard.tsx | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/components/cards/EthCard.tsx b/src/components/cards/EthCard.tsx index a8a2e88b6cb..18f4b67863a 100644 --- a/src/components/cards/EthCard.tsx +++ b/src/components/cards/EthCard.tsx @@ -39,6 +39,7 @@ import { ButtonPressAnimationTouchEvent } from '@/components/animations/ButtonPr import { useExternalToken } from '@/resources/assets/externalAssetsQuery'; import assetTypes from '@/entities/assetTypes'; import { Network } from '@/networks/types'; +import { getUniqueId } from '@/utils/ethereumUtils'; export const ETH_CARD_HEIGHT = 284.3; @@ -47,11 +48,19 @@ export const EthCard = () => { const { colors, isDarkMode } = useTheme(); const { navigate } = useNavigation(); const { isDamaged } = useWallets(); - const { data: ethAsset } = useExternalToken({ + const { data: externalEthAsset } = useExternalToken({ address: ETH_ADDRESS, network: Network.mainnet, currency: nativeCurrency, }); + + const ethAsset = { + ...externalEthAsset, + address: ETH_ADDRESS, + network: Network.mainnet, + uniqueId: getUniqueId(ETH_ADDRESS, Network.mainnet), + }; + const { loaded: accentColorLoaded } = useAccountAccentColor(); const { name: routeName } = useRoute(); const cardType = 'stretch'; @@ -110,16 +119,16 @@ export const EthCard = () => { const CHART_HEIGHT = 80; let isNegativePriceChange = false; - if (ethAsset?.native.change[0] === '-') { + if (ethAsset?.native?.change[0] === '-') { isNegativePriceChange = true; } const priceChangeDisplay = isNegativePriceChange - ? ethAsset?.native.change.substring(1) - : ethAsset?.native.change; + ? ethAsset?.native?.change.substring(1) + : ethAsset?.native?.change; const priceChangeColor = isNegativePriceChange ? colors.red : colors.green; - const loadedPrice = accentColorLoaded && ethAsset?.native.change; + const loadedPrice = accentColorLoaded && ethAsset?.native?.change; const loadedChart = throttledData?.points.length && loadedPrice; const [noChartData, setNoChartData] = useState(false); @@ -223,7 +232,7 @@ export const EthCard = () => { ) : ( - {ethAsset?.native.price.display} + {ethAsset?.native?.price.display} )}