Skip to content

Commit

Permalink
Constructor array parameters are displayed as concatenated strings in…
Browse files Browse the repository at this point in the history
…stead of arrays (#2407)

Fixes #2394
  • Loading branch information
tom2drum authored Nov 25, 2024
1 parent 712a4b1 commit f6307cd
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 53 deletions.
21 changes: 14 additions & 7 deletions configs/envs/.env.eth_sepolia
Original file line number Diff line number Diff line change
Expand Up @@ -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_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
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_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_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_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/sepolia.json
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xbf69c7abc4fee283b59a9633dadfdaedde5c5ee0fba3e80a08b5b8a3acbd4363
NEXT_PUBLIC_HAS_BEACON_CHAIN=true
NEXT_PUBLIC_HAS_USER_OPS=true
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_TESTNET=true
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_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_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL
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_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_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_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
Expand All @@ -55,11 +62,11 @@ NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
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_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_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_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
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
2 changes: 1 addition & 1 deletion mocks/contract/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const verified: SmartContract = {
verified_at: '2021-08-03T10:40:41.679421Z',
decoded_constructor_args: [
[ '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' } ],
],
external_libraries: [
Expand Down
2 changes: 1 addition & 1 deletion types/api/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export interface SmartContract {
}

export type SmartContractDecodedConstructorArg = [
string,
unknown,
{
internalType: SmartContractMethodArgType;
name: string;
Expand Down
106 changes: 106 additions & 0 deletions ui/address/contract/ContractDetailsConstructorArgs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
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);
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 5 additions & 44 deletions ui/address/contract/useContractDetailsTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Alert, Box, Flex } from '@chakra-ui/react';
import { Alert, Flex } from '@chakra-ui/react';
import React from 'react';

import type { SmartContract } from 'types/api/contract';

import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import RawDataSnippet from 'ui/shared/RawDataSnippet';

import ContractDetailsConstructorArgs from './ContractDetailsConstructorArgs';
import ContractDetailsVerificationButton from './ContractDetailsVerificationButton';
import ContractSourceCode from './ContractSourceCode';
import type { CONTRACT_DETAILS_TAB_IDS } from './utils';
Expand All @@ -25,38 +25,6 @@ interface Props {

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;

return React.useMemo(() => {
Expand All @@ -69,19 +37,12 @@ export default function useContractDetailsTabs({ data, isLoading, addressHash, s
);

return [
(constructorArgs || data?.source_code) ? {
(data?.constructor_args || data?.source_code) ? {
id: 'contract_source_code' as const,
title: 'Code',
component: (
<Flex flexDir="column" rowGap={ 6 }>
{ constructorArgs && (
<RawDataSnippet
data={ constructorArgs }
title="Constructor Arguments"
textareaMaxHeight="200px"
isLoading={ isLoading }
/>
) }
<ContractDetailsConstructorArgs data={ data } isLoading={ isLoading }/>
{ data?.source_code && (
<ContractSourceCode
data={ data }
Expand Down Expand Up @@ -152,5 +113,5 @@ export default function useContractDetailsTabs({ data, isLoading, addressHash, s
),
} : undefined,
].filter(Boolean);
}, [ isLoading, addressHash, data, constructorArgs, sourceAddress, canBeVerified ]);
}, [ isLoading, addressHash, data, sourceAddress, canBeVerified ]);
}

0 comments on commit f6307cd

Please sign in to comment.