diff --git a/configs/envs/.env.eth_sepolia b/configs/envs/.env.eth_sepolia index 3127563fb8..489bc29a91 100644 --- a/configs/envs/.env.eth_sepolia +++ b/configs/envs/.env.eth_sepolia @@ -18,7 +18,7 @@ NEXT_PUBLIC_NETWORK_RPC_URL=https://eth-sepolia.public.blastapi.io NEXT_PUBLIC_IS_TESTNET=true # api configuration -NEXT_PUBLIC_API_HOST=eth-sepolia.blockscout.com +NEXT_PUBLIC_API_HOST=eth-sepolia.k8s-dev.blockscout.com NEXT_PUBLIC_API_BASE_PATH=/ # ui config diff --git a/icons/verified_token.svg b/icons/certified.svg similarity index 100% rename from icons/verified_token.svg rename to icons/certified.svg diff --git a/mocks/contract/info.ts b/mocks/contract/info.ts index 4ff831e168..99f87f8742 100644 --- a/mocks/contract/info.ts +++ b/mocks/contract/info.ts @@ -51,6 +51,11 @@ export const verified: SmartContract = { minimal_proxy_address_hash: null, }; +export const certified: SmartContract = { + ...verified, + certified: true, +}; + export const withMultiplePaths: SmartContract = { ...verified, file_path: './simple_storage.sol', diff --git a/mocks/contracts/index.ts b/mocks/contracts/index.ts index bc3b4ecfb2..e5bf5e6de1 100644 --- a/mocks/contracts/index.ts +++ b/mocks/contracts/index.ts @@ -35,6 +35,7 @@ export const contract2: VerifiedContract = { watchlist_names: [], ens_domain_name: null, }, + certified: true, coin_balance: '9078234570352343999', compiler_version: 'v0.3.1+commit.0463ea4c', has_constructor_args: true, diff --git a/public/icons/name.d.ts b/public/icons/name.d.ts index 82909a5a3b..01a59cb9d7 100644 --- a/public/icons/name.d.ts +++ b/public/icons/name.d.ts @@ -24,6 +24,7 @@ | "brands/safe" | "brands/solidity_scan" | "burger" + | "certified" | "check" | "clock-light" | "clock" @@ -146,7 +147,6 @@ | "user_op_slim" | "user_op" | "validator" - | "verified_token" | "verified" | "verify-contract" | "wallet" diff --git a/types/api/contract.ts b/types/api/contract.ts index 50f4098d27..41fa0b5c4f 100644 --- a/types/api/contract.ts +++ b/types/api/contract.ts @@ -62,6 +62,7 @@ export interface SmartContract { minimal_proxy_address_hash: string | null; language: string | null; license_type: SmartContractLicenseType | null; + certified?: boolean; } export type SmartContractDecodedConstructorArg = [ diff --git a/types/api/contracts.ts b/types/api/contracts.ts index 65fe537568..75998004e9 100644 --- a/types/api/contracts.ts +++ b/types/api/contracts.ts @@ -3,6 +3,7 @@ import type { SmartContractLicenseType } from './contract'; export interface VerifiedContract { address: AddressParam; + certified?: boolean; coin_balance: string; compiler_version: string; language: 'vyper' | 'yul' | 'solidity'; diff --git a/ui/address/contract/ContractCode.pw.tsx b/ui/address/contract/ContractCode.pw.tsx index 3bcb857674..86fe6eef5c 100644 --- a/ui/address/contract/ContractCode.pw.tsx +++ b/ui/address/contract/ContractCode.pw.tsx @@ -109,6 +109,13 @@ test('with proxy address alert +@mobile', async({ render, mockApiResponse }) => await expect(component.getByRole('alert')).toHaveScreenshot(); }); +test('with certified icon +@mobile', async({ render, mockApiResponse, page }) => { + await mockApiResponse('contract', contractMock.certified, { pathParams: { hash: addressMock.contract.hash } }); + await render(, { hooksConfig }); + + await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1200, height: 120 } }); +}); + test('non verified', async({ render, mockApiResponse }) => { await mockApiResponse('contract', contractMock.nonVerified, { pathParams: { hash: addressMock.contract.hash } }); const component = await render(, { hooksConfig }, { withSocket: true }); diff --git a/ui/address/contract/ContractCode.tsx b/ui/address/contract/ContractCode.tsx index fddb9c8fd8..4403b3633f 100644 --- a/ui/address/contract/ContractCode.tsx +++ b/ui/address/contract/ContractCode.tsx @@ -16,6 +16,7 @@ import { getResourceKey } from 'lib/api/useApiQuery'; import { CONTRACT_LICENSES } from 'lib/contracts/licenses'; import dayjs from 'lib/date/dayjs'; import useSocketMessage from 'lib/socket/useSocketMessage'; +import ContractCertifiedLabel from 'ui/shared/ContractCertifiedLabel'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import Hint from 'ui/shared/Hint'; @@ -195,6 +196,13 @@ const ContractCode = ({ addressHash, contractQuery, channel }: Props) => { return null; })(); + const contractNameWithCertifiedIcon = data?.is_verified ? ( + + { data.name } + { data.certified && } + + ) : null; + return ( <> @@ -248,7 +256,7 @@ const ContractCode = ({ addressHash, contractQuery, channel }: Props) => { { data?.is_verified && ( - { data.name && } + { data.name && } { data.compiler_version && } { data.evm_version && } { licenseLink && ( diff --git a/ui/address/contract/__screenshots__/ContractCode.pw.tsx_default_with-certified-icon-mobile-1.png b/ui/address/contract/__screenshots__/ContractCode.pw.tsx_default_with-certified-icon-mobile-1.png new file mode 100644 index 0000000000..f9660a84e5 Binary files /dev/null and b/ui/address/contract/__screenshots__/ContractCode.pw.tsx_default_with-certified-icon-mobile-1.png differ diff --git a/ui/address/contract/__screenshots__/ContractCode.pw.tsx_mobile_with-certified-icon-mobile-1.png b/ui/address/contract/__screenshots__/ContractCode.pw.tsx_mobile_with-certified-icon-mobile-1.png new file mode 100644 index 0000000000..964b95edd1 Binary files /dev/null and b/ui/address/contract/__screenshots__/ContractCode.pw.tsx_mobile_with-certified-icon-mobile-1.png differ diff --git a/ui/pages/VerifiedContracts.pw.tsx b/ui/pages/VerifiedContracts.pw.tsx index ad0553eacd..da5de285ec 100644 --- a/ui/pages/VerifiedContracts.pw.tsx +++ b/ui/pages/VerifiedContracts.pw.tsx @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/experimental-ct-react'; +import { test, expect, devices } from '@playwright/experimental-ct-react'; import React from 'react'; import * as textAdMock from 'mocks/ad/textAd'; @@ -25,7 +25,7 @@ test.beforeEach(async({ page }) => { }); }); -test('base view +@mobile', async({ mount, page }) => { +test('base view', async({ mount, page }) => { await page.route(VERIFIED_CONTRACTS_API_URL, (route) => route.fulfill({ status: 200, body: JSON.stringify(verifiedContractsMock.baseResponse), @@ -43,3 +43,26 @@ test('base view +@mobile', async({ mount, page }) => { await expect(component).toHaveScreenshot(); }); + +test.describe('mobile', () => { + test.use({ viewport: devices['iPhone 13 Pro'].viewport }); + test('base view', async({ mount, page }) => { + + await page.route(VERIFIED_CONTRACTS_API_URL, (route) => route.fulfill({ + status: 200, + body: JSON.stringify(verifiedContractsMock.baseResponse), + })); + await page.route(VERIFIED_CONTRACTS_COUNTERS_API_URL, (route) => route.fulfill({ + status: 200, + body: JSON.stringify(verifiedContractsCountersMock), + })); + + const component = await mount( + + + , + ); + + await expect(component).toHaveScreenshot(); + }); +}); diff --git a/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_default_base-view-1.png b/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_default_base-view-1.png new file mode 100644 index 0000000000..89cb577685 Binary files /dev/null and b/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_default_base-view-1.png differ diff --git a/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_default_base-view-mobile-1.png b/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_default_base-view-mobile-1.png deleted file mode 100644 index 3a5eb8c821..0000000000 Binary files a/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_default_base-view-mobile-1.png and /dev/null differ diff --git a/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_default_mobile-base-view-1.png b/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_default_mobile-base-view-1.png new file mode 100644 index 0000000000..e90c55e337 Binary files /dev/null and b/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_default_mobile-base-view-1.png differ diff --git a/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_mobile_base-view-mobile-1.png b/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_mobile_base-view-mobile-1.png deleted file mode 100644 index 1d34aeadae..0000000000 Binary files a/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_mobile_base-view-mobile-1.png and /dev/null differ diff --git a/ui/searchResults/SearchResultListItem.tsx b/ui/searchResults/SearchResultListItem.tsx index d09cb77983..21ae64472c 100644 --- a/ui/searchResults/SearchResultListItem.tsx +++ b/ui/searchResults/SearchResultListItem.tsx @@ -69,7 +69,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => { textOverflow="ellipsis" /> - { data.is_verified_via_admin_panel && } + { data.is_verified_via_admin_panel && } ); } diff --git a/ui/searchResults/SearchResultTableItem.tsx b/ui/searchResults/SearchResultTableItem.tsx index d9a1cf6d74..8bb666873c 100644 --- a/ui/searchResults/SearchResultTableItem.tsx +++ b/ui/searchResults/SearchResultTableItem.tsx @@ -69,7 +69,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => { dangerouslySetInnerHTML={{ __html: highlightText(name, searchTerm) }} /> - { data.is_verified_via_admin_panel && } + { data.is_verified_via_admin_panel && } diff --git a/ui/shared/ContractCertifiedLabel.tsx b/ui/shared/ContractCertifiedLabel.tsx new file mode 100644 index 0000000000..98e79f7cbb --- /dev/null +++ b/ui/shared/ContractCertifiedLabel.tsx @@ -0,0 +1,21 @@ +import { Box, Tooltip, chakra } from '@chakra-ui/react'; +import React from 'react'; + +import IconSvg from './IconSvg'; + +type Props = { + iconSize: number; + className?: string; +} + +const ContractCertifiedLabel = ({ iconSize, className }: Props) => { + return ( + + + + + + ); +}; + +export default chakra(ContractCertifiedLabel); diff --git a/ui/shared/Page/specs/DefaultView.tsx b/ui/shared/Page/specs/DefaultView.tsx index 0c117d1411..b347b5e32f 100644 --- a/ui/shared/Page/specs/DefaultView.tsx +++ b/ui/shared/Page/specs/DefaultView.tsx @@ -32,7 +32,7 @@ const DefaultView = () => { const contentAfter = ( <> - + { const contentAfter = ( <> - + { const icon = ; - const verifiedIcon = ; + const verifiedIcon = ; const name = ( { { verifiedInfoQuery.data?.tokenAddress && ( - + ) } diff --git a/ui/verifiedContracts/VerifiedContractsListItem.tsx b/ui/verifiedContracts/VerifiedContractsListItem.tsx index 04765654a5..c0830fd8db 100644 --- a/ui/verifiedContracts/VerifiedContractsListItem.tsx +++ b/ui/verifiedContracts/VerifiedContractsListItem.tsx @@ -8,6 +8,7 @@ import config from 'configs/app'; import { CONTRACT_LICENSES } from 'lib/contracts/licenses'; import dayjs from 'lib/date/dayjs'; import { currencyUnits } from 'lib/units'; +import ContractCertifiedLabel from 'ui/shared/ContractCertifiedLabel'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import HashStringShorten from 'ui/shared/HashStringShorten'; @@ -36,12 +37,15 @@ const VerifiedContractsListItem = ({ data, isLoading }: Props) => { return ( - + + + { data.certified && } + diff --git a/ui/verifiedContracts/VerifiedContractsTableItem.tsx b/ui/verifiedContracts/VerifiedContractsTableItem.tsx index 3bafd4446e..5ba5053b5a 100644 --- a/ui/verifiedContracts/VerifiedContractsTableItem.tsx +++ b/ui/verifiedContracts/VerifiedContractsTableItem.tsx @@ -7,6 +7,7 @@ import type { VerifiedContract } from 'types/api/contracts'; import config from 'configs/app'; import { CONTRACT_LICENSES } from 'lib/contracts/licenses'; import dayjs from 'lib/date/dayjs'; +import ContractCertifiedLabel from 'ui/shared/ContractCertifiedLabel'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import HashStringShorten from 'ui/shared/HashStringShorten'; @@ -34,13 +35,15 @@ const VerifiedContractsTableItem = ({ data, isLoading }: Props) => { return ( - + + + { data.certified && } +