Skip to content

Commit

Permalink
Display proxy pattern info on contract page (#2230)
Browse files Browse the repository at this point in the history
* Display proxy pattern info in UI

Fixes #2209

* update texts

* [skip ci] adjust alert condition
  • Loading branch information
tom2drum authored Sep 12, 2024
1 parent 87ebabf commit d1632da
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 23 deletions.
6 changes: 3 additions & 3 deletions mocks/contract/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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,
};
16 changes: 15 additions & 1 deletion types/api/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
23 changes: 4 additions & 19 deletions ui/address/contract/ContractCode.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
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';
Expand All @@ -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';

Expand Down Expand Up @@ -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
Expand All @@ -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 }>
Expand Down
20 changes: 20 additions & 0 deletions ui/address/contract/ContractCodeProxyPattern.pw.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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();
});
89 changes: 89 additions & 0 deletions ui/address/contract/ContractCodeProxyPattern.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
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);
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.
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.

0 comments on commit d1632da

Please sign in to comment.