From cda88bf1847c5bf7e88ecae4c1b92e3f15a02b99 Mon Sep 17 00:00:00 2001 From: jinoosss Date: Wed, 6 Nov 2024 15:02:27 +0900 Subject: [PATCH] feat: [ADN-586] adds automatic registration for GRC20 and GRC721 tokens --- packages/adena-extension/src/App/use-app.ts | 8 +- .../src/hooks/use-token-metainfo.tsx | 88 +++++++++++++++++-- 2 files changed, 83 insertions(+), 13 deletions(-) diff --git a/packages/adena-extension/src/App/use-app.ts b/packages/adena-extension/src/App/use-app.ts index fc322baf..302de93e 100644 --- a/packages/adena-extension/src/App/use-app.ts +++ b/packages/adena-extension/src/App/use-app.ts @@ -1,12 +1,12 @@ import { useEffect } from 'react'; import { useLocation } from 'react-router-dom'; +import { useAccountName } from '@hooks/use-account-name'; +import { useWalletContext } from '@hooks/use-context'; import { useCurrentAccount } from '@hooks/use-current-account'; import { useNetwork } from '@hooks/use-network'; -import { useTokenMetainfo } from '@hooks/use-token-metainfo'; -import { useWalletContext } from '@hooks/use-context'; -import { useAccountName } from '@hooks/use-account-name'; import useScrollHistory from '@hooks/use-scroll-history'; +import { useTokenMetainfo } from '@hooks/use-token-metainfo'; const useApp = (): void => { const { wallet } = useWalletContext(); @@ -27,7 +27,7 @@ const useApp = (): void => { useEffect(() => { if (currentAccount && currentNetwork) { - initTokenMetainfos(); + initTokenMetainfos(true); } }, [currentAccount, currentNetwork]); diff --git a/packages/adena-extension/src/hooks/use-token-metainfo.tsx b/packages/adena-extension/src/hooks/use-token-metainfo.tsx index f4c7a401..ee04c112 100644 --- a/packages/adena-extension/src/hooks/use-token-metainfo.tsx +++ b/packages/adena-extension/src/hooks/use-token-metainfo.tsx @@ -1,16 +1,18 @@ import { useRecoilState } from 'recoil'; +import { isGRC20TokenModel, isNativeTokenModel } from '@common/validation/validation-token'; import { useAdenaContext } from './use-context'; import { useCurrentAccount } from './use-current-account'; -import { isGRC20TokenModel, isNativeTokenModel } from '@common/validation/validation-token'; import { useNetwork } from './use-network'; import { TokenState } from '@states'; import { GRC20TokenModel, TokenModel } from '@types'; import { Account } from 'adena-module'; -import { useCallback, useMemo } from 'react'; import BigNumber from 'bignumber.js'; +import { useCallback, useMemo } from 'react'; +import { useNFTCollectionHandler } from './nft/use-collection-handler'; import { useGRC20Tokens } from './use-grc20-tokens'; +import { useTransferTokens } from './wallet/use-transfer-tokens'; interface GRC20Token { tokenId: string; @@ -27,7 +29,7 @@ export type UseTokenMetainfoReturn = { currentTokenMetainfos: TokenModel[]; tokenLogoMap: Record; getTokenAmount: (amount: { value: string; denom: string }) => { value: string; denom: string }; - initTokenMetainfos: () => Promise; + initTokenMetainfos: (withTransferEvents?: boolean) => Promise; updateTokenMetainfos: (account: Account, tokenMetainfos: TokenModel[]) => Promise; addTokenMetainfo: (tokenMetainfo: GRC20TokenModel) => Promise; addGRC20TokenMetainfo: ({ @@ -37,6 +39,7 @@ export type UseTokenMetainfoReturn = { path, decimals, }: GRC20Token) => Promise; + addTokenMetainfos: (tokenMetainfos: TokenModel[]) => Promise; convertDenom: ( amount: string, denom: string, @@ -70,6 +73,8 @@ export const useTokenMetainfo = (): UseTokenMetainfoReturn => { const [tokenMetainfos, setTokenMetainfo] = useRecoilState(TokenState.tokenMetainfos); const { currentAccount } = useCurrentAccount(); const { currentNetwork } = useNetwork(); + const { fetchTransferTokens } = useTransferTokens(); + const { addCollections } = useNFTCollectionHandler(); const { data: grc20Tokens } = useGRC20Tokens(); const allTokenMetainfos = useMemo(() => { @@ -113,12 +118,44 @@ export const useTokenMetainfo = (): UseTokenMetainfoReturn => { }, {}); }, [currentTokenMetainfos]); - const initTokenMetainfos = async (): Promise => { - if (currentAccount) { - await tokenService.initAccountTokenMetainfos(currentAccount.id); - const tokenMetainfos = await tokenService.getTokenMetainfosByAccountId(currentAccount.id); - setTokenMetainfo([...tokenMetainfos]); + const initTokenMetainfos = async (withTransferEvents?: boolean): Promise => { + if (!currentAccount || !currentNetwork.apiUrl) { + return; } + + if (withTransferEvents) { + await setTokenMetainfo([]); + const currentAddress = await currentAccount.getAddress(currentNetwork.addressPrefix); + const transferTokens = await fetchTransferTokens(currentAddress).catch(() => null); + if (transferTokens) { + const storedGRC20Tokens = await tokenService.getTokenMetainfosByAccountId( + currentAccount.id, + ); + const storedCollections = await tokenService.getAccountGRC721Collections( + currentAccount.id, + currentNetwork.chainId, + ); + + const storedGRC20Packages = storedGRC20Tokens.map((grc20Token) => grc20Token.tokenId); + const storedGRC721Packages = storedCollections.map( + (grc721Token) => grc721Token.packagePath, + ); + + const filteredGRC20Packages = (transferTokens.grc20Packages || []).filter( + (grc20Token) => !storedGRC20Packages.includes(grc20Token.tokenId), + ); + const filteredGRC721Packages = (transferTokens.grc721Packages || []).filter( + (grc721Token) => !storedGRC721Packages.includes(grc721Token.packagePath), + ); + + await addTokenMetainfos(filteredGRC20Packages); + await addCollections(filteredGRC721Packages); + } + } + + await tokenService.initAccountTokenMetainfos(currentAccount.id); + const tokenMetainfos = await tokenService.getTokenMetainfosByAccountId(currentAccount.id); + setTokenMetainfo([...tokenMetainfos]); }; const updateTokenMetainfos = async ( @@ -183,7 +220,6 @@ export const useTokenMetainfo = (): UseTokenMetainfoReturn => { const getTokenImageByDenom = useCallback( (denom: string): string | null => { const key = makeTokenKeyByDenom(denom); - console.log(key, tokenLogoMap); return tokenLogoMap[key] || null; }, [tokenLogoMap], @@ -228,6 +264,39 @@ export const useTokenMetainfo = (): UseTokenMetainfoReturn => { return true; }; + const addTokenMetainfos = async (tokenMetainfos: TokenModel[]): Promise => { + if (!currentAccount) { + return false; + } + + if (tokenMetainfos.length === 0) { + return true; + } + + const currentTokenMetainfos = await tokenService.getTokenMetainfosByAccountId( + currentAccount.id, + ); + + const changedTokenMetainfos = tokenMetainfos + .filter( + (t1) => + !currentTokenMetainfos.find( + (t2) => t1.tokenId === t2.tokenId && t1.networkId === t2.networkId, + ), + ) + .map((tokenMetainfo) => ({ + ...tokenMetainfo, + main: false, + display: true, + image: getTokenImage(tokenMetainfo) ?? '', + })); + + await tokenService.updateTokenMetainfosByAccountId(currentAccount.id, [ + ...changedTokenMetainfos, + ]); + return true; + }; + const addGRC20TokenMetainfo = async ({ tokenId, name, @@ -258,6 +327,7 @@ export const useTokenMetainfo = (): UseTokenMetainfoReturn => { getTokenAmount, initTokenMetainfos, addTokenMetainfo, + addTokenMetainfos, addGRC20TokenMetainfo, convertDenom, getTokenImage,