diff --git a/mocks/address/address.ts b/mocks/address/address.ts index fbc81bf171..c6358afc0e 100644 --- a/mocks/address/address.ts +++ b/mocks/address/address.ts @@ -81,6 +81,7 @@ export const token: Address = { coin_balance: '1', creation_transaction_hash: '0xc38cf7377bf72d6436f63c37b01b24d032101f20ec1849286dc703c712f10c98', creator_address_hash: '0x34A9c688512ebdB575e82C50c9803F6ba2916E72', + creation_status: 'success', exchange_rate: '0.04311', has_logs: false, has_token_transfers: true, @@ -94,6 +95,7 @@ export const eoa: Address = { coin_balance: '2782650189688719421432220500', creation_transaction_hash: '0xf2aff6501b632604c39978b47d309813d8a1bcca721864bbe86abf59704f195e', creator_address_hash: '0x803ad3F50b9e1fF68615e8B053A186e1be288943', + creation_status: null, exchange_rate: '0.04311', has_logs: true, has_token_transfers: false, @@ -117,6 +119,7 @@ export const contract: Address = { coin_balance: '27826501896887194214322205', creation_transaction_hash: '0xf2aff6501b632604c39978b47d309813d8a1bcca721864bbe86abf59704f195e', creator_address_hash: '0x803ad3F50b9e1fF68615e8B053A186e1be288943', + creation_status: 'success', exchange_rate: '0.04311', has_logs: true, has_token_transfers: false, @@ -142,6 +145,7 @@ export const validator: Address = { coin_balance: '22910462800601256910890', creation_transaction_hash: null, creator_address_hash: null, + creation_status: null, exchange_rate: '0.00432018', has_logs: false, has_token_transfers: false, diff --git a/mocks/contract/info.ts b/mocks/contract/info.ts index b9a06aa243..bcaf60f901 100644 --- a/mocks/contract/info.ts +++ b/mocks/contract/info.ts @@ -7,6 +7,7 @@ export const verified: SmartContract = { compiler_version: 'v0.5.16+commit.9c3226ce', constructor_args: 'constructor_args', creation_bytecode: 'creation_bytecode', + creation_status: 'success', deployed_bytecode: 'deployed_bytecode', compiler_settings: { evmVersion: 'london', @@ -33,7 +34,6 @@ export const verified: SmartContract = { ], language: 'solidity', license_type: 'gnu_gpl_v3', - is_self_destructed: false, is_verified_via_eth_bytecode_db: null, is_changed_bytecode: null, is_verified_via_sourcify: null, @@ -88,7 +88,7 @@ export const withProxyAddress: SmartContract = { export const selfDestructed: SmartContract = { ...verified, - is_self_destructed: true, + creation_status: 'selfdestructed', }; export const withChangedByteCode: SmartContract = { @@ -122,7 +122,7 @@ export const nonVerified: SmartContract = { is_blueprint: false, creation_bytecode: 'creation_bytecode', deployed_bytecode: 'deployed_bytecode', - is_self_destructed: false, + creation_status: 'success', abi: null, compiler_version: null, evm_version: null, diff --git a/stubs/address.ts b/stubs/address.ts index 07c8ce2734..e66e9b41ee 100644 --- a/stubs/address.ts +++ b/stubs/address.ts @@ -21,6 +21,7 @@ export const ADDRESS_INFO: Address = { coin_balance: '810941268802273085757', creation_transaction_hash: null, creator_address_hash: ADDRESS_HASH, + creation_status: 'success', exchange_rate: null, has_logs: true, has_token_transfers: false, diff --git a/stubs/contract.ts b/stubs/contract.ts index 90b3429d27..271b0f1efd 100644 --- a/stubs/contract.ts +++ b/stubs/contract.ts @@ -10,7 +10,7 @@ import { STATS_COUNTER } from './stats'; export const CONTRACT_CODE_UNVERIFIED = { creation_bytecode: '0x60806040526e', deployed_bytecode: '0x608060405233', - is_self_destructed: false, + creation_status: 'success', } as SmartContract; export const CONTRACT_CODE_VERIFIED = { diff --git a/types/api/address.ts b/types/api/address.ts index 14e7c10647..30fc260c88 100644 --- a/types/api/address.ts +++ b/types/api/address.ts @@ -2,7 +2,7 @@ import type { Transaction } from 'types/api/transaction'; import type { UserTags, AddressImplementation, AddressParam, AddressFilecoinParams } from './addressParams'; import type { Block, EpochRewardsType } from './block'; -import type { SmartContractProxyType } from './contract'; +import type { SmartContractCreationStatus, SmartContractProxyType } from './contract'; import type { InternalTransaction } from './internalTransaction'; import type { MudWorldSchema, MudWorldTable } from './mudWorlds'; import type { NFTTokenType, TokenInfo, TokenInstance, TokenType } from './token'; @@ -14,6 +14,7 @@ export interface Address extends UserTags { creator_address_hash: string | null; creator_filecoin_robust_address?: string | null; creation_transaction_hash: string | null; + creation_status: SmartContractCreationStatus | null; exchange_rate: string | null; ens_domain_name: string | null; filecoin?: AddressFilecoinParams; diff --git a/types/api/contract.ts b/types/api/contract.ts index 4b8cf23059..ac3ee7f519 100644 --- a/types/api/contract.ts +++ b/types/api/contract.ts @@ -3,6 +3,8 @@ import type { Abi, AbiType } from 'abitype'; export type SmartContractMethodArgType = AbiType; export type SmartContractMethodStateMutability = 'view' | 'nonpayable' | 'payable'; +export type SmartContractCreationStatus = 'success' | 'failed' | 'selfdestructed'; + export type SmartContractLicenseType = 'none' | 'unlicense' | @@ -39,7 +41,7 @@ export type SmartContractProxyType = export interface SmartContract { deployed_bytecode: string | null; creation_bytecode: string | null; - is_self_destructed: boolean; + creation_status: SmartContractCreationStatus | null; abi: Abi | null; compiler_version: string | null; evm_version: string | null; diff --git a/ui/address/AddressDetails.tsx b/ui/address/AddressDetails.tsx index caec177a10..0fb15010f0 100644 --- a/ui/address/AddressDetails.tsx +++ b/ui/address/AddressDetails.tsx @@ -17,6 +17,7 @@ import DetailedInfoSponsoredItem from 'ui/shared/DetailedInfo/DetailedInfoSponso import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity'; +import ContractCreationStatus from 'ui/shared/statusTag/ContractCreationStatus'; import AddressAlternativeFormat from './details/AddressAlternativeFormat'; import AddressBalance from './details/AddressBalance'; @@ -152,6 +153,7 @@ const AddressDetails = ({ addressQuery, isLoading }: Props) => { /> at txn + { data.creation_status && } ) } diff --git a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_contract-1.png b/ui/address/__screenshots__/AddressDetails.pw.tsx_default_contract-1.png index d6c75b8200..b3c741f692 100644 Binary files a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_contract-1.png and b/ui/address/__screenshots__/AddressDetails.pw.tsx_default_contract-1.png differ diff --git a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-contract-1.png b/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-contract-1.png index 5f3e2d0098..debd75ae7b 100644 Binary files a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-contract-1.png and b/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-contract-1.png differ diff --git a/ui/address/contract/ContractDetailsByteCode.tsx b/ui/address/contract/ContractDetailsByteCode.tsx new file mode 100644 index 0000000000..0e1a91688a --- /dev/null +++ b/ui/address/contract/ContractDetailsByteCode.tsx @@ -0,0 +1,69 @@ +import { Flex } from '@chakra-ui/react'; +import React from 'react'; + +import type { Address } from 'types/api/address'; +import type { SmartContract } from 'types/api/contract'; + +import { Alert } from 'toolkit/chakra/alert'; +import RawDataSnippet from 'ui/shared/RawDataSnippet'; + +import ContractDetailsVerificationButton from './ContractDetailsVerificationButton'; + +interface Props { + data: SmartContract; + isLoading: boolean; + addressData: Address; +} + +const ContractDetailsByteCode = ({ data, isLoading, addressData }: Props) => { + const canBeVerified = ![ 'selfdestructed', 'failed' ].includes(data.creation_status || '') && !data?.is_verified && addressData.proxy_type !== 'eip7702'; + + const verificationButton = ( + + ); + + const creationStatusText = (() => { + switch (data.creation_status) { + case 'selfdestructed': + return 'This contract self-destructed after deployment and there is no runtime bytecode. Below is the raw creation bytecode.'; + case 'failed': + return 'Contract creation failed and there is no runtime bytecode. Below is the raw creation bytecode.'; + default: + return null; + } + })(); + + return ( + + { data?.creation_bytecode && ( + + { creationStatusText } + + ) : null } + textareaMaxHeight="300px" + isLoading={ isLoading } + /> + ) } + { data?.deployed_bytecode && ( + + ) } + + ); +}; + +export default React.memo(ContractDetailsByteCode); diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_dark-color-mode_full-view-source-code-dark-mode-1.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_dark-color-mode_full-view-source-code-dark-mode-1.png index 538bcf6e89..72e82b233d 100644 Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_dark-color-mode_full-view-source-code-dark-mode-1.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_dark-color-mode_full-view-source-code-dark-mode-1.png differ diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-abi-1.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-abi-1.png index e7b1772fb6..1e9d0fb79c 100644 Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-abi-1.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-abi-1.png differ diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-bytecode-1.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-bytecode-1.png index a999d45939..ea5ef79280 100644 Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-bytecode-1.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-bytecode-1.png differ diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-compiler-1.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-compiler-1.png index 24c7955e64..a8e0ebf70a 100644 Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-compiler-1.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-compiler-1.png differ diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-source-code-dark-mode-1.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-source-code-dark-mode-1.png index 2d840ff7c7..e3619c6ec3 100644 Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-source-code-dark-mode-1.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-source-code-dark-mode-1.png differ diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_mobile-view-source-code-1.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_mobile-view-source-code-1.png index 6772283e9c..7b687be72d 100644 Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_mobile-view-source-code-1.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_mobile-view-source-code-1.png differ diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_non-verified-1.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_non-verified-1.png index b3c60bd9c6..7ac6f202e3 100644 Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_non-verified-1.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_non-verified-1.png differ diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_self-destructed-1.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_self-destructed-1.png index c15b922be6..fd8098cf68 100644 Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_self-destructed-1.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_self-destructed-1.png differ diff --git a/ui/address/contract/useContractDetailsTabs.tsx b/ui/address/contract/useContractDetailsTabs.tsx index 836ba74713..47f21cb06d 100644 --- a/ui/address/contract/useContractDetailsTabs.tsx +++ b/ui/address/contract/useContractDetailsTabs.tsx @@ -4,12 +4,10 @@ import React from 'react'; import type { Address } from 'types/api/address'; import type { SmartContract } from 'types/api/contract'; -import { Alert } from 'toolkit/chakra/alert'; import CodeViewSnippet from 'ui/shared/CodeViewSnippet'; -import RawDataSnippet from 'ui/shared/RawDataSnippet'; +import ContractDetailsByteCode from './ContractDetailsByteCode'; import ContractDetailsConstructorArgs from './ContractDetailsConstructorArgs'; -import ContractDetailsVerificationButton from './ContractDetailsVerificationButton'; import ContractSourceCode from './ContractSourceCode'; import type { CONTRACT_DETAILS_TAB_IDS } from './utils'; @@ -28,16 +26,7 @@ interface Props { export default function useContractDetailsTabs({ data, isLoading, addressData, sourceAddress }: Props): Array { - const canBeVerified = !data?.is_self_destructed && !data?.is_verified && addressData?.proxy_type !== 'eip7702'; - return React.useMemo(() => { - const verificationButton = ( - - ); return [ (data?.constructor_args || data?.source_code) ? { @@ -87,36 +76,9 @@ export default function useContractDetailsTabs({ data, isLoading, addressData, s (data?.creation_bytecode || data?.deployed_bytecode) ? { id: 'contract_bytecode' as const, - title: 'ByteCode', - component: ( - - { data?.creation_bytecode && ( - - Contracts that self destruct in their constructors have no contract code published and cannot be verified. - Displaying the init data provided of the creating transaction. - - ) : null } - textareaMaxHeight="300px" - isLoading={ isLoading } - /> - ) } - { data?.deployed_bytecode && ( - - ) } - - ), + title: 'Bytecode', + component: , } : undefined, ].filter(Boolean); - }, [ isLoading, addressData, data, sourceAddress, canBeVerified ]); + }, [ isLoading, addressData, data, sourceAddress ]); } diff --git a/ui/address/utils/useAddressQuery.ts b/ui/address/utils/useAddressQuery.ts index 99794de944..29f43bf395 100644 --- a/ui/address/utils/useAddressQuery.ts +++ b/ui/address/utils/useAddressQuery.ts @@ -75,6 +75,7 @@ export default function useAddressQuery({ hash, isEnabled = true }: Params): Add coin_balance: balance.toString(), creator_address_hash: null, creation_transaction_hash: null, + creation_status: null, exchange_rate: null, ens_domain_name: null, has_logs: false, diff --git a/ui/shared/statusTag/ContractCreationStatus.tsx b/ui/shared/statusTag/ContractCreationStatus.tsx new file mode 100644 index 0000000000..62ac35d166 --- /dev/null +++ b/ui/shared/statusTag/ContractCreationStatus.tsx @@ -0,0 +1,40 @@ +import React from 'react'; + +import type { SmartContractCreationStatus } from 'types/api/contract'; + +import type { BadgeProps } from 'toolkit/chakra/badge'; +import { Badge } from 'toolkit/chakra/badge'; +import { Tooltip } from 'toolkit/chakra/tooltip'; + +import StatusTag from './StatusTag'; + +interface Props extends BadgeProps { + status: SmartContractCreationStatus; +} + +const ContractCreationStatus = ({ status, ...rest }: Props) => { + switch (status) { + case 'success': + return ( + + + + ); + case 'failed': + return ( + + + + ); + case 'selfdestructed': + return ( + + Self-destructed + + ); + default: + return null; + } +}; + +export default React.memo(ContractCreationStatus);