Skip to content

Commit

Permalink
TW-1577 Implement loading tokens metadata especially for activity his…
Browse files Browse the repository at this point in the history
…tory
  • Loading branch information
keshan3262 committed Feb 10, 2025
1 parent 343101d commit f7e3989
Show file tree
Hide file tree
Showing 39 changed files with 743 additions and 301 deletions.
28 changes: 28 additions & 0 deletions src/app/hooks/use-no-category-evm-assets-loading.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useMemo } from 'react';

import { dispatch } from 'app/store';
import { refreshNoCategoryEvmAssetsMetadataActions } from 'app/store/evm/no-category-assets-metadata/actions';
import { NO_CATEGORY_ASSETS_METADATA_SYNC_INTERVAL } from 'lib/fixed-times';
import { useInterval } from 'lib/ui/hooks';
import { useAllEvmChains } from 'temple/front';

export const useNoCategoryEvmAssetsLoading = (publicKeyHash: HexString) => {
const allEvmChains = useAllEvmChains();
const rpcUrls = useMemo(
() => Object.fromEntries(Object.entries(allEvmChains).map(([chainId, { rpcBaseURL }]) => [chainId, rpcBaseURL])),
[allEvmChains]
);

useInterval(
() => {
dispatch(
refreshNoCategoryEvmAssetsMetadataActions.submit({
associatedAccountPkh: publicKeyHash,
rpcUrls
})
);
},
[publicKeyHash, rpcUrls],
NO_CATEGORY_ASSETS_METADATA_SYNC_INTERVAL
);
};
4 changes: 2 additions & 2 deletions src/app/hooks/use-no-category-tezos-assets-loading.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useMemo } from 'react';

import { dispatch } from 'app/store';
import { refreshNoCategoryAssetsMetadataActions } from 'app/store/tezos/no-category-assets-metadata/actions';
import { refreshNoCategoryTezosAssetsMetadataActions } from 'app/store/tezos/no-category-assets-metadata/actions';
import { NO_CATEGORY_ASSETS_METADATA_SYNC_INTERVAL } from 'lib/fixed-times';
import { useInterval } from 'lib/ui/hooks';
import { useAllTezosChains } from 'temple/front';
Expand All @@ -16,7 +16,7 @@ export const useNoCategoryTezosAssetsLoading = (publicKeyHash: string) => {
useInterval(
() => {
dispatch(
refreshNoCategoryAssetsMetadataActions.submit({
refreshNoCategoryTezosAssetsMetadataActions.submit({
associatedAccountPkh: publicKeyHash,
rpcUrls
})
Expand Down
4 changes: 2 additions & 2 deletions src/app/pages/Send/form/SelectAssetButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { EvmAssetIconWithNetwork, TezosTokenIconWithNetwork } from 'app/template
import { EvmBalance, TezosBalance } from 'app/templates/Balance';
import { setAnotherSelector, setTestID, TestIDProperty } from 'lib/analytics';
import { T } from 'lib/i18n';
import { getAssetSymbol, useEvmAssetMetadata, useCategorizedTezosAssetMetadata } from 'lib/metadata';
import { getAssetSymbol, useEvmCategorizedAssetMetadata, useCategorizedTezosAssetMetadata } from 'lib/metadata';
import { EvmChain, OneOfChains, TezosChain } from 'temple/front';
import { TempleChainKind } from 'temple/types';

Expand Down Expand Up @@ -84,7 +84,7 @@ interface EvmContentProps {
}

const EvmContent = memo<EvmContentProps>(({ network, accountPkh, assetSlug }) => {
const assetMetadata = useEvmAssetMetadata(assetSlug, network.chainId);
const assetMetadata = useEvmCategorizedAssetMetadata(assetSlug, network.chainId);

return (
<>
Expand Down
4 changes: 2 additions & 2 deletions src/app/pages/Send/hooks/use-evm-estimation-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { FeeValuesEIP1559, FeeValuesLegacy, TransactionRequest } from 'viem';

import { toastError } from 'app/toaster';
import { isNativeTokenAddress } from 'lib/apis/temple/endpoints/evm/api.utils';
import { useEvmAssetMetadata } from 'lib/metadata';
import { useEvmCategorizedAssetMetadata } from 'lib/metadata';
import { useTypedSWR } from 'lib/swr';
import { getReadOnlyEvmForNetwork } from 'temple/evm';
import { EvmChain } from 'temple/front';
Expand Down Expand Up @@ -42,7 +42,7 @@ export const useEvmEstimationData = (
toFilled?: boolean,
amount?: string
) => {
const assetMetadata = useEvmAssetMetadata(assetSlug, network.chainId);
const assetMetadata = useEvmCategorizedAssetMetadata(assetSlug, network.chainId);

const estimate = useCallback(async (): Promise<EvmEstimationData | undefined> => {
try {
Expand Down
4 changes: 2 additions & 2 deletions src/app/pages/Send/modals/ConfirmSend/EvmContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { useEvmEstimationData } from 'app/pages/Send/hooks/use-evm-estimation-da
import { toastError, toastSuccess } from 'app/toaster';
import { EVM_TOKEN_SLUG } from 'lib/assets/defaults';
import { useEvmAssetBalance } from 'lib/balances/hooks';
import { useEvmAssetMetadata } from 'lib/metadata';
import { useEvmCategorizedAssetMetadata } from 'lib/metadata';
import { useTempleClient } from 'lib/temple/front';
import { ZERO } from 'lib/utils/numbers';
import { EvmTxParams } from 'temple/evm/types';
Expand All @@ -38,7 +38,7 @@ export const EvmContent: FC<EvmContentProps> = ({ data, onClose }) => {

const { value: balance = ZERO } = useEvmAssetBalance(assetSlug, accountPkh, network);
const { value: ethBalance = ZERO } = useEvmAssetBalance(EVM_TOKEN_SLUG, accountPkh, network);
const assetMetadata = useEvmAssetMetadata(assetSlug, network.chainId);
const assetMetadata = useEvmCategorizedAssetMetadata(assetSlug, network.chainId);

const form = useForm<EvmTxParamsFormData>({ mode: 'onChange' });
const { watch, formState, setValue } = form;
Expand Down
4 changes: 2 additions & 2 deletions src/app/pages/Token/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { ActivityListContainer, EvmActivityList, TezosActivityList } from 'app/t
import { AdvertisingBanner } from 'app/templates/advertising/advertising-banner/advertising-banner';
import { ExploreActionButtonsBar } from 'app/templates/ExploreActionButtons';
import { isTezAsset } from 'lib/assets';
import { useEvmAssetMetadata, useCategorizedTezosAssetMetadata } from 'lib/metadata';
import { useEvmCategorizedAssetMetadata, useCategorizedTezosAssetMetadata } from 'lib/metadata';
import { useBooleanState } from 'lib/ui/hooks';
import { HistoryAction, navigate, useLocation } from 'lib/woozie';
import { TempleChainKind } from 'temple/types';
Expand Down Expand Up @@ -119,7 +119,7 @@ interface EvmTokenPageProps {
const EvmTokenPage: FC<EvmTokenPageProps> = ({ chainId, assetSlug }) => {
const [infoModalOpen, setInfoModalOpen, setInfoModalClosed] = useBooleanState(false);

const assetMetadata = useEvmAssetMetadata(assetSlug, chainId);
const assetMetadata = useEvmCategorizedAssetMetadata(assetSlug, chainId);

const pageProps = useMemo<PageLayoutProps>(
() => ({
Expand Down
19 changes: 12 additions & 7 deletions src/app/root-hooks/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useConversionTracking } from 'app/hooks/use-conversion-tracking';
import { useTokensApyLoading } from 'app/hooks/use-load-tokens-apy.hook';
import { useLongRefreshLoading } from 'app/hooks/use-long-refresh-loading.hook';
import { useMetadataRefresh } from 'app/hooks/use-metadata-refresh';
import { useNoCategoryEvmAssetsLoading } from 'app/hooks/use-no-category-evm-assets-loading';
import { useNoCategoryTezosAssetsLoading } from 'app/hooks/use-no-category-tezos-assets-loading';
import { useShowAgreementsSync } from 'app/hooks/use-show-agreements-sync';
import { useStorageAnalytics } from 'app/hooks/use-storage-analytics';
Expand Down Expand Up @@ -84,10 +85,14 @@ const TezosAccountHooks = memo<{ publicKeyHash: string }>(({ publicKeyHash }) =>
);
});

const EvmAccountHooks = memo<{ publicKeyHash: HexString }>(({ publicKeyHash }) => (
<>
<AppEvmTokensExchangeRatesLoading publicKeyHash={publicKeyHash} />
<AppEvmTokensMetadataLoading publicKeyHash={publicKeyHash} />
<AppEvmBalancesLoading publicKeyHash={publicKeyHash} />
</>
));
const EvmAccountHooks = memo<{ publicKeyHash: HexString }>(({ publicKeyHash }) => {
useNoCategoryEvmAssetsLoading(publicKeyHash);

return (
<>
<AppEvmTokensExchangeRatesLoading publicKeyHash={publicKeyHash} />
<AppEvmTokensMetadataLoading publicKeyHash={publicKeyHash} />
<AppEvmBalancesLoading publicKeyHash={publicKeyHash} />
</>
);
});
2 changes: 1 addition & 1 deletion src/app/store/evm/balances/epics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const loadEvmBalanceOnChainEpic: Epic = action$ =>

return from(evmOnChainBalancesRequestsExecutor.executeRequest(payload)).pipe(
switchMap(balance =>
forkJoin([Promise.resolve(balance), evmOnChainBalancesRequestsExecutor.queueIsEmpty(network.chainId)])
forkJoin([Promise.resolve(balance), evmOnChainBalancesRequestsExecutor.poolIsEmpty(network.chainId)])
),
switchMap(([balance, queueIsEmpty]) => {
const updateBalanceObservable = of(
Expand Down
10 changes: 3 additions & 7 deletions src/app/store/evm/balances/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { isNativeTokenAddress } from 'lib/apis/temple/endpoints/evm/api.utils';
import { toTokenSlug } from 'lib/assets';
import { EVM_TOKEN_SLUG } from 'lib/assets/defaults';
import { fetchEvmRawBalance } from 'lib/evm/on-chain/balance';
import { EvmRpcRequestsExecutor, ExecutionQueueCallbacks } from 'lib/evm/on-chain/utils/evm-rpc-requests-executor';
import { EvmRpcRequestsExecutor } from 'lib/evm/on-chain/utils/evm-rpc-requests-executor';
import { isPositiveCollectibleBalance, isPositiveTokenBalance } from 'lib/utils/evm.utils';

import { LoadOnChainBalancePayload } from './actions';
Expand Down Expand Up @@ -37,12 +37,8 @@ export const getTokenSlugBalanceRecord = (data: BalanceItem[], chainId: number)
return acc;
}, {});

class EvmOnChainBalancesRequestsExecutor extends EvmRpcRequestsExecutor<
LoadOnChainBalancePayload & ExecutionQueueCallbacks<BigNumber>,
BigNumber,
number
> {
protected getQueueKey(payload: LoadOnChainBalancePayload) {
class EvmOnChainBalancesRequestsExecutor extends EvmRpcRequestsExecutor<LoadOnChainBalancePayload, BigNumber, number> {
protected getRequestsPoolKey(payload: LoadOnChainBalancePayload) {
return payload.network.chainId;
}

Expand Down
20 changes: 20 additions & 0 deletions src/app/store/evm/no-category-assets-metadata/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createActions } from 'lib/store';

import { ChainId, NoCategoryAssetMetadata } from './state';

export type AssetsMetadataInput = Record<ChainId, StringRecord<NoCategoryAssetMetadata | undefined>>;

export const loadNoCategoryEvmAssetsMetadataActions = createActions<
{
rpcUrl: string;
associatedAccountPkh: HexString;
chainId: number;
slugs: string[];
},
{ records: AssetsMetadataInput; associatedAccountPkh: HexString; poolsAreEmpty: boolean }
>('evm/no-category-assets-metadata/LOAD');

export const refreshNoCategoryEvmAssetsMetadataActions = createActions<
{ associatedAccountPkh: HexString; rpcUrls: Record<number, string> },
{ records: AssetsMetadataInput; poolsAreEmpty: boolean }
>('evm/no-category-assets-metadata/REFRESH_MULTIPLE');
148 changes: 148 additions & 0 deletions src/app/store/evm/no-category-assets-metadata/epics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { isEqual } from 'lodash';
import { Action } from 'redux';
import { combineEpics, Epic } from 'redux-observable';
import { EMPTY, forkJoin, from, of } from 'rxjs';
import { bufferTime, concatMap, exhaustMap, map, mergeMap, withLatestFrom } from 'rxjs/operators';
import { ofType, toPayload } from 'ts-action-operators';

import { RootState } from 'app/store/root-state.type';
import { fromAssetSlug } from 'lib/assets';
import { fetchEvmAssetMetadataFromChain } from 'lib/evm/on-chain/metadata';
import { EvmRpcRequestsExecutor } from 'lib/evm/on-chain/utils/evm-rpc-requests-executor';

import {
loadNoCategoryEvmAssetsMetadataActions,
refreshNoCategoryEvmAssetsMetadataActions,
AssetsMetadataInput
} from './actions';
import { NoCategoryAssetMetadata } from './state';

interface GetEvmAssetMetadataPayload {
rpcUrl: string;
chainId: number;
assetSlug: string;
}

class GetEvmAssetMetadataExecutor extends EvmRpcRequestsExecutor<
GetEvmAssetMetadataPayload,
NoCategoryAssetMetadata | undefined,
string
> {
constructor() {
super(3);
}

protected getRequestsPoolKey(payload: GetEvmAssetMetadataPayload) {
return payload.rpcUrl;
}

protected requestsAreSame(a: GetEvmAssetMetadataPayload, b: GetEvmAssetMetadataPayload) {
return isEqual(a, b);
}

protected async getResult(payload: GetEvmAssetMetadataPayload) {
console.log('evm fetch', { payload, ts: Date.now() });
return fetchEvmAssetMetadataFromChain({ chainId: payload.chainId, rpcBaseURL: payload.rpcUrl }, payload.assetSlug);
}
}

const getEvmAssetMetadataExecutor = new GetEvmAssetMetadataExecutor();

const getAssetsMetadata$ = (rpcUrl: string, chainId: number, slugs: string[]) => {
return slugs.length === 0
? of<AssetsMetadataInput>({})
: from(slugs).pipe(
mergeMap(assetSlug =>
from(
getEvmAssetMetadataExecutor
.executeRequest({ rpcUrl, chainId, assetSlug })
.then(res => [chainId, assetSlug, res] as const)
.catch(() => [chainId, assetSlug, undefined] as const)
)
),
bufferTime(1000),
map(results =>
results.reduce<AssetsMetadataInput>((acc, [chainId, assetSlug, metadata]) => {
if (!metadata) {
return acc;
}

acc[chainId] = acc[chainId] || {};
acc[chainId][assetSlug] = metadata;

return acc;
}, {})
)
);
};

const loadNoCategoryAssetsMetadataEpic: Epic = action$ =>
action$.pipe(
ofType(loadNoCategoryEvmAssetsMetadataActions.submit),
toPayload(),
mergeMap(({ rpcUrl, chainId, associatedAccountPkh, slugs }) => {
console.log('evm load 1', { rpcUrl, chainId, associatedAccountPkh, slugs, ts: Date.now() });

return getAssetsMetadata$(rpcUrl, chainId, slugs).pipe(
concatMap(records => forkJoin([of(records), from(getEvmAssetMetadataExecutor.allPoolsAreEmpty())])),
concatMap(([records, poolsAreEmpty]) => {
if (Object.keys(records).length === 0 && !poolsAreEmpty) {
return EMPTY;
}

console.log('evm load 2', { records, associatedAccountPkh, poolsAreEmpty, ts: Date.now() });

return of(loadNoCategoryEvmAssetsMetadataActions.success({ records, associatedAccountPkh, poolsAreEmpty }));
})
);
})
);

const refreshAllAssetsMetadataEpic: Epic<Action, Action, RootState> = (action$, state$) =>
action$.pipe(
ofType(refreshNoCategoryEvmAssetsMetadataActions.submit),
toPayload(),
withLatestFrom(state$),
exhaustMap(([{ rpcUrls, associatedAccountPkh }, { evmNoCategoryAssetMetadata }]) => {
const { contractsChainIds, accountToAssetAssociations } = evmNoCategoryAssetMetadata;
const slugs = accountToAssetAssociations[associatedAccountPkh] || [];
console.log('evm refresh 1', { slugs, associatedAccountPkh, ts: Date.now() });

const slugsByChainIds = slugs.reduce<Record<number, string[]>>((acc, slug) => {
const [address] = fromAssetSlug(slug);
const chainId = contractsChainIds[address];
if (rpcUrls[chainId]) {
acc[chainId] = acc[chainId] || [];
acc[chainId].push(slug);
}

return acc;
}, {});

return Object.keys(slugsByChainIds).length === 0
? EMPTY
: from(Object.entries(slugsByChainIds)).pipe(
mergeMap(([chainId, slugs]) => getAssetsMetadata$(rpcUrls[Number(chainId)], Number(chainId), slugs))
);
}),
concatMap(results => forkJoin([of(results), from(getEvmAssetMetadataExecutor.allPoolsAreEmpty())])),
concatMap(([records, poolsAreEmpty]) => {
if (Object.keys(records).length === 0 && !poolsAreEmpty) {
return EMPTY;
}

console.log('evm refresh 2', { records, poolsAreEmpty, ts: Date.now() });

return of(
refreshNoCategoryEvmAssetsMetadataActions.success({
records,
poolsAreEmpty
})
);
})
);

export const evmNoCategoryAssetsMetadataEpics = combineEpics(
loadNoCategoryAssetsMetadataEpic,
refreshAllAssetsMetadataEpic
);
Loading

0 comments on commit f7e3989

Please sign in to comment.