Skip to content

Commit

Permalink
Display proxy pattern info in UI
Browse files Browse the repository at this point in the history
Fixes #2209
  • Loading branch information
tom2drum committed Sep 11, 2024
1 parent a3cdf62 commit c5e3d2e
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 20 deletions.
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 && (
<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: 'Proxy via public implementation getter',
},
basic_get_implementation: {
name: 'Proxy via public getImplementation getter',
},
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.

0 comments on commit c5e3d2e

Please sign in to comment.