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

feat(HW-1044): Contracts store refactoring #2260

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
17 changes: 10 additions & 7 deletions src/components/json-rpc-sign/json-rpc-common-transaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -41,7 +46,7 @@ export interface JsonRpcCommonTransactionProps {
fee: Fee | null | undefined;
tx: Partial<JsonRpcTransactionRequest> | undefined;
parsedInput: ethers.utils.TransactionDescription | undefined;
chainId: number;
chainId: ChainId;
onFeePress: () => void;
}

Expand Down Expand Up @@ -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]);

Expand Down
6 changes: 3 additions & 3 deletions src/components/modals/providers-bottom-sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -24,7 +24,7 @@ export function ProvidersBottomSheet({
}: Modals[ModalType.providersBottomSheet]) {
const [searchProviderValue, setSearchProviderValue] = useState('');
const bsRef = useRef<BottomSheetRef>(null);
const selectedProvider = useRef(0);
const selectedProvider = useRef<ChainId>(0);

const providers = useMemo(() => {
if (outProviders?.length) {
Expand All @@ -43,7 +43,7 @@ export function ProvidersBottomSheet({
[closeDistance],
);
const onPressProvider = useCallback(
async (providerChainId: number) => {
async (providerChainId: ChainId) => {
if (
!!initialProviderChainId &&
providerChainId === initialProviderChainId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ({
Expand Down
3 changes: 2 additions & 1 deletion src/components/wallet-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -28,7 +29,7 @@ export type WalletRowProps = {
disabled?: boolean;
type?: WalletRowTypes;
hideBalance?: boolean;
chainId?: number;
chainId?: ChainId;
};

export const WalletRow = ({
Expand Down
3 changes: 2 additions & 1 deletion src/components/wallet-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<ViewStyle>;
chainId?: number;
chainId?: ChainId;
onWalletSelected?(address: string): void;
}

Expand Down
3 changes: 2 additions & 1 deletion src/components/web3-browser/web3-browser-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -33,7 +34,7 @@ interface Web3BrowserHeaderProps {
webviewNavigationData: WebViewNavigation;
siteUrl: string;
popup: boolean;
chainId: number;
chainId: ChainId;

onPressMore(): void;

Expand Down
4 changes: 2 additions & 2 deletions src/helpers/await-for-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
4 changes: 2 additions & 2 deletions src/helpers/await-for-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -21,7 +21,7 @@ export interface AwaitForWalletParams {
initialAddress?: string;
autoSelectWallet?: boolean;
suggestedAddress?: string;
chainId?: number;
chainId?: ChainId;
eventSuffix?: string | number;
hideBalance?: boolean;
}
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/get-rpc-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
}
16 changes: 6 additions & 10 deletions src/models/app/app.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -25,16 +25,12 @@ class AppStore {
makePersistable(this, {
name: this.constructor.name,
properties: [
// @ts-ignore
'_isOnboarded',
'isDeveloperModeEnabled',
'isTesterModeEnabled',
// @ts-ignore
'_networkLoggerEnabled',
// @ts-ignore
'_testnetsEnabledForAllNetworks',
// @ts-ignore
'_networkLogsCacheSize',
'isDeveloperModeEnabled',
'isTesterModeEnabled',
],
storage,
});
Expand Down
126 changes: 126 additions & 0 deletions src/models/contract/contract.store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
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';

import {ContractStoreData, IndexerContract} from './contract.types';

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) {
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.id]: contract,
}),
{} as Record<string, IndexerContract>,
);

return {
...this._data,
...acc,
[cId]: {
...this._data[cId],
...reducedContracts,
},
};
}, {} as ContractStoreData);
});
}
};

/**
* 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<IndexerContract | null> => {
let contract: IndexerContract | null = null;
const contractId = AddressUtils.toHaqq(contractAddress);

if (!chainId) {
// Check already fetched contracts
contract = this._searchContract(contractId);

// 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][contractId] ?? null;
if (!contract) {
await this.fetch([contractAddress], chainId);
contract = this._data[chainId][contractId] ?? null;
}
}

return contract;
};
}

const instance = new Contract(Boolean(process.env.JEST_WORKER_ID));
export {instance as Contract};
Loading