Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TW-683: Tokens rework #997

Draft
wants to merge 28 commits into
base: development
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
30ef46c
TW-683: Tokens rework. Account's tokens -> Redux
alex-tsx Sep 18, 2023
5af4077
TW-683: Assets rework. Tokens. + Whitelist
alex-tsx Sep 20, 2023
cf72a72
TW-683: Assets rework. Tokens. 'Add Asset' page
alex-tsx Sep 20, 2023
1e51586
TW-683: Assets rework. Collectibles. Redux
alex-tsx Sep 21, 2023
1b494af
TW-683: Assets rework. Tokens. Manage page. Fix: listing other accoun…
alex-tsx Sep 22, 2023
b4531aa
TW-683: Assets rework. Tokens. Refactor
alex-tsx Sep 24, 2023
72dab21
TW-683: Assets rework. Collectibles. Optimized loading
alex-tsx Sep 25, 2023
9617df9
TW-683: Assets rework. Collectibles. Optimized loading
alex-tsx Sep 26, 2023
8bd49d0
TW-683: Assets rework. Refactor
alex-tsx Sep 26, 2023
1e8d956
TW-683: Assets rework. Optimized balances loading
alex-tsx Sep 26, 2023
f17eea5
TW-683: Assets rework. Collectibles. Fix details loading from OBJKT
alex-tsx Sep 27, 2023
751dc39
TW-683: Assets rework. Migration from IndexedDB
alex-tsx Sep 27, 2023
b820d09
TW-683: Assets rework. Refactor
alex-tsx Sep 27, 2023
6cd1830
Merge branch 'development' into TW-683-tokens-rework
alex-tsx Sep 27, 2023
024faee
TW-683: Assets rework. Refactor
alex-tsx Sep 27, 2023
793f1c6
TW-683: Assets rework. Refactor
alex-tsx Sep 28, 2023
d4e85ee
TW-683: Assets rework. + ESLint no-cycle rule
alex-tsx Sep 28, 2023
b1cb514
TW-683: Assets rework. Refactor
alex-tsx Sep 28, 2023
515b544
TW-683: Assets rework. Migrations made async
alex-tsx Sep 28, 2023
fcf7b80
TW-683: Assets rework. Refactor
alex-tsx Sep 29, 2023
4bead31
TW-683: Assets rework. Refactor
alex-tsx Sep 29, 2023
2e62d4f
TW-683: Assets rework. Not keeping metadata from TZKT
alex-tsx Oct 2, 2023
425fad9
TW-683: Assets rework. Refactor
alex-tsx Oct 2, 2023
62699cf
Merge branch 'development' into TW-683-tokens-rework
alex-tsx Oct 5, 2023
d25e612
TW-683: Assets rework. + __TEMPORARY_COLLECTIBLES_LOAD_LIMIT__
alex-tsx Oct 19, 2023
d4942ac
TW-683: Assets rework. Fix tokens re-render on account switch
alex-tsx Oct 20, 2023
1252c7b
TW-683: Assets rework. Fix page crash, when switching to Ghostnet
alex-tsx Oct 20, 2023
7afd83c
Merge branch 'development' into TW-683-tokens-rework
alex-tsx Dec 11, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"plugin:import/warnings",
"plugin:import/typescript"
],
"plugins": ["import", "prettier"],
"plugins": ["import", "prettier", "no-type-assertion"],
"parser": "@typescript-eslint/parser",
"overrides": [{
"files": ["*.ts", "*.tsx"],
Expand Down Expand Up @@ -51,9 +51,12 @@
"newlines-between": "always"
}
],
"no-type-assertion/no-type-assertion": "warn",
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/no-non-null-assertion": "warn",
"react-hooks/rules-of-hooks": "warn",
"react-hooks/exhaustive-deps": "warn"
"react-hooks/exhaustive-deps": ["warn", {
"additionalHooks": "(useMemoWithCompare|useDidUpdate)"
}]
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ManageAssetsSelectors } from 'src/app/pages/ManageAssets/ManageAssets.selectors';
import { ManageAssetsSelectors } from 'src/app/pages/ManageAssets/selectors';

import { Page } from '../../classes/page.class';
import { createPageElement } from '../../utils/search.utils';
Expand Down
2 changes: 1 addition & 1 deletion e2e/src/page-objects/pages/manage-assets-tokens.page.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import retry from 'async-retry';
import { ManageAssetsSelectors } from 'src/app/pages/ManageAssets/ManageAssets.selectors';
import { ManageAssetsSelectors } from 'src/app/pages/ManageAssets/selectors';

import { RETRY_OPTIONS, SHORT_TIMEOUT, VERY_SHORT_TIMEOUT } from 'e2e/src/utils/timing.utils';

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
"eslint-config-react-app": "^7.0.1",
"eslint-import-resolver-typescript": "^3",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-no-type-assertion": "^1.3.0",
"eslint-plugin-prettier": "^4",
"eslint-webpack-plugin": "^3.2.0",
"fast-glob": "^3.2.12",
Expand Down
3 changes: 2 additions & 1 deletion src/app/ConfirmPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ import { ModifyFeeAndLimit } from 'app/templates/ExpensesView/ExpensesView';
import NetworkBanner from 'app/templates/NetworkBanner';
import OperationView from 'app/templates/OperationView';
import { CustomRpcContext } from 'lib/analytics';
import { useGasToken } from 'lib/assets/hooks';
import { T, t } from 'lib/i18n';
import { useRetryableSWR } from 'lib/swr';
import { useTempleClient, useAccount, useRelevantAccounts, useCustomChainId, useGasToken } from 'lib/temple/front';
import { useTempleClient, useAccount, useRelevantAccounts, useCustomChainId } from 'lib/temple/front';
import { TempleAccountType, TempleDAppPayload, TempleAccount, TempleChainId } from 'lib/temple/types';
import { useSafeState } from 'lib/ui/hooks';
import { delay } from 'lib/utils';
Expand Down
2 changes: 1 addition & 1 deletion src/app/PageRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import DApps from 'app/pages/DApps';
import Delegate from 'app/pages/Delegate';
import Home from 'app/pages/Home/Home';
import ImportAccount from 'app/pages/ImportAccount';
import ManageAssets from 'app/pages/ManageAssets/ManageAssets';
import ManageAssets from 'app/pages/ManageAssets';
import { CreateWallet } from 'app/pages/NewWallet/CreateWallet';
import { ImportWallet } from 'app/pages/NewWallet/ImportWallet';
import AttentionPage from 'app/pages/Onboarding/pages/AttentionPage';
Expand Down
21 changes: 12 additions & 9 deletions src/app/WithDataLoading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@ import React, { FC, useEffect } from 'react';

import { useDispatch } from 'react-redux';

import { useAdvertisingLoading } from 'app/hooks/use-advertising.hook';
import { useCollectiblesDetailsLoading } from 'app/hooks/use-collectibles-details-loading';
import { useTokensApyLoading } from 'app/hooks/use-load-tokens-apy.hook';
import { useLongRefreshLoading } from 'app/hooks/use-long-refresh-loading.hook';
import { useMetadataLoading } from 'app/hooks/use-metadata-loading';
import { useStorageAnalytics } from 'app/hooks/use-storage-analytics';
import { useTokensLoading } from 'app/hooks/use-tokens-loading';
import { loadSwapDexesAction, loadSwapTokensAction } from 'app/store/swap/actions';
import { useBalancesLoading } from 'lib/temple/front/load-balances';

import { useAdvertisingLoading } from './hooks/use-advertising.hook';
import { useAssetsLoading } from './hooks/use-assets-loading';
import { useAssetsMigrations } from './hooks/use-assets-migrations';
import { useBalancesLoading } from './hooks/use-balances-loading';
import { useCollectiblesDetailsLoading } from './hooks/use-collectibles-details-loading';
import { useTokensApyLoading } from './hooks/use-load-tokens-apy.hook';
import { useLongRefreshLoading } from './hooks/use-long-refresh-loading.hook';
import { useMetadataLoading } from './hooks/use-metadata-loading';
import { useMetadataRefresh } from './hooks/use-metadata-refresh';
import { useStorageAnalytics } from './hooks/use-storage-analytics';

export const WithDataLoading: FC<PropsWithChildren> = ({ children }) => {
useTokensLoading();
useAssetsMigrations();

useAssetsLoading();
useMetadataLoading();
useMetadataRefresh();
useBalancesLoading();
Expand Down
3 changes: 2 additions & 1 deletion src/app/hooks/AliceBob/use-disabled-proceed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { useMemo } from 'react';

import BigNumber from 'bignumber.js';

import { useAccount, useBalance } from 'lib/temple/front';
import { useBalance } from 'lib/balances';
import { useAccount } from 'lib/temple/front';

export const useDisabledProceed = (
inputAmount: number | undefined,
Expand Down
30 changes: 30 additions & 0 deletions src/app/hooks/use-assets-loading.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useEffect } from 'react';

import { dispatch } from 'app/store';
import {
loadAccountTokensActions,
loadTokensWhitelistActions,
loadAccountCollectiblesActions
} from 'app/store/assets/actions';
import { ASSETS_SYNC_INTERVAL } from 'lib/fixed-times';
import { useAccount, useChainId } from 'lib/temple/front';
import { TempleChainId } from 'lib/temple/types';
import { useInterval } from 'lib/ui/hooks';

export const useAssetsLoading = () => {
const chainId = useChainId()!;
const { publicKeyHash } = useAccount();

useEffect(() => {
if (chainId === TempleChainId.Mainnet) dispatch(loadTokensWhitelistActions.submit());
}, [chainId]);

useInterval(
() => {
dispatch(loadAccountTokensActions.submit({ account: publicKeyHash, chainId }));
dispatch(loadAccountCollectiblesActions.submit({ account: publicKeyHash, chainId }));
},
ASSETS_SYNC_INTERVAL,
[chainId, publicKeyHash]
);
};
18 changes: 18 additions & 0 deletions src/app/hooks/use-assets-migrations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useTokensMetadataSelector } from 'app/store/tokens-metadata/selectors';
import { migrateFromIndexedDB } from 'lib/assets/migrations';
import { migrate } from 'lib/local-storage/migrator';
import { useDidMount } from 'lib/ui/hooks';

export const useAssetsMigrations = () => {
const allMetadatas = useTokensMetadataSelector();

useDidMount(
() =>
void migrate([
{
name: '[email protected]',
up: () => migrateFromIndexedDB(allMetadatas)
}
])
);
};
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import { useCallback } from 'react';
import { useCallback, useEffect } from 'react';

import { useDispatch } from 'react-redux';

import { loadTokensBalancesFromTzktAction } from 'app/store/balances/actions';
import { BALANCES_SYNC_INTERVAL } from 'lib/fixed-times';
import { useAccount, useChainId, useNetwork } from 'lib/temple/front';
import { useAccount, useChainId } from 'lib/temple/front';
import { useInterval } from 'lib/ui/hooks';

export const useBalancesLoading = () => {
const dispatch = useDispatch();
const chainId = useChainId(true)!;
const { publicKeyHash } = useAccount();

const chainId = useChainId(true) ?? '';
const { rpcBaseURL: rpcUrl } = useNetwork();
const dispatch = useDispatch();

const { publicKeyHash } = useAccount();
useEffect(
() => void dispatch(loadTokensBalancesFromTzktAction.submit({ publicKeyHash, chainId, gasOnly: true })),
[]
);

const load = useCallback(
() => void dispatch(loadTokensBalancesFromTzktAction.submit({ publicKeyHash, chainId })),
[chainId, publicKeyHash, rpcUrl]
[chainId, publicKeyHash]
);

useInterval(load, BALANCES_SYNC_INTERVAL, [load]);
// Not calling immediately, because balances are also loaded via assets loading
useInterval(load, BALANCES_SYNC_INTERVAL, [load], false);
};
3 changes: 2 additions & 1 deletion src/app/hooks/use-balances-with-decimals.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { BigNumber } from 'bignumber.js';
import { useBalancesSelector } from 'app/store/balances/selectors';
import { useTokensMetadataSelector } from 'app/store/tokens-metadata/selectors';
import { TEZ_TOKEN_SLUG } from 'lib/assets';
import { useAccount, useChainId, useGasToken } from 'lib/temple/front';
import { useGasToken } from 'lib/assets/hooks';
import { useAccount, useChainId } from 'lib/temple/front';
import { atomsToTokens } from 'lib/temple/helpers';

export const useBalancesWithDecimals = () => {
Expand Down
21 changes: 8 additions & 13 deletions src/app/hooks/use-collectibles-details-loading.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,25 @@
import { isEqual } from 'lodash';
import { useDispatch } from 'react-redux';

import { dispatch } from 'app/store';
import { loadCollectiblesDetailsActions } from 'app/store/collectibles/actions';
import { useAccountCollectibles } from 'lib/assets/hooks';
import { COLLECTIBLES_DETAILS_SYNC_INTERVAL } from 'lib/fixed-times';
import { useAccount, useChainId, useCollectibleTokens } from 'lib/temple/front';
import { useAccount, useChainId } from 'lib/temple/front';
import { useInterval, useMemoWithCompare } from 'lib/ui/hooks';

export const useCollectiblesDetailsLoading = () => {
const chainId = useChainId()!;
const { publicKeyHash } = useAccount();
const { data: collectibles } = useCollectibleTokens(chainId, publicKeyHash);
const dispatch = useDispatch();
const collectibles = useAccountCollectibles(publicKeyHash, chainId);

const slugs = useMemoWithCompare(
() => collectibles.map(({ tokenSlug }) => tokenSlug).sort(),
[collectibles],
isEqual
);
const slugs = useMemoWithCompare(() => collectibles.map(({ slug }) => slug).sort(), [collectibles], isEqual);

useInterval(
() => {
if (slugs.length < 1) return;

dispatch(loadCollectiblesDetailsActions.submit(slugs));
// Is it necessary for collectibles on non-Mainnet networks too?
if (slugs.length) dispatch(loadCollectiblesDetailsActions.submit(slugs));
},
COLLECTIBLES_DETAILS_SYNC_INTERVAL,
[slugs, dispatch]
[slugs]
);
};
39 changes: 21 additions & 18 deletions src/app/hooks/use-metadata-loading.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,43 @@
import { useEffect } from 'react';

import { isDefined } from '@rnw-community/shared';
import { isEqual } from 'lodash';
import { useDispatch } from 'react-redux';

import {
loadTokensMetadataAction,
loadWhitelistAction,
resetTokensMetadataLoadingAction
} from 'app/store/tokens-metadata/actions';
import { useAccountAssetsSelector } from 'app/store/assets/selectors';
import { loadTokensMetadataAction, resetTokensMetadataLoadingAction } from 'app/store/tokens-metadata/actions';
import { useTokensMetadataSelector } from 'app/store/tokens-metadata/selectors';
import { METADATA_SYNC_INTERVAL } from 'lib/fixed-times';
import { useChainId, useTezos } from 'lib/temple/front';
import { useAllStoredTokensSlugs } from 'lib/temple/front/assets';
import { TempleChainId } from 'lib/temple/types';
import { useAccount, useChainId, useTezos } from 'lib/temple/front';
import { useInterval, useMemoWithCompare } from 'lib/ui/hooks';

export const useMetadataLoading = () => {
const chainId = useChainId(true)!;
const { publicKeyHash: account } = useAccount();
const dispatch = useDispatch();
const tezos = useTezos();

const tokensMetadata = useTokensMetadataSelector();
const tokens = useAccountAssetsSelector(account, chainId, 'tokens');
const collectibles = useAccountAssetsSelector(account, chainId, 'collectibles');

const { data: tokensSlugs } = useAllStoredTokensSlugs(chainId);
const assetsMetadata = useTokensMetadataSelector();

const slugsWithoutMetadata = useMemoWithCompare(
() => tokensSlugs?.filter(slug => !isDefined(tokensMetadata[slug])).sort(),
[tokensSlugs, tokensMetadata],
() => {
const tokensSlugs = tokens.reduce<string[]>(
(acc, { slug }) => (assetsMetadata[slug] ? acc : acc.concat(slug)),
[]
);
const collectiblesSlugs = collectibles.reduce<string[]>(
(acc, { slug }) => (assetsMetadata[slug] ? acc : acc.concat(slug)),
[]
);

return tokensSlugs.concat(collectiblesSlugs).sort();
},
[tokens, collectibles, assetsMetadata],
isEqual
);

useEffect(() => {
if (chainId === TempleChainId.Mainnet) dispatch(loadWhitelistAction.submit());
}, [chainId]);

useEffect(() => {
dispatch(resetTokensMetadataLoadingAction());

Expand All @@ -43,7 +46,7 @@ export const useMetadataLoading = () => {

useInterval(
() => {
if (!slugsWithoutMetadata || slugsWithoutMetadata.length < 1) return;
if (slugsWithoutMetadata.length < 1) return;

const rpcUrl = tezos.rpc.getRpcUrl();

Expand Down
2 changes: 1 addition & 1 deletion src/app/hooks/use-metadata-refresh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const useMetadataRefresh = () => {
const slug = slugsOnAppLoad[index]!;
const [address, id] = slug.split('_');

const metadata = buildTokenMetadataFromFetched(token, address, Number(id));
const metadata = token && buildTokenMetadataFromFetched(token, address, Number(id));

return metadata ? acc.concat(metadata) : acc;
}, [])
Expand Down
9 changes: 0 additions & 9 deletions src/app/hooks/use-tokens-loading.ts

This file was deleted.

3 changes: 2 additions & 1 deletion src/app/layouts/PageLayout/Header/AccountDropdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import { ReactComponent as LockIcon } from 'app/icons/lock.svg';
import { ReactComponent as MaximiseIcon } from 'app/icons/maximise.svg';
import { ReactComponent as SettingsIcon } from 'app/icons/settings.svg';
import SearchField from 'app/templates/SearchField';
import { useGasToken } from 'lib/assets/hooks';
import { T, t } from 'lib/i18n';
import { useAccount, useRelevantAccounts, useSetAccountPkh, useTempleClient, useGasToken } from 'lib/temple/front';
import { useAccount, useRelevantAccounts, useSetAccountPkh, useTempleClient } from 'lib/temple/front';
import { PopperRenderProps } from 'lib/ui/Popper';
import { HistoryAction, navigate } from 'lib/woozie';

Expand Down
21 changes: 10 additions & 11 deletions src/app/pages/AddAsset/AddAsset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import { Alert, FormField, FormSubmitButton, NoSpaceField } from 'app/atoms';
import Spinner from 'app/atoms/Spinner/Spinner';
import { ReactComponent as AddIcon } from 'app/icons/add.svg';
import PageLayout from 'app/layouts/PageLayout';
import { addTokensMetadataAction } from 'app/store/tokens-metadata/actions';
import { setAssetStatusAction } from 'app/store/assets/actions';
import { putTokensMetadataAction } from 'app/store/tokens-metadata/actions';
import { useFormAnalytics } from 'lib/analytics';
import { TokenMetadataResponse } from 'lib/apis/temple';
import { toTokenSlug } from 'lib/assets';
Expand All @@ -22,12 +23,11 @@ import {
} from 'lib/assets/standards';
import { getBalanceSWRKey } from 'lib/balances';
import { T, t } from 'lib/i18n';
import type { TokenMetadata } from 'lib/metadata';
import { isCollectible, TokenMetadata } from 'lib/metadata';
import { fetchOneTokenMetadata } from 'lib/metadata/fetch';
import { TokenMetadataNotFoundError } from 'lib/metadata/on-chain';
import { loadContract } from 'lib/temple/contract';
import { useTezos, useNetwork, useChainId, useAccount, validateContractAddress } from 'lib/temple/front';
import * as Repo from 'lib/temple/repo';
import { useSafeState } from 'lib/ui/hooks';
import { delay } from 'lib/utils';
import { navigate } from 'lib/woozie';
Expand Down Expand Up @@ -209,17 +209,16 @@ const Form: FC = () => {
id: tokenId
};

dispatch(addTokensMetadataAction([tokenMetadata]));
dispatch(putTokensMetadataAction([tokenMetadata]));

await Repo.accountTokens.put(
{
dispatch(
setAssetStatusAction({
isCollectible: isCollectible(tokenMetadata),
chainId,
account: accountPkh,
tokenSlug,
status: Repo.ITokenStatus.Enabled,
addedAt: Date.now()
},
Repo.toAccountTokenKey(chainId, accountPkh, tokenSlug)
slug: tokenSlug,
status: 'enabled'
})
);

swrCache.delete(unstable_serialize(getBalanceSWRKey(tezos, tokenSlug, accountPkh)));
Expand Down
Loading
Loading