Skip to content

Commit c3330d4

Browse files
authored
Merge pull request #2960 from blockscout/release/v2-3-0
Fixes for release v2.3.0
2 parents e1890b6 + e1111ee commit c3330d4

File tree

47 files changed

+489
-160
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+489
-160
lines changed

.github/workflows/deploy-review-l2.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ on:
1414
- arbitrum_nova
1515
- arbitrum_sepolia
1616
- base
17+
- celo
1718
- celo_alfajores
1819
- garnet
1920
- gnosis

.github/workflows/deploy-review.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ on:
1414
- arbitrum_nova
1515
- arbitrum_sepolia
1616
- base
17+
- celo
1718
- celo_alfajores
1819
- garnet
1920
- gnosis

.vscode/tasks.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@
364364
"arbitrum_sepolia",
365365
"base",
366366
"blackfort_testnet",
367+
"celo",
367368
"celo_alfajores",
368369
"garnet",
369370
"gnosis",

configs/app/features/celo.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import { getEnvValue } from '../utils';
44

55
const title = 'Celo chain';
66

7-
const config: Feature<{ }> = (() => {
7+
const config: Feature<{ nativeTokenAddress?: string }> = (() => {
88

99
if (getEnvValue('NEXT_PUBLIC_CELO_ENABLED') === 'true') {
1010
return Object.freeze({
1111
title,
1212
isEnabled: true,
13+
nativeTokenAddress: getEnvValue('NEXT_PUBLIC_CELO_NATIVE_TOKEN_ADDRESS'),
1314
});
1415
}
1516

configs/envs/.env.celo

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Set of ENVs for Celo Mainnet network explorer
2+
# https://celo.blockscout.com
3+
# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=celo"
4+
5+
# Local ENVs
6+
NEXT_PUBLIC_APP_PROTOCOL=http
7+
NEXT_PUBLIC_APP_HOST=localhost
8+
NEXT_PUBLIC_APP_PORT=3000
9+
NEXT_PUBLIC_APP_ENV=development
10+
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
11+
12+
NEXT_PUBLIC_CELO_NATIVE_TOKEN_ADDRESS=0x471EcE3750Da237f93B8E339c536989b8978a438
13+
14+
# Instance ENVs
15+
NEXT_PUBLIC_ADDRESS_3RD_PARTY_WIDGETS=['talentprotocol', 'efp', 'webacy', 'deepdao', 'humanpassport', 'bankless', 'blockscoutbadges']
16+
NEXT_PUBLIC_ADDRESS_3RD_PARTY_WIDGETS_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/widgets/config.json
17+
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
18+
NEXT_PUBLIC_API_BASE_PATH=/
19+
NEXT_PUBLIC_API_HOST=celo.blockscout.com
20+
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
21+
NEXT_PUBLIC_CELO_ENABLED=true
22+
NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]
23+
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
24+
NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swapscout','icon':'swap','dappId':'swapscout'},{'text':'Revokescout','icon':'integration/partial','dappId':'revokescout'}]
25+
NEXT_PUBLIC_DEX_POOLS_ENABLED=true
26+
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/celo.json
27+
NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge
28+
NEXT_PUBLIC_GAS_TRACKER_ENABLED=false
29+
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xa214ff978b8535434d5806d8e9d0d5f8c946905fbba1def121014e7af36c1e8f
30+
NEXT_PUBLIC_HAS_USER_OPS=true
31+
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
32+
NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['rgba(252, 255, 82, 1)'],'text_color':['rgba(0, 0, 0, 1)']}
33+
NEXT_PUBLIC_HOMEPAGE_STATS=['total_blocks','average_block_time','total_txs','wallet_addresses','current_epoch']
34+
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
35+
NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL=https://gist.githubusercontent.com/0xdeval/b27a4aecaad513fa033e37430a4f9a47/raw/3a2fa70068ea27c3e6d58dc4cdbeb732968d62f3/revokescout-banner.html
36+
NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL=https://revoke.blockscout.com?utm_source=blockscout&utm_medium=celo
37+
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json
38+
NEXT_PUBLIC_MARKETPLACE_ENABLED=true
39+
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL
40+
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
41+
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
42+
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/apps']
43+
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
44+
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=CELO
45+
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=CELO
46+
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/celo-icon-light.svg
47+
NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/celo-icon-dark.svg
48+
NEXT_PUBLIC_NETWORK_ID=42220
49+
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/celo-logo-light.svg
50+
NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/celo-logo-dark.svg
51+
NEXT_PUBLIC_NETWORK_MULTIPLE_GAS_CURRENCIES=true
52+
NEXT_PUBLIC_NETWORK_NAME=Celo Mainnet
53+
NEXT_PUBLIC_NETWORK_RPC_URL=https://forno.celo.org
54+
NEXT_PUBLIC_NETWORK_SHORT_NAME=Mainnet
55+
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true
56+
NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/celo.png
57+
NEXT_PUBLIC_PUZZLE_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/capyPuzzleBadge
58+
NEXT_PUBLIC_STATS_API_BASE_PATH=/stats-service
59+
NEXT_PUBLIC_STATS_API_HOST=https://celo.blockscout.com
60+
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
61+
NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS=['burnt_fees']
62+
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
63+
NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED=true
64+
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com

deploy/tools/envs-validator/schema.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,16 @@ const celoSchema = yup
371371
.object()
372372
.shape({
373373
NEXT_PUBLIC_CELO_ENABLED: yup.boolean(),
374+
NEXT_PUBLIC_CELO_NATIVE_TOKEN_ADDRESS: yup
375+
.string()
376+
.min(42)
377+
.max(42)
378+
.matches(regexp.HEX_REGEXP_WITH_0X)
379+
.when('NEXT_PUBLIC_CELO_ENABLED', {
380+
is: (value: boolean) => value,
381+
then: (schema) => schema,
382+
otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_CELO_NATIVE_TOKEN_ADDRESS can only be used if NEXT_PUBLIC_CELO_ENABLED is set to \'true\''),
383+
}),
374384
});
375385

376386
const apiDocsScheme = yup
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
NEXT_PUBLIC_CELO_ENABLED=true
1+
NEXT_PUBLIC_CELO_ENABLED=true
2+
NEXT_PUBLIC_CELO_NATIVE_TOKEN_ADDRESS=0x471EcE3750Da237f93B8E339c536989b8978a438

docs/ENVS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,7 @@ For blockchains that use the Celo platform. _Note_, that once the Celo mainnet b
793793
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
794794
| --- | --- | --- | --- | --- | --- | --- |
795795
| NEXT_PUBLIC_CELO_ENABLED | `boolean` | Indicates that it is a Celo-based chain. | - | - | `true` | v1.37.0+ |
796+
| NEXT_PUBLIC_CELO_NATIVE_TOKEN_ADDRESS | `string` | The address of the CELO ERC-20 token. Used to exclude its balance from the net worth value of user tokens. | - | - | `0x471EcE3750Da237f93B8E339c536989b8978a438` | v2.3.0+ |
796797

797798
&nbsp;
798799

nextjs/getServerSideProps/handlers.ts

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import config from 'configs/app';
88
import * as cookies from 'lib/cookies';
99
import type * as metadata from 'lib/metadata';
1010

11-
import detectBotRequest from '../utils/detectBotRequest';
11+
import { isLikelyHumanBrowser, isKnownBotRequest } from '../utils/checkRealBrowser';
1212

1313
const adBannerFeature = config.features.adsBanner;
1414

@@ -45,28 +45,30 @@ Promise<GetServerSidePropsResult<Props<Pathname>>> => {
4545
}
4646

4747
const isTrackingDisabled = process.env.DISABLE_TRACKING === 'true';
48-
const isBot = Boolean(detectBotRequest(req));
4948

50-
if (!isTrackingDisabled && !isBot) {
49+
if (!isTrackingDisabled) {
50+
const isRealUser = isLikelyHumanBrowser(req) && !isKnownBotRequest(req);
51+
if (isRealUser) {
5152
// log pageview
52-
const hostname = req.headers.host;
53-
const timestamp = new Date().toISOString();
54-
const chainId = process.env.NEXT_PUBLIC_NETWORK_ID;
55-
const chainName = process.env.NEXT_PUBLIC_NETWORK_NAME;
56-
const publicRPC = process.env.NEXT_PUBLIC_NETWORK_RPC_URL;
53+
const hostname = req.headers.host;
54+
const timestamp = new Date().toISOString();
55+
const chainId = process.env.NEXT_PUBLIC_NETWORK_ID;
56+
const chainName = process.env.NEXT_PUBLIC_NETWORK_NAME;
57+
const publicRPC = process.env.NEXT_PUBLIC_NETWORK_RPC_URL;
5758

58-
fetch('https://monitor.blockscout.com/count', {
59-
method: 'POST',
60-
headers: { 'Content-Type': 'application/json' },
61-
body: JSON.stringify({
62-
hostname,
63-
timestamp,
64-
chainId,
65-
chainName,
66-
publicRPC,
67-
uuid,
68-
}),
69-
});
59+
fetch('https://monitor.blockscout.com/count', {
60+
method: 'POST',
61+
headers: { 'Content-Type': 'application/json' },
62+
body: JSON.stringify({
63+
hostname,
64+
timestamp,
65+
chainId,
66+
chainName,
67+
publicRPC,
68+
uuid,
69+
}),
70+
});
71+
}
7072
}
7173

7274
return {

nextjs/utils/checkRealBrowser.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import type { GetServerSidePropsContext } from 'next';
2+
3+
const GENERIC_BOT_MARKERS = [
4+
'bot',
5+
'spider',
6+
'crawler',
7+
'httpclient',
8+
'headlesschrome',
9+
'phantomjs',
10+
'puppeteer',
11+
'playwright',
12+
'node-fetch',
13+
'axios',
14+
'curl',
15+
'wget',
16+
'python-requests',
17+
'go-http-client',
18+
'libwww-perl',
19+
'okhttp',
20+
'postman',
21+
'insomnia',
22+
];
23+
24+
function toLower(input: string | null | undefined): string {
25+
return (input || '').toLowerCase();
26+
}
27+
28+
function isGenericBotUA(userAgent: string): boolean {
29+
const ua = toLower(userAgent);
30+
return GENERIC_BOT_MARKERS.some((m) => ua.includes(m));
31+
}
32+
33+
function hasBrowserHeuristics(req: GetServerSidePropsContext['req']): boolean {
34+
const acceptLanguage = req.headers['accept-language'];
35+
const secChUa = req.headers['sec-ch-ua'];
36+
const secFetchSite = req.headers['sec-fetch-site'];
37+
const secFetchMode = req.headers['sec-fetch-mode'];
38+
const upgradeInsecure = req.headers['upgrade-insecure-requests'];
39+
40+
const hasLang = Boolean(acceptLanguage);
41+
const hasSecHeaders = Boolean(secChUa || secFetchSite || secFetchMode || upgradeInsecure);
42+
43+
return hasLang && hasSecHeaders;
44+
}
45+
46+
export function isLikelyHumanBrowser(req: GetServerSidePropsContext['req']): boolean {
47+
const userAgent = req.headers['user-agent'];
48+
if (!userAgent) return false;
49+
50+
if (isGenericBotUA(userAgent)) return false;
51+
if (!hasBrowserHeuristics(req)) return false;
52+
53+
// Inclusive check for common engines + brands across desktop and mobile
54+
const hasEngineToken = /applewebkit|gecko/i.test(userAgent);
55+
const hasKnownBrowserBrand = /chrome|safari|firefox|crios|fxios|edg|opr|samsungbrowser|vivaldi|whale|duckduckgo/i.test(userAgent);
56+
57+
return hasEngineToken && hasKnownBrowserBrand;
58+
}
59+
60+
export function isKnownBotRequest(req: GetServerSidePropsContext['req']): boolean {
61+
const ua = toLower(req.headers['user-agent']);
62+
if (!ua) return false;
63+
64+
// Social preview bots
65+
if (ua.includes('twitter')) return true;
66+
if (ua.includes('facebook')) return true;
67+
if (ua.includes('telegram')) return true;
68+
if (ua.includes('slack')) return true;
69+
70+
// Search engine bots
71+
if (ua.includes('googlebot')) return true;
72+
if (ua.includes('bingbot')) return true;
73+
if (ua.includes('yahoo')) return true;
74+
if (ua.includes('duckduck')) return true;
75+
76+
// Generic markers
77+
if (isGenericBotUA(ua)) return true;
78+
79+
return false;
80+
}

0 commit comments

Comments
 (0)