From c4df4f39887f54ecb88c084fde49c9ee79f72638 Mon Sep 17 00:00:00 2001 From: Dmitriy Kudasov Date: Tue, 21 Jan 2025 10:10:58 +0300 Subject: [PATCH 1/6] feat(HW-1044): Fix mobx ts-ignores --- src/models/app/app.store.ts | 12 ++++-------- src/models/nft/index.ts | 4 ++-- src/models/nft/{nft-store.ts => nft.store.ts} | 0 src/models/nft/{nft-types.ts => nft.types.ts} | 0 4 files changed, 6 insertions(+), 10 deletions(-) rename src/models/nft/{nft-store.ts => nft.store.ts} (100%) rename src/models/nft/{nft-types.ts => nft.types.ts} (100%) diff --git a/src/models/app/app.store.ts b/src/models/app/app.store.ts index 7190e2718..8ec3ea38e 100644 --- a/src/models/app/app.store.ts +++ b/src/models/app/app.store.ts @@ -25,16 +25,12 @@ class AppStore { makePersistable(this, { name: this.constructor.name, properties: [ - // @ts-ignore - '_isOnboarded', + 'isOnboarded', 'isDeveloperModeEnabled', 'isTesterModeEnabled', - // @ts-ignore - '_networkLoggerEnabled', - // @ts-ignore - '_testnetsEnabledForAllNetworks', - // @ts-ignore - '_networkLogsCacheSize', + 'networkLoggerEnabled', + 'testnetsEnabledForAllNetworks', + 'networkLogsCacheSize', ], storage, }); diff --git a/src/models/nft/index.ts b/src/models/nft/index.ts index 6661e0ce8..19308dd91 100644 --- a/src/models/nft/index.ts +++ b/src/models/nft/index.ts @@ -1,2 +1,2 @@ -export * from './nft-store'; -export * from './nft-types'; +export * from './nft.store'; +export * from './nft.types'; diff --git a/src/models/nft/nft-store.ts b/src/models/nft/nft.store.ts similarity index 100% rename from src/models/nft/nft-store.ts rename to src/models/nft/nft.store.ts diff --git a/src/models/nft/nft-types.ts b/src/models/nft/nft.types.ts similarity index 100% rename from src/models/nft/nft-types.ts rename to src/models/nft/nft.types.ts From 837b47caac8c64b4f7ec9dd095c7c2210e68e08f Mon Sep 17 00:00:00 2001 From: Dmitriy Kudasov Date: Tue, 21 Jan 2025 14:46:40 +0300 Subject: [PATCH 2/6] feat(HW-1044): Contracts store --- package.json | 2 +- .../json-rpc-common-transaction.tsx | 17 +- .../modals/providers-bottom-sheet.tsx | 6 +- .../settings-providers-all-networks-row.tsx | 5 +- .../settings-providers-row.tsx | 5 +- .../settings-providers/settings-providers.tsx | 5 +- src/components/wallet-row.tsx | 3 +- src/components/wallet-selector.tsx | 3 +- .../web3-browser/web3-browser-header.tsx | 3 +- src/helpers/await-for-provider.ts | 4 +- src/helpers/await-for-wallet.ts | 4 +- src/helpers/get-rpc-provider.ts | 2 +- src/models/app/app.store.ts | 16 +- src/models/contract/contract.store.ts | 109 ++++++++++++ src/models/contract/contract.types.ts | 28 ++++ src/models/contract/index.ts | 2 + src/models/nft/nft.store.ts | 140 ++++++++-------- src/models/provider/provider.model.ts | 3 +- src/models/tokens.ts | 157 ++++++++---------- src/route-types.ts | 4 +- .../ProvidersStack/settings-providers.tsx | 3 +- .../TransactionStack/transaction-sum.tsx | 2 +- src/services/cosmos.ts | 2 +- src/services/eth-network/eth-network.ts | 10 +- src/services/indexer/indexer.ts | 4 +- src/services/indexer/indexer.types.ts | 3 +- .../json-rpc-methods-handlers.ts | 4 +- src/services/tron-network/tron-network.ts | 5 +- src/services/wallet-connect.ts | 4 +- src/types.ts | 10 +- yarn.lock | 18 +- 31 files changed, 360 insertions(+), 223 deletions(-) create mode 100644 src/models/contract/contract.store.ts create mode 100644 src/models/contract/contract.types.ts create mode 100644 src/models/contract/index.ts diff --git a/package.json b/package.json index 6c581402b..07d0c6af5 100644 --- a/package.json +++ b/package.json @@ -220,7 +220,7 @@ "rn-nodeify": "10.3.0", "ts-jest": "29.1.1", "ts-node": "10.9.1", - "typescript": "5.1.6", + "typescript": "5.7.3", "typescript-coverage-report": "0.8.0", "zx": "7.2.3" }, diff --git a/src/components/json-rpc-sign/json-rpc-common-transaction.tsx b/src/components/json-rpc-sign/json-rpc-common-transaction.tsx index 979525682..6af671cb3 100644 --- a/src/components/json-rpc-sign/json-rpc-common-transaction.tsx +++ b/src/components/json-rpc-sign/json-rpc-common-transaction.tsx @@ -20,12 +20,17 @@ import {createTheme} from '@app/helpers'; import {shortAddress} from '@app/helpers/short-address'; import {useEffectAsync} from '@app/hooks/use-effect-async'; import {I18N} from '@app/i18n'; +import {Contract} from '@app/models/contract'; import {Fee} from '@app/models/fee'; import {ProviderModel} from '@app/models/provider'; import {Token} from '@app/models/tokens'; import {Balance} from '@app/services/balance'; -import {Indexer} from '@app/services/indexer'; -import {IToken, JsonRpcMetadata, JsonRpcTransactionRequest} from '@app/types'; +import { + ChainId, + IToken, + JsonRpcMetadata, + JsonRpcTransactionRequest, +} from '@app/types'; import {getHostnameFromUrl, openInAppBrowser} from '@app/utils'; import {STRINGS} from '@app/variables/common'; @@ -41,7 +46,7 @@ export interface JsonRpcCommonTransactionProps { fee: Fee | null | undefined; tx: Partial | undefined; parsedInput: ethers.utils.TransactionDescription | undefined; - chainId: number; + chainId: ChainId; onFeePress: () => void; } @@ -151,10 +156,8 @@ export const JsonRpcCommonTransaction = ({ }, [provider, parsedInput, delegatorAddress]); useEffectAsync(async () => { - const resp = await Indexer.instance.getAddresses({ - [chainId]: [tx?.to!], - }); - const t = resp[chainId]?.[0] ?? Token.UNKNOWN_TOKEN; + const contract = await Contract.getById(tx?.to!, chainId); + const t = contract ?? Token.UNKNOWN_TOKEN; setToken(t as unknown as IToken); }, [tx, chainId]); diff --git a/src/components/modals/providers-bottom-sheet.tsx b/src/components/modals/providers-bottom-sheet.tsx index cd3708dab..9a2ae9b22 100644 --- a/src/components/modals/providers-bottom-sheet.tsx +++ b/src/components/modals/providers-bottom-sheet.tsx @@ -8,7 +8,7 @@ import {createTheme} from '@app/helpers'; import {useCalculatedDimensionsValue} from '@app/hooks/use-calculated-dimensions-value'; import {I18N} from '@app/i18n'; import {ALL_NETWORKS_ID, Provider} from '@app/models/provider'; -import {ModalType, Modals} from '@app/types'; +import {ChainId, ModalType, Modals} from '@app/types'; import {SettingsProvidersAllNetworksRow} from '../settings/settings-providers/settings-providers-all-networks-row'; import {SettingsProvidersRow} from '../settings/settings-providers/settings-providers-row'; @@ -24,7 +24,7 @@ export function ProvidersBottomSheet({ }: Modals[ModalType.providersBottomSheet]) { const [searchProviderValue, setSearchProviderValue] = useState(''); const bsRef = useRef(null); - const selectedProvider = useRef(0); + const selectedProvider = useRef(0); const providers = useMemo(() => { if (outProviders?.length) { @@ -43,7 +43,7 @@ export function ProvidersBottomSheet({ [closeDistance], ); const onPressProvider = useCallback( - async (providerChainId: number) => { + async (providerChainId: ChainId) => { if ( !!initialProviderChainId && providerChainId === initialProviderChainId diff --git a/src/components/settings/settings-providers/settings-providers-all-networks-row.tsx b/src/components/settings/settings-providers/settings-providers-all-networks-row.tsx index d0a3522f5..deab80b49 100644 --- a/src/components/settings/settings-providers/settings-providers-all-networks-row.tsx +++ b/src/components/settings/settings-providers/settings-providers-all-networks-row.tsx @@ -6,11 +6,12 @@ import {Color} from '@app/colors'; import {DataContent, Icon, IconsName, Spacer} from '@app/components/ui'; import {createTheme} from '@app/helpers'; import {ALL_NETWORKS_CHAIN_ID, ProviderModel} from '@app/models/provider'; +import {ChainId} from '@app/types'; export type SettingsProvidersAllNetworksRowProps = { item: ProviderModel; - providerChainId: number; - onPress: (providerChainId: number) => void; + providerChainId: ChainId; + onPress: (providerChainId: ChainId) => void; }; export const SettingsProvidersAllNetworksRow = ({ item, diff --git a/src/components/settings/settings-providers/settings-providers-row.tsx b/src/components/settings/settings-providers/settings-providers-row.tsx index dd907be6d..b72a19d1d 100644 --- a/src/components/settings/settings-providers/settings-providers-row.tsx +++ b/src/components/settings/settings-providers/settings-providers-row.tsx @@ -6,13 +6,14 @@ import {Color} from '@app/colors'; import {ImageWrapper} from '@app/components/image-wrapper'; import {DataContent, Icon, Spacer} from '@app/components/ui'; import {ALL_NETWORKS_ID, ProviderModel} from '@app/models/provider'; +import {ChainId} from '@app/types'; import {SettingsProvidersAllNetworksRow} from './settings-providers-all-networks-row'; export type SettingsProvidersRowProps = { item: ProviderModel; - providerChainId: number; - onPress: (providerChainId: number) => void; + providerChainId: ChainId; + onPress: (providerChainId: ChainId) => void; }; export const SettingsProvidersRow = ({ item, diff --git a/src/components/settings/settings-providers/settings-providers.tsx b/src/components/settings/settings-providers/settings-providers.tsx index 42077ccdf..40dbd0e9f 100644 --- a/src/components/settings/settings-providers/settings-providers.tsx +++ b/src/components/settings/settings-providers/settings-providers.tsx @@ -3,13 +3,14 @@ import React from 'react'; import {FlatList, StyleSheet} from 'react-native'; import {ProviderModel} from '@app/models/provider'; +import {ChainId} from '@app/types'; import {SettingsProvidersRow} from './settings-providers-row'; export type SettingsProvidersProps = { providers: ProviderModel[]; - providerChainId: number; - onSelect: (providerChainId: number) => void; + providerChainId: ChainId; + onSelect: (providerChainId: ChainId) => void; }; export const SettingsProviders = ({ diff --git a/src/components/wallet-row.tsx b/src/components/wallet-row.tsx index 7b510bdd0..252d8f3fa 100644 --- a/src/components/wallet-row.tsx +++ b/src/components/wallet-row.tsx @@ -4,6 +4,7 @@ import {StyleProp, ViewStyle} from 'react-native'; import {Provider} from '@app/models/provider'; import {WalletModel} from '@app/models/wallet'; +import {ChainId} from '@app/types'; import {WalletRowVariant1} from './wallet-row-variant-1'; import {WalletRowVariant2} from './wallet-row-variant-2'; @@ -28,7 +29,7 @@ export type WalletRowProps = { disabled?: boolean; type?: WalletRowTypes; hideBalance?: boolean; - chainId?: number; + chainId?: ChainId; }; export const WalletRow = ({ diff --git a/src/components/wallet-selector.tsx b/src/components/wallet-selector.tsx index 688419ebb..4c8ff8b09 100644 --- a/src/components/wallet-selector.tsx +++ b/src/components/wallet-selector.tsx @@ -6,12 +6,13 @@ import {PopupContainer} from '@app/components/ui'; import {WalletRow, WalletRowTypes} from '@app/components/wallet-row'; import {createTheme} from '@app/helpers'; import {WalletModel} from '@app/models/wallet'; +import {ChainId} from '@app/types'; interface Props { initialAddress?: string; wallets: WalletModel[]; style?: StyleProp; - chainId?: number; + chainId?: ChainId; onWalletSelected?(address: string): void; } diff --git a/src/components/web3-browser/web3-browser-header.tsx b/src/components/web3-browser/web3-browser-header.tsx index bc085f4bc..377ec0018 100644 --- a/src/components/web3-browser/web3-browser-header.tsx +++ b/src/components/web3-browser/web3-browser-header.tsx @@ -17,6 +17,7 @@ import {WebViewNavigation} from 'react-native-webview'; import {Color} from '@app/colors'; import {createTheme} from '@app/helpers'; import {Wallet} from '@app/models/wallet'; +import {ChainId} from '@app/types'; import {IS_ANDROID} from '@app/variables/common'; import {clearUrl} from '../../helpers/web3-browser-utils'; @@ -33,7 +34,7 @@ interface Web3BrowserHeaderProps { webviewNavigationData: WebViewNavigation; siteUrl: string; popup: boolean; - chainId: number; + chainId: ChainId; onPressMore(): void; diff --git a/src/helpers/await-for-provider.ts b/src/helpers/await-for-provider.ts index c49804669..60135fe7f 100644 --- a/src/helpers/await-for-provider.ts +++ b/src/helpers/await-for-provider.ts @@ -2,14 +2,14 @@ import {app} from '@app/contexts'; import {showModal} from '@app/helpers/modal'; import {I18N} from '@app/i18n'; import {ProviderModel} from '@app/models/provider'; -import {ModalType} from '@app/types'; +import {ChainId, ModalType} from '@app/types'; import {getWindowHeight} from './scaling-utils'; export interface AwaitProviderParams { title: I18N; providers?: ProviderModel[]; - initialProviderChainId?: number; + initialProviderChainId?: ChainId; disableAllNetworksOption?: boolean; } diff --git a/src/helpers/await-for-wallet.ts b/src/helpers/await-for-wallet.ts index beba7621a..d7f5a04c6 100644 --- a/src/helpers/await-for-wallet.ts +++ b/src/helpers/await-for-wallet.ts @@ -5,7 +5,7 @@ import {Provider} from '@app/models/provider'; import {Wallet, WalletModel} from '@app/models/wallet'; import {navigator} from '@app/navigator'; import {HomeStackRoutes} from '@app/route-types'; -import {Eventable} from '@app/types'; +import {ChainId, Eventable} from '@app/types'; import {getWindowHeight} from './scaling-utils'; @@ -21,7 +21,7 @@ export interface AwaitForWalletParams { initialAddress?: string; autoSelectWallet?: boolean; suggestedAddress?: string; - chainId?: number; + chainId?: ChainId; eventSuffix?: string | number; hideBalance?: boolean; } diff --git a/src/helpers/get-rpc-provider.ts b/src/helpers/get-rpc-provider.ts index a0ac6405c..6b3d4a9d6 100644 --- a/src/helpers/get-rpc-provider.ts +++ b/src/helpers/get-rpc-provider.ts @@ -7,7 +7,7 @@ import {EthRpcEndpointAvailability} from './eth-rpc-endpoint-availability'; export async function getRpcProvider(provider: ProviderModel) { await EthRpcEndpointAvailability.awaitForInitialization(); return new ethers.providers.StaticJsonRpcProvider(provider.ethRpcEndpoint, { - chainId: provider.ethChainId, + chainId: provider.ethChainId as number, name: provider.name, }); } diff --git a/src/models/app/app.store.ts b/src/models/app/app.store.ts index 8ec3ea38e..cdc66a9c4 100644 --- a/src/models/app/app.store.ts +++ b/src/models/app/app.store.ts @@ -13,10 +13,10 @@ class AppStore { private _isInitialized = false; // Hydrated properties - private _isOnboarded = false; - private _networkLoggerEnabled = false; - private _networkLogsCacheSize = 500; // count of stored http request - private _testnetsEnabledForAllNetworks = true; + _isOnboarded = false; + _networkLoggerEnabled = false; + _networkLogsCacheSize = 500; // count of stored http request + _testnetsEnabledForAllNetworks = true; isDeveloperModeEnabled = Config.IS_DEVELOPMENT === 'true'; isTesterModeEnabled = Config.IS_TESTMODE === 'true'; @@ -25,12 +25,12 @@ class AppStore { makePersistable(this, { name: this.constructor.name, properties: [ - 'isOnboarded', + '_isOnboarded', + '_networkLoggerEnabled', + '_testnetsEnabledForAllNetworks', + '_networkLogsCacheSize', 'isDeveloperModeEnabled', 'isTesterModeEnabled', - 'networkLoggerEnabled', - 'testnetsEnabledForAllNetworks', - 'networkLogsCacheSize', ], storage, }); diff --git a/src/models/contract/contract.store.ts b/src/models/contract/contract.store.ts new file mode 100644 index 000000000..f0027990f --- /dev/null +++ b/src/models/contract/contract.store.ts @@ -0,0 +1,109 @@ +import {makeAutoObservable, runInAction} from 'mobx'; +import {makePersistable} from 'mobx-persist-store'; + +import {Indexer, IndexerAddressesResponse} from '@app/services/indexer'; +import {storage} from '@app/services/mmkv'; +import {ChainId} from '@app/types'; + +import {ContractStoreData, IndexerContract} from './contract.types'; + +import {ALL_NETWORKS_ID, Provider} from '../provider'; + +class Contract { + _data: ContractStoreData = {}; + + constructor(shouldSkipPersisting: boolean = false) { + makeAutoObservable(this); + if (!shouldSkipPersisting) { + makePersistable(this, { + name: this.constructor.name, + properties: ['_data'], + storage, + }); + } + } + + /** + * Fetch contracts' information and store it into this._data + * + * @param contractAddresses Array of contract's addresses which shoul be fetched + * @param chainId Chain id in which contracts will be fetched. If no chainId provided then contracts will be fetched for all chains + */ + fetch = async (contractAddresses: string[], chainId?: ChainId) => { + let contracts: IndexerAddressesResponse | null = null; + + if (!chainId) { + const headers = Indexer.instance.getProvidersHeader( + contractAddresses, + Provider.getById(ALL_NETWORKS_ID), + ); + contracts = await Indexer.instance.getAddresses(headers); + } else { + contracts = await Indexer.instance.getAddresses({ + [chainId]: contractAddresses, + }); + } + + if (contracts) { + runInAction(() => { + this._data = Object.entries(contracts).reduce((acc, [cId, data]) => { + const reducedContracts = data.reduce( + (prev, contract) => ({ + ...prev, + [contract.eth_address]: contract, + }), + {} as Record, + ); + + return { + ...this._data, + ...acc, + [cId]: { + ...this._data[cId], + ...reducedContracts, + }, + }; + }, {} as ContractStoreData); + }); + } + }; + + get data() { + return this._data; + } + + /** + * Fetch contract information and update this._data with it + * + * @param contractAddress Contract's address which shoul be fetched + * @param chainId Chain id in which contracts will be fetched. If no chainId provided then contracts will be fetched for all chains + * @returns Contract for {contractAddress} + */ + getById = async ( + contractAddress: string, + chainId?: ChainId, + ): Promise => { + let contract: IndexerContract | null = null; + + if (!chainId) { + const headers = Indexer.instance.getProvidersHeader( + [contractAddress], + Provider.getById(ALL_NETWORKS_ID), + ); + const contracts = await Indexer.instance.getAddresses(headers); + const contractFlatMap = Object.entries(contracts).flatMap(([_, v]) => v); + contract = contractFlatMap?.find(t => t.name) ?? null; + } else { + contract = this._data[chainId][contractAddress] ?? null; + if (!contract) { + await this.fetch([contractAddress], chainId); + contract = this._data[chainId][contractAddress] ?? null; + } + } + + return contract; + }; +} + +const instance = new Contract(Boolean(process.env.JEST_WORKER_ID)); +export {instance as Contract}; diff --git a/src/models/contract/contract.types.ts b/src/models/contract/contract.types.ts new file mode 100644 index 000000000..4f64493f3 --- /dev/null +++ b/src/models/contract/contract.types.ts @@ -0,0 +1,28 @@ +import {AddressCosmosHaqq, AddressEthereum, ChainId} from '@app/types'; + +export type ContractStoreData = Record< + ChainId, + Record +>; + +export type IndexerContract = { + chain_id: ChainId; + coingecko_id: string | null; + created_at: string; + decimals: number | null; + eth_address: AddressEthereum; + ibc: null; + icon: string | null; + id: AddressCosmosHaqq; + is_coingecko_watch: boolean | null; + is_erc1155: boolean | null; + is_erc20: boolean | null; + is_erc721: boolean | null; + is_in_white_list: boolean | null; + is_skip_eth_tx: boolean | null; + is_transfer_prohibinden: boolean | null; + min_input_amount: number | null; + name: string | null; + symbol: string | null; + updated_at: string; +}; diff --git a/src/models/contract/index.ts b/src/models/contract/index.ts new file mode 100644 index 000000000..2d9887888 --- /dev/null +++ b/src/models/contract/index.ts @@ -0,0 +1,2 @@ +export * from './contract.store'; +export * from './contract.types'; diff --git a/src/models/nft/nft.store.ts b/src/models/nft/nft.store.ts index 046bf9e22..388fff175 100644 --- a/src/models/nft/nft.store.ts +++ b/src/models/nft/nft.store.ts @@ -10,9 +10,10 @@ import { import {Socket} from '@app/models/socket'; import {Wallet} from '@app/models/wallet'; import {Indexer} from '@app/services/indexer'; -import {AddressCosmosHaqq, IContract} from '@app/types'; +import {AddressCosmosHaqq} from '@app/types'; import {RPCMessage} from '@app/types/rpc'; +import {Contract, IndexerContract} from '../contract'; import {ALL_NETWORKS_ID, Provider} from '../provider'; class NftStore { @@ -106,43 +107,47 @@ class NftStore { ); } - update(item: NftItem) { - const itemToUpdate = this.getNftById(item.contract, item.tokenId); + update(item: NftItem | null) { + if (item) { + const itemToUpdate = this.getNftById(item.contract, item.tokenId); - const existingCollection = this.getCollectionById(item.contract); + const existingCollection = this.getCollectionById(item.contract); - if (!existingCollection) { - this.fetchNft(); - } else { - const existingNftIndex = - existingCollection?.nfts.findIndex( - nft => nft.tokenId === itemToUpdate?.tokenId, - ) ?? -1; - - const newItem = { - ...(itemToUpdate ?? {}), - ...item, - }; + if (!existingCollection) { + this.fetchNft(); + } else { + const existingNftIndex = + existingCollection?.nfts.findIndex( + nft => nft.tokenId === itemToUpdate?.tokenId, + ) ?? -1; - const nfts = - existingNftIndex !== -1 - ? (existingCollection?.nfts ?? []).splice( - existingNftIndex, - 1, - newItem, - ) - : [newItem]; + const newItem = { + ...(itemToUpdate ?? {}), + ...item, + }; - this.data = { - ...this.data, - [item.contract]: { - ...(existingCollection ?? {}), - nfts, - }, - }; + const nfts = + existingNftIndex !== -1 + ? (existingCollection?.nfts ?? []).splice( + existingNftIndex, + 1, + newItem, + ) + : [newItem]; + + this.data = { + ...this.data, + [item.contract]: { + ...(existingCollection ?? {}), + nfts, + }, + }; + } + + return true; } - return true; + return false; } fetchNft = async () => { @@ -158,20 +163,24 @@ class NftStore { data.forEach(async item => { const contract = await this.getContract(item.address); - const contractType = contract.is_erc721 - ? ContractType.erc721 - : ContractType.erc1155; - - runInAction(() => { - this.data[item.id] = { - ...item, - description: item.description || '', - created_at: Date.now(), - contractType: contractType, - is_transfer_prohibinden: Boolean(contract.is_transfer_prohibinden), - nfts: item.nfts.map(nft => this.parseIndexerNft(nft, contract)), - }; - }); + if (contract) { + const contractType = contract.is_erc721 + ? ContractType.erc721 + : ContractType.erc1155; + + runInAction(() => { + this.data[item.id] = { + ...item, + description: item.description || '', + created_at: Date.now(), + contractType: contractType, + is_transfer_prohibinden: Boolean(contract.is_transfer_prohibinden), + nfts: item.nfts + .map(nft => this.parseIndexerNft(nft, contract)) + .filter(i => i !== null), + }; + }); + } }); }; @@ -182,29 +191,30 @@ class NftStore { : Provider.selectedProvider ).ethChainId; - const contracts = await Indexer.instance.getAddresses({ - [_providerEthChainId]: [contractAddress], - }); - return contracts[_providerEthChainId][0]; + return await Contract.getById(contractAddress, _providerEthChainId); }; private readonly parseIndexerNft = ( data: NftItemIndexer, - contract: IContract, - ): NftItem => { - const contractType = contract.is_erc721 - ? ContractType.erc721 - : ContractType.erc1155; - - return { - ...data, - contractType: contractType, - name: data.name || 'Unknown', - description: data.description || '-', - tokenId: Number(data.token_id), - price: undefined, // FIXME Calculate price by token - is_transfer_prohibinden: Boolean(contract.is_transfer_prohibinden), - }; + contract: IndexerContract | null, + ): NftItem | null => { + if (contract) { + const contractType = contract.is_erc721 + ? ContractType.erc721 + : ContractType.erc1155; + + return { + ...data, + contractType: contractType, + name: data.name || 'Unknown', + description: data.description || '-', + tokenId: Number(data.token_id), + price: undefined, // FIXME Calculate price by token + is_transfer_prohibinden: Boolean(contract.is_transfer_prohibinden), + }; + } + + return null; }; onMessage = async (message: RPCMessage) => { diff --git a/src/models/provider/provider.model.ts b/src/models/provider/provider.model.ts index 5438901d1..f7e382367 100644 --- a/src/models/provider/provider.model.ts +++ b/src/models/provider/provider.model.ts @@ -5,6 +5,7 @@ import { NetworkProviderStage, NetworkProviderTypes, } from '@app/services/backend'; +import {ChainId} from '@app/types'; import {RemoteProviderConfig} from './provider-config'; @@ -52,7 +53,7 @@ export class ProviderModel { return this.model.entry_point; } - get ethChainId() { + get ethChainId(): ChainId { return this.model.chain_id; } diff --git a/src/models/tokens.ts b/src/models/tokens.ts index 77dc336ac..69f5bb974 100644 --- a/src/models/tokens.ts +++ b/src/models/tokens.ts @@ -5,7 +5,7 @@ import {AddressUtils, NATIVE_TOKEN_ADDRESS} from '@app/helpers/address-utils'; import {Socket} from '@app/models/socket'; import {IWalletModel, Wallet} from '@app/models/wallet'; import {Balance} from '@app/services/balance'; -import {Indexer, IndexerAddressesResponse} from '@app/services/indexer'; +import {Indexer} from '@app/services/indexer'; import {storage} from '@app/services/mmkv'; import { AddressEthereum, @@ -20,6 +20,7 @@ import { import {RPCMessage} from '@app/types/rpc'; import {createAsyncTask} from '@app/utils'; +import {Contract} from './contract'; import {Provider, ProviderModel} from './provider'; const logger = Logger.create('TokensStore', { @@ -240,38 +241,28 @@ class TokensStore implements MobXStore { */ private _safeLoadUnknownToken = createAsyncTask(async (id: string) => { try { - if (this.fetchedUnknownTokens[id]) { - return; - } - - const headers = Indexer.instance.getProvidersHeader([id]); - const contracts = await Indexer.instance.getAddresses(headers); - const tokensForAnyChains = Object.entries(contracts).flatMap( - ([chain_id, v]) => v.map(c => ({...c, chain_id: Number(chain_id)})), - ) as (IContract & {chain_id: number})[]; - - runInAction(() => { - this.fetchedUnknownTokens[id] = true; - }); - - // find token with name, symbol, decimals and is_erc20 - const token = tokensForAnyChains?.find( - t => t.name && (t.is_in_white_list || t.is_erc20), - ); - - if (token) { + if (!this.fetchedUnknownTokens[id]) { runInAction(() => { - this.data[AddressUtils.toEth(token.id)] = { - ...token, - contract_created_at: token.created_at, - contract_updated_at: token.updated_at, - value: new Balance('0x0', token.decimals!, token.symbol!), - chain_id: token.chain_id!, - image: token.icon - ? {uri: token.icon} - : require('@assets/images/empty-icon.png'), - }; + this.fetchedUnknownTokens[id] = true; }); + + // find token with name, symbol, decimals and is_erc20 + const contract = await Contract.getById(id); + + if (contract) { + runInAction(() => { + this.data[AddressUtils.toEth(contract.id)] = { + ...contract, + contract_created_at: contract.created_at, + contract_updated_at: contract.updated_at, + value: new Balance('0x0', contract.decimals!, contract.symbol!), + chain_id: contract.chain_id!, + image: contract.icon + ? {uri: contract.icon} + : require('@assets/images/empty-icon.png'), + }; + }); + } } } catch (e) { Logger.error('TokensStore: _safeLoadUnknownToken: error', { @@ -331,19 +322,22 @@ class TokensStore implements MobXStore { .filter(p => p.isTron) .map(p => p.ethChainId as ChainId); - const contracts = await Indexer.instance.getAddresses( - updates.tokens.reduce( - (prev, cur) => { - const tokens = Array.from( - new Set([...(prev[cur.chain_id] || []), cur.contract]), - ); - return { - ...prev, - [cur.chain_id]: tokens, - }; - }, - {} as Record, - ), + const contractMap = updates.tokens.reduce( + (prev, cur) => { + const tokens = Array.from( + new Set([...(prev[cur.chain_id] || []), cur.contract]), + ); + return { + ...prev, + [cur.chain_id]: tokens, + }; + }, + {} as Record, + ); + await Promise.allSettled( + Object.entries(contractMap).map(([chainId, contracts]) => { + Contract.fetch(contracts, chainId); + }), ); for await (const t of updates.tokens) { @@ -353,7 +347,7 @@ class TokensStore implements MobXStore { continue; } - const token = await this.parseIToken(t, contracts); + const token = await this.parseIToken(t); if (!token) { logger.error('fetchTokens: skipping token', { @@ -476,46 +470,35 @@ class TokensStore implements MobXStore { }; }; - private parseIToken = async ( - token: IndexerToken, - contracts: IndexerAddressesResponse, - ) => { - const contratsForChainId = contracts[token.chain_id] || []; - const contract = contratsForChainId.find(c => c.id === token.contract); - - if (!contract) { - logger.error('parseIToken: Contract not found', { - token, - contratsForChainId, - }); - return null; + private parseIToken = async (token: IndexerToken): Promise => { + const contract = await Contract.getById(token.contract, token.chain_id); + if (contract) { + return { + id: AddressUtils.toHaqq(contract.id), + contract_created_at: contract.created_at, + contract_updated_at: contract.updated_at, + value: new Balance( + token.value, + contract.decimals || Provider.selectedProvider.decimals, + contract.symbol || Provider.selectedProvider.denom, + ), + decimals: contract.decimals, + is_erc20: contract.is_erc20, + is_erc721: contract.is_erc721, + is_erc1155: contract.is_erc1155, + is_in_white_list: contract.is_in_white_list, + name: contract.name, + symbol: contract.symbol, + created_at: token.created_at, + updated_at: token.updated_at, + chain_id: token.chain_id, + image: contract.icon + ? {uri: contract.icon} + : require('@assets/images/empty-icon.png'), + } as IToken; } - const result: IToken = { - id: AddressUtils.toHaqq(contract.id), - contract_created_at: contract.created_at, - contract_updated_at: contract.updated_at, - value: new Balance( - token.value, - contract.decimals || Provider.selectedProvider.decimals, - contract.symbol || Provider.selectedProvider.denom, - ), - decimals: contract.decimals, - is_erc20: contract.is_erc20, - is_erc721: contract.is_erc721, - is_erc1155: contract.is_erc1155, - is_in_white_list: contract.is_in_white_list, - name: contract.name, - symbol: contract.symbol, - created_at: token.created_at, - updated_at: token.updated_at, - chain_id: token.chain_id, - image: contract.icon - ? {uri: contract.icon} - : require('@assets/images/empty-icon.png'), - }; - - return result; + return null; }; onMessage = async (message: RPCMessage) => { @@ -524,13 +507,7 @@ class TokensStore implements MobXStore { } const tokenData = message.data; - const contracts = await Indexer.instance.getAddresses({ - [tokenData.chain_id || Provider.selectedProvider.ethChainId]: [ - tokenData.contract, - ], - }); - - const token = await this.parseIToken(tokenData, contracts); + const token = await this.parseIToken(tokenData); if (token) { this.update(token.id, token); } diff --git a/src/route-types.ts b/src/route-types.ts index 11174a5b9..23c67c9b1 100644 --- a/src/route-types.ts +++ b/src/route-types.ts @@ -317,7 +317,7 @@ export enum ProvidersStackRoutes { export type ProvidersStackParamList = { [ProvidersStackRoutes.SettingsProviders]: undefined; [ProvidersStackRoutes.SettingsProviderForm]: { - id?: number; + id?: ChainId; data?: Partial; }; }; @@ -455,7 +455,7 @@ export type HomeStackParamList = { wallets: WalletModel[]; title: string; initialAddress?: string; - chainId?: number; + chainId?: ChainId; }; [HomeStackRoutes.TotalValueInfo]?: { tab?: TotalValueTabNames; diff --git a/src/screens/HomeStack/ProvidersStack/settings-providers.tsx b/src/screens/HomeStack/ProvidersStack/settings-providers.tsx index 2fd6dc3c3..4c810faba 100644 --- a/src/screens/HomeStack/ProvidersStack/settings-providers.tsx +++ b/src/screens/HomeStack/ProvidersStack/settings-providers.tsx @@ -10,13 +10,14 @@ import {I18N} from '@app/i18n'; import {AppStore} from '@app/models/app'; import {Provider} from '@app/models/provider'; import {ProvidersStackParamList, ProvidersStackRoutes} from '@app/route-types'; +import {ChainId} from '@app/types'; export const SettingsProvidersScreen = observer(() => { const navigation = useTypedNavigation(); const providers = Provider.getAll(); const onSelectProvider = useCallback( - (chainId: number) => { + (chainId: ChainId) => { navigation.navigate(ProvidersStackRoutes.SettingsProviderForm, { id: chainId, }); diff --git a/src/screens/HomeStack/TransactionStack/transaction-sum.tsx b/src/screens/HomeStack/TransactionStack/transaction-sum.tsx index e2fbb434d..3df4b4bf6 100644 --- a/src/screens/HomeStack/TransactionStack/transaction-sum.tsx +++ b/src/screens/HomeStack/TransactionStack/transaction-sum.tsx @@ -145,7 +145,7 @@ export const TransactionSumScreen = observer(() => { if (provider.isTron) { // fee can be zero for TRON if user has enough bandwidth (freezed TRX) - successCondition = !!estimate?.expectedFee ?? false; + successCondition = !!estimate?.expectedFee; } else { successCondition = estimate?.expectedFee.isPositive() ?? false; } diff --git a/src/services/cosmos.ts b/src/services/cosmos.ts index c4e30718d..cfa7eac1b 100644 --- a/src/services/cosmos.ts +++ b/src/services/cosmos.ts @@ -99,7 +99,7 @@ export class Cosmos { get haqqChain(): Chain { return { - chainId: this._provider.ethChainId, + chainId: this._provider.ethChainId as number, cosmosChainId: this._provider.cosmosChainId, }; } diff --git a/src/services/eth-network/eth-network.ts b/src/services/eth-network/eth-network.ts index b66ef6fce..39db585d7 100644 --- a/src/services/eth-network/eth-network.ts +++ b/src/services/eth-network/eth-network.ts @@ -1,6 +1,5 @@ -import {TransactionRequest} from '@ethersproject/abstract-provider'; import {Deferrable} from '@ethersproject/properties'; -import {ProviderInterface} from '@haqq/rn-wallet-providers'; +import {ProviderInterface, TransactionRequest} from '@haqq/rn-wallet-providers'; import Decimal from 'decimal.js'; import {BigNumber, utils} from 'ethers'; @@ -14,6 +13,7 @@ import {Balance} from '@app/services/balance'; import {getERC1155TransferData} from '@app/services/eth-network/erc1155'; import {getERC721TransferData} from '@app/services/eth-network/erc721'; import {storage} from '@app/services/mmkv'; +import {ChainId} from '@app/types'; import {getERC20TransferData} from './erc20'; import { @@ -26,7 +26,7 @@ import { import {TronNetwork} from '../tron-network'; export class EthNetwork { - static chainId: number = getDefaultChainId(); + static chainId: ChainId = getDefaultChainId(); static explorer: string | undefined; static init(provider: ProviderModel) { @@ -38,7 +38,7 @@ export class EthNetwork { estimate: CalculatedFees, {from, to, value = Balance.Empty, data = '0x'}: TxEstimationParams, provider = Provider.selectedProvider, - ) { + ): Promise { try { if (!AddressUtils.isEthAddress(to)) { throw new Error('Invalid "from" address'); @@ -68,7 +68,7 @@ export class EthNetwork { const tx = await utils.resolveProperties(transaction); return { - chainId: tx.chainId || undefined, + chainId: (tx.chainId as number) || undefined, data: tx.data || undefined, gasLimit: tx.gasLimit || undefined, type: tx.type, diff --git a/src/services/indexer/indexer.ts b/src/services/indexer/indexer.ts index 743c8ee96..179649cd1 100644 --- a/src/services/indexer/indexer.ts +++ b/src/services/indexer/indexer.ts @@ -129,14 +129,14 @@ export class Indexer { } }); - getAddresses = async (accounts: string[] | Record) => { + getAddresses = async (accountsMap: Record) => { try { this.checkIndexerAvailability(); return await jsonrpcRequest( RemoteConfig.get('proxy_server')!, 'addresses', - [accounts], + [accountsMap], ); } catch (err) { if (err instanceof JSONRPCError) { diff --git a/src/services/indexer/indexer.types.ts b/src/services/indexer/indexer.types.ts index 2ee43c931..7b27afc52 100644 --- a/src/services/indexer/indexer.types.ts +++ b/src/services/indexer/indexer.types.ts @@ -1,3 +1,4 @@ +import {IndexerContract} from '@app/models/contract'; import { AddressCosmosHaqq, ChainId, @@ -29,7 +30,7 @@ export type IndexerUpdatesResponse = { rates: RatesResponse; }; -export type IndexerAddressesResponse = Record; +export type IndexerAddressesResponse = Record; export type SushiRoute = { fee: number; diff --git a/src/services/json-rpc-middleware/json-rpc-methods-handlers.ts b/src/services/json-rpc-middleware/json-rpc-methods-handlers.ts index b5140f75c..8df5ac495 100644 --- a/src/services/json-rpc-middleware/json-rpc-methods-handlers.ts +++ b/src/services/json-rpc-middleware/json-rpc-methods-handlers.ts @@ -30,7 +30,7 @@ import {Wallet} from '@app/models/wallet'; import {Web3BrowserSession} from '@app/models/web3-browser-session'; import {getDefaultNetwork} from '@app/network'; import {getAppVersion} from '@app/services/version'; -import {AddressCosmosHaqq, WalletType} from '@app/types'; +import {AddressCosmosHaqq, ChainId, WalletType} from '@app/types'; import {makeID, requestQRScannerPermission} from '@app/utils'; import {MAIN_NETWORK_ID} from '@app/variables/common'; @@ -98,7 +98,7 @@ const requestAccount = async ({helper}: JsonRpcMethodHandlerParams) => { ? session.selectedAccount : undefined; - let chainId = 1; + let chainId: ChainId = 1; if (Provider.selectedProvider.isEVM) { chainId = Provider.selectedProvider.ethChainId; diff --git a/src/services/tron-network/tron-network.ts b/src/services/tron-network/tron-network.ts index 3d0bc6825..e8f54cc13 100644 --- a/src/services/tron-network/tron-network.ts +++ b/src/services/tron-network/tron-network.ts @@ -221,9 +221,8 @@ export class TronNetwork { const resources = await tronWeb.trx.getAccountResources(tronAddress); const freeNetRemaining = - (resources.freeNetLimit ?? 0) - (resources.freeNetUsed ?? 0) ?? 0; - const netRemaining = - (resources.NetLimit ?? 0) - (resources.NetUsed ?? 0) ?? 0; + (resources.freeNetLimit ?? 0) - (resources.freeNetUsed ?? 0); + const netRemaining = (resources.NetLimit ?? 0) - (resources.NetUsed ?? 0); return { freeNetUsed: resources.freeNetUsed || 0, diff --git a/src/services/wallet-connect.ts b/src/services/wallet-connect.ts index c56b2f404..13736e1a1 100644 --- a/src/services/wallet-connect.ts +++ b/src/services/wallet-connect.ts @@ -22,7 +22,7 @@ import {VariablesBool} from '@app/models/variables-bool'; import {VariablesString} from '@app/models/variables-string'; import {WalletConnectSessionMetadata} from '@app/models/wallet-connect-session-metadata'; import {message as sendMessage, sendNotification} from '@app/services/toast'; -import {ModalType} from '@app/types'; +import {ChainId, ModalType} from '@app/types'; import {filterWalletConnectSessionsByAddress, isError, sleep} from '@app/utils'; import {AppUtils} from './app-utils'; @@ -176,7 +176,7 @@ export class WalletConnect extends Initializable { } } - public async emitChainChange(chainId: number | `${number}`, topic: string) { + public async emitChainChange(chainId: ChainId, topic: string) { try { await this._client?.emitSessionEvent({ topic, diff --git a/src/types.ts b/src/types.ts index 50aa0737a..631062f45 100644 --- a/src/types.ts +++ b/src/types.ts @@ -572,7 +572,7 @@ export type RootStackParamList = { wallets: IWalletModel[]; title: string; initialAddress?: string; - chainId?: number; + chainId?: ChainId; }; valueSelector: { title: string; @@ -607,7 +607,7 @@ export type RootStackParamList = { jsonRpcSign: { request: PartialJsonRpcRequest; metadata: JsonRpcMetadata; - chainId?: number; + chainId?: ChainId; selectedAccount?: string; hideContractAttention?: boolean; }; @@ -1098,7 +1098,7 @@ export type Modals = { autoSelectWallet?: boolean; initialAddress?: string; hideBalance?: boolean; - chainId?: number; + chainId?: ChainId; }; transactionError: { onClose?: () => void; @@ -1111,7 +1111,7 @@ export type Modals = { onClose?: () => void; title: I18N; providers?: ProviderModel[]; - initialProviderChainId: number; + initialProviderChainId: ChainId; disableAllNetworksOption?: boolean; closeDistance?: () => number; eventSuffix?: string; @@ -1574,7 +1574,7 @@ export type IToken = { symbol: IContract['symbol']; created_at: string; updated_at: string; - chain_id: number; + chain_id: ChainId; image: ImageSourcePropType; }; diff --git a/yarn.lock b/yarn.lock index e79f8cf8d..5dedae2c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11473,7 +11473,7 @@ __metadata: text-encoding: 0.7.0 ts-jest: 29.1.1 ts-node: 10.9.1 - typescript: 5.1.6 + typescript: 5.7.3 typescript-coverage-report: 0.8.0 url: 0.11.1 url-parse: 1.5.10 @@ -19140,23 +19140,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:5.1.6": - version: 5.1.6 - resolution: "typescript@npm:5.1.6" +"typescript@npm:5.7.3": + version: 5.7.3 + resolution: "typescript@npm:5.7.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: b2f2c35096035fe1f5facd1e38922ccb8558996331405eb00a5111cc948b2e733163cc22fab5db46992aba7dd520fff637f2c1df4996ff0e134e77d3249a7350 + checksum: 6c38b1e989918e576f0307e6ee013522ea480dfce5f3ca85c9b2d8adb1edeffd37f4f30cd68de0c38a44563d12ba922bdb7e36aa2dac9c51de5d561e6e9a2e9c languageName: node linkType: hard -"typescript@patch:typescript@5.1.6#~builtin": - version: 5.1.6 - resolution: "typescript@patch:typescript@npm%3A5.1.6#~builtin::version=5.1.6&hash=5da071" +"typescript@patch:typescript@5.7.3#~builtin": + version: 5.7.3 + resolution: "typescript@patch:typescript@npm%3A5.7.3#~builtin::version=5.7.3&hash=5da071" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: f53bfe97f7c8b2b6d23cf572750d4e7d1e0c5fff1c36d859d0ec84556a827b8785077bc27676bf7e71fae538e517c3ecc0f37e7f593be913d884805d931bc8be + checksum: 633cd749d6cd7bc842c6b6245847173bba99742a60776fae3c0fbcc0d1733cd51a733995e5f4dadd8afb0e64e57d3c7dbbeae953a072ee303940eca69e22f311 languageName: node linkType: hard From f956c251fe34f9284e9eaea7b0147f9b66903636 Mon Sep 17 00:00:00 2001 From: Dmitriy Kudasov Date: Tue, 21 Jan 2025 14:56:30 +0300 Subject: [PATCH 3/6] feat(HW-1044): Store contracts by id --- src/models/contract/contract.store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/contract/contract.store.ts b/src/models/contract/contract.store.ts index f0027990f..cfe6f165c 100644 --- a/src/models/contract/contract.store.ts +++ b/src/models/contract/contract.store.ts @@ -50,7 +50,7 @@ class Contract { const reducedContracts = data.reduce( (prev, contract) => ({ ...prev, - [contract.eth_address]: contract, + [contract.id]: contract, }), {} as Record, ); From cbf7c50094252271300663d61ef0a28f6ffae2e5 Mon Sep 17 00:00:00 2001 From: Dmitriy Kudasov Date: Tue, 21 Jan 2025 15:03:38 +0300 Subject: [PATCH 4/6] feat(HW-1044): Check fetched contracts --- src/models/contract/contract.store.ts | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/models/contract/contract.store.ts b/src/models/contract/contract.store.ts index cfe6f165c..1190a0fa9 100644 --- a/src/models/contract/contract.store.ts +++ b/src/models/contract/contract.store.ts @@ -68,10 +68,6 @@ class Contract { } }; - get data() { - return this._data; - } - /** * Fetch contract information and update this._data with it * @@ -86,13 +82,24 @@ class Contract { let contract: IndexerContract | null = null; if (!chainId) { - const headers = Indexer.instance.getProvidersHeader( - [contractAddress], - Provider.getById(ALL_NETWORKS_ID), + // Check already fetched contracts + const fetchedContractFlatMap = Object.entries(this._data).flatMap( + ([_, v]) => Object.entries(v).flatMap(([__, c]) => c), ); - const contracts = await Indexer.instance.getAddresses(headers); - const contractFlatMap = Object.entries(contracts).flatMap(([_, v]) => v); - contract = contractFlatMap?.find(t => t.name) ?? null; + contract = fetchedContractFlatMap?.find(t => t.name) ?? null; + + // If fetched contract doesn't exists than fetch and find contract from all chains + if (!contract) { + const headers = Indexer.instance.getProvidersHeader( + [contractAddress], + Provider.getById(ALL_NETWORKS_ID), + ); + const contracts = await Indexer.instance.getAddresses(headers); + const contractFlatMap = Object.entries(contracts).flatMap( + ([_, v]) => v, + ); + contract = contractFlatMap?.find(t => t.name) ?? null; + } } else { contract = this._data[chainId][contractAddress] ?? null; if (!contract) { From d1886670a5cd944d92ed52798b886ba5e643a79c Mon Sep 17 00:00:00 2001 From: Dmitriy Kudasov Date: Tue, 21 Jan 2025 16:39:42 +0300 Subject: [PATCH 5/6] feat(HW-1044): _searchContract --- src/models/contract/contract.store.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/models/contract/contract.store.ts b/src/models/contract/contract.store.ts index 1190a0fa9..973275dd8 100644 --- a/src/models/contract/contract.store.ts +++ b/src/models/contract/contract.store.ts @@ -1,6 +1,7 @@ import {makeAutoObservable, runInAction} from 'mobx'; import {makePersistable} from 'mobx-persist-store'; +import {AddressUtils} from '@app/helpers/address-utils'; import {Indexer, IndexerAddressesResponse} from '@app/services/indexer'; import {storage} from '@app/services/mmkv'; import {ChainId} from '@app/types'; @@ -12,6 +13,17 @@ import {ALL_NETWORKS_ID, Provider} from '../provider'; class Contract { _data: ContractStoreData = {}; + private _searchContract = (contractAddress: string) => { + const fetchedContractFlatMap = Object.entries(this._data).flatMap( + ([_, v]) => Object.entries(v).flatMap(([__, c]) => c), + ); + return ( + fetchedContractFlatMap?.find(t => + AddressUtils.equals(t.id, contractAddress), + ) ?? null + ); + }; + constructor(shouldSkipPersisting: boolean = false) { makeAutoObservable(this); if (!shouldSkipPersisting) { @@ -83,10 +95,7 @@ class Contract { if (!chainId) { // Check already fetched contracts - const fetchedContractFlatMap = Object.entries(this._data).flatMap( - ([_, v]) => Object.entries(v).flatMap(([__, c]) => c), - ); - contract = fetchedContractFlatMap?.find(t => t.name) ?? null; + contract = this._searchContract(contractAddress); // If fetched contract doesn't exists than fetch and find contract from all chains if (!contract) { From 5cb2ca14929e03de6828b53fa917ccacd8ab5887 Mon Sep 17 00:00:00 2001 From: Dmitriy Kudasov Date: Wed, 22 Jan 2025 10:44:38 +0300 Subject: [PATCH 6/6] feat(HW-1044): Fix contract search function by using haqq address --- src/models/contract/contract.store.ts | 7 ++++--- src/models/nft/nft.store.ts | 19 ++++++------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/models/contract/contract.store.ts b/src/models/contract/contract.store.ts index 973275dd8..ef6a23c72 100644 --- a/src/models/contract/contract.store.ts +++ b/src/models/contract/contract.store.ts @@ -92,10 +92,11 @@ class Contract { chainId?: ChainId, ): Promise => { let contract: IndexerContract | null = null; + const contractId = AddressUtils.toHaqq(contractAddress); if (!chainId) { // Check already fetched contracts - contract = this._searchContract(contractAddress); + contract = this._searchContract(contractId); // If fetched contract doesn't exists than fetch and find contract from all chains if (!contract) { @@ -110,10 +111,10 @@ class Contract { contract = contractFlatMap?.find(t => t.name) ?? null; } } else { - contract = this._data[chainId][contractAddress] ?? null; + contract = this._data[chainId][contractId] ?? null; if (!contract) { await this.fetch([contractAddress], chainId); - contract = this._data[chainId][contractAddress] ?? null; + contract = this._data[chainId][contractId] ?? null; } } diff --git a/src/models/nft/nft.store.ts b/src/models/nft/nft.store.ts index 388fff175..36e88d27b 100644 --- a/src/models/nft/nft.store.ts +++ b/src/models/nft/nft.store.ts @@ -14,7 +14,6 @@ import {AddressCosmosHaqq} from '@app/types'; import {RPCMessage} from '@app/types/rpc'; import {Contract, IndexerContract} from '../contract'; -import {ALL_NETWORKS_ID, Provider} from '../provider'; class NftStore { data: Record = {}; @@ -162,7 +161,8 @@ class NftStore { this.data = {}; data.forEach(async item => { - const contract = await this.getContract(item.address); + const contract = await Contract.getById(item.address, item.chain_id); + if (contract) { const contractType = contract.is_erc721 ? ContractType.erc721 @@ -184,16 +184,6 @@ class NftStore { }); }; - private readonly getContract = async (contractAddress: string) => { - const _providerEthChainId = ( - Provider.selectedProviderId === ALL_NETWORKS_ID - ? Provider.defaultProvider - : Provider.selectedProvider - ).ethChainId; - - return await Contract.getById(contractAddress, _providerEthChainId); - }; - private readonly parseIndexerNft = ( data: NftItemIndexer, contract: IndexerContract | null, @@ -222,7 +212,10 @@ class NftStore { return; } - const contract = await this.getContract(message.data.contract); + const contract = await Contract.getById( + message.data.contract, + message.data.chain_id, + ); this.update(this.parseIndexerNft(message.data, contract)); };