Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suave customization #1256

Merged
merged 12 commits into from
Oct 9, 2023
Merged
1 change: 1 addition & 0 deletions configs/app/features/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ export { default as safe } from './safe';
export { default as sentry } from './sentry';
export { default as sol2uml } from './sol2uml';
export { default as stats } from './stats';
export { default as suave } from './suave';
export { default as web3Wallet } from './web3Wallet';
export { default as verifiedTokens } from './verifiedTokens';
21 changes: 21 additions & 0 deletions configs/app/features/suave.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Feature } from './types';

import { getEnvValue } from '../utils';

const title = 'SUAVE chain';

const config: Feature<{ isEnabled: true }> = (() => {
if (getEnvValue('NEXT_PUBLIC_IS_SUAVE_CHAIN') === 'true') {
return Object.freeze({
title,
isEnabled: true,
});
}

return Object.freeze({
title,
isEnabled: false,
});
})();

export default config;
1 change: 1 addition & 0 deletions deploy/tools/envs-validator/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ const schema = yup
NEXT_PUBLIC_PROMOTE_BLOCKSCOUT_IN_TITLE: yup.boolean(),
NEXT_PUBLIC_OG_DESCRIPTION: yup.string(),
NEXT_PUBLIC_OG_IMAGE_URL: yup.string().test(urlTest),
NEXT_PUBLIC_IS_SUAVE_CHAIN: yup.boolean(),

// 6. External services envs
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: yup.string(),
Expand Down
2 changes: 2 additions & 0 deletions deploy/values/review/values.yaml.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ frontend:
exact:
# - "/(apps|auth/profile|account)"
- "/"
- "/envs.js"
prefix:
- "/_next"
- "/node-api"
- "/account"
- "/apps"
- "/static"
- "/assets"
- "/favicon"
- "/auth/profile"
- "/auth/unverified-email"
Expand Down
11 changes: 11 additions & 0 deletions docs/ENVS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ The app instance could be customized by passing following variables to NodeJS en
- [Blockchain statistics](ENVS.md#blockchain-statistics)
- [Web3 wallet integration](ENVS.md#web3-wallet-integration-add-token-or-network-to-the-wallet) (add token or network to the wallet)
- [Verified tokens info](ENVS.md#verified-tokens-info)
- [SUAVE chain](ENVS.md#suave-chain)
- [Sentry error monitoring](ENVS.md#sentry-error-monitoring)
- [3rd party services configuration](ENVS.md#external-services-configuration)

Expand Down Expand Up @@ -411,6 +412,16 @@ For the smart contract addresses which are [Safe{Core} accounts](https://safe.gl

&nbsp;

### SUAVE chain

For blockchains that implementing SUAVE architecture additional fields will be shown on the transaction page ("Allowed peekers", "Computor"). Users also will be able to see the list of all transaction for a particular Computor in the separate view.

| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_IS_SUAVE_CHAIN | `boolean` | Set to true for blockchains with [SUAVE architecture](https://writings.flashbots.net/mevm-suave-centauri-and-beyond) | Required | - | `true` |

&nbsp;

### Sentry error monitoring

| Variable | Type| Description | Compulsoriness | Default value | Example value |
Expand Down
8 changes: 7 additions & 1 deletion lib/api/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,11 @@ export const RESOURCES = {
path: '/api/v2/transactions/watchlist',
filterFields: [ ],
},
txs_execution_node: {
path: '/api/v2/transactions/execution-node/:hash',
pathParams: [ 'hash' as const ],
filterFields: [ ],
},
tx: {
path: '/api/v2/transactions/:hash',
pathParams: [ 'hash' as const ],
Expand Down Expand Up @@ -533,7 +538,7 @@ export interface ResourceError<T = unknown> {
export type ResourceErrorAccount<T> = ResourceError<{ errors: T }>

export type PaginatedResources = 'blocks' | 'block_txs' |
'txs_validated' | 'txs_pending' | 'txs_watchlist' |
'txs_validated' | 'txs_pending' | 'txs_watchlist' | 'txs_execution_node' |
'tx_internal_txs' | 'tx_logs' | 'tx_token_transfers' | 'tx_state_changes' |
'addresses' |
'address_txs' | 'address_internal_txs' | 'address_token_transfers' | 'address_blocks_validated' | 'address_coin_balance' |
Expand Down Expand Up @@ -577,6 +582,7 @@ Q extends 'block_withdrawals' ? BlockWithdrawalsResponse :
Q extends 'txs_validated' ? TransactionsResponseValidated :
Q extends 'txs_pending' ? TransactionsResponsePending :
Q extends 'txs_watchlist' ? TransactionsResponseWatchlist :
Q extends 'txs_execution_node' ? TransactionsResponseValidated :
Q extends 'tx' ? Transaction :
Q extends 'tx_internal_txs' ? InternalTransactionsResponse :
Q extends 'tx_logs' ? LogsResponseTx :
Expand Down
1 change: 1 addition & 0 deletions lib/metadata/getPageOgType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type OGPageType = 'Homepage' | 'Root page' | 'Regular page';
const OG_TYPE_DICT: Record<Route['pathname'], OGPageType> = {
'/': 'Homepage',
'/txs': 'Root page',
'/txs/computor/[hash]': 'Regular page',
'/tx/[hash]': 'Regular page',
'/blocks': 'Root page',
'/block/[height_or_hash]': 'Regular page',
Expand Down
1 change: 1 addition & 0 deletions lib/metadata/templates/description.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const DEFAULT_TEMPLATE = 'Blockscout is the #1 open-source blockchain explorer a
const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/': DEFAULT_TEMPLATE,
'/txs': DEFAULT_TEMPLATE,
'/txs/computor/[hash]': DEFAULT_TEMPLATE,
'/tx/[hash]': 'View transaction %hash% on %network_title%',
'/blocks': DEFAULT_TEMPLATE,
'/block/[height_or_hash]': 'View the transactions, token transfers, and uncles for block %height_or_hash%',
Expand Down
1 change: 1 addition & 0 deletions lib/metadata/templates/title.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Route } from 'nextjs-routes';
const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/': 'blockchain explorer',
'/txs': 'transactions',
'/txs/computor/[hash]': 'computor %hash% transactions',
'/tx/[hash]': 'transaction %hash%',
'/blocks': 'blocks',
'/block/[height_or_hash]': 'block %height_or_hash%',
Expand Down
1 change: 1 addition & 0 deletions lib/mixpanel/getPageType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Route } from 'nextjs-routes';
export const PAGE_TYPE_DICT: Record<Route['pathname'], string> = {
'/': 'Homepage',
'/txs': 'Transactions',
'/txs/computor/[hash]': 'Computor transactions',
'/tx/[hash]': 'Transaction details',
'/blocks': 'Blocks',
'/block/[height_or_hash]': 'Block details',
Expand Down
10 changes: 10 additions & 0 deletions nextjs/getServerSideProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,13 @@ export const stats: GetServerSideProps<Props> = async(context) => {

return base(context);
};

export const suave: GetServerSideProps<Props> = async(context) => {
if (!config.features.suave.isEnabled) {
return {
notFound: true,
};
}

return base(context);
};
1 change: 1 addition & 0 deletions nextjs/nextjs-routes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ declare module "nextjs-routes" {
| DynamicRoute<"/token/[hash]/instance/[id]", { "hash": string; "id": string }>
| StaticRoute<"/tokens">
| DynamicRoute<"/tx/[hash]", { "hash": string }>
| DynamicRoute<"/txs/computor/[hash]", { "hash": string }>
| StaticRoute<"/txs">
| StaticRoute<"/verified-contracts">
| StaticRoute<"/visualize/sol2uml">
Expand Down
20 changes: 20 additions & 0 deletions pages/txs/computor/[hash].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { NextPage } from 'next';
import dynamic from 'next/dynamic';
import React from 'react';

import type { Props } from 'nextjs/getServerSideProps';
import PageNextJs from 'nextjs/PageNextJs';

const ComputorTxs = dynamic(() => import('ui/pages/ComputorTxs'), { ssr: false });

const Page: NextPage<Props> = (props: Props) => {
return (
<PageNextJs pathname="/txs/computor/[hash]" query={ props }>
<ComputorTxs/>
</PageNextJs>
);
};

export default Page;

export { suave as getServerSideProps } from 'nextjs/getServerSideProps';
File renamed without changes.
7 changes: 7 additions & 0 deletions types/api/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ export type TransactionRevertReason = {
raw: string;
} | DecodedInput;

type WrappedTransactionFields = 'decoded_input' | 'fee' | 'gas_limit' | 'gas_price' | 'hash' | 'max_fee_per_gas' |
'max_priority_fee_per_gas' | 'method' | 'nonce' | 'raw_input' | 'to' | 'type' | 'value';

export type Transaction = {
to: AddressParam | null;
created_contract: AddressParam | null;
Expand Down Expand Up @@ -48,6 +51,10 @@ export type Transaction = {
l1_gas_price?: string;
l1_gas_used?: string;
has_error_in_internal_txs: boolean | null;
// SUAVE fields
execution_node?: AddressParam | null;
allowed_peekers?: Array<string>;
wrapped?: Pick<Transaction, WrappedTransactionFields>;
}

export type TransactionsResponse = TransactionsResponseValidated | TransactionsResponsePending;
Expand Down
2 changes: 2 additions & 0 deletions types/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ export type ArrayElement<ArrType> = ArrType extends ReadonlyArray<infer ElementT

export type ExcludeNull<T> = T extends null ? never : T;

export type ExcludeUndefined<T> = T extends undefined ? never : T;

export type KeysOfObjectOrNull<T> = keyof ExcludeNull<T>;
14 changes: 3 additions & 11 deletions ui/block/BlockDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import Icon from 'ui/shared/chakra/Icon';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
Expand Down Expand Up @@ -81,15 +82,6 @@ const BlockDetails = ({ query }: Props) => {
return null;
}

const sectionGap = (
<GridItem
colSpan={{ base: undefined, lg: 2 }}
mt={{ base: 2, lg: 3 }}
mb={{ base: 0, lg: 3 }}
borderBottom="1px solid"
borderColor="divider"
/>
);
const { totalReward, staticReward, burntFees, txFees } = getBlockReward(data);

const validatorTitle = getNetworkValidatorTitle();
Expand Down Expand Up @@ -243,7 +235,7 @@ const BlockDetails = ({ query }: Props) => {
))
}

{ sectionGap }
<DetailsInfoItemDivider/>

<DetailsInfoItem
title="Gas used"
Expand Down Expand Up @@ -442,7 +434,7 @@ const BlockDetails = ({ query }: Props) => {
</Box>
</DetailsInfoItem>

{ sectionGap }
<DetailsInfoItemDivider/>

<DetailsInfoItem
title="Hash"
Expand Down
42 changes: 42 additions & 0 deletions ui/pages/ComputorTxs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useRouter } from 'next/router';
import React from 'react';

import getQueryParamString from 'lib/router/getQueryParamString';
import { TX } from 'stubs/tx';
import { generateListStub } from 'stubs/utils';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import PageTitle from 'ui/shared/Page/PageTitle';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import TxsContent from 'ui/txs/TxsContent';

const ComputorTxs = () => {
const router = useRouter();

const hash = getQueryParamString(router.query.hash);

const query = useQueryWithPages({
resourceName: 'txs_execution_node',
pathParams: { hash },
options: {
placeholderData: generateListStub<'txs_execution_node'>(TX, 50, { next_page_params: {
block_number: 9005713,
index: 5,
items_count: 50,
filter: 'validated',
} }),
},
});

return (
<>
<PageTitle title="Computor transactions" withTextAd/>
<AddressEntity address={{ hash }} mb={ 6 }/>
<TxsContent
query={ query }
showSocketInfo={ false }
/>
</>
);
};

export default ComputorTxs;
34 changes: 24 additions & 10 deletions ui/pages/Transaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react';

import type { RoutedTab } from 'ui/shared/Tabs/types';

import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/contexts/app';
import useIsMobile from 'lib/hooks/useIsMobile';
Expand All @@ -13,22 +14,16 @@ import EntityTags from 'ui/shared/EntityTags';
import NetworkExplorers from 'ui/shared/NetworkExplorers';
import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton';
import useTabIndexFromQuery from 'ui/shared/Tabs/useTabIndexFromQuery';
import TxDetails from 'ui/tx/TxDetails';
import TxDetailsWrapped from 'ui/tx/TxDetailsWrapped';
import TxInternals from 'ui/tx/TxInternals';
import TxLogs from 'ui/tx/TxLogs';
import TxRawTrace from 'ui/tx/TxRawTrace';
import TxState from 'ui/tx/TxState';
import TxTokenTransfer from 'ui/tx/TxTokenTransfer';

const TABS: Array<RoutedTab> = [
{ id: 'index', title: 'Details', component: <TxDetails/> },
{ id: 'token_transfers', title: 'Token transfers', component: <TxTokenTransfer/> },
{ id: 'internal', title: 'Internal txns', component: <TxInternals/> },
{ id: 'logs', title: 'Logs', component: <TxLogs/> },
{ id: 'state', title: 'State', component: <TxState/> },
{ id: 'raw_trace', title: 'Raw trace', component: <TxRawTrace/> },
];

const TransactionPageContent = () => {
const router = useRouter();
const appProps = useAppContext();
Expand All @@ -44,6 +39,20 @@ const TransactionPageContent = () => {
},
});

const tabs: Array<RoutedTab> = [
{ id: 'index', title: config.features.suave.isEnabled && data?.wrapped ? 'Confidential compute tx details' : 'Details', component: <TxDetails/> },
config.features.suave.isEnabled && data?.wrapped ?
{ id: 'wrapped', title: 'Regular tx details', component: <TxDetailsWrapped data={ data.wrapped }/> } :
undefined,
{ id: 'token_transfers', title: 'Token transfers', component: <TxTokenTransfer/> },
{ id: 'internal', title: 'Internal txns', component: <TxInternals/> },
{ id: 'logs', title: 'Logs', component: <TxLogs/> },
{ id: 'state', title: 'State', component: <TxState/> },
{ id: 'raw_trace', title: 'Raw trace', component: <TxRawTrace/> },
].filter(Boolean);

const tabIndex = useTabIndexFromQuery(tabs);

const tags = (
<EntityTags
isLoading={ isPlaceholderData }
Expand Down Expand Up @@ -75,7 +84,12 @@ const TransactionPageContent = () => {
backLink={ backLink }
contentAfter={ tags }
/>
<RoutedTabs tabs={ TABS }/>
{ isPlaceholderData ? (
<>
<TabsSkeleton tabs={ tabs } mt={ 6 }/>
{ tabs[tabIndex]?.component }
</>
) : <RoutedTabs tabs={ tabs }/> }
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { GridItem } from '@chakra-ui/react';
import React from 'react';

const TokenInstanceDivider = () => {
const DetailsInfoItemDivider = () => {
return (
<GridItem
colSpan={{ base: undefined, lg: 2 }}
Expand All @@ -13,4 +13,4 @@ const TokenInstanceDivider = () => {
);
};

export default TokenInstanceDivider;
export default DetailsInfoItemDivider;
Loading
Loading