Commit e0663888 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Merge pull request #2495 from blockscout/release/v1-37-2

Release v1.37.2
parents 20335b62 049b439b
......@@ -6,4 +6,8 @@ NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN=xxx
NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY=xxx
NEXT_PUBLIC_AUTH0_CLIENT_ID=xxx
NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY=xxx
NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY=xxx
\ No newline at end of file
NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY=xxx
## DEPRECATED
NEXT_PUBLIC_SENTRY_DSN=xxx
NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY=xxx
\ No newline at end of file
......@@ -31,7 +31,7 @@ const config: Feature<{
type,
L1BaseUrl: stripTrailingSlash(L1BaseUrl),
L2WithdrawalUrl: type === 'optimistic' ? L2WithdrawalUrl : undefined,
outputRootsEnabled: type === 'optimistic' && getEnvValue('NEXT_PUBLIC_ROLLUP_OUTPUT_ROOTS_ENABLED') !== 'false',
outputRootsEnabled: type === 'optimistic' && getEnvValue('NEXT_PUBLIC_ROLLUP_OUTPUT_ROOTS_ENABLED') === 'true',
parentChainName: type === 'arbitrum' ? getEnvValue('NEXT_PUBLIC_ROLLUP_PARENT_CHAIN_NAME') : undefined,
homepage: {
showLatestBlocks: getEnvValue('NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS') === 'true',
......
# Set of ENVs for Shibarium network explorer
# https://www.shibariumscan.io
# This is an auto-generated file. To update all values, run "yarn preset:sync --name=shibarium"
# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=shibarium"
# Local ENVs
NEXT_PUBLIC_APP_PROTOCOL=http
......@@ -23,6 +23,7 @@ NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethere
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Get gas','icon':'gas','dappId':'smol-refuel'}]
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/shibarium-mainnet.json
NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge
NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG={'name': 'Need gas?', 'url_template': 'https://smolrefuel.com/?outboundChain={chainId}&partner=blockscout&utm_source=blockscout&disableBridges=true', 'dapp_id': 'smol-refuel', 'logo': 'https://blockscout-content.s3.amazonaws.com/smolrefuel-logo-action-button.png'}
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xce531d29c0c469fb00b443b8091b8c059b4f13d7e025dd0ef843401d02b9a1a9
NEXT_PUBLIC_HAS_CONTRACT_AUDIT_REPORTS=true
......@@ -31,15 +32,17 @@ NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs', 'coin_price', 'market_cap']
NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['linear-gradient(180deg, rgba(224, 111, 44, 1) 0%, rgba(228, 144, 52, 1) 100%)'],'text_color':['rgba(255, 255, 255, 1)']}
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_LOGOUT_URL=https://shibarium.us.auth0.com/v2/logout
NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE=<p>Participated in our recent Blockscout activities? <a href="https://badges.blockscout.com?utm_source=instance&utm_medium=shibarium" target="_blank">Check your eligibility</a> and claim your NFT Scout badges. More exciting things are coming soon!</p>
NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE=<p>Joined recent campaigns? Mint your Merit Badge <a href="https://badges.blockscout.com?utm_source=instance&utm_medium=shibarium">here</a></p>
NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL=https://gist.githubusercontent.com/maikReal/974c47f86a3158c1a86b092ae2f044b3/raw/abcc7e02150cd85d4974503a0357162c0a2c35a9/merits-banner.html
NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL=https://swap.blockscout.com?utm_source=blockscout&utm_medium=shibarium
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json
NEXT_PUBLIC_MARKETPLACE_ENABLED=true
NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY=patbqG4V2CI998jAq.9810c58c9de973ba2650621c94559088cbdfa1a914498e385621ed035d33c0d0
NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID=appGkvtmKI7fXE4Vs
NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-security-reports/default.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/apps']
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=BONE
......
......@@ -455,7 +455,7 @@ This feature is **enabled by default** with the `coinzilla` ads provider. To swi
| NEXT_PUBLIC_FAULT_PROOF_ENABLED | `boolean` | Set to `true` for chains with fault proof system enabled (Optimistic stack only) | - | - | `true` | v1.31.0+ |
| NEXT_PUBLIC_HAS_MUD_FRAMEWORK | `boolean` | Set to `true` for instances that use MUD framework (Optimistic stack only) | - | - | `true` | v1.33.0+ |
| NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS | `boolean` | Set to `true` to display "Latest blocks" widget instead of "Latest batches" on the home page | - | - | `true` | v1.36.0+ |
| NEXT_PUBLIC_ROLLUP_OUTPUT_ROOTS_ENABLED | `boolean` | Enables "Output roots" page (Optimistic stack only) | - | `true` | `false` | v1.37.0+ |
| NEXT_PUBLIC_ROLLUP_OUTPUT_ROOTS_ENABLED | `boolean` | Enables "Output roots" page (Optimistic stack only) | - | `false` | `true` | v1.37.0+ |
| NEXT_PUBLIC_ROLLUP_PARENT_CHAIN_NAME | `string` | Set to customize L1 transaction status labels in the UI (e.g., "Sent to <chain-name>"). This setting is applicable only for Arbitrum-based chains. | - | - | `DuckChain` | v1.37.0+ |
&nbsp;
......
export const ABSENT_PARAM_ERROR_MESSAGE = 'Required param not provided';
export default function throwOnAbsentParamError(param: unknown) {
if (!param) {
throw new Error('Required param not provided', { cause: { status: 404 } });
throw new Error(ABSENT_PARAM_ERROR_MESSAGE, { cause: { status: 404 } });
}
}
......@@ -5,11 +5,9 @@ import isNeedProxy from 'lib/api/isNeedProxy';
import { getResourceKey } from 'lib/api/useApiQuery';
import * as cookies from 'lib/cookies';
import useFetch from 'lib/hooks/useFetch';
import { useRollbar } from 'lib/rollbar';
export default function useGetCsrfToken() {
const nodeApiFetch = useFetch();
const rollbar = useRollbar();
return useQuery({
queryKey: getResourceKey('csrf'),
......@@ -20,11 +18,13 @@ export default function useGetCsrfToken() {
const csrfFromHeader = apiResponse.headers.get('x-bs-account-csrf');
if (!csrfFromHeader) {
rollbar?.warn('Client fetch failed', {
resource: 'csrf',
status_code: 500,
status_text: 'Unable to obtain csrf token from header',
});
// I am not sure should we log this error or not
// so I commented it out for now
// rollbar?.warn('Client fetch failed', {
// resource: 'csrf',
// status_code: 500,
// status_text: 'Unable to obtain csrf token from header',
// });
return;
}
......
......@@ -3,6 +3,10 @@ import type React from 'react';
import type { Configuration } from 'rollbar';
import config from 'configs/app';
import { ABSENT_PARAM_ERROR_MESSAGE } from 'lib/errors/throwOnAbsentParamError';
import { RESOURCE_LOAD_ERROR_MESSAGE } from 'lib/errors/throwOnResourceLoadError';
import { isBot, isHeadlessBrowser, isNextJsChunkError, getRequestInfo } from './utils';
const feature = config.features.rollbar;
......@@ -20,4 +24,42 @@ export const clientConfig: Configuration | undefined = feature.isEnabled ? {
code_version: feature.codeVersion,
app_instance: feature.instance,
},
checkIgnore(isUncaught, args, item) {
if (isBot(window.navigator.userAgent)) {
return true;
}
if (isHeadlessBrowser(window.navigator.userAgent)) {
return true;
}
if (isNextJsChunkError(getRequestInfo(item)?.url)) {
return true;
}
return false;
},
hostSafeList: [ config.app.host ].filter(Boolean),
ignoredMessages: [
// these are React errors - "NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node."
// they could be caused by browser extensions
// one of the examples - https://github.com/facebook/react/issues/11538
// we can ignore them for now
'NotFoundError',
// these are errors that we throw on when make a call to the API
RESOURCE_LOAD_ERROR_MESSAGE,
ABSENT_PARAM_ERROR_MESSAGE,
// Filter out network-related errors that are usually not actionable
'Network Error',
'Failed to fetch',
// Filter out CORS errors from third-party extensions
'cross-origin',
// Filter out client-side navigation cancellations
'cancelled navigation',
],
maxItems: 10, // Max items per page load
} : undefined;
import type { Dictionary } from 'rollbar';
export function isBot(userAgent: string | undefined) {
if (!userAgent) return false;
const botPatterns = [
'Googlebot', // Google
'Baiduspider', // Baidu
'bingbot', // Bing
'YandexBot', // Yandex
'DuckDuckBot', // DuckDuckGo
'Slurp', // Yahoo
'Applebot', // Apple
'facebookexternalhit', // Facebook
'Twitterbot', // Twitter
'rogerbot', // Moz
'Alexa', // Alexa
'AhrefsBot', // Ahrefs
'SemrushBot', // Semrush
'spider', // Generic spiders
'crawler', // Generic crawlers
];
return botPatterns.some(pattern =>
userAgent.toLowerCase().includes(pattern.toLowerCase()),
);
}
export function isHeadlessBrowser(userAgent: string | undefined) {
if (!userAgent) return false;
if (
userAgent.includes('headless') ||
userAgent.includes('phantomjs') ||
userAgent.includes('selenium') ||
userAgent.includes('puppeteer')
) {
return true;
}
}
export function isNextJsChunkError(url: unknown) {
if (typeof url !== 'string') return false;
return url.includes('/_next/');
}
export function getRequestInfo(item: Dictionary): { url: string } | undefined {
if (
!item.request ||
item.request === null ||
typeof item.request !== 'object' ||
!('url' in item.request) ||
typeof item.request.url !== 'string'
) {
return undefined;
}
return { url: item.request.url };
}
......@@ -41,6 +41,7 @@ const LOCAL_ENVS = {
const IGNORED_ENVS = [
'NEXT_PUBLIC_GIT_COMMIT_SHA',
'NEXT_PUBLIC_GIT_TAG',
'NEXT_PUBLIC_ICON_SPRITE_HASH',
];
function parseScriptArgs() {
......
import type * as bens from '@blockscout/bens-types';
import type { TokenType } from 'types/api/token';
export type SearchResultType = 'token' | 'address' | 'block' | 'transaction' | 'contract';
......@@ -47,6 +48,7 @@ export interface SearchResultDomain {
expiry_date?: string;
name: string;
names_count: number;
protocol?: bens.ProtocolInfo;
};
}
......
......@@ -118,8 +118,10 @@ export default function useAddressQuery({ hash, isEnabled = true }: Params): Add
}, [ rpcQuery.data, rpcQuery.isPlaceholderData ]);
const isRpcQuery = Boolean(
(apiQuery.isError || apiQuery.isPlaceholderData) &&
!(apiQuery.error?.status && NO_RPC_FALLBACK_ERROR_CODES.includes(apiQuery.error?.status)) &&
!NO_RPC_FALLBACK_ERROR_CODES.includes(apiQuery.error?.status ?? 999) &&
apiQuery.errorUpdateCount > 0 &&
rpcQuery.data &&
publicClient,
......
......@@ -265,7 +265,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
case 'ens_domain': {
return (
<EnsEntity.Container>
<EnsEntity.Icon/>
<EnsEntity.Icon protocol={ data.ens_info.protocol }/>
<LinkInternal
href={ route({ pathname: '/address/[hash]', query: { hash: data.address } }) }
fontWeight={ 700 }
......
......@@ -384,7 +384,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
<>
<Td fontSize="sm">
<EnsEntity.Container>
<EnsEntity.Icon/>
<EnsEntity.Icon protocol={ data.ens_info.protocol }/>
<LinkInternal
href={ route({ pathname: '/address/[hash]', query: { hash: data.address } }) }
fontWeight={ 700 }
......
......@@ -25,7 +25,7 @@ interface Props {
const ERROR_TEXTS: Record<string, { title: string; text: string }> = {
'403': {
title: 'Forbidden',
title: 'Alert',
text: 'Access to this resource is restricted.',
},
'404': {
......
......@@ -7,11 +7,12 @@ import type { SearchResultDomain } from 'types/api/search';
import { toBech32Address } from 'lib/address/bech32';
import dayjs from 'lib/date/dayjs';
import highlightText from 'lib/highlightText';
import * as EnsEntity from 'ui/shared/entities/ens/EnsEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import IconSvg from 'ui/shared/IconSvg';
const SearchBarSuggestDomain = ({ data, isMobile, searchTerm, addressFormat }: ItemsProps<SearchResultDomain>) => {
const icon = <IconSvg name="ENS_slim" boxSize={ 5 } color="gray.500"/>;
const icon = <EnsEntity.Icon protocol={ data.ens_info.protocol }/>;
const hash = data.filecoin_robust_address || (addressFormat === 'bech32' ? toBech32Address(data.address) : data.address);
const name = (
......@@ -48,7 +49,7 @@ const SearchBarSuggestDomain = ({ data, isMobile, searchTerm, addressFormat }: I
if (isMobile) {
return (
<>
<Flex alignItems="center" overflow="hidden" gap={ 2 }>
<Flex alignItems="center" overflow="hidden">
{ icon }
{ name }
</Flex>
......@@ -63,7 +64,7 @@ const SearchBarSuggestDomain = ({ data, isMobile, searchTerm, addressFormat }: I
return (
<Grid alignItems="center" gridTemplateColumns="228px minmax(auto, max-content) auto" gap={ 2 }>
<Flex alignItems="center" gap={ 2 }>
<Flex alignItems="center">
{ icon }
{ name }
</Flex>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment