Skip to content

Commit

Permalink
Fix userAssets query enabled config (#6243)
Browse files Browse the repository at this point in the history
* Remove unused useUserAssetsByChain hook

* Only trigger retry logic if there are chainIdsWithErrorsInResponse

* Remove unnecessary export of userAssetsByChainQueryFunction

* Update default for empty cache for userAssetsByChainQueryFunction

* Results from retry can be undefined, so check before using Object.values on the results

* Fix endpoint we were hitting on retry logic which was causing NO AUTH error

* Fix userAssets query to only be enabled when both address and currency are available

* Remove isSwapsOpen check when keeping user assets store in sync

* Current search cache can sometimes be an empty object instead of a Map and fails when trying to index get function

* Ignore persistence serialization and deserialization of searchCache in userAssets store

* Only update the user assets store when swaps is not open or if it is open but missing user assets data

* Update enabled logic for userAssetsQuery from userAssetsSync

* Use partialize for userAssets store

* Re-add address check in user assets query
  • Loading branch information
jinchung authored Nov 1, 2024
1 parent 992499d commit cb2b8a0
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 48 deletions.
1 change: 0 additions & 1 deletion src/__swaps__/screens/Swap/resources/assets/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export { useUserAssets } from './userAssets';
export type { UserAssetsArgs } from './userAssets';
export { useUserAssetsByChain } from './userAssetsByChain';
export type { UserAssetsByChainArgs } from './userAssetsByChain';
24 changes: 14 additions & 10 deletions src/__swaps__/screens/Swap/resources/assets/userAssets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,14 @@ async function userAssetsQueryFunction({
const chainIdsWithErrorsInResponse = res?.data?.meta?.chain_ids_with_errors || [];
const assets = res?.data?.payload?.assets || [];
if (address) {
userAssetsQueryFunctionRetryByChain({
address,
chainIds: chainIdsWithErrorsInResponse,
currency,
testnetMode,
});
if (chainIdsWithErrorsInResponse.length) {
userAssetsQueryFunctionRetryByChain({
address,
chainIds: chainIdsWithErrorsInResponse,
currency,
testnetMode,
});
}
if (assets.length && chainIdsInResponse.length) {
const parsedAssetsDict = await parseUserAssets({
assets,
Expand Down Expand Up @@ -186,9 +188,11 @@ async function userAssetsQueryFunctionRetryByChain({
}
const parsedRetries = await Promise.all(retries);
for (const parsedAssets of parsedRetries) {
const values = Object.values(parsedAssets);
if (values[0]) {
cachedUserAssets[values[0].chainId] = parsedAssets;
if (parsedAssets) {
const values = Object.values(parsedAssets);
if (values[0]) {
cachedUserAssets[values[0].chainId] = parsedAssets;
}
}
}
queryClient.setQueryData(userAssetsQueryKey({ address, currency, testnetMode }), cachedUserAssets);
Expand Down Expand Up @@ -236,9 +240,9 @@ export function useUserAssets<TSelectResult = UserAssetsResult>(
config: QueryConfigWithSelect<UserAssetsResult, Error, TSelectResult, UserAssetsQueryKey> = {}
) {
const { connectedToHardhat } = useConnectedToHardhatStore();

return useQuery(userAssetsQueryKey({ address, currency, testnetMode: connectedToHardhat }), userAssetsQueryFunction, {
...config,
enabled: !!address && !!currency,
refetchInterval: USER_ASSETS_REFETCH_INTERVAL,
staleTime: process.env.IS_TESTING === 'true' ? 0 : 1000,
});
Expand Down
36 changes: 8 additions & 28 deletions src/__swaps__/screens/Swap/resources/assets/userAssetsByChain.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { useQuery } from '@tanstack/react-query';
import { Address } from 'viem';

import { QueryConfigWithSelect, QueryFunctionArgs, QueryFunctionResult, createQueryKey, queryClient } from '@/react-query';
Expand All @@ -12,8 +11,6 @@ import { parseUserAssets, userAssetsQueryKey } from './userAssets';
import { RainbowFetchClient } from '@/rainbow-fetch';
import { ADDYS_API_KEY } from 'react-native-dotenv';

const USER_ASSETS_REFETCH_INTERVAL = 60000;

const addysHttp = new RainbowFetchClient({
baseURL: 'https://addys.p.rainbow.me/v3',
headers: {
Expand Down Expand Up @@ -59,15 +56,19 @@ export async function fetchUserAssetsByChain<TSelectData = UserAssetsByChainResu
// ///////////////////////////////////////////////
// Query Function

export async function userAssetsByChainQueryFunction({
async function userAssetsByChainQueryFunction({
queryKey: [{ address, chainId, currency }],
}: QueryFunctionArgs<typeof userAssetsByChainQueryKey>): Promise<Record<string, ParsedUserAsset>> {
const cache = queryClient.getQueryCache();
const cachedUserAssets = (cache.find(userAssetsQueryKey({ address, currency }))?.state?.data || {}) as ParsedAssetsDictByChain;
const cachedDataForChain = cachedUserAssets?.[chainId];
const cachedDataForChain = cachedUserAssets?.[chainId] || {};
try {
const url = `/${chainId}/${address}/assets/?currency=${currency.toLowerCase()}`;
const res = await addysHttp.get<AddressAssetsReceivedMessage>(url);
const url = `/${chainId}/${address}/assets`;
const res = await addysHttp.get<AddressAssetsReceivedMessage>(url, {
params: {
currency: currency.toLowerCase(),
},
});
const chainIdsInResponse = res?.data?.meta?.chain_ids || [];
const assets = res?.data?.payload?.assets || [];
if (assets.length && chainIdsInResponse.length) {
Expand All @@ -90,24 +91,3 @@ export async function userAssetsByChainQueryFunction({
}

type UserAssetsByChainResult = QueryFunctionResult<typeof userAssetsByChainQueryFunction>;

// ///////////////////////////////////////////////
// Query Hook

export function useUserAssetsByChain<TSelectResult = UserAssetsByChainResult>(
{ address, chainId, currency }: UserAssetsByChainArgs,
config: QueryConfigWithSelect<UserAssetsByChainResult, Error, TSelectResult, UserAssetsByChainQueryKey> = {}
) {
return useQuery(
userAssetsByChainQueryKey({
address,
chainId,
currency,
}),
userAssetsByChainQueryFunction,
{
...config,
refetchInterval: USER_ASSETS_REFETCH_INTERVAL,
}
);
}
36 changes: 30 additions & 6 deletions src/state/assets/userAssets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,34 @@ export interface UserAssetsState {
setUserAssets: (userAssets: Map<UniqueId, ParsedSearchAsset> | ParsedSearchAsset[]) => void;
}

type UserAssetsStateToPersist = Omit<
Partial<UserAssetsState>,
| 'currentAbortController'
| 'inputSearchQuery'
| 'searchCache'
| 'getBalanceSortedChainList'
| 'getChainsWithBalance'
| 'getFilteredUserAssetIds'
| 'getHighestValueEth'
| 'getUserAsset'
| 'getUserAssets'
| 'selectUserAssetIds'
| 'selectUserAssets'
| 'setSearchCache'
| 'setSearchQuery'
| 'setUserAssets'
>;

// NOTE: We are serializing Map as an Array<[UniqueId, ParsedSearchAsset]>
type UserAssetsStateWithTransforms = Omit<Partial<UserAssetsState>, 'chainBalances' | 'idsByChain' | 'userAssets'> & {
type UserAssetsStateToPersistWithTransforms = Omit<UserAssetsStateToPersist, 'chainBalances' | 'idsByChain' | 'userAssets'> & {
chainBalances: Array<[ChainId, number]>;
idsByChain: Array<[UserAssetFilter, UniqueId[]]>;
userAssets: Array<[UniqueId, ParsedSearchAsset]>;
};

function serializeUserAssetsState(state: Partial<UserAssetsState>, version?: number) {
function serializeUserAssetsState(state: UserAssetsStateToPersist, version?: number) {
try {
const transformedStateToPersist: UserAssetsStateWithTransforms = {
const transformedStateToPersist: UserAssetsStateToPersistWithTransforms = {
...state,
chainBalances: state.chainBalances ? Array.from(state.chainBalances.entries()) : [],
idsByChain: state.idsByChain ? Array.from(state.idsByChain.entries()) : [],
Expand All @@ -76,7 +94,7 @@ function serializeUserAssetsState(state: Partial<UserAssetsState>, version?: num
}

function deserializeUserAssetsState(serializedState: string) {
let parsedState: { state: UserAssetsStateWithTransforms; version: number };
let parsedState: { state: UserAssetsStateToPersistWithTransforms; version: number };
try {
parsedState = JSON.parse(serializedState);
} catch (error) {
Expand Down Expand Up @@ -332,7 +350,13 @@ export const createUserAssetsStore = (address: Address | string) =>
}),
{
storageKey: `userAssets_${address}`,
version: 0,
partialize: state => ({
chainBalances: state.chainBalances,
filter: state.filter,
idsByChain: state.idsByChain,
userAssets: state.userAssets,
}),
version: 1,
serializer: serializeUserAssetsState,
deserializer: deserializeUserAssetsState,
}
Expand Down Expand Up @@ -375,6 +399,6 @@ export function useUserAssetsStore<T>(selector: (state: UserAssetsState) => T) {
return useStore(store, useCallback(selector, []));
}

function getCurrentSearchCache(): Map<string, UniqueId[]> | undefined {
function getCurrentSearchCache(): Map<string, UniqueId[]> {
return getOrCreateStore().getState().searchCache;
}
7 changes: 4 additions & 3 deletions src/state/sync/UserAssetsSync.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,24 @@ import { ChainId } from '@/chains/types';

export const UserAssetsSync = function UserAssetsSync() {
const { accountAddress, nativeCurrency: currentCurrency } = useAccountSettings();

const isSwapsOpen = useSwapsStore(state => state.isSwapsOpen);
const isUserAssetsStoreMissingData = userAssetsStore.getState().getUserAssets()?.length === 0;
const enabled = (!isSwapsOpen || isUserAssetsStoreMissingData) && !!accountAddress && !!currentCurrency;

useUserAssets(
{
address: accountAddress,
currency: currentCurrency,
},
{
enabled: !isSwapsOpen,
enabled,
select: data =>
selectorFilterByUserChains({
data,
selector: selectUserAssetsList,
}),
onSuccess: data => {
if (!isSwapsOpen) {
if (!isSwapsOpen || isUserAssetsStoreMissingData) {
userAssetsStore.getState().setUserAssets(data as ParsedSearchAsset[]);

const inputAsset = userAssetsStore.getState().getHighestValueEth();
Expand Down

0 comments on commit cb2b8a0

Please sign in to comment.