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

Display proxy pattern info on contract page (#2230)

* Display proxy pattern info in UI

Fixes #2209

* update texts

* [skip ci] adjust alert condition
parent 87ebabfa
......@@ -43,7 +43,7 @@ export const verified: SmartContract = {
file_path: '',
additional_sources: [],
verified_twin_address_hash: null,
minimal_proxy_address_hash: null,
proxy_type: null,
};
export const certified: SmartContract = {
......@@ -85,7 +85,7 @@ export const withProxyAddress: SmartContract = {
...verified,
is_verified: false,
verified_twin_address_hash: '0xa62744bee8646e237441cdbfdedd3458861748a8',
minimal_proxy_address_hash: '0xa62744bee8646e237441cdbfdedd3458861748a8',
proxy_type: 'eip1967',
};
export const selfDestructed: SmartContract = {
......@@ -133,7 +133,7 @@ export const nonVerified: SmartContract = {
additional_sources: [],
external_libraries: null,
verified_twin_address_hash: null,
minimal_proxy_address_hash: null,
proxy_type: null,
language: null,
license_type: null,
};
......@@ -19,6 +19,20 @@ export type SmartContractLicenseType =
'gnu_agpl_v3' |
'bsl_1_1';
export type SmartContractProxyType =
| 'eip1167'
| 'eip1967'
| 'eip1822'
| 'eip930'
| 'eip2535'
| 'master_copy'
| 'basic_implementation'
| 'basic_get_implementation'
| 'comptroller'
| 'clone_with_immutable_arguments'
| 'unknown'
| null;
export interface SmartContract {
deployed_bytecode: string | null;
creation_bytecode: string | null;
......@@ -53,7 +67,7 @@ export interface SmartContract {
remappings?: Array<string>;
};
verified_twin_address_hash: string | null;
minimal_proxy_address_hash: string | null;
proxy_type: SmartContractProxyType | null;
language: string | null;
license_type: SmartContractLicenseType | null;
certified?: boolean;
......
import { Flex, Skeleton, Button, Grid, GridItem, Alert, Link, chakra, Box, useColorModeValue } from '@chakra-ui/react';
import { Flex, Skeleton, Button, Grid, GridItem, Alert, chakra, Box, useColorModeValue } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import { useQueryClient } from '@tanstack/react-query';
import type { Channel } from 'phoenix';
......@@ -24,6 +24,7 @@ import LinkExternal from 'ui/shared/links/LinkExternal';
import LinkInternal from 'ui/shared/links/LinkInternal';
import RawDataSnippet from 'ui/shared/RawDataSnippet';
import ContractCodeProxyPattern from './ContractCodeProxyPattern';
import ContractSecurityAudits from './ContractSecurityAudits';
import ContractSourceCode from './ContractSourceCode';
......@@ -230,7 +231,7 @@ const ContractCode = ({ addressHash, contractQuery, channel }: Props) => {
Warning! Contract bytecode has been changed and does not match the verified one. Therefore, interaction with this smart contract may be risky.
</Alert>
) }
{ !data?.is_verified && data?.verified_twin_address_hash && !data?.minimal_proxy_address_hash && (
{ !data?.is_verified && data?.verified_twin_address_hash && (!data?.proxy_type || data.proxy_type === 'unknown') && (
<Alert status="warning" whiteSpace="pre-wrap" flexWrap="wrap">
<span>Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB </span>
<AddressEntity
......@@ -246,23 +247,7 @@ const ContractCode = ({ addressHash, contractQuery, channel }: Props) => {
<span> page</span>
</Alert>
) }
{ data?.minimal_proxy_address_hash && (
<Alert status="warning" flexWrap="wrap" whiteSpace="pre-wrap">
<span>Minimal Proxy Contract for </span>
<AddressEntity
address={{ hash: data.minimal_proxy_address_hash, is_contract: true }}
truncation="constant"
fontSize="sm"
fontWeight="500"
noCopy
/>
<span>. </span>
<Box>
<Link href="https://eips.ethereum.org/EIPS/eip-1167">EIP-1167</Link>
<span> - minimal bytecode implementation that delegates all calls to a known address</span>
</Box>
</Alert>
) }
{ data?.proxy_type && <ContractCodeProxyPattern type={ data.proxy_type }/> }
</Flex>
{ data?.is_verified && (
<Grid templateColumns={{ base: '1fr', lg: '1fr 1fr' }} rowGap={ 4 } columnGap={ 6 } mb={ 8 }>
......
import React from 'react';
import { test, expect } from 'playwright/lib';
import ContractCodeProxyPattern from './ContractCodeProxyPattern';
test('proxy type with link +@mobile', async({ render }) => {
const component = await render(<ContractCodeProxyPattern type="eip1167"/>);
await expect(component).toHaveScreenshot();
});
test('proxy type with link but without description', async({ render }) => {
const component = await render(<ContractCodeProxyPattern type="master_copy"/>);
await expect(component).toHaveScreenshot();
});
test('proxy type without link', async({ render }) => {
const component = await render(<ContractCodeProxyPattern type="basic_implementation"/>);
await expect(component).toHaveScreenshot();
});
import { Alert } from '@chakra-ui/react';
import React from 'react';
import type { SmartContractProxyType } from 'types/api/contract';
import LinkExternal from 'ui/shared/links/LinkExternal';
interface Props {
type: NonNullable<SmartContractProxyType>;
}
const PROXY_TYPES: Record<NonNullable<SmartContractProxyType>, {
name: string;
link?: string;
description?: string;
}> = {
eip1167: {
name: 'EIP-1167',
link: 'https://eips.ethereum.org/EIPS/eip-1167',
description: 'Minimal proxy',
},
eip1967: {
name: 'EIP-1967',
link: 'https://eips.ethereum.org/EIPS/eip-1967',
description: 'Proxy storage slots',
},
eip1822: {
name: 'EIP-1822',
link: 'https://eips.ethereum.org/EIPS/eip-1822',
description: 'Universal upgradeable proxy standard (UUPS)',
},
eip2535: {
name: 'EIP-2535',
link: 'https://eips.ethereum.org/EIPS/eip-2535',
description: 'Diamond proxy',
},
eip930: {
name: 'ERC-930',
link: 'https://github.com/ethereum/EIPs/issues/930',
description: 'Eternal storage',
},
clone_with_immutable_arguments: {
name: 'Clones with immutable arguments',
link: 'https://github.com/wighawag/clones-with-immutable-args',
},
master_copy: {
name: 'GnosisSafe',
link: 'https://github.com/safe-global/safe-smart-account',
},
comptroller: {
name: 'Compound protocol',
link: 'https://github.com/compound-finance/compound-protocol',
},
basic_implementation: {
name: 'public implementation getter in proxy smart-contract',
},
basic_get_implementation: {
name: 'public getImplementation getter in proxy smart-contract',
},
unknown: {
name: 'Unknown proxy pattern',
},
};
const ContractCodeProxyPattern = ({ type }: Props) => {
const proxyInfo = PROXY_TYPES[type];
if (!proxyInfo || type === 'unknown') {
return null;
}
return (
<Alert status="warning" flexWrap="wrap" whiteSpace="pre-wrap">
{ proxyInfo.link ? (
<>
This proxy smart-contract is detected via <LinkExternal href={ proxyInfo.link }>{ proxyInfo.name }</LinkExternal>
{ proxyInfo.description && ` - ${ proxyInfo.description }` }
</>
) : (
<>
This proxy smart-contract is detected via { proxyInfo.name }
{ proxyInfo.description && ` - ${ proxyInfo.description }` }
</>
) }
</Alert>
);
};
export default React.memo(ContractCodeProxyPattern);
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