Commit eef534bb authored by tom goriunov's avatar tom goriunov Committed by GitHub

Update API Endpoint for fetching `has_methods_*` and `has_custom_meth… (#1842)

* Update API Endpoint for fetching `has_methods_*` and `has_custom_methods_*` fields

Fixes #1821

* don't load address txs when navigating to contract tab

* fix test
parent ee5467fa
import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import type { Address } from 'types/api/address'; import type { Address } from 'types/api/address';
import useApiQuery from 'lib/api/useApiQuery';
import getQueryParamString from 'lib/router/getQueryParamString';
import useSocketChannel from 'lib/socket/useSocketChannel';
import * as stubs from 'stubs/contract';
import ContractCode from 'ui/address/contract/ContractCode'; import ContractCode from 'ui/address/contract/ContractCode';
import ContractRead from 'ui/address/contract/ContractRead'; import ContractRead from 'ui/address/contract/ContractRead';
import ContractWrite from 'ui/address/contract/ContractWrite'; import ContractWrite from 'ui/address/contract/ContractWrite';
export default function useContractTabs(data: Address | undefined) { const CONTRACT_TAB_IDS = [
'contract_code',
'read_contract',
'read_proxy',
'read_custom_methods',
'write_contract',
'write_proxy',
'write_custom_methods',
] as const;
interface ContractTab {
id: typeof CONTRACT_TAB_IDS[number];
title: string;
component: JSX.Element;
}
interface ReturnType {
tabs: Array<ContractTab>;
isLoading: boolean;
}
export default function useContractTabs(data: Address | undefined, isPlaceholderData: boolean): ReturnType {
const [ isQueryEnabled, setIsQueryEnabled ] = React.useState(false);
const router = useRouter();
const tab = getQueryParamString(router.query.tab);
const isEnabled = Boolean(data?.hash) && !isPlaceholderData && CONTRACT_TAB_IDS.concat('contract' as never).includes(tab);
const enableQuery = React.useCallback(() => {
setIsQueryEnabled(true);
}, []);
const contractQuery = useApiQuery('contract', {
pathParams: { hash: data?.hash },
queryOptions: {
enabled: isEnabled && isQueryEnabled,
refetchOnMount: false,
placeholderData: data?.is_verified ? stubs.CONTRACT_CODE_VERIFIED : stubs.CONTRACT_CODE_UNVERIFIED,
},
});
const channel = useSocketChannel({
topic: `addresses:${ data?.hash?.toLowerCase() }`,
isDisabled: !isEnabled,
onJoin: enableQuery,
onSocketError: enableQuery,
});
return React.useMemo(() => { return React.useMemo(() => {
return [ return {
{ id: 'contact_code', title: 'Code', component: <ContractCode addressHash={ data?.hash }/> }, tabs: [
// this is not implemented in api yet {
// data?.has_decompiled_code ? id: 'contract_code' as const,
// { id: 'contact_decompiled_code', title: 'Decompiled code', component: <div>Decompiled code</div> } : title: 'Code',
// undefined, component: <ContractCode contractQuery={ contractQuery } channel={ channel } addressHash={ data?.hash }/>,
data?.has_methods_read ? },
{ id: 'read_contract', title: 'Read contract', component: <ContractRead/> } : contractQuery.data?.has_methods_read ?
undefined, { id: 'read_contract' as const, title: 'Read contract', component: <ContractRead isLoading={ contractQuery.isPlaceholderData }/> } :
data?.has_methods_read_proxy ? undefined,
{ id: 'read_proxy', title: 'Read proxy', component: <ContractRead/> } : contractQuery.data?.has_methods_read_proxy ?
undefined, { id: 'read_proxy' as const, title: 'Read proxy', component: <ContractRead isLoading={ contractQuery.isPlaceholderData }/> } :
data?.has_custom_methods_read ? undefined,
{ id: 'read_custom_methods', title: 'Read custom', component: <ContractRead/> } : contractQuery.data?.has_custom_methods_read ?
undefined, { id: 'read_custom_methods' as const, title: 'Read custom', component: <ContractRead isLoading={ contractQuery.isPlaceholderData }/> } :
data?.has_methods_write ? undefined,
{ id: 'write_contract', title: 'Write contract', component: <ContractWrite/> } : contractQuery.data?.has_methods_write ?
undefined, { id: 'write_contract' as const, title: 'Write contract', component: <ContractWrite isLoading={ contractQuery.isPlaceholderData }/> } :
data?.has_methods_write_proxy ? undefined,
{ id: 'write_proxy', title: 'Write proxy', component: <ContractWrite/> } : contractQuery.data?.has_methods_write_proxy ?
undefined, { id: 'write_proxy' as const, title: 'Write proxy', component: <ContractWrite isLoading={ contractQuery.isPlaceholderData }/> } :
data?.has_custom_methods_write ? undefined,
{ id: 'write_custom_methods', title: 'Write custom', component: <ContractWrite/> } : contractQuery.data?.has_custom_methods_write ?
undefined, { id: 'write_custom_methods' as const, title: 'Write custom', component: <ContractWrite isLoading={ contractQuery.isPlaceholderData }/> } :
].filter(Boolean); undefined,
}, [ data ]); ].filter(Boolean),
isLoading: contractQuery.isPlaceholderData,
};
}, [ contractQuery, channel, data?.hash ]);
} }
...@@ -59,14 +59,8 @@ export const token: Address = { ...@@ -59,14 +59,8 @@ export const token: Address = {
creator_address_hash: '0x34A9c688512ebdB575e82C50c9803F6ba2916E72', creator_address_hash: '0x34A9c688512ebdB575e82C50c9803F6ba2916E72',
exchange_rate: null, exchange_rate: null,
implementation_address: null, implementation_address: null,
has_custom_methods_read: false,
has_custom_methods_write: false,
has_decompiled_code: false, has_decompiled_code: false,
has_logs: false, has_logs: false,
has_methods_read: false,
has_methods_read_proxy: false,
has_methods_write: false,
has_methods_write_proxy: false,
has_token_transfers: true, has_token_transfers: true,
has_tokens: true, has_tokens: true,
has_validated_blocks: false, has_validated_blocks: false,
...@@ -79,14 +73,8 @@ export const contract: Address = { ...@@ -79,14 +73,8 @@ export const contract: Address = {
creation_tx_hash: '0xf2aff6501b632604c39978b47d309813d8a1bcca721864bbe86abf59704f195e', creation_tx_hash: '0xf2aff6501b632604c39978b47d309813d8a1bcca721864bbe86abf59704f195e',
creator_address_hash: '0x803ad3F50b9e1fF68615e8B053A186e1be288943', creator_address_hash: '0x803ad3F50b9e1fF68615e8B053A186e1be288943',
exchange_rate: '0.04311', exchange_rate: '0.04311',
has_custom_methods_read: false,
has_custom_methods_write: false,
has_decompiled_code: false, has_decompiled_code: false,
has_logs: true, has_logs: true,
has_methods_read: true,
has_methods_read_proxy: true,
has_methods_write: true,
has_methods_write_proxy: true,
has_token_transfers: false, has_token_transfers: false,
has_tokens: false, has_tokens: false,
has_validated_blocks: false, has_validated_blocks: false,
...@@ -110,14 +98,8 @@ export const validator: Address = { ...@@ -110,14 +98,8 @@ export const validator: Address = {
creation_tx_hash: null, creation_tx_hash: null,
creator_address_hash: null, creator_address_hash: null,
exchange_rate: '0.00432018', exchange_rate: '0.00432018',
has_custom_methods_read: false,
has_custom_methods_write: false,
has_decompiled_code: false, has_decompiled_code: false,
has_logs: false, has_logs: false,
has_methods_read: false,
has_methods_read_proxy: false,
has_methods_write: false,
has_methods_write_proxy: false,
has_token_transfers: false, has_token_transfers: false,
has_tokens: false, has_tokens: false,
has_validated_blocks: true, has_validated_blocks: true,
......
/* eslint-disable max-len */ /* eslint-disable max-len */
import type { SmartContract } from 'types/api/contract'; import type { SmartContract } from 'types/api/contract';
export const verified: Partial<SmartContract> = { export const verified: SmartContract = {
abi: [ { anonymous: false, inputs: [ { indexed: true, internalType: 'address', name: 'src', type: 'address' }, { indexed: true, internalType: 'address', name: 'guy', type: 'address' }, { indexed: false, internalType: 'uint256', name: 'wad', type: 'uint256' } ], name: 'Approval', type: 'event' } ], abi: [ { anonymous: false, inputs: [ { indexed: true, internalType: 'address', name: 'src', type: 'address' }, { indexed: true, internalType: 'address', name: 'guy', type: 'address' }, { indexed: false, internalType: 'uint256', name: 'wad', type: 'uint256' } ], name: 'Approval', type: 'event' } ],
can_be_visualized_via_sol2uml: true, can_be_visualized_via_sol2uml: true,
compiler_version: 'v0.5.16+commit.9c3226ce', compiler_version: 'v0.5.16+commit.9c3226ce',
...@@ -32,9 +32,26 @@ export const verified: Partial<SmartContract> = { ...@@ -32,9 +32,26 @@ export const verified: Partial<SmartContract> = {
], ],
language: 'solidity', language: 'solidity',
license_type: 'gnu_gpl_v3', license_type: 'gnu_gpl_v3',
has_methods_read: true,
has_methods_read_proxy: false,
has_methods_write: true,
has_methods_write_proxy: false,
has_custom_methods_read: false,
has_custom_methods_write: false,
is_self_destructed: false,
is_verified_via_eth_bytecode_db: null,
is_changed_bytecode: null,
is_verified_via_sourcify: null,
is_fully_verified: null,
is_partially_verified: null,
sourcify_repo_url: null,
file_path: '',
additional_sources: [],
verified_twin_address_hash: null,
minimal_proxy_address_hash: null,
}; };
export const withMultiplePaths: Partial<SmartContract> = { export const withMultiplePaths: SmartContract = {
...verified, ...verified,
file_path: './simple_storage.sol', file_path: './simple_storage.sol',
additional_sources: [ additional_sources: [
...@@ -45,7 +62,7 @@ export const withMultiplePaths: Partial<SmartContract> = { ...@@ -45,7 +62,7 @@ export const withMultiplePaths: Partial<SmartContract> = {
], ],
}; };
export const verifiedViaSourcify: Partial<SmartContract> = { export const verifiedViaSourcify: SmartContract = {
...verified, ...verified,
is_verified_via_sourcify: true, is_verified_via_sourcify: true,
is_fully_verified: false, is_fully_verified: false,
...@@ -53,36 +70,67 @@ export const verifiedViaSourcify: Partial<SmartContract> = { ...@@ -53,36 +70,67 @@ export const verifiedViaSourcify: Partial<SmartContract> = {
sourcify_repo_url: 'https://repo.sourcify.dev/contracts//full_match/99/0x51891596E158b2857e5356DC847e2D15dFbCF2d0/', sourcify_repo_url: 'https://repo.sourcify.dev/contracts//full_match/99/0x51891596E158b2857e5356DC847e2D15dFbCF2d0/',
}; };
export const verifiedViaEthBytecodeDb: Partial<SmartContract> = { export const verifiedViaEthBytecodeDb: SmartContract = {
...verified, ...verified,
is_verified_via_eth_bytecode_db: true, is_verified_via_eth_bytecode_db: true,
}; };
export const withTwinAddress: Partial<SmartContract> = { export const withTwinAddress: SmartContract = {
...verified, ...verified,
is_verified: false, is_verified: false,
verified_twin_address_hash: '0xa62744bee8646e237441cdbfdedd3458861748a8', verified_twin_address_hash: '0xa62744bee8646e237441cdbfdedd3458861748a8',
}; };
export const withProxyAddress: Partial<SmartContract> = { export const withProxyAddress: SmartContract = {
...verified, ...verified,
is_verified: false, is_verified: false,
verified_twin_address_hash: '0xa62744bee8646e237441cdbfdedd3458861748a8', verified_twin_address_hash: '0xa62744bee8646e237441cdbfdedd3458861748a8',
minimal_proxy_address_hash: '0xa62744bee8646e237441cdbfdedd3458861748a8', minimal_proxy_address_hash: '0xa62744bee8646e237441cdbfdedd3458861748a8',
}; };
export const selfDestructed: Partial<SmartContract> = { export const selfDestructed: SmartContract = {
...verified, ...verified,
is_self_destructed: true, is_self_destructed: true,
}; };
export const withChangedByteCode: Partial<SmartContract> = { export const withChangedByteCode: SmartContract = {
...verified, ...verified,
is_changed_bytecode: true, is_changed_bytecode: true,
}; };
export const nonVerified: Partial<SmartContract> = { export const nonVerified: SmartContract = {
is_verified: false, is_verified: false,
creation_bytecode: 'creation_bytecode', creation_bytecode: 'creation_bytecode',
deployed_bytecode: 'deployed_bytecode', deployed_bytecode: 'deployed_bytecode',
is_self_destructed: false,
abi: null,
compiler_version: null,
evm_version: null,
optimization_enabled: null,
optimization_runs: null,
name: null,
verified_at: null,
is_verified_via_eth_bytecode_db: null,
is_changed_bytecode: null,
has_methods_read: false,
has_methods_read_proxy: false,
has_methods_write: false,
has_methods_write_proxy: false,
has_custom_methods_read: false,
has_custom_methods_write: false,
is_verified_via_sourcify: null,
is_fully_verified: null,
is_partially_verified: null,
sourcify_repo_url: null,
source_code: null,
constructor_args: null,
decoded_constructor_args: null,
can_be_visualized_via_sol2uml: null,
file_path: '',
additional_sources: [],
external_libraries: null,
verified_twin_address_hash: null,
minimal_proxy_address_hash: null,
language: null,
license_type: null,
}; };
...@@ -31,4 +31,7 @@ export const ENVS_MAP: Record<string, Array<[string, string]>> = { ...@@ -31,4 +31,7 @@ export const ENVS_MAP: Record<string, Array<[string, string]>> = {
userOps: [ userOps: [
[ 'NEXT_PUBLIC_HAS_USER_OPS', 'true' ], [ 'NEXT_PUBLIC_HAS_USER_OPS', 'true' ],
], ],
hasContractAuditReports: [
[ 'NEXT_PUBLIC_HAS_CONTRACT_AUDIT_REPORTS', 'true' ],
],
}; };
...@@ -47,12 +47,6 @@ export const viewsEnvs = { ...@@ -47,12 +47,6 @@ export const viewsEnvs = {
}, },
}; };
export const UIEnvs = {
hasContractAuditReports: [
{ name: 'NEXT_PUBLIC_HAS_CONTRACT_AUDIT_REPORTS', value: 'true' },
],
};
export const stabilityEnvs = [ export const stabilityEnvs = [
{ name: 'NEXT_PUBLIC_VIEWS_ADDRESS_HIDDEN_VIEWS', value: '["top_accounts"]' }, { name: 'NEXT_PUBLIC_VIEWS_ADDRESS_HIDDEN_VIEWS', value: '["top_accounts"]' },
{ name: 'NEXT_PUBLIC_VIEWS_TX_HIDDEN_FIELDS', value: '["value","fee_currency","gas_price","gas_fees","burnt_fees"]' }, { name: 'NEXT_PUBLIC_VIEWS_TX_HIDDEN_FIELDS', value: '["value","fee_currency","gas_price","gas_fees","burnt_fees"]' },
......
...@@ -19,22 +19,16 @@ export const ADDRESS_INFO: Address = { ...@@ -19,22 +19,16 @@ export const ADDRESS_INFO: Address = {
creation_tx_hash: null, creation_tx_hash: null,
creator_address_hash: ADDRESS_HASH, creator_address_hash: ADDRESS_HASH,
exchange_rate: null, exchange_rate: null,
has_custom_methods_read: false,
has_custom_methods_write: false,
has_decompiled_code: false, has_decompiled_code: false,
has_logs: true, has_logs: true,
has_methods_read: false,
has_methods_read_proxy: false,
has_methods_write: false,
has_methods_write_proxy: false,
has_token_transfers: false, has_token_transfers: false,
has_tokens: false, has_tokens: false,
has_validated_blocks: false, has_validated_blocks: false,
hash: ADDRESS_HASH, hash: ADDRESS_HASH,
implementation_address: null, implementation_address: null,
implementation_name: null, implementation_name: null,
is_contract: false, is_contract: true,
is_verified: false, is_verified: true,
name: 'ChainLink Token (goerli)', name: 'ChainLink Token (goerli)',
token: TOKEN_INFO_ERC_20, token: TOKEN_INFO_ERC_20,
private_tags: [], private_tags: [],
......
...@@ -7,6 +7,12 @@ export const CONTRACT_CODE_UNVERIFIED = { ...@@ -7,6 +7,12 @@ export const CONTRACT_CODE_UNVERIFIED = {
creation_bytecode: '0x60806040526e', creation_bytecode: '0x60806040526e',
deployed_bytecode: '0x608060405233', deployed_bytecode: '0x608060405233',
is_self_destructed: false, is_self_destructed: false,
has_methods_read: true,
has_methods_read_proxy: true,
has_methods_write: true,
has_methods_write_proxy: true,
has_custom_methods_read: true,
has_custom_methods_write: true,
} as SmartContract; } as SmartContract;
export const CONTRACT_CODE_VERIFIED = { export const CONTRACT_CODE_VERIFIED = {
...@@ -41,6 +47,12 @@ export const CONTRACT_CODE_VERIFIED = { ...@@ -41,6 +47,12 @@ export const CONTRACT_CODE_VERIFIED = {
source_code: 'source_code', source_code: 'source_code',
verified_at: '2023-02-21T14:39:16.906760Z', verified_at: '2023-02-21T14:39:16.906760Z',
license_type: 'mit', license_type: 'mit',
has_methods_read: true,
has_methods_read_proxy: true,
has_methods_write: true,
has_methods_write_proxy: true,
has_custom_methods_read: true,
has_custom_methods_write: true,
} as unknown as SmartContract; } as unknown as SmartContract;
export const VERIFIED_CONTRACT_INFO: VerifiedContract = { export const VERIFIED_CONTRACT_INFO: VerifiedContract = {
......
...@@ -15,14 +15,8 @@ export interface Address extends UserTags { ...@@ -15,14 +15,8 @@ export interface Address extends UserTags {
ens_domain_name: string | null; ens_domain_name: string | null;
// TODO: if we are happy with tabs-counters method, should we delete has_something fields? // TODO: if we are happy with tabs-counters method, should we delete has_something fields?
has_beacon_chain_withdrawals?: boolean; has_beacon_chain_withdrawals?: boolean;
has_custom_methods_read: boolean;
has_custom_methods_write: boolean;
has_decompiled_code: boolean; has_decompiled_code: boolean;
has_logs: boolean; has_logs: boolean;
has_methods_read: boolean;
has_methods_read_proxy: boolean;
has_methods_write: boolean;
has_methods_write_proxy: boolean;
has_token_transfers: boolean; has_token_transfers: boolean;
has_tokens: boolean; has_tokens: boolean;
has_validated_blocks: boolean; has_validated_blocks: boolean;
......
...@@ -33,6 +33,14 @@ export interface SmartContract { ...@@ -33,6 +33,14 @@ export interface SmartContract {
is_verified: boolean | null; is_verified: boolean | null;
is_verified_via_eth_bytecode_db: boolean | null; is_verified_via_eth_bytecode_db: boolean | null;
is_changed_bytecode: boolean | null; is_changed_bytecode: boolean | null;
has_methods_read: boolean;
has_methods_read_proxy: boolean;
has_methods_write: boolean;
has_methods_write_proxy: boolean;
has_custom_methods_read: boolean;
has_custom_methods_write: boolean;
// sourcify info >>> // sourcify info >>>
is_verified_via_sourcify: boolean | null; is_verified_via_sourcify: boolean | null;
is_fully_verified: boolean | null; is_fully_verified: boolean | null;
......
...@@ -7,24 +7,29 @@ import Web3ModalProvider from 'ui/shared/Web3ModalProvider'; ...@@ -7,24 +7,29 @@ import Web3ModalProvider from 'ui/shared/Web3ModalProvider';
interface Props { interface Props {
tabs: Array<RoutedSubTab>; tabs: Array<RoutedSubTab>;
addressHash?: string; isLoading: boolean;
shouldRender?: boolean;
} }
const TAB_LIST_PROPS = { const TAB_LIST_PROPS = {
columnGap: 3, columnGap: 3,
}; };
const AddressContract = ({ tabs }: Props) => { const AddressContract = ({ tabs, isLoading, shouldRender }: Props) => {
const fallback = React.useCallback(() => { const fallback = React.useCallback(() => {
const noProviderTabs = tabs.filter(({ id }) => id === 'contact_code' || id.startsWith('read_')); const noProviderTabs = tabs.filter(({ id }) => id === 'contract_code' || id.startsWith('read_'));
return ( return (
<RoutedTabs tabs={ noProviderTabs } variant="outline" colorScheme="gray" size="sm" tabListProps={ TAB_LIST_PROPS }/> <RoutedTabs tabs={ noProviderTabs } variant="outline" colorScheme="gray" size="sm" tabListProps={ TAB_LIST_PROPS } isLoading={ isLoading }/>
); );
}, [ tabs ]); }, [ isLoading, tabs ]);
if (!shouldRender) {
return null;
}
return ( return (
<Web3ModalProvider fallback={ fallback }> <Web3ModalProvider fallback={ fallback }>
<RoutedTabs tabs={ tabs } variant="outline" colorScheme="gray" size="sm" tabListProps={ TAB_LIST_PROPS }/> <RoutedTabs tabs={ tabs } variant="outline" colorScheme="gray" size="sm" tabListProps={ TAB_LIST_PROPS } isLoading={ isLoading }/>
</Web3ModalProvider> </Web3ModalProvider>
); );
}; };
......
This diff is collapsed.
import { Flex, Skeleton, Button, Grid, GridItem, Alert, Link, chakra, Box, useColorModeValue } from '@chakra-ui/react'; import { Flex, Skeleton, Button, Grid, GridItem, Alert, Link, chakra, Box, useColorModeValue } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import type { Channel } from 'phoenix';
import React from 'react'; import React from 'react';
import type { SocketMessage } from 'lib/socket/types'; import type { SocketMessage } from 'lib/socket/types';
import type { Address as AddressInfo } from 'types/api/address'; import type { Address as AddressInfo } from 'types/api/address';
import type { SmartContract } from 'types/api/contract';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; import type { ResourceError } from 'lib/api/resources';
import { getResourceKey } from 'lib/api/useApiQuery';
import { CONTRACT_LICENSES } from 'lib/contracts/licenses'; import { CONTRACT_LICENSES } from 'lib/contracts/licenses';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
import * as stubs from 'stubs/contract';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import Hint from 'ui/shared/Hint'; import Hint from 'ui/shared/Hint';
...@@ -26,8 +28,8 @@ import ContractSourceCode from './ContractSourceCode'; ...@@ -26,8 +28,8 @@ import ContractSourceCode from './ContractSourceCode';
type Props = { type Props = {
addressHash?: string; addressHash?: string;
// prop for pw tests only contractQuery: UseQueryResult<SmartContract, ResourceError<unknown>>;
noSocket?: boolean; channel: Channel | undefined;
} }
type InfoItemProps = { type InfoItemProps = {
...@@ -57,21 +59,13 @@ const InfoItem = chakra(({ label, content, hint, className, isLoading }: InfoIte ...@@ -57,21 +59,13 @@ const InfoItem = chakra(({ label, content, hint, className, isLoading }: InfoIte
</GridItem> </GridItem>
)); ));
const ContractCode = ({ addressHash, noSocket }: Props) => { const ContractCode = ({ addressHash, contractQuery, channel }: Props) => {
const [ isQueryEnabled, setIsQueryEnabled ] = React.useState(false);
const [ isChangedBytecodeSocket, setIsChangedBytecodeSocket ] = React.useState<boolean>(); const [ isChangedBytecodeSocket, setIsChangedBytecodeSocket ] = React.useState<boolean>();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const addressInfo = queryClient.getQueryData<AddressInfo>(getResourceKey('address', { pathParams: { hash: addressHash } })); const addressInfo = queryClient.getQueryData<AddressInfo>(getResourceKey('address', { pathParams: { hash: addressHash } }));
const { data, isPlaceholderData, isError } = useApiQuery('contract', { const { data, isPlaceholderData, isError } = contractQuery;
pathParams: { hash: addressHash },
queryOptions: {
enabled: Boolean(addressHash) && (noSocket || isQueryEnabled),
refetchOnMount: false,
placeholderData: addressInfo?.is_verified ? stubs.CONTRACT_CODE_VERIFIED : stubs.CONTRACT_CODE_UNVERIFIED,
},
});
const handleChangedBytecodeMessage: SocketMessage.AddressChangedBytecode['handler'] = React.useCallback(() => { const handleChangedBytecodeMessage: SocketMessage.AddressChangedBytecode['handler'] = React.useCallback(() => {
setIsChangedBytecodeSocket(true); setIsChangedBytecodeSocket(true);
...@@ -86,14 +80,6 @@ const ContractCode = ({ addressHash, noSocket }: Props) => { ...@@ -86,14 +80,6 @@ const ContractCode = ({ addressHash, noSocket }: Props) => {
}); });
}, [ addressHash, queryClient ]); }, [ addressHash, queryClient ]);
const enableQuery = React.useCallback(() => setIsQueryEnabled(true), []);
const channel = useSocketChannel({
topic: `addresses:${ addressHash?.toLowerCase() }`,
isDisabled: !addressHash,
onJoin: enableQuery,
onSocketError: enableQuery,
});
useSocketMessage({ useSocketMessage({
channel, channel,
event: 'changed_bytecode', event: 'changed_bytecode',
......
...@@ -19,7 +19,11 @@ import ContractReadResult from './ContractReadResult'; ...@@ -19,7 +19,11 @@ import ContractReadResult from './ContractReadResult';
import ContractMethodForm from './methodForm/ContractMethodForm'; import ContractMethodForm from './methodForm/ContractMethodForm';
import useWatchAccount from './useWatchAccount'; import useWatchAccount from './useWatchAccount';
const ContractRead = () => { interface Props {
isLoading?: boolean;
}
const ContractRead = ({ isLoading }: Props) => {
const apiFetch = useApiFetch(); const apiFetch = useApiFetch();
const account = useWatchAccount(); const account = useWatchAccount();
const router = useRouter(); const router = useRouter();
...@@ -36,7 +40,7 @@ const ContractRead = () => { ...@@ -36,7 +40,7 @@ const ContractRead = () => {
from: account?.address, from: account?.address,
}, },
queryOptions: { queryOptions: {
enabled: Boolean(addressHash), enabled: !isLoading,
}, },
}); });
......
...@@ -19,7 +19,11 @@ import ContractMethodForm from './methodForm/ContractMethodForm'; ...@@ -19,7 +19,11 @@ import ContractMethodForm from './methodForm/ContractMethodForm';
import useContractAbi from './useContractAbi'; import useContractAbi from './useContractAbi';
import { getNativeCoinValue, prepareAbi } from './utils'; import { getNativeCoinValue, prepareAbi } from './utils';
const ContractWrite = () => { interface Props {
isLoading?: boolean;
}
const ContractWrite = ({ isLoading }: Props) => {
const { data: walletClient } = useWalletClient(); const { data: walletClient } = useWalletClient();
const { isConnected, chainId } = useAccount(); const { isConnected, chainId } = useAccount();
const { switchChainAsync } = useSwitchChain(); const { switchChainAsync } = useSwitchChain();
...@@ -37,7 +41,7 @@ const ContractWrite = () => { ...@@ -37,7 +41,7 @@ const ContractWrite = () => {
is_custom_abi: isCustomAbi ? 'true' : 'false', is_custom_abi: isCustomAbi ? 'true' : 'false',
}, },
queryOptions: { queryOptions: {
enabled: Boolean(addressHash), enabled: !isLoading,
refetchOnMount: false, refetchOnMount: false,
}, },
}); });
......
import { useRouter } from 'next/router';
import useApiQuery from 'lib/api/useApiQuery';
import useContractTabs from 'lib/hooks/useContractTabs';
import getQueryParamString from 'lib/router/getQueryParamString';
const ContractCode = () => {
const router = useRouter();
const hash = getQueryParamString(router.query.hash);
const addressQuery = useApiQuery('address', { pathParams: { hash } });
const { tabs } = useContractTabs(addressQuery.data, false);
const content = tabs.find(({ id }) => id === 'contract_code')?.component;
return content ?? null;
};
export default ContractCode;
...@@ -39,7 +39,7 @@ export default function useContractAbi({ addressHash, isProxy, isCustomAbi }: Pa ...@@ -39,7 +39,7 @@ export default function useContractAbi({ addressHash, isProxy, isCustomAbi }: Pa
pathParams: { hash: addressHash }, pathParams: { hash: addressHash },
queryParams: { is_custom_abi: 'true' }, queryParams: { is_custom_abi: 'true' },
queryOptions: { queryOptions: {
enabled: Boolean(addressInfo?.has_custom_methods_write), enabled: Boolean(contractInfo?.has_custom_methods_write),
refetchOnMount: false, refetchOnMount: false,
}, },
}); });
......
...@@ -74,14 +74,8 @@ export default function useAddressQuery({ hash }: Params): AddressQuery { ...@@ -74,14 +74,8 @@ export default function useAddressQuery({ hash }: Params): AddressQuery {
creation_tx_hash: null, creation_tx_hash: null,
exchange_rate: null, exchange_rate: null,
ens_domain_name: null, ens_domain_name: null,
has_custom_methods_read: false,
has_custom_methods_write: false,
has_decompiled_code: false, has_decompiled_code: false,
has_logs: false, has_logs: false,
has_methods_read: false,
has_methods_read_proxy: false,
has_methods_write: false,
has_methods_write_proxy: false,
has_token_transfers: false, has_token_transfers: false,
has_tokens: false, has_tokens: false,
has_validated_blocks: false, has_validated_blocks: false,
......
...@@ -87,7 +87,7 @@ const AddressPageContent = () => { ...@@ -87,7 +87,7 @@ const AddressPageContent = () => {
const isSafeAddress = useIsSafeAddress(!addressQuery.isPlaceholderData && addressQuery.data?.is_contract ? hash : undefined); const isSafeAddress = useIsSafeAddress(!addressQuery.isPlaceholderData && addressQuery.data?.is_contract ? hash : undefined);
const safeIconColor = useColorModeValue('black', 'white'); const safeIconColor = useColorModeValue('black', 'white');
const contractTabs = useContractTabs(addressQuery.data); const contractTabs = useContractTabs(addressQuery.data, addressQuery.isPlaceholderData);
const isLoading = addressQuery.isPlaceholderData || (config.features.userOps.isEnabled && userOpsAccountQuery.isPlaceholderData); const isLoading = addressQuery.isPlaceholderData || (config.features.userOps.isEnabled && userOpsAccountQuery.isPlaceholderData);
const isTabsLoading = isLoading || addressTabsCountersQuery.isPlaceholderData; const isTabsLoading = isLoading || addressTabsCountersQuery.isPlaceholderData;
...@@ -178,8 +178,8 @@ const AddressPageContent = () => { ...@@ -178,8 +178,8 @@ const AddressPageContent = () => {
return 'Contract'; return 'Contract';
}, },
component: <AddressContract tabs={ contractTabs }/>, component: <AddressContract tabs={ contractTabs.tabs } shouldRender={ !isTabsLoading } isLoading={ contractTabs.isLoading }/>,
subTabs: contractTabs.map(tab => tab.id), subTabs: contractTabs.tabs.map(tab => tab.id),
} : undefined, } : undefined,
].filter(Boolean); ].filter(Boolean);
}, [ addressQuery.data, contractTabs, addressTabsCountersQuery.data, userOpsAccountQuery.data, isTabsLoading ]); }, [ addressQuery.data, contractTabs, addressTabsCountersQuery.data, userOpsAccountQuery.data, isTabsLoading ]);
......
...@@ -69,7 +69,7 @@ const TokenPageContent = () => { ...@@ -69,7 +69,7 @@ const TokenPageContent = () => {
}, },
}); });
const contractQuery = useApiQuery('address', { const addressQuery = useApiQuery('address', {
pathParams: { hash: hashString }, pathParams: { hash: hashString },
queryOptions: { queryOptions: {
enabled: isQueryEnabled && Boolean(router.query.hash), enabled: isQueryEnabled && Boolean(router.query.hash),
...@@ -119,7 +119,7 @@ const TokenPageContent = () => { ...@@ -119,7 +119,7 @@ const TokenPageContent = () => {
} }
}, [ tokenQuery.data, tokenQuery.isPlaceholderData ]); }, [ tokenQuery.data, tokenQuery.isPlaceholderData ]);
const hasData = (tokenQuery.data && !tokenQuery.isPlaceholderData) && (contractQuery.data && !contractQuery.isPlaceholderData); const hasData = (tokenQuery.data && !tokenQuery.isPlaceholderData) && (addressQuery.data && !addressQuery.isPlaceholderData);
const hasInventoryTab = tokenQuery.data?.type && NFT_TOKEN_TYPE_IDS.includes(tokenQuery.data.type); const hasInventoryTab = tokenQuery.data?.type && NFT_TOKEN_TYPE_IDS.includes(tokenQuery.data.type);
const transfersQuery = useQueryWithPages({ const transfersQuery = useQueryWithPages({
...@@ -172,7 +172,7 @@ const TokenPageContent = () => { ...@@ -172,7 +172,7 @@ const TokenPageContent = () => {
queryOptions: { enabled: Boolean(tokenQuery.data) && config.features.verifiedTokens.isEnabled }, queryOptions: { enabled: Boolean(tokenQuery.data) && config.features.verifiedTokens.isEnabled },
}); });
const contractTabs = useContractTabs(contractQuery.data); const contractTabs = useContractTabs(addressQuery.data, addressQuery.isPlaceholderData);
const tabs: Array<RoutedTab> = [ const tabs: Array<RoutedTab> = [
hasInventoryTab ? { hasInventoryTab ? {
...@@ -182,10 +182,10 @@ const TokenPageContent = () => { ...@@ -182,10 +182,10 @@ const TokenPageContent = () => {
} : undefined, } : undefined,
{ id: 'token_transfers', title: 'Token transfers', component: <TokenTransfer transfersQuery={ transfersQuery } token={ tokenQuery.data }/> }, { id: 'token_transfers', title: 'Token transfers', component: <TokenTransfer transfersQuery={ transfersQuery } token={ tokenQuery.data }/> },
{ id: 'holders', title: 'Holders', component: <TokenHolders token={ tokenQuery.data } holdersQuery={ holdersQuery }/> }, { id: 'holders', title: 'Holders', component: <TokenHolders token={ tokenQuery.data } holdersQuery={ holdersQuery }/> },
contractQuery.data?.is_contract ? { addressQuery.data?.is_contract ? {
id: 'contract', id: 'contract',
title: () => { title: () => {
if (contractQuery.data?.is_verified) { if (addressQuery.data?.is_verified) {
return ( return (
<> <>
<span>Contract</span> <span>Contract</span>
...@@ -196,8 +196,8 @@ const TokenPageContent = () => { ...@@ -196,8 +196,8 @@ const TokenPageContent = () => {
return 'Contract'; return 'Contract';
}, },
component: <AddressContract tabs={ contractTabs }/>, component: <AddressContract tabs={ contractTabs.tabs } isLoading={ contractTabs.isLoading }/>,
subTabs: contractTabs.map(tab => tab.id), subTabs: contractTabs.tabs.map(tab => tab.id),
} : undefined, } : undefined,
].filter(Boolean); ].filter(Boolean);
...@@ -255,8 +255,8 @@ const TokenPageContent = () => { ...@@ -255,8 +255,8 @@ const TokenPageContent = () => {
</Tooltip> </Tooltip>
) } ) }
<EntityTags <EntityTags
data={ contractQuery.data } data={ addressQuery.data }
isLoading={ tokenQuery.isPlaceholderData || contractQuery.isPlaceholderData } isLoading={ tokenQuery.isPlaceholderData || addressQuery.isPlaceholderData }
tagsBefore={ [ tagsBefore={ [
tokenQuery.data ? { label: tokenQuery.data?.type, display_name: tokenQuery.data?.type } : undefined, tokenQuery.data ? { label: tokenQuery.data?.type, display_name: tokenQuery.data?.type } : undefined,
config.features.bridgedTokens.isEnabled && tokenQuery.data?.is_bridged ? config.features.bridgedTokens.isEnabled && tokenQuery.data?.is_bridged ?
...@@ -273,19 +273,19 @@ const TokenPageContent = () => { ...@@ -273,19 +273,19 @@ const TokenPageContent = () => {
</> </>
); );
const isLoading = tokenQuery.isPlaceholderData || contractQuery.isPlaceholderData; const isLoading = tokenQuery.isPlaceholderData || addressQuery.isPlaceholderData;
const titleSecondRow = ( const titleSecondRow = (
<Flex alignItems="center" w="100%" minW={ 0 } columnGap={ 2 } rowGap={ 2 } flexWrap={{ base: 'wrap', lg: 'nowrap' }}> <Flex alignItems="center" w="100%" minW={ 0 } columnGap={ 2 } rowGap={ 2 } flexWrap={{ base: 'wrap', lg: 'nowrap' }}>
<AddressEntity <AddressEntity
address={{ ...contractQuery.data, name: '' }} address={{ ...addressQuery.data, name: '' }}
isLoading={ isLoading } isLoading={ isLoading }
fontFamily="heading" fontFamily="heading"
fontSize="lg" fontSize="lg"
fontWeight={ 500 } fontWeight={ 500 }
/> />
{ !isLoading && tokenQuery.data && <AddressAddToWallet token={ tokenQuery.data } variant="button"/> } { !isLoading && tokenQuery.data && <AddressAddToWallet token={ tokenQuery.data } variant="button"/> }
<AddressQrCode address={ contractQuery.data } isLoading={ isLoading }/> <AddressQrCode address={ addressQuery.data } isLoading={ isLoading }/>
<AccountActionsMenu isLoading={ isLoading }/> <AccountActionsMenu isLoading={ isLoading }/>
<Flex ml={{ base: 0, lg: 'auto' }} columnGap={ 2 } flexGrow={{ base: 1, lg: 0 }}> <Flex ml={{ base: 0, lg: 'auto' }} columnGap={ 2 } flexGrow={{ base: 1, lg: 0 }}>
<TokenVerifiedInfo verifiedInfoQuery={ verifiedInfoQuery }/> <TokenVerifiedInfo verifiedInfoQuery={ verifiedInfoQuery }/>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment