mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
base functionality
This commit is contained in:
+2
-1
@@ -44,9 +44,10 @@ NEXT_PUBLIC_AD_ADBUTLER_ON=__PLACEHOLDER_FORNEXT_PUBLIC_AD_ADBUTLER_ON__
|
||||
# api config
|
||||
NEXT_PUBLIC_API_HOST=__PLACEHOLDER_FOR_NEXT_PUBLIC_API_HOST__
|
||||
NEXT_PUBLIC_API_BASE_PATH=__PLACEHOLDER_FOR_NEXT_PUBLIC_API_BASE_PATH__
|
||||
NEXT_PUBLIC_STATS_API_HOST=__PLACEHOLDER_FOR_NEXT_PUBLIC_STATS_API_HOST__
|
||||
NEXT_PUBLIC_API_PROTOCOL=__PLACEHOLDER_FOR_NEXT_PUBLIC_API_PROTOCOL__
|
||||
NEXT_PUBLIC_API_PORT=__PLACEHOLDER_FOR_NEXT_PUBLIC_API_PORT__
|
||||
NEXT_PUBLIC_STATS_API_HOST=__PLACEHOLDER_FOR_NEXT_PUBLIC_STATS_API_HOST__
|
||||
NEXT_PUBLIC_VISUALIZE_API_HOST=__PLACEHOLDER_FOR_NEXT_PUBLIC_VISUALIZE_API_HOST__
|
||||
|
||||
# external services config
|
||||
NEXT_PUBLIC_SENTRY_DSN=__PLACEHOLDER_FOR_NEXT_PUBLIC_SENTRY_DSN__
|
||||
|
||||
@@ -96,6 +96,7 @@ The app instance could be customized by passing following variables to NodeJS en
|
||||
| NEXT_PUBLIC_API_HOST | `string` *(optional)* | By default the API endpoint base URL will be set as `https://blockscout.com`. If it is not the case, pass the API host in this variable | `my-host.com` |
|
||||
| NEXT_PUBLIC_API_BASE_PATH | `string` *(optional)* | Base path for API endpoint url | `/poa/core` |
|
||||
| NEXT_PUBLIC_STATS_API_HOST | `string` *(optional)* | Pass the Stats API host in this variable | `https://my-host.com` |
|
||||
| NEXT_PUBLIC_VISUALIZE_API_HOST | `string` *(optional)* | Pass the Visualize API host in this variable | `https://my-host.com` |
|
||||
|
||||
|
||||
### Featured network configuration properties
|
||||
|
||||
@@ -108,6 +108,10 @@ const config = Object.freeze({
|
||||
endpoint: getEnvValue(process.env.NEXT_PUBLIC_STATS_API_HOST),
|
||||
basePath: '',
|
||||
},
|
||||
visualizeApi: {
|
||||
endpoint: getEnvValue(process.env.NEXT_PUBLIC_VISUALIZE_API_HOST),
|
||||
basePath: '',
|
||||
},
|
||||
homepage: {
|
||||
charts: parseEnvJson<Array<ChainIndicatorId>>(getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_CHARTS)) || [],
|
||||
plateGradient: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_PLATE_GRADIENT) ||
|
||||
|
||||
@@ -13,3 +13,4 @@ NEXT_PUBLIC_FOOTER_TWITTER_LINK=https://www.twitter.com/blockscoutcom
|
||||
# api config
|
||||
NEXT_PUBLIC_API_HOST=blockscout.com
|
||||
NEXT_PUBLIC_STATS_API_HOST=https://stats-test.aws-k8s.blockscout.com
|
||||
NEXT_PUBLIC_VISUALIZE_API_HOST=http://94.131.100.174:8050
|
||||
|
||||
@@ -542,6 +542,8 @@ frontend:
|
||||
_default: blockscout.com/eth/goerli
|
||||
NEXT_PUBLIC_STATS_API_HOST:
|
||||
_default: https://stats-test.aws-k8s.blockscout.com/
|
||||
NEXT_PUBLIC_VISUALIZE_API_HOST:
|
||||
_default: http://94.131.100.174:8050
|
||||
NEXT_PUBLIC_APP_HOST:
|
||||
_default: blockscout.com/eth/goerli
|
||||
NEXT_PUBLIC_LOGOUT_URL:
|
||||
|
||||
@@ -382,6 +382,8 @@ frontend:
|
||||
review: blockscout-main.test.aws-k8s.blockscout.com
|
||||
NEXT_PUBLIC_STATS_API_HOST:
|
||||
_default: https://stats-test.aws-k8s.blockscout.com/
|
||||
NEXT_PUBLIC_VISUALIZE_API_HOST:
|
||||
_default: http://94.131.100.174:8050
|
||||
NEXT_PUBLIC_AUTH_URL:
|
||||
_default: https://blockscout-main.test.aws-k8s.blockscout.com
|
||||
NEXT_PUBLIC_API_BASE_PATH:
|
||||
|
||||
@@ -29,6 +29,7 @@ import type { TokensResponse, TokensFilters } from 'types/api/tokens';
|
||||
import type { TokenTransferResponse, TokenTransferFilters } from 'types/api/tokenTransfer';
|
||||
import type { TransactionsResponseValidated, TransactionsResponsePending, Transaction } from 'types/api/transaction';
|
||||
import type { TTxsFilters } from 'types/api/txsFilters';
|
||||
import type { VisualizedContract } from 'types/api/visualization';
|
||||
import type ArrayElement from 'types/utils/ArrayElement';
|
||||
|
||||
import appConfig from 'configs/app/config';
|
||||
@@ -83,6 +84,13 @@ export const RESOURCES = {
|
||||
basePath: appConfig.statsApi.basePath,
|
||||
},
|
||||
|
||||
// VISUALIZATION
|
||||
visualize_sol2uml: {
|
||||
path: '/api/v1/solidity\\:visualize-contracts',
|
||||
endpoint: appConfig.visualizeApi.endpoint,
|
||||
basePath: appConfig.visualizeApi.basePath,
|
||||
},
|
||||
|
||||
// BLOCKS, TXS
|
||||
blocks: {
|
||||
path: '/api/v2/blocks',
|
||||
@@ -356,6 +364,7 @@ Q extends 'contract_methods_read' ? Array<SmartContractReadMethod> :
|
||||
Q extends 'contract_methods_read_proxy' ? Array<SmartContractReadMethod> :
|
||||
Q extends 'contract_methods_write' ? Array<SmartContractWriteMethod> :
|
||||
Q extends 'contract_methods_write_proxy' ? Array<SmartContractWriteMethod> :
|
||||
Q extends 'visualize_sol2uml' ? VisualizedContract :
|
||||
never;
|
||||
/* eslint-enable @typescript-eslint/indent */
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { NextPage } from 'next';
|
||||
import Head from 'next/head';
|
||||
import React from 'react';
|
||||
|
||||
import getNetworkTitle from 'lib/networks/getNetworkTitle';
|
||||
import Sol2Uml from 'ui/pages/Sol2Uml';
|
||||
|
||||
const Sol2UmlPage: NextPage = () => {
|
||||
const title = getNetworkTitle();
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{ title }</title>
|
||||
</Head>
|
||||
<Sol2Uml/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sol2UmlPage;
|
||||
|
||||
export { getServerSideProps } from 'lib/next/getServerSideProps';
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface VisualizedContract {
|
||||
png: string | null;
|
||||
svg: string | null;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box, chakra, Flex, Link, Text, Tooltip } from '@chakra-ui/react';
|
||||
import { Box, chakra, Flex, Text, Tooltip } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { SmartContract } from 'types/api/contract';
|
||||
@@ -6,6 +6,7 @@ import type { SmartContract } from 'types/api/contract';
|
||||
import link from 'lib/link/link';
|
||||
import CodeEditor from 'ui/shared/CodeEditor';
|
||||
import CopyToClipboard from 'ui/shared/CopyToClipboard';
|
||||
import LinkInternal from 'ui/shared/LinkInternal';
|
||||
|
||||
interface Props {
|
||||
data: string;
|
||||
@@ -26,13 +27,13 @@ const ContractSourceCode = ({ data, hasSol2Yml, address, isViper, filePath, addi
|
||||
|
||||
const diagramLink = hasSol2Yml && address ? (
|
||||
<Tooltip label="Visualize contract code using Sol2Uml JS library">
|
||||
<Link
|
||||
<LinkInternal
|
||||
href={ link('visualize_sol2uml', undefined, { address }) }
|
||||
ml="auto"
|
||||
mr={ 3 }
|
||||
>
|
||||
View UML diagram
|
||||
</Link>
|
||||
</LinkInternal>
|
||||
</Tooltip>
|
||||
) : null;
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import Address from 'ui/shared/address/Address';
|
||||
import AddressIcon from 'ui/shared/address/AddressIcon';
|
||||
import AddressLink from 'ui/shared/address/AddressLink';
|
||||
import Page from 'ui/shared/Page/Page';
|
||||
import PageTitle from 'ui/shared/Page/PageTitle';
|
||||
import Sol2UmlDiagram from 'ui/sol2uml/Sol2UmlDiagram';
|
||||
|
||||
const Sol2Uml = () => {
|
||||
const router = useRouter();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const addressHash = router.query.address?.toString() || '';
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<PageTitle text="Solidity UML diagram"/>
|
||||
<Flex mb={ 10 }>
|
||||
<span>For contract</span>
|
||||
<Address ml={ 3 }>
|
||||
<AddressIcon address={{ hash: addressHash, is_contract: true, implementation_name: null }}/>
|
||||
<AddressLink hash={ addressHash } type="address" ml={ 2 } truncation={ isMobile ? 'constant' : 'dynamic' }/>
|
||||
</Address>
|
||||
</Flex>
|
||||
<Sol2UmlDiagram addressHash={ addressHash }/>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sol2Uml;
|
||||
@@ -0,0 +1,90 @@
|
||||
import { chakra, Tooltip, useColorModeValue } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { SmartContract } from 'types/api/contract';
|
||||
|
||||
import useApiQuery from 'lib/api/useApiQuery';
|
||||
import ContentLoader from 'ui/shared/ContentLoader';
|
||||
|
||||
interface Props {
|
||||
addressHash: string;
|
||||
}
|
||||
|
||||
function composeSources(contract: SmartContract | undefined) {
|
||||
if (!contract) {
|
||||
return {};
|
||||
}
|
||||
const additionalSources = contract.additional_sources?.reduce<Record<string, string>>((result, item) => {
|
||||
result[item.file_path] = item.source_code;
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
return {
|
||||
[contract.file_path || 'index.sol']: contract.source_code,
|
||||
...additionalSources,
|
||||
};
|
||||
}
|
||||
|
||||
const Sol2UmlDiagram = ({ addressHash }: Props) => {
|
||||
const contractQuery = useApiQuery('contract', {
|
||||
pathParams: { id: addressHash },
|
||||
queryOptions: {
|
||||
enabled: Boolean(addressHash),
|
||||
refetchOnMount: false,
|
||||
},
|
||||
});
|
||||
|
||||
const umlQuery = useApiQuery('visualize_sol2uml', {
|
||||
fetchParams: {
|
||||
method: 'POST',
|
||||
body: {
|
||||
sources: composeSources(contractQuery.data),
|
||||
},
|
||||
},
|
||||
queryOptions: {
|
||||
enabled: Boolean(contractQuery.data),
|
||||
refetchOnMount: false,
|
||||
},
|
||||
});
|
||||
|
||||
const imgUrl = `data:image/svg+xml;base64,${ umlQuery.data?.svg }`;
|
||||
const imgFilter = useColorModeValue('invert(0)', 'invert(1)');
|
||||
|
||||
const handleClick = React.useCallback(() => {
|
||||
const image = new Image();
|
||||
image.src = imgUrl;
|
||||
|
||||
const newWindow = window.open(imgUrl);
|
||||
newWindow?.document.write(image.outerHTML);
|
||||
}, [ imgUrl ]);
|
||||
|
||||
if (contractQuery.isError) {
|
||||
throw Error('Contract fetch error', { cause: contractQuery.error as unknown as Error });
|
||||
}
|
||||
|
||||
if (umlQuery.isError) {
|
||||
throw Error('Uml diagram fetch error', { cause: contractQuery.error as unknown as Error });
|
||||
}
|
||||
|
||||
if (contractQuery.isLoading || umlQuery.isLoading) {
|
||||
return <ContentLoader/>;
|
||||
}
|
||||
|
||||
if (!umlQuery.data.svg) {
|
||||
return <span>No data for visualization</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip label="Click on image to zoom" placement="top">
|
||||
<chakra.img
|
||||
src={ `data:image/svg+xml;base64,${ umlQuery.data.svg }` }
|
||||
alt={ `Contract ${ contractQuery.data.name } UML diagram` }
|
||||
onClick={ handleClick }
|
||||
cursor="pointer"
|
||||
filter={ imgFilter }
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Sol2UmlDiagram);
|
||||
Reference in New Issue
Block a user