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

Constructor array parameters are displayed as concatenated strings instead of arrays (#2407)

Fixes #2394
parent 712a4b13
...@@ -14,31 +14,38 @@ NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP={ "id": "632019", "width": "728", "height ...@@ -14,31 +14,38 @@ NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP={ "id": "632019", "width": "728", "height
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE={ "id": "632018", "width": "320", "height": "100" } NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE={ "id": "632018", "width": "320", "height": "100" }
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/ NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=eth-sepolia.k8s-dev.blockscout.com NEXT_PUBLIC_API_HOST=eth-sepolia.blockscout.com
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}] NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED=true NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED=true
NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'cow-swap'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'},{'text':'Get gas','icon':'gas','dappId':'smol-refuel'}] NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'cow-swap'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'}]
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/eth-sepolia.json NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/eth-sepolia.json
NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/sepolia.json NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/sepolia.json
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xbf69c7abc4fee283b59a9633dadfdaedde5c5ee0fba3e80a08b5b8a3acbd4363 NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xbf69c7abc4fee283b59a9633dadfdaedde5c5ee0fba3e80a08b5b8a3acbd4363
NEXT_PUBLIC_HAS_BEACON_CHAIN=true NEXT_PUBLIC_HAS_BEACON_CHAIN=true
NEXT_PUBLIC_HAS_USER_OPS=true NEXT_PUBLIC_HAS_USER_OPS=true
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs'] NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['rgba(51, 53, 67, 1)'],'text_color':['rgba(165, 252, 122, 1)']}
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_IS_TESTNET=true NEXT_PUBLIC_IS_TESTNET=true
NEXT_PUBLIC_LOGOUT_URL=https://blockscout-goerli.us.auth0.com/v2/logout NEXT_PUBLIC_LOGOUT_URL=https://blockscout-goerli.us.auth0.com/v2/logout
NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE=<p>Participated in our recent Blockscout activities? <a href="https://badges.blockscout.com?utm_source=instance&utm_medium=sepolia" target="_blank">Check your eligibility</a> and claim your NFT Scout badges. More exciting things are coming soon!</p>
NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL=https://gist.githubusercontent.com/maikReal/974c47f86a3158c1a86b092ae2f044b3/raw/abcc7e02150cd85d4974503a0357162c0a2c35a9/merits-banner.html
NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL=https://swap.blockscout.com?utm_source=blockscout&utm_medium=eth-sepolia
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json
NEXT_PUBLIC_MARKETPLACE_ENABLED=true NEXT_PUBLIC_MARKETPLACE_ENABLED=true
NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY=patbqG4V2CI998jAq.9810c58c9de973ba2650621c94559088cbdfa1a914498e385621ed035d33c0d0
NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID=appGkvtmKI7fXE4Vs
NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-security-reports/default.json NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-security-reports/default.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
NEXT_PUBLIC_METASUITES_ENABLED=true NEXT_PUBLIC_METASUITES_ENABLED=true
NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'dapp_id': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview?utm_source=blockscout&utm_medium=address', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'}] NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'},{'name': 'zapper', 'url_template': 'https://zapper.xyz/account/{address}', 'logo': 'https://blockscout-content.s3.amazonaws.com/zapper-icon.png'}]
NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/apps','/account/rewards'] NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/apps']
NEXT_PUBLIC_NAVIGATION_LAYOUT=horizontal
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
...@@ -55,11 +62,11 @@ NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation ...@@ -55,11 +62,11 @@ NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/sepolia-testnet.png NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/sepolia-testnet.png
NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://sepolia.drpc.org?ref=559183','text':'Public RPC'}] NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://sepolia.drpc.org?ref=559183','text':'Public RPC'}]
NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://merits.blockscout.com
NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-sepolia.safe.global NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-sepolia.safe.global
NEXT_PUBLIC_SENTRY_ENABLE_TRACING=true NEXT_PUBLIC_SENTRY_ENABLE_TRACING=true
NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s-dev.blockscout.com NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=noves NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=noves
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://points.k8s-dev.blockscout.com
NEXT_PUBLIC_XSTAR_SCORE_URL=https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm?utm_source=blockscout&utm_medium=address NEXT_PUBLIC_XSTAR_SCORE_URL=https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm?utm_source=blockscout&utm_medium=address
\ No newline at end of file
...@@ -24,7 +24,7 @@ export const verified: SmartContract = { ...@@ -24,7 +24,7 @@ export const verified: SmartContract = {
verified_at: '2021-08-03T10:40:41.679421Z', verified_at: '2021-08-03T10:40:41.679421Z',
decoded_constructor_args: [ decoded_constructor_args: [
[ '0xc59615da2da226613b1c78f0c6676cac497910bc', { internalType: 'address', name: '_token', type: 'address' } ], [ '0xc59615da2da226613b1c78f0c6676cac497910bc', { internalType: 'address', name: '_token', type: 'address' } ],
[ '1800', { internalType: 'uint256', name: '_duration', type: 'uint256' } ], [ [ 1800, 3600, 7200 ], { internalType: 'uint256[]', name: '_durations', type: 'uint256[]' } ],
[ '900000000', { internalType: 'uint256', name: '_totalSupply', type: 'uint256' } ], [ '900000000', { internalType: 'uint256', name: '_totalSupply', type: 'uint256' } ],
], ],
external_libraries: [ external_libraries: [
......
...@@ -76,7 +76,7 @@ export interface SmartContract { ...@@ -76,7 +76,7 @@ export interface SmartContract {
} }
export type SmartContractDecodedConstructorArg = [ export type SmartContractDecodedConstructorArg = [
string, unknown,
{ {
internalType: SmartContractMethodArgType; internalType: SmartContractMethodArgType;
name: string; name: string;
......
import { Box } from '@chakra-ui/react';
import React from 'react';
import type { SmartContract } from 'types/api/contract';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import type { Truncation } from 'ui/shared/entities/base/components';
import RawDataSnippet from 'ui/shared/RawDataSnippet';
import { matchArray } from './methods/form/utils';
interface DecodedItemProps {
value: unknown;
type: string;
addressTruncation?: Truncation;
}
const DecodedItemValue = ({ value, type, addressTruncation = 'dynamic' }: DecodedItemProps) => {
const arrayMatch = matchArray(type);
if (arrayMatch && Array.isArray(value)) {
return value.map((item, index) => (
<>
<DecodedItemValue key={ index } value={ item } type={ arrayMatch.itemType } addressTruncation="constant"/>
{ index < value.length - 1 && ', ' }
</>
));
}
if (type === 'address' && typeof value === 'string') {
return (
<AddressEntity
address={{ hash: value }}
noIcon
display="inline-flex"
maxW="100%"
truncation={ addressTruncation }
/>
);
}
const content = (() => {
if (value === null || value === undefined || value === '') {
return '""';
}
if (typeof value === 'object') {
return JSON.stringify(value);
}
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
return value;
}
return String(value);
})();
return <span>{ content }</span>;
};
interface Props {
data: SmartContract | undefined;
isLoading: boolean;
}
const ContractDetailsConstructorArgs = ({ data, isLoading }: Props) => {
const content = React.useMemo(() => {
if (!data?.decoded_constructor_args) {
return data?.constructor_args;
}
const decoded = data.decoded_constructor_args
.map(([ value, { name, type } ], index) => {
return (
<Box key={ index }>
<span>Arg [{ index }] { name || '' } ({ type }): </span>
<DecodedItemValue value={ value } type={ type }/>
</Box>
);
});
return (
<>
<span>{ data.constructor_args }</span>
<br/><br/>
{ decoded }
</>
);
}, [ data?.constructor_args, data?.decoded_constructor_args ]);
if (!content) {
return null;
}
return (
<RawDataSnippet
data={ content }
title="Constructor Arguments"
textareaMaxHeight="200px"
isLoading={ isLoading }
/>
);
};
export default React.memo(ContractDetailsConstructorArgs);
import { Alert, Box, Flex } from '@chakra-ui/react'; import { Alert, Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { SmartContract } from 'types/api/contract'; import type { SmartContract } from 'types/api/contract';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import RawDataSnippet from 'ui/shared/RawDataSnippet'; import RawDataSnippet from 'ui/shared/RawDataSnippet';
import ContractDetailsConstructorArgs from './ContractDetailsConstructorArgs';
import ContractDetailsVerificationButton from './ContractDetailsVerificationButton'; import ContractDetailsVerificationButton from './ContractDetailsVerificationButton';
import ContractSourceCode from './ContractSourceCode'; import ContractSourceCode from './ContractSourceCode';
import type { CONTRACT_DETAILS_TAB_IDS } from './utils'; import type { CONTRACT_DETAILS_TAB_IDS } from './utils';
...@@ -25,38 +25,6 @@ interface Props { ...@@ -25,38 +25,6 @@ interface Props {
export default function useContractDetailsTabs({ data, isLoading, addressHash, sourceAddress }: Props): Array<Tab> { export default function useContractDetailsTabs({ data, isLoading, addressHash, sourceAddress }: Props): Array<Tab> {
const constructorArgs = React.useMemo(() => {
if (!data?.decoded_constructor_args) {
return data?.constructor_args;
}
const decoded = data.decoded_constructor_args
.map(([ value, { name, type } ], index) => {
const valueEl = type === 'address' ? (
<AddressEntity
address={{ hash: value }}
noIcon
display="inline-flex"
maxW="100%"
/>
) : <span>{ value }</span>;
return (
<Box key={ index }>
<span>Arg [{ index }] { name || '' } ({ type }): </span>
{ valueEl }
</Box>
);
});
return (
<>
<span>{ data.constructor_args }</span>
<br/><br/>
{ decoded }
</>
);
}, [ data?.decoded_constructor_args, data?.constructor_args ]);
const canBeVerified = !data?.is_self_destructed && !data?.is_verified; const canBeVerified = !data?.is_self_destructed && !data?.is_verified;
return React.useMemo(() => { return React.useMemo(() => {
...@@ -69,19 +37,12 @@ export default function useContractDetailsTabs({ data, isLoading, addressHash, s ...@@ -69,19 +37,12 @@ export default function useContractDetailsTabs({ data, isLoading, addressHash, s
); );
return [ return [
(constructorArgs || data?.source_code) ? { (data?.constructor_args || data?.source_code) ? {
id: 'contract_source_code' as const, id: 'contract_source_code' as const,
title: 'Code', title: 'Code',
component: ( component: (
<Flex flexDir="column" rowGap={ 6 }> <Flex flexDir="column" rowGap={ 6 }>
{ constructorArgs && ( <ContractDetailsConstructorArgs data={ data } isLoading={ isLoading }/>
<RawDataSnippet
data={ constructorArgs }
title="Constructor Arguments"
textareaMaxHeight="200px"
isLoading={ isLoading }
/>
) }
{ data?.source_code && ( { data?.source_code && (
<ContractSourceCode <ContractSourceCode
data={ data } data={ data }
...@@ -152,5 +113,5 @@ export default function useContractDetailsTabs({ data, isLoading, addressHash, s ...@@ -152,5 +113,5 @@ export default function useContractDetailsTabs({ data, isLoading, addressHash, s
), ),
} : undefined, } : undefined,
].filter(Boolean); ].filter(Boolean);
}, [ isLoading, addressHash, data, constructorArgs, sourceAddress, canBeVerified ]); }, [ isLoading, addressHash, data, sourceAddress, canBeVerified ]);
} }
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