mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
Merge pull request #484 from blockscout/contract-creation-code
contract creation code
This commit is contained in:
@@ -14,6 +14,7 @@ import type {
|
||||
} from 'types/api/address';
|
||||
import type { BlocksResponse, BlockTransactionsResponse, Block, BlockFilters } from 'types/api/block';
|
||||
import type { ChartMarketResponse, ChartTransactionResponse } from 'types/api/charts';
|
||||
import type { SmartContract } from 'types/api/contract';
|
||||
import type { IndexingStatus } from 'types/api/indexingStatus';
|
||||
import type { InternalTransactionsResponse } from 'types/api/internalTransaction';
|
||||
import type { JsonRpcUrlResponse } from 'types/api/jsonRpcUrl';
|
||||
@@ -163,6 +164,11 @@ export const RESOURCES = {
|
||||
filterFields: [ ],
|
||||
},
|
||||
|
||||
// CONTRACT
|
||||
contract: {
|
||||
path: '/api/v2/smart-contracts/:id',
|
||||
},
|
||||
|
||||
// TOKEN
|
||||
token: {
|
||||
path: '/api/v2/tokens/:hash',
|
||||
@@ -270,6 +276,7 @@ Q extends 'address_logs' ? LogsResponseAddress :
|
||||
Q extends 'token' ? TokenInfo :
|
||||
Q extends 'token_counters' ? TokenCounters :
|
||||
Q extends 'config_json_rpc' ? JsonRpcUrlResponse :
|
||||
Q extends 'contract' ? SmartContract :
|
||||
never;
|
||||
/* eslint-enable @typescript-eslint/indent */
|
||||
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
"apps": "/apps",
|
||||
"app_index": "/apps/:id",
|
||||
"search_results": "/search-results",
|
||||
"other": "/search-results",
|
||||
"auth": "/auth/auth0",
|
||||
"stats": "/stats"
|
||||
"stats": "/stats",
|
||||
"visualize_sol2uml": "/visualize/sol2uml"
|
||||
}
|
||||
|
||||
+3
-3
@@ -94,9 +94,9 @@ export const ROUTES = {
|
||||
pattern: PATHS.search_results,
|
||||
},
|
||||
|
||||
// ??? what URL will be here
|
||||
other: {
|
||||
pattern: PATHS.other,
|
||||
// VISUALIZE
|
||||
visualize_sol2uml: {
|
||||
pattern: PATHS.visualize_sol2uml,
|
||||
},
|
||||
|
||||
// AUTH
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"@tanstack/react-query-devtools": "^4.0.10",
|
||||
"@types/papaparse": "^5.3.5",
|
||||
"@types/react-scroll": "^1.8.4",
|
||||
"ace-builds": "^1.14.0",
|
||||
"bignumber.js": "^9.1.0",
|
||||
"d3": "^7.6.1",
|
||||
"dayjs": "^1.11.5",
|
||||
@@ -57,6 +58,7 @@
|
||||
"pino-pretty": "^9.1.1",
|
||||
"qrcode": "^1.5.1",
|
||||
"react": "18.2.0",
|
||||
"react-ace": "^10.1.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "^7.33.1",
|
||||
"react-identicons": "^1.2.5",
|
||||
|
||||
@@ -6,6 +6,8 @@ import { mode } from '@chakra-ui/theme-tools';
|
||||
const { defineMultiStyleConfig, definePartsStyle } =
|
||||
createMultiStyleConfigHelpers(parts.keys);
|
||||
|
||||
import Button from './Button/Button';
|
||||
|
||||
const variantSoftRounded = definePartsStyle((props) => {
|
||||
return {
|
||||
tab: {
|
||||
@@ -26,11 +28,32 @@ const variantSoftRounded = definePartsStyle((props) => {
|
||||
};
|
||||
});
|
||||
|
||||
const variantOutline = definePartsStyle((props) => {
|
||||
return {
|
||||
tab: {
|
||||
...Button.variants?.outline(props),
|
||||
...Button.baseStyle,
|
||||
_selected: Button.variants?.outline(props)._active,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const sizes = {
|
||||
sm: definePartsStyle({
|
||||
tab: Button.sizes?.sm,
|
||||
}),
|
||||
md: definePartsStyle({
|
||||
tab: Button.sizes?.md,
|
||||
}),
|
||||
};
|
||||
|
||||
const variants = {
|
||||
'soft-rounded': variantSoftRounded,
|
||||
outline: variantOutline,
|
||||
};
|
||||
|
||||
const Tabs = defineMultiStyleConfig({
|
||||
sizes,
|
||||
variants,
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
export interface SmartContract {
|
||||
deployed_bytecode: string | null;
|
||||
creation_bytecode: string | null;
|
||||
is_self_destructed: boolean;
|
||||
abi: Array<Record<string, unknown>> | null;
|
||||
compiler_version: string | null;
|
||||
evm_version: string | null;
|
||||
optimization_enabled: boolean | null;
|
||||
optimization_runs: number | null;
|
||||
name: string | null;
|
||||
verified_at: string | null;
|
||||
is_verified: boolean | null;
|
||||
source_code: string | null;
|
||||
can_be_visualized_via_sol2uml: boolean | null;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
import type { RoutedSubTab } from 'ui/shared/RoutedTabs/types';
|
||||
|
||||
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
|
||||
|
||||
interface Props {
|
||||
tabs: Array<RoutedSubTab>;
|
||||
}
|
||||
|
||||
const AddressContract = ({ tabs }: Props) => {
|
||||
return <RoutedTabs tabs={ tabs } variant="outline" colorScheme="gray" size="sm" tabListProps={{ columnGap: 3 }}/>;
|
||||
};
|
||||
|
||||
export default React.memo(AddressContract);
|
||||
@@ -24,6 +24,10 @@ const AddressCoinBalanceChart = ({ addressHash }: Props) => {
|
||||
return <DataFetchAlert/>;
|
||||
}
|
||||
|
||||
if (!items?.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ChartWidget
|
||||
chartHeight="200px"
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import { Flex, Skeleton, Button, Grid, GridItem, Text } from '@chakra-ui/react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
|
||||
import useApiQuery from 'lib/api/useApiQuery';
|
||||
import link from 'lib/link/link';
|
||||
import DataFetchAlert from 'ui/shared/DataFetchAlert';
|
||||
import RawDataSnippet from 'ui/shared/RawDataSnippet';
|
||||
|
||||
const DynamicContractSourceCode = dynamic(
|
||||
() => import('./ContractSourceCode'),
|
||||
{ ssr: false },
|
||||
);
|
||||
|
||||
const InfoItem = ({ label, value }: { label: string; value: string }) => (
|
||||
<GridItem display="flex" columnGap={ 6 }>
|
||||
<Text w="170px" flexShrink={ 0 } fontWeight={ 500 }>{ label }</Text>
|
||||
<Text wordBreak="break-all">{ value }</Text>
|
||||
</GridItem>
|
||||
);
|
||||
|
||||
const ContractCode = () => {
|
||||
const router = useRouter();
|
||||
|
||||
const { data, isLoading, isError } = useApiQuery('contract', {
|
||||
pathParams: { id: router.query.id?.toString() },
|
||||
queryOptions: {
|
||||
enabled: Boolean(router.query.id),
|
||||
},
|
||||
});
|
||||
|
||||
if (isError) {
|
||||
return <DataFetchAlert/>;
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<>
|
||||
<Flex justifyContent="space-between" mb={ 2 }>
|
||||
<Skeleton w="180px" h={ 5 } borderRadius="full"/>
|
||||
<Skeleton w={ 5 } h={ 5 }/>
|
||||
</Flex>
|
||||
<Skeleton w="100%" h="250px" borderRadius="md"/>
|
||||
<Flex justifyContent="space-between" mb={ 2 } mt={ 6 }>
|
||||
<Skeleton w="180px" h={ 5 } borderRadius="full"/>
|
||||
<Skeleton w={ 5 } h={ 5 }/>
|
||||
</Flex>
|
||||
<Skeleton w="100%" h="400px" borderRadius="md"/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const verificationButton = (
|
||||
<Button
|
||||
size="sm"
|
||||
ml="auto"
|
||||
mr={ 3 }
|
||||
as="a"
|
||||
href={ link('address_contract_verification', { id: router.query.id?.toString() }) }
|
||||
>
|
||||
Verify & publish
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{ data.is_verified && (
|
||||
<Grid templateColumns={{ base: '1fr', lg: '1fr 1fr' }} rowGap={ 4 } columnGap={ 6 } mb={ 8 }>
|
||||
{ data.name && <InfoItem label="Contract name" value={ data.name }/> }
|
||||
{ data.compiler_version && <InfoItem label="Compiler version" value={ data.compiler_version }/> }
|
||||
{ data.evm_version && <InfoItem label="EVM version" value={ data.evm_version }/> }
|
||||
{ typeof data.optimization_enabled === 'boolean' && <InfoItem label="Optimization enabled" value={ data.optimization_enabled ? 'true' : 'false' }/> }
|
||||
{ data.optimization_runs && <InfoItem label="Optimization runs" value={ String(data.optimization_runs) }/> }
|
||||
{ data.verified_at && <InfoItem label="Verified at" value={ data.verified_at }/> }
|
||||
</Grid>
|
||||
) }
|
||||
<Flex flexDir="column" rowGap={ 6 }>
|
||||
{ data.source_code && (
|
||||
<DynamicContractSourceCode
|
||||
data={ data.source_code }
|
||||
hasSol2Yml={ Boolean(data.can_be_visualized_via_sol2uml) }
|
||||
address={ router.query.id?.toString() }
|
||||
/>
|
||||
) }
|
||||
{ data.abi && (
|
||||
<RawDataSnippet
|
||||
data={ JSON.stringify(data.abi) }
|
||||
title="Contract ABI"
|
||||
textareaMinHeight="200px"
|
||||
/>
|
||||
) }
|
||||
{ data.creation_bytecode && (
|
||||
<RawDataSnippet
|
||||
data={ data.creation_bytecode }
|
||||
title="Contract creation code"
|
||||
rightSlot={ data.is_verified ? null : verificationButton }
|
||||
/>
|
||||
) }
|
||||
{ data.deployed_bytecode && (
|
||||
<RawDataSnippet
|
||||
data={ data.deployed_bytecode }
|
||||
title="Deployed ByteCode"
|
||||
/>
|
||||
) }
|
||||
</Flex>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContractCode;
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Box, Flex, Link, Text, Tooltip } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import link from 'lib/link/link';
|
||||
import CodeEditor from 'ui/shared/CodeEditor';
|
||||
import CopyToClipboard from 'ui/shared/CopyToClipboard';
|
||||
|
||||
interface Props {
|
||||
data: string;
|
||||
hasSol2Yml: boolean;
|
||||
address?: string;
|
||||
}
|
||||
|
||||
const ContractSourceCode = ({ data, hasSol2Yml, address }: Props) => {
|
||||
return (
|
||||
<Box>
|
||||
<Flex justifyContent="space-between" alignItems="center" mb={ 3 }>
|
||||
<Text fontWeight={ 500 }>Contract source code</Text>
|
||||
{ hasSol2Yml && address && (
|
||||
<Tooltip label="Visualize contract code using Sol2Uml JS library">
|
||||
<Link
|
||||
href={ link('visualize_sol2uml', undefined, { address }) }
|
||||
ml="auto"
|
||||
mr={ 3 }
|
||||
>
|
||||
View Sol2uml
|
||||
</Link>
|
||||
</Tooltip>
|
||||
) }
|
||||
<CopyToClipboard text={ data }/>
|
||||
</Flex>
|
||||
<CodeEditor value={ data } id="source_code"/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ContractSourceCode);
|
||||
+18
-1
@@ -8,17 +8,28 @@ import useApiQuery from 'lib/api/useApiQuery';
|
||||
import notEmpty from 'lib/notEmpty';
|
||||
import AddressBlocksValidated from 'ui/address/AddressBlocksValidated';
|
||||
import AddressCoinBalance from 'ui/address/AddressCoinBalance';
|
||||
import AddressContract from 'ui/address/AddressContract';
|
||||
import AddressDetails from 'ui/address/AddressDetails';
|
||||
import AddressInternalTxs from 'ui/address/AddressInternalTxs';
|
||||
import AddressLogs from 'ui/address/AddressLogs';
|
||||
import AddressTokenTransfers from 'ui/address/AddressTokenTransfers';
|
||||
import AddressTxs from 'ui/address/AddressTxs';
|
||||
import AddressLogs from 'ui/address/logs/AddressLogs';
|
||||
import ContractCode from 'ui/address/contract/ContractCode';
|
||||
import TextAd from 'ui/shared/ad/TextAd';
|
||||
import Page from 'ui/shared/Page/Page';
|
||||
import PageTitle from 'ui/shared/Page/PageTitle';
|
||||
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
|
||||
import SkeletonTabs from 'ui/shared/skeletons/SkeletonTabs';
|
||||
|
||||
const CONTRACT_TABS = [
|
||||
{ id: 'contact_code', title: 'Code', component: <ContractCode/> },
|
||||
{ id: 'contact_decompiled_code', title: 'Decompiled code', component: <div>Decompiled code</div> },
|
||||
{ id: 'read_contract', title: 'Read contract', component: <div>Read contract</div> },
|
||||
{ id: 'read_proxy', title: 'Read proxy', component: <div>Read proxy</div> },
|
||||
{ id: 'write_contract', title: 'Write contract', component: <div>Write contract</div> },
|
||||
{ id: 'write_proxy', title: 'Write proxy', component: <div>Write proxy</div> },
|
||||
];
|
||||
|
||||
const AddressPageContent = () => {
|
||||
const router = useRouter();
|
||||
|
||||
@@ -46,6 +57,12 @@ const AddressPageContent = () => {
|
||||
// later api will return info about available tabs
|
||||
{ id: 'blocks_validated', title: 'Blocks validated', component: <AddressBlocksValidated/> },
|
||||
isContract ? { id: 'logs', title: 'Logs', component: <AddressLogs/> } : undefined,
|
||||
isContract ? {
|
||||
id: 'contract',
|
||||
title: 'Contract',
|
||||
component: <AddressContract tabs={ CONTRACT_TABS }/>,
|
||||
subTabs: CONTRACT_TABS,
|
||||
} : undefined,
|
||||
].filter(notEmpty);
|
||||
}, [ isContract ]);
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { chakra, useColorModeValue } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import AceEditor from 'react-ace';
|
||||
import 'ace-builds/src-noconflict/mode-javascript';
|
||||
import 'ace-builds/src-noconflict/theme-tomorrow';
|
||||
import 'ace-builds/src-noconflict/theme-tomorrow_night';
|
||||
import 'ace-builds/src-noconflict/ext-language_tools';
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
value: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const CodeEditorBase = chakra(({ id, value, className }: Props) => {
|
||||
|
||||
const theme = useColorModeValue('tomorrow', 'tomorrow_night');
|
||||
|
||||
return (
|
||||
<AceEditor
|
||||
className={ className }
|
||||
mode="javascript" // TODO need to find mode for solidity
|
||||
theme={ theme }
|
||||
value={ value }
|
||||
name={ id }
|
||||
editorProps={{ $blockScrolling: true }}
|
||||
readOnly
|
||||
width="100%"
|
||||
showPrintMargin={ false }
|
||||
maxLines={ 25 }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const CodeEditor = ({ id, value }: Props) => {
|
||||
// see theme/components/Textarea.ts variantFilledInactive
|
||||
const bgColor = useColorModeValue('#f5f5f6', '#1a1b1b');
|
||||
const gutterBgColor = useColorModeValue('gray.100', '#25282c');
|
||||
|
||||
return (
|
||||
<CodeEditorBase
|
||||
id={ id }
|
||||
value={ value }
|
||||
bgColor={ bgColor }
|
||||
borderRadius="md"
|
||||
overflow="hidden"
|
||||
sx={{
|
||||
'.ace_gutter': {
|
||||
backgroundColor: gutterBgColor,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(CodeEditor);
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Box, Flex, Text, Textarea, chakra } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import CopyToClipboard from './CopyToClipboard';
|
||||
|
||||
interface Props {
|
||||
data: string;
|
||||
title?: string;
|
||||
className?: string;
|
||||
rightSlot?: React.ReactNode;
|
||||
textareaMinHeight?: string;
|
||||
}
|
||||
|
||||
const RawDataSnippet = ({ data, className, title, rightSlot, textareaMinHeight }: Props) => {
|
||||
return (
|
||||
<Box className={ className }>
|
||||
<Flex justifyContent={ title ? 'space-between' : 'flex-end' } alignItems="center" mb={ 3 }>
|
||||
{ title && <Text fontWeight={ 500 }>{ title }</Text> }
|
||||
{ rightSlot }
|
||||
<CopyToClipboard text={ data }/>
|
||||
</Flex>
|
||||
<Textarea
|
||||
variant="filledInactive"
|
||||
p={ 4 }
|
||||
minHeight={ textareaMinHeight || '400px' }
|
||||
value={ data }
|
||||
fontSize="sm"
|
||||
borderRadius="md"
|
||||
readOnly
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(chakra(RawDataSnippet));
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ChakraProps } from '@chakra-ui/react';
|
||||
import type { ChakraProps, ThemingProps } from '@chakra-ui/react';
|
||||
import {
|
||||
Tab,
|
||||
Tabs,
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
TabPanels,
|
||||
Box,
|
||||
useColorModeValue,
|
||||
chakra,
|
||||
} from '@chakra-ui/react';
|
||||
import type { StyleProps } from '@chakra-ui/styled-system';
|
||||
import { useRouter } from 'next/router';
|
||||
@@ -28,14 +29,15 @@ const hiddenItemStyles: StyleProps = {
|
||||
visibility: 'hidden',
|
||||
};
|
||||
|
||||
interface Props {
|
||||
interface Props extends ThemingProps<'Tabs'> {
|
||||
tabs: Array<RoutedTab>;
|
||||
tabListProps?: ChakraProps;
|
||||
rightSlot?: React.ReactNode;
|
||||
stickyEnabled?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) => {
|
||||
const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled, className, ...themeProps }: Props) => {
|
||||
const router = useRouter();
|
||||
const scrollDirection = useScrollDirection();
|
||||
const [ activeTabIndex, setActiveTabIndex ] = useState<number>(tabs.length + 1);
|
||||
@@ -58,8 +60,9 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) =>
|
||||
useEffect(() => {
|
||||
if (router.isReady) {
|
||||
let tabIndex = 0;
|
||||
if (router.query.tab) {
|
||||
tabIndex = tabs.findIndex(({ id }) => id === router.query.tab);
|
||||
const tabFromRoute = router.query.tab;
|
||||
if (tabFromRoute) {
|
||||
tabIndex = tabs.findIndex(({ id, subTabs }) => id === tabFromRoute || subTabs?.some(({ id }) => id === tabFromRoute));
|
||||
if (tabIndex < 0) {
|
||||
tabIndex = 0;
|
||||
}
|
||||
@@ -89,12 +92,14 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) =>
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
variant="soft-rounded"
|
||||
colorScheme="blue"
|
||||
className={ className }
|
||||
variant={ themeProps.variant || 'soft-rounded' }
|
||||
colorScheme={ themeProps.colorScheme || 'blue' }
|
||||
isLazy
|
||||
onChange={ handleTabChange }
|
||||
index={ activeTabIndex }
|
||||
position="relative"
|
||||
size={ themeProps.size || 'md' }
|
||||
>
|
||||
<TabList
|
||||
marginBottom={{ base: 6, lg: 8 }}
|
||||
@@ -155,6 +160,7 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) =>
|
||||
ref={ tabsRefs[index] }
|
||||
{ ...(index < tabsCut ? {} : hiddenItemStyles) }
|
||||
scrollSnapAlign="start"
|
||||
flexShrink={ 0 }
|
||||
>
|
||||
{ tab.title }
|
||||
</Tab>
|
||||
@@ -169,4 +175,4 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) =>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(RoutedTabs);
|
||||
export default React.memo(chakra(RoutedTabs));
|
||||
|
||||
@@ -2,8 +2,11 @@ export interface RoutedTab {
|
||||
id: string;
|
||||
title: string;
|
||||
component: React.ReactNode;
|
||||
subTabs?: Array<RoutedSubTab>;
|
||||
}
|
||||
|
||||
export type RoutedSubTab = Omit<RoutedTab, 'subTabs'>;
|
||||
|
||||
export interface MenuButton {
|
||||
id: null;
|
||||
title: string;
|
||||
|
||||
+4
-16
@@ -1,11 +1,11 @@
|
||||
import { Flex, Textarea, Skeleton } from '@chakra-ui/react';
|
||||
import { Flex, Skeleton } from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
|
||||
import useApiQuery from 'lib/api/useApiQuery';
|
||||
import { SECOND } from 'lib/consts';
|
||||
import CopyToClipboard from 'ui/shared/CopyToClipboard';
|
||||
import DataFetchAlert from 'ui/shared/DataFetchAlert';
|
||||
import RawDataSnippet from 'ui/shared/RawDataSnippet';
|
||||
import TxPendingAlert from 'ui/tx/TxPendingAlert';
|
||||
import TxSocketAlert from 'ui/tx/TxSocketAlert';
|
||||
import useFetchTxInfo from 'ui/tx/useFetchTxInfo';
|
||||
@@ -41,24 +41,12 @@ const TxRawTrace = () => {
|
||||
}
|
||||
|
||||
if (data.length === 0) {
|
||||
return <span>There is no raw trace for this transaction.</span>;
|
||||
return <span>No trace entries found.</span>;
|
||||
}
|
||||
|
||||
const text = JSON.stringify(data, undefined, 4);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex justifyContent="end" mb={ 2 }>
|
||||
<CopyToClipboard text={ text }/>
|
||||
</Flex>
|
||||
<Textarea
|
||||
variant="filledInactive"
|
||||
minHeight="500px"
|
||||
p={ 4 }
|
||||
value={ text }
|
||||
/>
|
||||
</>
|
||||
);
|
||||
return <RawDataSnippet data={ text }/>;
|
||||
};
|
||||
|
||||
export default TxRawTrace;
|
||||
|
||||
@@ -3864,6 +3864,11 @@ abort-controller@^3.0.0:
|
||||
dependencies:
|
||||
event-target-shim "^5.0.0"
|
||||
|
||||
ace-builds@^1.14.0, ace-builds@^1.4.14:
|
||||
version "1.14.0"
|
||||
resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.14.0.tgz#85a6733b4fa17b0abc3dbfe38cd8d823cad79716"
|
||||
integrity sha512-3q8LvawomApRCt4cC0OzxVjDsZ609lDbm8l0Xl9uqG06dKEq4RT0YXLUyk7J2SxmqIp5YXzZNw767Dr8GKUruw==
|
||||
|
||||
acorn-globals@^7.0.0:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3"
|
||||
@@ -5012,6 +5017,11 @@ detect-node-es@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493"
|
||||
integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==
|
||||
|
||||
diff-match-patch@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz#abb584d5f10cd1196dfc55aa03701592ae3f7b37"
|
||||
integrity sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==
|
||||
|
||||
diff-sequences@^29.3.1:
|
||||
version "29.3.1"
|
||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.3.1.tgz#104b5b95fe725932421a9c6e5b4bef84c3f2249e"
|
||||
@@ -7244,6 +7254,16 @@ lodash.debounce@^4.0.8:
|
||||
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
||||
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
|
||||
|
||||
lodash.get@^4.4.2:
|
||||
version "4.4.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
|
||||
integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==
|
||||
|
||||
lodash.isequal@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||
integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==
|
||||
|
||||
[email protected]:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||
@@ -8035,6 +8055,17 @@ quick-format-unescaped@^4.0.3:
|
||||
resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7"
|
||||
integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==
|
||||
|
||||
react-ace@^10.1.0:
|
||||
version "10.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-10.1.0.tgz#d348eac2b16475231779070b6cd16768deed565f"
|
||||
integrity sha512-VkvUjZNhdYTuKOKQpMIZi7uzZZVgzCjM7cLYu6F64V0mejY8a2XTyPUIMszC6A4trbeMIHbK5fYFcT/wkP/8VA==
|
||||
dependencies:
|
||||
ace-builds "^1.4.14"
|
||||
diff-match-patch "^1.0.5"
|
||||
lodash.get "^4.4.2"
|
||||
lodash.isequal "^4.5.0"
|
||||
prop-types "^15.7.2"
|
||||
|
||||
react-clientside-effect@^1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz#29f9b14e944a376b03fb650eed2a754dd128ea3a"
|
||||
|
||||
Reference in New Issue
Block a user