From 7884356b268d9f0154e27fdd34bcc0b4d8fa5f2c Mon Sep 17 00:00:00 2001 From: tom goriunov Date: Mon, 6 May 2024 21:13:10 +0400 Subject: [PATCH] Get smart-contract bytecode from websocket (#1851) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Get smart-contract bytecode from websocket Fixes #1769 * fix test * i am stupid 😓 * Update values.yaml.gotmpl --------- Co-authored-by: Yan Vaskov <72267126+yvaskov@users.noreply.github.com> --- deploy/values/review/values.yaml.gotmpl | 8 +++--- lib/socket/types.ts | 2 ++ mocks/address/tabCounters.ts | 11 +++++++++ playwright/fixtures/socketServer.ts | 1 + ui/pages/Address.pw.tsx | 33 +++++++++++++++++++++++++ ui/pages/Address.tsx | 22 ++++++++++++++--- 6 files changed, 70 insertions(+), 7 deletions(-) create mode 100644 mocks/address/tabCounters.ts create mode 100644 ui/pages/Address.pw.tsx diff --git a/deploy/values/review/values.yaml.gotmpl b/deploy/values/review/values.yaml.gotmpl index ad040c80bd..0fc85885e2 100644 --- a/deploy/values/review/values.yaml.gotmpl +++ b/deploy/values/review/values.yaml.gotmpl @@ -50,10 +50,10 @@ frontend: NEXT_PUBLIC_APP_ENV: development NEXT_PUBLIC_APP_INSTANCE: review NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE: validation - NEXT_PUBLIC_FEATURED_NETWORKS: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-goerli.json - NEXT_PUBLIC_NETWORK_LOGO: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/goerli.svg - NEXT_PUBLIC_NETWORK_ICON: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/goerli.svg - NEXT_PUBLIC_API_HOST: eth-sepolia.blockscout.com + NEXT_PUBLIC_FEATURED_NETWORKS: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-sepolia.json + NEXT_PUBLIC_NETWORK_LOGO: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/sepolia.svg + NEXT_PUBLIC_NETWORK_ICON: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/sepolia.png + NEXT_PUBLIC_API_HOST: eth-sepolia.k8s-dev.blockscout.com NEXT_PUBLIC_STATS_API_HOST: https://stats-goerli.k8s-dev.blockscout.com/ NEXT_PUBLIC_VISUALIZE_API_HOST: http://visualizer-svc.visualizer-testing.svc.cluster.local/ NEXT_PUBLIC_CONTRACT_INFO_API_HOST: https://contracts-info-test.k8s-dev.blockscout.com diff --git a/lib/socket/types.ts b/lib/socket/types.ts index b2d8d7301a..d0f7564e56 100644 --- a/lib/socket/types.ts +++ b/lib/socket/types.ts @@ -28,6 +28,7 @@ SocketMessage.AddressTxs | SocketMessage.AddressTxsPending | SocketMessage.AddressTokenTransfer | SocketMessage.AddressChangedBytecode | +SocketMessage.AddressFetchedBytecode | SocketMessage.SmartContractWasVerified | SocketMessage.TokenTransfers | SocketMessage.TokenTotalSupply | @@ -64,6 +65,7 @@ export namespace SocketMessage { export type AddressTxsPending = SocketMessageParamsGeneric<'pending_transaction', { transactions: Array }>; export type AddressTokenTransfer = SocketMessageParamsGeneric<'token_transfer', { token_transfers: Array }>; export type AddressChangedBytecode = SocketMessageParamsGeneric<'changed_bytecode', Record>; + export type AddressFetchedBytecode = SocketMessageParamsGeneric<'fetched_bytecode', { fetched_bytecode: string }>; export type SmartContractWasVerified = SocketMessageParamsGeneric<'smart_contract_was_verified', Record>; export type TokenTransfers = SocketMessageParamsGeneric<'token_transfer', {token_transfer: number }>; export type TokenTotalSupply = SocketMessageParamsGeneric<'total_supply', {total_supply: number }>; diff --git a/mocks/address/tabCounters.ts b/mocks/address/tabCounters.ts new file mode 100644 index 0000000000..3853ffab4d --- /dev/null +++ b/mocks/address/tabCounters.ts @@ -0,0 +1,11 @@ +import type { AddressTabsCounters } from 'types/api/address'; + +export const base: AddressTabsCounters = { + internal_txs_count: 13, + logs_count: 51, + token_balances_count: 3, + token_transfers_count: 3, + transactions_count: 51, + validations_count: 42, + withdrawals_count: 11, +}; diff --git a/playwright/fixtures/socketServer.ts b/playwright/fixtures/socketServer.ts index f630cdd012..f6d730ce42 100644 --- a/playwright/fixtures/socketServer.ts +++ b/playwright/fixtures/socketServer.ts @@ -71,6 +71,7 @@ export function sendMessage(socket: WebSocket, channel: Channel, msg: 'new_block export function sendMessage(socket: WebSocket, channel: Channel, msg: 'verification_result', payload: SmartContractVerificationResponse): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'total_supply', payload: { total_supply: number}): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'changed_bytecode', payload: Record): void; +export function sendMessage(socket: WebSocket, channel: Channel, msg: 'fetched_bytecode', payload: { fetched_bytecode: string }): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'smart_contract_was_verified', payload: Record): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'token_transfer', payload: { token_transfers: Array }): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: string, payload: unknown): void { diff --git a/ui/pages/Address.pw.tsx b/ui/pages/Address.pw.tsx new file mode 100644 index 0000000000..b58cdd7841 --- /dev/null +++ b/ui/pages/Address.pw.tsx @@ -0,0 +1,33 @@ +import React from 'react'; + +import * as addressMock from 'mocks/address/address'; +import * as addressCountersMock from 'mocks/address/counters'; +import * as addressTabCountersMock from 'mocks/address/tabCounters'; +import * as socketServer from 'playwright/fixtures/socketServer'; +import { test, expect } from 'playwright/lib'; + +import Address from './Address'; + +const hooksConfig = { + router: { + query: { hash: addressMock.hash }, + }, +}; + +test.describe('fetched bytecode', () => { + test('should refetch address query', async({ render, mockApiResponse, createSocket, page }) => { + const addressApiUrl = await mockApiResponse('address', addressMock.validator, { pathParams: { hash: addressMock.hash } }); + await mockApiResponse('address_counters', addressCountersMock.forValidator, { pathParams: { hash: addressMock.hash } }); + await mockApiResponse('address_tabs_counters', addressTabCountersMock.base, { pathParams: { hash: addressMock.hash } }); + await mockApiResponse('address_txs', { items: [], next_page_params: null }, { pathParams: { hash: addressMock.hash } }); + await render(
, { hooksConfig }, { withSocket: true }); + + const socket = await createSocket(); + const channel = await socketServer.joinChannel(socket, `addresses:${ addressMock.hash.toLowerCase() }`); + socketServer.sendMessage(socket, channel, 'fetched_bytecode', { fetched_bytecode: '0x0123' }); + + const request = await page.waitForRequest(addressApiUrl); + + expect(request).toBeTruthy(); + }); +}); diff --git a/ui/pages/Address.tsx b/ui/pages/Address.tsx index be1a7b4daf..c21ae2d0e1 100644 --- a/ui/pages/Address.tsx +++ b/ui/pages/Address.tsx @@ -10,6 +10,8 @@ import { useAppContext } from 'lib/contexts/app'; import useContractTabs from 'lib/hooks/useContractTabs'; import useIsSafeAddress from 'lib/hooks/useIsSafeAddress'; import getQueryParamString from 'lib/router/getQueryParamString'; +import useSocketChannel from 'lib/socket/useSocketChannel'; +import useSocketMessage from 'lib/socket/useSocketMessage'; import { ADDRESS_TABS_COUNTERS } from 'stubs/address'; import { USER_OPS_ACCOUNT } from 'stubs/userOps'; import AddressAccountHistory from 'ui/address/AddressAccountHistory'; @@ -69,14 +71,28 @@ const AddressPageContent = () => { }, }); + const isLoading = addressQuery.isPlaceholderData || (config.features.userOps.isEnabled && userOpsAccountQuery.isPlaceholderData); + const isTabsLoading = isLoading || addressTabsCountersQuery.isPlaceholderData; + + const handleFetchedBytecodeMessage = React.useCallback(() => { + addressQuery.refetch(); + }, [ addressQuery ]); + + const channel = useSocketChannel({ + topic: `addresses:${ hash?.toLowerCase() }`, + isDisabled: isTabsLoading || addressQuery.isDegradedData || Boolean(addressQuery.data?.is_contract), + }); + useSocketMessage({ + channel, + event: 'fetched_bytecode', + handler: handleFetchedBytecodeMessage, + }); + const isSafeAddress = useIsSafeAddress(!addressQuery.isPlaceholderData && addressQuery.data?.is_contract ? hash : undefined); const safeIconColor = useColorModeValue('black', 'white'); const contractTabs = useContractTabs(addressQuery.data, addressQuery.isPlaceholderData); - const isLoading = addressQuery.isPlaceholderData || (config.features.userOps.isEnabled && userOpsAccountQuery.isPlaceholderData); - const isTabsLoading = isLoading || addressTabsCountersQuery.isPlaceholderData; - const tabs: Array = React.useMemo(() => { return [ {