Commit 751e2e36 authored by tom goriunov's avatar tom goriunov Committed by GitHub

app feature list (#1079)

* metadata helpers and values for txs pages

* update titles to the rest of the pages

* refactor getServerSideProps

* dynamically update metadata for token and dapp page

* test

* 404 error page

* make new config

* make use of new config, delete old one

* feature reporter: dev implementation

* feature reporter: docker integration

* resctructure ENVS.md

* sentry feature

* refinements

* add spaces between sections

* tweaks

* switch to camelCase

* clean up

* remove NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS

* v
parent c9bc73e1
......@@ -211,7 +211,7 @@ module.exports = {
object: 'process',
property: 'env',
// FIXME: restrict the rule only NEXT_PUBLIC variables
message: 'Please use configs/app/config.ts to import any NEXT_PUBLIC environment variables. For other properties please disable this rule for a while.',
message: 'Please use configs/app/index.ts to import any NEXT_PUBLIC environment variables. For other properties please disable this rule for a while.',
} ],
'react/jsx-key': 'error',
......@@ -289,7 +289,7 @@ module.exports = {
},
},
{
files: [ 'configs/**/*.js', 'configs/**/*.ts', '*.config.ts', 'playwright/**/*.ts', 'deploy/tools/**/*.ts' ],
files: [ 'configs/**/*.js', 'configs/**/*.ts', '*.config.ts', 'playwright/**/*.ts', 'deploy/tools/**' ],
rules: {
// for configs allow to consume env variables from process.env directly
'no-restricted-properties': [ 0 ],
......
......@@ -12,6 +12,14 @@ COPY package.json yarn.lock ./
RUN apk add git
RUN yarn --frozen-lockfile
### FEATURE REPORTER
# Install dependencies
WORKDIR /feature-reporter
COPY ./deploy/tools/feature-reporter/package.json ./deploy/tools/feature-reporter/yarn.lock ./
RUN yarn --frozen-lockfile
### ENV VARIABLES CHECKER
# Install dependencies
WORKDIR /envs-validator
......@@ -52,6 +60,13 @@ RUN ./make_envs_template.sh ./docs/ENVS.md
RUN yarn build
### FEATURE REPORTER
# Copy dependencies and source code, then build
COPY --from=deps /feature-reporter/node_modules ./deploy/tools/feature-reporter/node_modules
RUN cd ./deploy/tools/feature-reporter && yarn compile_config
RUN cd ./deploy/tools/feature-reporter && yarn build
### ENV VARIABLES CHECKER
# Copy dependencies and source code, then build
WORKDIR /envs-validator
......@@ -81,6 +96,7 @@ COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /envs-validator/index.js ./envs-validator.js
COPY --from=builder /app/deploy/tools/feature-reporter/index.js ./feature-reporter.js
# Copy scripts and ENVs file
COPY --chmod=+x ./deploy/scripts/entrypoint.sh .
......
import stripTrailingSlash from 'lib/stripTrailingSlash';
import { getEnvValue } from './utils';
const apiHost = getEnvValue(process.env.NEXT_PUBLIC_API_HOST);
const apiSchema = getEnvValue(process.env.NEXT_PUBLIC_API_PROTOCOL) || 'https';
const apiPort = getEnvValue(process.env.NEXT_PUBLIC_API_PORT);
const apiEndpoint = [
apiSchema || 'https',
'://',
apiHost,
apiPort && ':' + apiPort,
].filter(Boolean).join('');
const socketSchema = getEnvValue(process.env.NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL) || 'wss';
const api = Object.freeze({
host: apiHost,
endpoint: apiEndpoint,
socket: `${ socketSchema }://${ apiHost }`,
basePath: stripTrailingSlash(getEnvValue(process.env.NEXT_PUBLIC_API_BASE_PATH) || ''),
});
export default api;
import { getEnvValue } from './utils';
const appPort = getEnvValue(process.env.NEXT_PUBLIC_APP_PORT);
const appSchema = getEnvValue(process.env.NEXT_PUBLIC_APP_PROTOCOL);
const appHost = getEnvValue(process.env.NEXT_PUBLIC_APP_HOST);
const baseUrl = [
appSchema || 'https',
'://',
appHost,
appPort && ':' + appPort,
].filter(Boolean).join('');
const isDev = process.env.NODE_ENV === 'development';
const app = Object.freeze({
isDev,
protocol: appSchema,
host: appHost,
port: appPort,
baseUrl,
useProxy: getEnvValue(process.env.NEXT_PUBLIC_USE_NEXT_JS_PROXY) === 'true',
});
export default app;
import { getEnvValue } from './utils';
const DEFAULT_CURRENCY_DECIMALS = 18;
const chain = Object.freeze({
id: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_ID),
name: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_NAME),
shortName: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_SHORT_NAME),
currency: {
name: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_CURRENCY_NAME),
symbol: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL),
decimals: Number(getEnvValue(process.env.NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS)) || DEFAULT_CURRENCY_DECIMALS,
},
rpcUrl: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_RPC_URL),
isTestnet: getEnvValue(process.env.NEXT_PUBLIC_IS_TESTNET) === 'true',
verificationType: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE) || 'mining',
});
export default chain;
/* eslint-disable no-restricted-properties */
import type { AdButlerConfig } from 'types/client/adButlerConfig';
import type { AdBannerProviders, AdTextProviders } from 'types/client/adProviders';
import type { NavItemExternal } from 'types/client/navigation-items';
import type { WalletType } from 'types/client/wallets';
import type { NetworkExplorer } from 'types/networks';
import type { ChainIndicatorId } from 'ui/home/indicators/types';
import stripTrailingSlash from 'lib/stripTrailingSlash';
const getEnvValue = <T extends string>(env: T | undefined): T | undefined => env?.replaceAll('\'', '"') as T;
const parseEnvJson = <DataType>(env: string | undefined): DataType | null => {
try {
return JSON.parse(env || 'null') as DataType | null;
} catch (error) {
return null;
}
};
const getWeb3DefaultWallet = (): WalletType => {
const envValue = getEnvValue(process.env.NEXT_PUBLIC_WEB3_DEFAULT_WALLET);
const SUPPORTED_WALLETS: Array<WalletType> = [
'metamask',
'coinbase',
];
return (envValue && SUPPORTED_WALLETS.includes(envValue) ? envValue : 'metamask') as WalletType;
};
const getAdBannerProvider = (): AdBannerProviders => {
const envValue = getEnvValue(process.env.NEXT_PUBLIC_AD_BANNER_PROVIDER);
const SUPPORTED_AD_BANNER_PROVIDERS: Array<AdBannerProviders> = [ 'slise', 'adbutler', 'coinzilla', 'none' ];
return (envValue && SUPPORTED_AD_BANNER_PROVIDERS.includes(envValue) ? envValue : 'slise') as AdBannerProviders;
};
const getAdTextProvider = (): AdTextProviders => {
const envValue = getEnvValue(process.env.NEXT_PUBLIC_AD_TEXT_PROVIDER);
const SUPPORTED_AD_BANNER_PROVIDERS: Array<AdTextProviders> = [ 'coinzilla', 'none' ];
return (envValue && SUPPORTED_AD_BANNER_PROVIDERS.includes(envValue) ? envValue : 'slise') as AdTextProviders;
};
const env = process.env.NODE_ENV;
const isDev = env === 'development';
const appPort = getEnvValue(process.env.NEXT_PUBLIC_APP_PORT);
const appSchema = getEnvValue(process.env.NEXT_PUBLIC_APP_PROTOCOL);
const appHost = getEnvValue(process.env.NEXT_PUBLIC_APP_HOST);
const baseUrl = [
appSchema || 'https',
'://',
appHost,
appPort && ':' + appPort,
].filter(Boolean).join('');
const authUrl = getEnvValue(process.env.NEXT_PUBLIC_AUTH_URL) || baseUrl;
const apiHost = getEnvValue(process.env.NEXT_PUBLIC_API_HOST);
const apiSchema = getEnvValue(process.env.NEXT_PUBLIC_API_PROTOCOL) || 'https';
const apiPort = getEnvValue(process.env.NEXT_PUBLIC_API_PORT);
const apiEndpoint = apiHost ? [
apiSchema || 'https',
'://',
apiHost,
apiPort && ':' + apiPort,
].filter(Boolean).join('') : 'https://blockscout.com';
const socketSchema = getEnvValue(process.env.NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL) || 'wss';
const logoutUrl = (() => {
try {
const envUrl = getEnvValue(process.env.NEXT_PUBLIC_LOGOUT_URL);
const auth0ClientId = getEnvValue(process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID);
const returnUrl = authUrl + '/auth/logout';
if (!envUrl || !auth0ClientId) {
throw Error();
}
const url = new URL(envUrl);
url.searchParams.set('client_id', auth0ClientId);
url.searchParams.set('returnTo', returnUrl);
return url.toString();
} catch (error) {
return;
}
})();
const DEFAULT_CURRENCY_DECIMALS = 18;
const config = Object.freeze({
env,
isDev,
network: {
logo: {
'default': getEnvValue(process.env.NEXT_PUBLIC_NETWORK_LOGO),
dark: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_LOGO_DARK),
},
icon: {
'default': getEnvValue(process.env.NEXT_PUBLIC_NETWORK_ICON),
dark: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_ICON_DARK),
},
name: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_NAME),
id: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_ID),
shortName: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_SHORT_NAME),
currency: {
name: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_CURRENCY_NAME),
symbol: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL),
decimals: Number(getEnvValue(process.env.NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS)) || DEFAULT_CURRENCY_DECIMALS,
address: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS),
},
explorers: parseEnvJson<Array<NetworkExplorer>>(getEnvValue(process.env.NEXT_PUBLIC_NETWORK_EXPLORERS)) || [],
verificationType: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE) || 'mining',
rpcUrl: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_RPC_URL),
isTestnet: getEnvValue(process.env.NEXT_PUBLIC_IS_TESTNET) === 'true',
},
navigation: {
otherLinks: parseEnvJson<Array<NavItemExternal>>(getEnvValue(process.env.NEXT_PUBLIC_OTHER_LINKS)) || [],
featuredNetworks: getEnvValue(process.env.NEXT_PUBLIC_FEATURED_NETWORKS),
},
footer: {
links: getEnvValue(process.env.NEXT_PUBLIC_FOOTER_LINKS),
frontendVersion: getEnvValue(process.env.NEXT_PUBLIC_GIT_TAG),
frontendCommit: getEnvValue(process.env.NEXT_PUBLIC_GIT_COMMIT_SHA),
},
marketplace: {
configUrl: getEnvValue(process.env.NEXT_PUBLIC_MARKETPLACE_CONFIG_URL),
submitForm: getEnvValue(process.env.NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM),
},
account: {
isEnabled: getEnvValue(process.env.NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED) === 'true',
authUrl,
logoutUrl,
},
app: {
protocol: appSchema,
host: appHost,
port: appPort,
baseUrl,
useNextJsProxy: getEnvValue(process.env.NEXT_PUBLIC_USE_NEXT_JS_PROXY) === 'true',
},
ad: {
adBannerProvider: getAdBannerProvider(),
adTextProvider: getAdTextProvider(),
adButlerConfigDesktop: parseEnvJson<AdButlerConfig>(getEnvValue(process.env.NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP)),
adButlerConfigMobile: parseEnvJson<AdButlerConfig>(getEnvValue(process.env.NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE)),
},
web3: {
defaultWallet: getWeb3DefaultWallet(),
disableAddTokenToWallet: getEnvValue(process.env.NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET) === 'true',
},
api: {
host: apiHost,
endpoint: apiEndpoint,
socket: apiHost ? `${ socketSchema }://${ apiHost }` : 'wss://blockscout.com',
basePath: stripTrailingSlash(getEnvValue(process.env.NEXT_PUBLIC_API_BASE_PATH) || ''),
},
L2: {
isL2Network: getEnvValue(process.env.NEXT_PUBLIC_IS_L2_NETWORK) === 'true',
L1BaseUrl: getEnvValue(process.env.NEXT_PUBLIC_L1_BASE_URL),
withdrawalUrl: getEnvValue(process.env.NEXT_PUBLIC_L2_WITHDRAWAL_URL) || '',
},
beaconChain: {
hasBeaconChain: getEnvValue(process.env.NEXT_PUBLIC_HAS_BEACON_CHAIN) === 'true',
currencySymbol: getEnvValue(process.env.NEXT_PUBLIC_BEACON_CHAIN_CURRENCY_SYMBOL) || getEnvValue(process.env.NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL),
},
statsApi: {
endpoint: getEnvValue(process.env.NEXT_PUBLIC_STATS_API_HOST),
basePath: '',
},
visualizeApi: {
endpoint: getEnvValue(process.env.NEXT_PUBLIC_VISUALIZE_API_HOST),
basePath: '',
},
contractInfoApi: {
endpoint: getEnvValue(process.env.NEXT_PUBLIC_CONTRACT_INFO_API_HOST),
basePath: '',
},
adminServiceApi: {
endpoint: getEnvValue(process.env.NEXT_PUBLIC_ADMIN_SERVICE_API_HOST),
basePath: '',
},
homepage: {
charts: parseEnvJson<Array<ChainIndicatorId>>(getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_CHARTS)) || [],
plate: {
background: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND) ||
'radial-gradient(103.03% 103.03% at 0% 0%, rgba(183, 148, 244, 0.8) 0%, rgba(0, 163, 196, 0.8) 100%), var(--chakra-colors-blue-400)',
textColor: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR) || 'white',
},
showGasTracker: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER) === 'false' ? false : true,
showAvgBlockTime: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME) === 'false' ? false : true,
},
walletConnect: {
projectId: getEnvValue(process.env.NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID),
},
apiDoc: {
specUrl: getEnvValue(process.env.NEXT_PUBLIC_API_SPEC_URL),
},
reCaptcha: {
siteKey: getEnvValue(process.env.NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY) || '',
},
googleAnalytics: {
propertyId: getEnvValue(process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID),
},
mixpanel: {
projectToken: getEnvValue(process.env.NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN),
},
graphQL: {
defaultTxnHash: getEnvValue(process.env.NEXT_PUBLIC_GRAPHIQL_TRANSACTION) || '',
},
hideIndexingAlert: getEnvValue(process.env.NEXT_PUBLIC_HIDE_INDEXING_ALERT),
});
export default config;
import stripTrailingSlash from 'lib/stripTrailingSlash';
import app from '../app';
import { getEnvValue } from '../utils';
const authUrl = stripTrailingSlash(getEnvValue(process.env.NEXT_PUBLIC_AUTH_URL) || app.baseUrl);
const logoutUrl = (() => {
try {
const envUrl = getEnvValue(process.env.NEXT_PUBLIC_LOGOUT_URL);
const auth0ClientId = getEnvValue(process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID);
const returnUrl = authUrl + '/auth/logout';
if (!envUrl || !auth0ClientId) {
throw Error();
}
const url = new URL(envUrl);
url.searchParams.set('client_id', auth0ClientId);
url.searchParams.set('returnTo', returnUrl);
return url.toString();
} catch (error) {
return;
}
})();
export default Object.freeze({
title: 'My account',
isEnabled: getEnvValue(process.env.NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED) === 'true',
authUrl,
logoutUrl,
});
import { getEnvValue } from '../utils';
import account from './account';
import verifiedTokens from './verifiedTokens';
const adminServiceApiHost = getEnvValue(process.env.NEXT_PUBLIC_ADMIN_SERVICE_API_HOST);
export default Object.freeze({
title: 'Address verification in "My account"',
isEnabled: account.isEnabled && verifiedTokens.isEnabled && Boolean(adminServiceApiHost),
api: {
endpoint: adminServiceApiHost,
basePath: '',
},
});
import type { AdButlerConfig } from 'types/client/adButlerConfig';
import type { AdBannerProviders } from 'types/client/adProviders';
import { getEnvValue, parseEnvJson } from '../utils';
const provider: AdBannerProviders = (() => {
const envValue = getEnvValue(process.env.NEXT_PUBLIC_AD_BANNER_PROVIDER) as AdBannerProviders;
const SUPPORTED_AD_BANNER_PROVIDERS: Array<AdBannerProviders> = [ 'slise', 'adbutler', 'coinzilla', 'none' ];
return envValue && SUPPORTED_AD_BANNER_PROVIDERS.includes(envValue) ? envValue : 'slise';
})();
export default Object.freeze({
title: 'Banner ads',
isEnabled: provider !== 'none',
provider,
adButler: {
config: {
desktop: parseEnvJson<AdButlerConfig>(getEnvValue(process.env.NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP)) ?? undefined,
mobile: parseEnvJson<AdButlerConfig>(getEnvValue(process.env.NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE)) ?? undefined,
},
},
});
import type { AdTextProviders } from 'types/client/adProviders';
import { getEnvValue } from '../utils';
const provider: AdTextProviders = (() => {
const envValue = getEnvValue(process.env.NEXT_PUBLIC_AD_TEXT_PROVIDER);
const SUPPORTED_AD_BANNER_PROVIDERS = [ 'coinzilla', 'none' ];
return envValue && SUPPORTED_AD_BANNER_PROVIDERS.includes(envValue) ? envValue as AdTextProviders : 'coinzilla';
})();
export default Object.freeze({
title: 'Text ads',
isEnabled: provider !== 'none',
provider,
});
import { getEnvValue } from '../utils';
export default Object.freeze({
title: 'Beacon chain',
isEnabled: getEnvValue(process.env.NEXT_PUBLIC_HAS_BEACON_CHAIN) === 'true',
currency: {
symbol: getEnvValue(process.env.NEXT_PUBLIC_BEACON_CHAIN_CURRENCY_SYMBOL) || getEnvValue(process.env.NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL),
},
});
import chain from '../chain';
import { getEnvValue } from '../utils';
const walletConnectProjectId = getEnvValue(process.env.NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID);
export default Object.freeze({
title: 'Blockchain interaction (writing to contract, etc.)',
isEnabled: Boolean(
// all chain parameters are required for wagmi provider
// @wagmi/chains/dist/index.d.ts
chain.id &&
chain.name &&
chain.currency.name &&
chain.currency.symbol &&
chain.currency.decimals &&
chain.rpcUrl &&
walletConnectProjectId,
),
walletConnect: {
projectId: walletConnectProjectId ?? '',
},
});
import { getEnvValue } from '../utils';
const reCaptchaSiteKey = getEnvValue(process.env.NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY);
export default Object.freeze({
title: 'Export data to CSV file',
isEnabled: Boolean(reCaptchaSiteKey),
reCaptcha: {
siteKey: reCaptchaSiteKey ?? '',
},
});
import { getEnvValue } from '../utils';
const propertyId = getEnvValue(process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID);
export default Object.freeze({
title: 'Google analytics',
isEnabled: Boolean(propertyId),
propertyId,
});
import { getEnvValue } from '../utils';
const defaultTxHash = getEnvValue(process.env.NEXT_PUBLIC_GRAPHIQL_TRANSACTION);
export default Object.freeze({
title: 'GraphQL API documentation',
isEnabled: true,
defaultTxHash,
});
export { default as account } from './account';
export { default as addressVerification } from './addressVerification';
export { default as adsBanner } from './adsBanner';
export { default as adsText } from './adsText';
export { default as beaconChain } from './beaconChain';
export { default as blockchainInteraction } from './blockchainInteraction';
export { default as csvExport } from './csvExport';
export { default as googleAnalytics } from './googleAnalytics';
export { default as graphqlApiDocs } from './graphqlApiDocs';
export { default as marketplace } from './marketplace';
export { default as mixpanel } from './mixpanel';
export { default as restApiDocs } from './restApiDocs';
export { default as rollup } from './rollup';
export { default as sentry } from './sentry';
export { default as sol2uml } from './sol2uml';
export { default as stats } from './stats';
export { default as web3Wallet } from './web3Wallet';
export { default as verifiedTokens } from './verifiedTokens';
import chain from '../chain';
import { getEnvValue } from '../utils';
const configUrl = getEnvValue(process.env.NEXT_PUBLIC_MARKETPLACE_CONFIG_URL);
const submitForm = getEnvValue(process.env.NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM);
export default Object.freeze({
title: 'Marketplace',
isEnabled: Boolean(chain.rpcUrl && configUrl && submitForm),
configUrl: configUrl ?? '',
submitFormUrl: submitForm ?? '',
});
import { getEnvValue } from '../utils';
const projectToken = getEnvValue(process.env.NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN);
export default Object.freeze({
title: 'Mixpanel analytics',
isEnabled: Boolean(projectToken),
projectToken: projectToken ?? '',
});
import { getEnvValue } from '../utils';
const specUrl = getEnvValue(process.env.NEXT_PUBLIC_API_SPEC_URL);
export default Object.freeze({
title: 'REST API documentation',
isEnabled: Boolean(specUrl),
specUrl,
});
import { getEnvValue } from '../utils';
export default Object.freeze({
title: 'Rollup (L2) chain',
isEnabled: getEnvValue(process.env.NEXT_PUBLIC_IS_L2_NETWORK) === 'true',
L1BaseUrl: getEnvValue(process.env.NEXT_PUBLIC_L1_BASE_URL) ?? '',
withdrawalUrl: getEnvValue(process.env.NEXT_PUBLIC_L2_WITHDRAWAL_URL) ?? '',
});
import { getEnvValue } from '../utils';
const dsn = getEnvValue(process.env.NEXT_PUBLIC_SENTRY_DSN);
// TODO @tom2drum check sentry setup
export default Object.freeze({
title: 'Sentry error monitoring',
isEnabled: Boolean(dsn),
dsn,
environment: getEnvValue(process.env.NEXT_PUBLIC_APP_ENV) || getEnvValue(process.env.NODE_ENV),
cspReportUrl: getEnvValue(process.env.SENTRY_CSP_REPORT_URI),
instance: getEnvValue(process.env.NEXT_PUBLIC_APP_INSTANCE),
});
import { getEnvValue } from '../utils';
const apiEndpoint = getEnvValue(process.env.NEXT_PUBLIC_VISUALIZE_API_HOST);
export default Object.freeze({
title: 'Solidity to UML diagrams',
isEnabled: Boolean(apiEndpoint),
api: {
endpoint: apiEndpoint,
basePath: '',
},
});
import { getEnvValue } from '../utils';
const apiEndpoint = getEnvValue(process.env.NEXT_PUBLIC_STATS_API_HOST);
export default Object.freeze({
title: 'Blockchain statistics',
isEnabled: Boolean(apiEndpoint),
api: {
endpoint: apiEndpoint,
basePath: '',
},
});
import { getEnvValue } from '../utils';
const contractInfoApiHost = getEnvValue(process.env.NEXT_PUBLIC_CONTRACT_INFO_API_HOST);
export default Object.freeze({
title: 'Verified tokens info',
isEnabled: Boolean(contractInfoApiHost),
api: {
endpoint: contractInfoApiHost,
basePath: '',
},
});
import type { WalletType } from 'types/client/wallets';
import { getEnvValue } from '../utils';
const defaultWallet = ((): WalletType => {
const envValue = getEnvValue(process.env.NEXT_PUBLIC_WEB3_DEFAULT_WALLET) as WalletType;
const SUPPORTED_WALLETS: Array<WalletType> = [
'metamask',
'coinbase',
];
return envValue && SUPPORTED_WALLETS.includes(envValue) ? envValue : 'metamask';
})();
export default Object.freeze({
title: 'Web3 wallet integration (add token or network to the wallet)',
isEnabled: defaultWallet !== 'none',
defaultWallet,
addToken: {
isDisabled: getEnvValue(process.env.NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET) === 'true',
},
addNetwork: {},
});
import api from './api';
import app from './app';
import chain from './chain';
import * as features from './features';
import services from './services';
import UI from './ui';
const config = Object.freeze({
app,
chain,
api,
UI,
features,
services,
});
export default config;
import { getEnvValue } from './utils';
export default Object.freeze({
reCaptcha: {
siteKey: getEnvValue(process.env.NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY),
},
});
import type { NavItemExternal } from 'types/client/navigation-items';
import type { NetworkExplorer } from 'types/networks';
import type { ChainIndicatorId } from 'ui/home/indicators/types';
import { getEnvValue, parseEnvJson } from './utils';
// eslint-disable-next-line max-len
const HOMEPAGE_PLATE_BACKGROUND_DEFAULT = 'radial-gradient(103.03% 103.03% at 0% 0%, rgba(183, 148, 244, 0.8) 0%, rgba(0, 163, 196, 0.8) 100%), var(--chakra-colors-blue-400)';
const UI = Object.freeze({
sidebar: {
logo: {
'default': getEnvValue(process.env.NEXT_PUBLIC_NETWORK_LOGO),
dark: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_LOGO_DARK),
},
icon: {
'default': getEnvValue(process.env.NEXT_PUBLIC_NETWORK_ICON),
dark: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_ICON_DARK),
},
otherLinks: parseEnvJson<Array<NavItemExternal>>(getEnvValue(process.env.NEXT_PUBLIC_OTHER_LINKS)) || [],
featuredNetworks: getEnvValue(process.env.NEXT_PUBLIC_FEATURED_NETWORKS),
},
footer: {
links: getEnvValue(process.env.NEXT_PUBLIC_FOOTER_LINKS),
frontendVersion: getEnvValue(process.env.NEXT_PUBLIC_GIT_TAG),
frontendCommit: getEnvValue(process.env.NEXT_PUBLIC_GIT_COMMIT_SHA),
},
homepage: {
charts: parseEnvJson<Array<ChainIndicatorId>>(getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_CHARTS)) || [],
plate: {
background: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND) || HOMEPAGE_PLATE_BACKGROUND_DEFAULT,
textColor: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR) || 'white',
},
showGasTracker: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER) === 'false' ? false : true,
showAvgBlockTime: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME) === 'false' ? false : true,
},
indexingAlert: {
isHidden: getEnvValue(process.env.NEXT_PUBLIC_HIDE_INDEXING_ALERT),
},
explorers: {
items: parseEnvJson<Array<NetworkExplorer>>(getEnvValue(process.env.NEXT_PUBLIC_NETWORK_EXPLORERS)) || [],
},
});
export default UI;
export const getEnvValue = <T extends string>(env: T | undefined): T | undefined => env?.replaceAll('\'', '"') as T;
export const parseEnvJson = <DataType>(env: string | undefined): DataType | null => {
try {
return JSON.parse(env || 'null') as DataType | null;
} catch (error) {
return null;
}
};
# Set of ENVs for Ethereum network explorer
# https://eth.blockscout.com/
# app config
# app configuration
NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development
# ui config
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth.json
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Etherscan','baseUrl':'https://etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
# network config
# blockchain parameters
NEXT_PUBLIC_NETWORK_NAME=Ethereum
NEXT_PUBLIC_NETWORK_SHORT_NAME=ETH
NEXT_PUBLIC_NETWORK_LOGO=
NEXT_PUBLIC_NETWORK_ICON=
NEXT_PUBLIC_NETWORK_ID=1
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS=
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_NETWORK_RPC_URL=https://eth.llamarpc.com
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs', 'coin_price', 'market_cap']
NEXT_PUBLIC_IS_TESTNET=false
NEXT_PUBLIC_HAS_BEACON_CHAIN=true
# api config
# api configuration
NEXT_PUBLIC_API_HOST=eth.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/
# ui config
## homepage
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs', 'coin_price', 'market_cap']
## sidebar
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth.json
## footer
## misc
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Etherscan','baseUrl':'https://etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]
# app features
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_HAS_BEACON_CHAIN=true
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3000
NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout
NEXT_PUBLIC_STATS_API_HOST=https://stats-eth-main.k8s.blockscout.com
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
......
# Set of ENVs for Goerli testnet network explorer
# https://eth-goerli.blockscout.com/
# app config
# app configuration
NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development
# ui config
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-goerli.json
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
# network config
# blockchain parameters
NEXT_PUBLIC_NETWORK_NAME=Goerli
NEXT_PUBLIC_NETWORK_SHORT_NAME=Goerli
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/network-logos/goerli.svg
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/network-icons/goerli.svg
NEXT_PUBLIC_NETWORK_ID=5
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3000
NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_NETWORK_RPC_URL=https://rpc.ankr.com/eth_goerli
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/eth-goerli.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_IS_TESTNET=true
# api config
# api configuration
NEXT_PUBLIC_API_HOST=eth-goerli.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/
# ui config
## homepage
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
## sidebar
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/dev/configs/network-logos/goerli.svg
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/network-icons/goerli.svg
## footer
## misc
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]
# app features
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3000
NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/eth-goerli.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_STATS_API_HOST=https://stats-goerli.k8s-dev.blockscout.com
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
......
# Set of ENVs for Jest unit tests
# app config
# app configuration
NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_INSTANCE=jest
NEXT_PUBLIC_APP_ENV=testing
# blockchain parameters
NEXT_PUBLIC_NETWORK_NAME=Blockscout
NEXT_PUBLIC_NETWORK_SHORT_NAME=Blockscout
NEXT_PUBLIC_NETWORK_ID=1
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_NETWORK_RPC_URL=https://localhost:1111
NEXT_PUBLIC_IS_TESTNET=true
# api configuration
NEXT_PUBLIC_API_HOST=localhost
NEXT_PUBLIC_API_PORT=3003
NEXT_PUBLIC_API_BASE_PATH=/
# ui config
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]
NEXT_PUBLIC_GIT_TAG=v1.0.11
## homepage
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap']
NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true
NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=true
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=
NEXT_PUBLIC_FEATURED_NETWORKS=
NEXT_PUBLIC_FOOTER_LINKS=
## sidebar
NEXT_PUBLIC_NETWORK_LOGO=
NEXT_PUBLIC_NETWORK_LOGO_DARK=
NEXT_PUBLIC_NETWORK_ICON=
NEXT_PUBLIC_NETWORK_ICON_DARK=
NEXT_PUBLIC_NETWORK_RPC_URL=https://localhost:1111
NEXT_PUBLIC_IS_TESTNET=true
NEXT_PUBLIC_FEATURED_NETWORKS=
## footer
NEXT_PUBLIC_FOOTER_LINKS=
NEXT_PUBLIC_GIT_TAG=v1.0.11
## misc
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]
# app features
NEXT_PUBLIC_APP_INSTANCE=jest
NEXT_PUBLIC_APP_ENV=testing
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://localhost:3000/marketplace-config.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://localhost:3000/marketplace-submit-form
NEXT_PUBLIC_IS_L2_NETWORK=false
# network config
NEXT_PUBLIC_NETWORK_NAME=Blockscout
NEXT_PUBLIC_NETWORK_SHORT_NAME=Blockscout
NEXT_PUBLIC_NETWORK_ID=1
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS=
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://localhost:3000/marketplace-submit-form
# api config
NEXT_PUBLIC_API_HOST=localhost
NEXT_PUBLIC_API_PORT=3003
NEXT_PUBLIC_STATS_API_HOST=https://localhost:3004
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://localhost:3005
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://localhost:3006
NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx
NEXT_PUBLIC_API_BASE_PATH=/
......@@ -2,34 +2,40 @@
# frontend app URL - https://localhost:3000/
# API URL - https://localhost:3001/
# app config
# app configuration
NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development
# ui config
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-goerli.json
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/poa/core/transaction','address':'/ethereum/poa/core/address'}}]
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap']
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
# network config
# blockchain parameters
NEXT_PUBLIC_NETWORK_NAME=POA
NEXT_PUBLIC_NETWORK_SHORT_NAME=POA
NEXT_PUBLIC_NETWORK_ID=99
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=POA
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=POA
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS=0x029a799563238d0e75e20be2f4bda0ea68d00172
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_NETWORK_RPC_URL=https://core.poa.network
# api config
# api configuration
NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=localhost
NEXT_PUBLIC_API_PROTOCOL=http
NEXT_PUBLIC_API_PORT=3001
# ui config
## homepage
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap']
## sidebar
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-goerli.json
## footer
## misc
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/poa/core/transaction','address':'/ethereum/poa/core/address'}}]
# app features
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3000
NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout
# Set of ENVs for Develompent network explorer
# https://blockscout-main.k8s-dev.blockscout.com/
# app config
# app configuration
NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development
# ui config
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-goerli.json
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
# network config
# blockchain parameters
NEXT_PUBLIC_NETWORK_NAME=Goerli
NEXT_PUBLIC_NETWORK_SHORT_NAME=Goerli
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/network-logos/goerli.svg
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/network-icons/goerli.svg
NEXT_PUBLIC_NETWORK_ID=5
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3000
NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_NETWORK_RPC_URL=https://rpc.ankr.com/eth_goerli
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/eth-goerli.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_IS_TESTNET=true
# api config
# api configuration
NEXT_PUBLIC_API_HOST=blockscout-main.k8s-dev.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/
# ui config
## homepage
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
## sidebar
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/dev/configs/network-logos/goerli.svg
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/network-icons/goerli.svg
## footer
## misc
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]
# app features
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3000
NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/eth-goerli.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_STATS_API_HOST=https://stats-goerli.k8s-dev.blockscout.com
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.k8s-dev.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info-test.k8s-dev.blockscout.com
......
# Set of ENVs for Develompent L2 network explorer
# https://blockscout-optimism-goerli.k8s-dev.blockscout.com/
# app config
# app configuration
NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development
# ui config
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/base-goerli.json
NEXT_PUBLIC_NETWORK_EXPLORERS=
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=linear-gradient(136.9deg,rgb(107 94 236) 1.5%,rgb(0 82 255) 56.84%,rgb(82 62 231) 98.54%)
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62
NEXT_PUBLIC_WEB3_DEFAULT_WALLET=coinbase
NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET=true
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
# network config
# blockchain parameters
NEXT_PUBLIC_NETWORK_NAME=Base Göerli
NEXT_PUBLIC_NETWORK_SHORT_NAME=Base
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/network-logos/base.svg
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/network-icons/base.svg
NEXT_PUBLIC_NETWORK_ID=84531
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS=
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_NETWORK_RPC_URL=https://goerli.base.org
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/base-goerli.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_IS_TESTNET=true
# api config
# api configuration
NEXT_PUBLIC_API_HOST=blockscout-optimism-goerli.k8s-dev.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_STATS_API_HOST=https://stats-optimism-goerli.k8s-dev.blockscout.com
# l2 config
# ui config
## homepage
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=linear-gradient(136.9deg,rgb(107 94 236) 1.5%,rgb(0 82 255) 56.84%,rgb(82 62 231) 98.54%)
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
## sidebar
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/base-goerli.json
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/network-logos/base.svg
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/network-icons/base.svg
## footer
## misc
# app features
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_WEB3_DEFAULT_WALLET=coinbase
NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET=true
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3000
NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/base-goerli.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_STATS_API_HOST=https://stats-optimism-goerli.k8s-dev.blockscout.com
NEXT_PUBLIC_IS_L2_NETWORK=true
NEXT_PUBLIC_L1_BASE_URL=https://blockscout-main.k8s-dev.blockscout.com
NEXT_PUBLIC_L2_WITHDRAWAL_URL=https://app.optimism.io/bridge/withdraw
# Set of ENVs for POA network explorer
# https://blockscout.com/poa/core/
# app config
# app configuration
NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development
# ui config
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-goerli.json
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/poa/core/transaction','address':'/ethereum/poa/core/address','block':'/ethereum/poa/core/block'}}]
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap']
#NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND='no-repeat bottom 20% right 0px/100% url(https://neon-labs.org/images/index/banner.jpg)'
#NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR=\#DCFE76
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/network-logos/poa.svg
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/network-icons/poa.svg
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
# network config
# blockchain parameters
NEXT_PUBLIC_NETWORK_NAME=POA
NEXT_PUBLIC_NETWORK_SHORT_NAME=POA
NEXT_PUBLIC_NETWORK_ID=99
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=POA
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=POA
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS=0x029a799563238d0e75e20be2f4bda0ea68d00172
#NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_NETWORK_RPC_URL=https://core.poa.network
#NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
# api config
# api configuration
NEXT_PUBLIC_API_HOST=blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/poa/core
# ui config
## homepage
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap']
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND='no-repeat bottom 20% right 0px/100% url(https://neon-labs.org/images/index/banner.jpg)'
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR=\#DCFE76
## sidebar
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/dev/configs/network-logos/poa.svg
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/network-icons/poa.svg
## footer
## misc
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/poa/core/transaction','address':'/ethereum/poa/core/address','block':'/ethereum/poa/core/block'}}]
# app features
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3000
NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_NETWORK_RPC_URL=https://core.poa.network
# Set of ENVs for Playwright components tests
# app config
# app configuration
NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3100
NEXT_PUBLIC_APP_INSTANCE=pw
NEXT_PUBLIC_APP_ENV=testing
# blockchain parameters
NEXT_PUBLIC_NETWORK_NAME=Blockscout
NEXT_PUBLIC_NETWORK_SHORT_NAME=Blockscout
NEXT_PUBLIC_NETWORK_ID=1
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_RPC_URL=https://localhost:1111
NEXT_PUBLIC_IS_TESTNET=true
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
# api configuration
NEXT_PUBLIC_API_HOST=localhost
NEXT_PUBLIC_API_PORT=3003
NEXT_PUBLIC_API_BASE_PATH=/
# ui config
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]
NEXT_PUBLIC_GIT_TAG=v1.0.11
## homepage
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap']
NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true
NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=true
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=
## sidebar
NEXT_PUBLIC_FEATURED_NETWORKS=
NEXT_PUBLIC_FOOTER_LINKS=
NEXT_PUBLIC_NETWORK_LOGO=
NEXT_PUBLIC_NETWORK_LOGO_DARK=
NEXT_PUBLIC_NETWORK_ICON=
NEXT_PUBLIC_NETWORK_ICON_DARK=
NEXT_PUBLIC_NETWORK_RPC_URL=https://localhost:1111
NEXT_PUBLIC_IS_TESTNET=true
## footer
NEXT_PUBLIC_GIT_TAG=v1.0.11
NEXT_PUBLIC_FOOTER_LINKS=
## misc
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]
# app features
NEXT_PUBLIC_APP_ENV=testing
NEXT_PUBLIC_APP_INSTANCE=pw
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://localhost:3000/marketplace-config.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://localhost:3000/marketplace-submit-form
NEXT_PUBLIC_IS_L2_NETWORK=false
NEXT_PUBLIC_AD_BANNER_PROVIDER=slise
# network config
NEXT_PUBLIC_NETWORK_NAME=Blockscout
NEXT_PUBLIC_NETWORK_SHORT_NAME=Blockscout
NEXT_PUBLIC_NETWORK_ID=1
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS=
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://localhost:3000/marketplace-submit-form
# api config
NEXT_PUBLIC_API_HOST=localhost
NEXT_PUBLIC_API_PORT=3003
NEXT_PUBLIC_STATS_API_HOST=https://localhost:3004
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://localhost:3005
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://localhost:3006
NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx
NEXT_PUBLIC_API_BASE_PATH=/
import type * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/tracing';
import appConfig from 'configs/app';
export const config: Sentry.BrowserOptions = {
environment: process.env.NEXT_PUBLIC_APP_ENV || process.env.NODE_ENV,
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
release: process.env.NEXT_PUBLIC_GIT_COMMIT_SHA,
environment: appConfig.features.sentry.environment,
dsn: appConfig.features.sentry.dsn,
release: process.env.NEXT_PUBLIC_GIT_TAG || process.env.NEXT_PUBLIC_GIT_COMMIT_SHA,
integrations: [ new BrowserTracing() ],
// We recommend adjusting this value in production, or using tracesSampler
// for finer control
......@@ -55,5 +57,5 @@ export const config: Sentry.BrowserOptions = {
};
export function configureScope(scope: Sentry.Scope) {
scope.setTag('app_instance', process.env.NEXT_PUBLIC_APP_INSTANCE);
scope.setTag('app_instance', appConfig.features.sentry.instance);
}
......@@ -9,5 +9,8 @@ fi
# Execute script for replace build-time ENVs placeholders with their values at runtime
./replace_envs.sh
echo "starting Nextjs"
# Print list of enabled features
node ./feature-reporter.js
echo "Starting Next.js application"
exec "$@"
\ No newline at end of file
......@@ -64,11 +64,13 @@ async function checkPlaceholdersCongruity(runTimeEnvs: Record<string, string>) {
}
if (inconsistencies.length > 0) {
console.log(`🚸 For the following environment variables placeholders were not generated at build-time:`);
console.log('🚸 For the following environment variables placeholders were not generated at build-time:');
inconsistencies.forEach((env) => {
console.log(` ${ env }`);
});
console.log('They are either deprecated or running the app with them may lead to unexpected behavior. Please check the documentation for more details.');
console.log(` They are either deprecated or running the app with them may lead to unexpected behavior.
Please check the documentation for more details - https://github.com/blockscout/frontend/blob/main/docs/ENVS.md
`);
throw new Error();
}
......
/node_modules
/build
index.js
\ No newline at end of file
#!/bin/bash
rm -rf ./build
yarn compile_config
yarn build
dotenv -e ../../../configs/envs/.env.main -e ../../../configs/envs/.env.secrets yarn print_report
\ No newline at end of file
/* eslint-disable no-console */
const config = require('./build/configs/app').default;
run();
async function run() {
console.log();
try {
console.log(`📋 Here is the list of the features enabled for the running instance.
To adjust their configuration, please refer to the documentation - https://github.com/blockscout/frontend/blob/main/docs/ENVS.md#app-features
`);
Object.entries(config.features)
.forEach(([ , feature ]) => {
const mark = feature.isEnabled ? 'v' : ' ';
console.log(` [${ mark }] ${ feature.title }`);
});
} catch (error) {
console.log('🚨 An error occurred while generating the feature report.');
process.exit(1);
}
console.log();
}
{
"name": "feature-reporter",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"compile_config": "yarn tsc -p ./tsconfig.json && yarn tsc-alias -p ./tsconfig.json",
"build": "yarn webpack-cli -c ./webpack.config.js",
"print_report": "node ./index.js",
"dev": "./dev.sh"
},
"dependencies": {
"tsc": "^2.0.4",
"tsc-alias": "^1.8.7",
"typescript": "5.1",
"webpack": "^5.88.2",
"webpack-cli": "^5.1.4"
},
"devDependencies": {
"dotenv-cli": "^7.2.1"
}
}
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"noEmit": false,
"module": "CommonJS",
"outDir": "./build",
"paths": {
"nextjs-routes": ["./types/nextjs-routes.d.ts"],
}
},
"include": [ "../../../configs/app/index.ts" ],
"tsc-alias": {
"verbose": true,
"resolveFullPaths": true,
}
}
const path = require('path');
module.exports = {
mode: 'production',
target: 'node',
entry: path.resolve(__dirname, '/entry.js'),
resolve: {
extensions: [ '.js' ],
},
output: {
filename: 'index.js',
path: path.resolve(__dirname),
},
};
This diff is collapsed.
......@@ -186,7 +186,6 @@ frontend:
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/base-goerli.json
NEXT_PUBLIC_NETWORK_EXPLORERS: ''
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND: "linear-gradient(136.9deg,rgb(107 94 236) 1.5%,rgb(0 82 255) 56.84%,rgb(82 62 231) 98.54%)"
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS: ''
NEXT_PUBLIC_NETWORK_RPC_URL: https://goerli.optimism.io
NEXT_PUBLIC_WEB3_DEFAULT_WALLET: coinbase
NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET: true
......@@ -199,6 +198,5 @@ frontend:
NEXT_PUBLIC_L2_WITHDRAWAL_URL: https://app.optimism.io/bridge/withdraw
NEXT_PUBLIC_GRAPHIQL_TRANSACTION: 0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62
envFromSecret:
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS: ref+vault://deployment-values/blockscout/dev/l2-optimism-goerli?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS
NEXT_PUBLIC_AUTH0_CLIENT_ID: ref+vault://deployment-values/blockscout/dev/l2-optimism-goerli?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_AUTH0_CLIENT_ID
NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ref+vault://deployment-values/blockscout/dev/l2-optimism-goerli?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID
......@@ -144,7 +144,6 @@ frontend:
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_NETWORK_TOKEN_ADDRESS: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE: validation
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM: https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_APP_ENV: stable
......@@ -160,7 +159,6 @@ frontend:
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/eth-goerli.json
NEXT_PUBLIC_GRAPHIQL_TRANSACTION: 0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
envFromSecret:
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS: ref+vault://deployment-values/blockscout/dev/front-main?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS
NEXT_PUBLIC_SENTRY_DSN: ref+vault://deployment-values/blockscout/dev/front-main?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_SENTRY_DSN
SENTRY_CSP_REPORT_URI: ref+vault://deployment-values/blockscout/dev/front-main?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/SENTRY_CSP_REPORT_URI
NEXT_PUBLIC_AUTH0_CLIENT_ID: ref+vault://deployment-values/blockscout/dev/front-main?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_AUTH0_CLIENT_ID
......
......@@ -107,8 +107,6 @@ frontend:
_default: ''
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND:
_default: "linear-gradient(136.9deg,rgb(107 94 236) 1.5%,rgb(0 82 255) 56.84%,rgb(82 62 231) 98.54%)"
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS:
_default: ''
NEXT_PUBLIC_NETWORK_RPC_URL:
_default: https://goerli.optimism.io
NEXT_PUBLIC_WEB3_DEFAULT_WALLET:
......@@ -133,8 +131,6 @@ frontend:
_default: https://app.optimism.io/bridge/withdraw
NEXT_PUBLIC_GRAPHIQL_TRANSACTION:
_default: 0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS:
_default: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS
NEXT_PUBLIC_SENTRY_DSN:
_default: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_SENTRY_DSN
SENTRY_CSP_REPORT_URI:
......
......@@ -111,8 +111,6 @@ frontend:
_default: true
NEXT_PUBLIC_GRAPHIQL_TRANSACTION:
_default: 0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS:
_default: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS
NEXT_PUBLIC_SENTRY_DSN:
_default: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_SENTRY_DSN
SENTRY_CSP_REPORT_URI:
......
......@@ -59,7 +59,7 @@ For all types of dependencies:
These are the steps that you have to follow to make everything work:
1. First and foremost, document variable in the [/docs/ENVS.md](./ENVS.md) file; provide short description, its expected type, requirement flag, default and example value; **do not skip this step** otherwise the app will not receive variable value at run-time
2. Make sure that you have added a property to React app config (`/configs/app/config.ts`) that is associated with this variable; do not use ENV variable values directly in the application code
2. Make sure that you have added a property to React app config (`/configs/app/index.ts`) in appropriate section that is associated with this variable; do not use ENV variable values directly in the application code
3. For local development purposes add the variable with its appropriate values to pre-defined ENV configs `/configs/envs` where it is needed
4. Add the variable to CI configs where it is needed
- `deploy/values/review/values.yaml.gotmpl` - review development environment
......
This diff is collapsed.
import { compile } from 'path-to-regexp';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import isNeedProxy from './isNeedProxy';
import { RESOURCES } from './resources';
......@@ -12,8 +12,8 @@ export default function buildUrl<R extends ResourceName>(
queryParams?: Record<string, string | Array<string> | number | null | undefined>,
): string {
const resource: ApiResource = RESOURCES[resourceName];
const baseUrl = isNeedProxy() ? appConfig.app.baseUrl : (resource.endpoint || appConfig.api.endpoint);
const basePath = resource.basePath !== undefined ? resource.basePath : appConfig.api.basePath;
const baseUrl = isNeedProxy() ? config.app.baseUrl : (resource.endpoint || config.api.endpoint);
const basePath = resource.basePath !== undefined ? resource.basePath : config.api.basePath;
const path = isNeedProxy() ? '/node-api/proxy' + basePath + resource.path : basePath + resource.path;
const url = new URL(compile(path)(pathParams), baseUrl);
......
import { compile } from 'path-to-regexp';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import { RESOURCES } from './resources';
import type { ApiResource, ResourceName } from './resources';
......@@ -11,8 +11,8 @@ export default function buildUrlNode(
queryParams?: Record<string, string | number | undefined>,
) {
const resource: ApiResource = typeof _resource === 'string' ? RESOURCES[_resource] : _resource;
const baseUrl = resource.endpoint || appConfig.api.endpoint;
const basePath = resource.basePath !== undefined ? resource.basePath : appConfig.api.basePath;
const baseUrl = resource.endpoint || config.api.endpoint;
const basePath = resource.basePath !== undefined ? resource.basePath : config.api.basePath;
const path = basePath + resource.path;
const url = new URL(compile(path)(pathParams), baseUrl);
......
import appConfig from 'configs/app/config';
import config from 'configs/app';
// FIXME
// I was not able to figure out how to send CORS with credentials from localhost
// unsuccessfully tried different ways, even custom local dev domain
// so for local development we have to use next.js api as proxy server
export default function isNeedProxy() {
if (appConfig.app.useNextJsProxy) {
if (config.app.useProxy) {
return true;
}
return appConfig.app.host === 'localhost' && appConfig.app.host !== appConfig.api.host;
return config.app.host === 'localhost' && config.app.host !== config.api.host;
}
......@@ -59,7 +59,7 @@ import type { VisualizedContract } from 'types/api/visualization';
import type { WithdrawalsResponse, WithdrawalsCounters } from 'types/api/withdrawals';
import type { ArrayElement } from 'types/utils';
import appConfig from 'configs/app/config';
import config from 'configs/app';
export interface ApiResource {
path: ResourcePath;
......@@ -111,58 +111,58 @@ export const RESOURCES = {
address_verification: {
path: '/api/v1/chains/:chainId/verified-addresses:type',
pathParams: [ 'chainId' as const, 'type' as const ],
endpoint: appConfig.contractInfoApi.endpoint,
basePath: appConfig.contractInfoApi.basePath,
endpoint: config.features.verifiedTokens.api.endpoint,
basePath: config.features.verifiedTokens.api.basePath,
needAuth: true,
},
verified_addresses: {
path: '/api/v1/chains/:chainId/verified-addresses',
pathParams: [ 'chainId' as const ],
endpoint: appConfig.contractInfoApi.endpoint,
basePath: appConfig.contractInfoApi.basePath,
endpoint: config.features.verifiedTokens.api.endpoint,
basePath: config.features.verifiedTokens.api.basePath,
needAuth: true,
},
token_info_applications_config: {
path: '/api/v1/chains/:chainId/token-info-submissions/selectors',
pathParams: [ 'chainId' as const ],
endpoint: appConfig.adminServiceApi.endpoint,
basePath: appConfig.adminServiceApi.basePath,
endpoint: config.features.addressVerification.api.endpoint,
basePath: config.features.addressVerification.api.basePath,
needAuth: true,
},
token_info_applications: {
path: '/api/v1/chains/:chainId/token-info-submissions/:id?',
pathParams: [ 'chainId' as const, 'id' as const ],
endpoint: appConfig.adminServiceApi.endpoint,
basePath: appConfig.adminServiceApi.basePath,
endpoint: config.features.addressVerification.api.endpoint,
basePath: config.features.addressVerification.api.basePath,
needAuth: true,
},
// STATS
stats_counters: {
path: '/api/v1/counters',
endpoint: appConfig.statsApi.endpoint,
basePath: appConfig.statsApi.basePath,
endpoint: config.features.stats.api.endpoint,
basePath: config.features.stats.api.basePath,
},
stats_lines: {
path: '/api/v1/lines',
endpoint: appConfig.statsApi.endpoint,
basePath: appConfig.statsApi.basePath,
endpoint: config.features.stats.api.endpoint,
basePath: config.features.stats.api.basePath,
},
stats_line: {
path: '/api/v1/lines/:id',
pathParams: [ 'id' as const ],
endpoint: appConfig.statsApi.endpoint,
basePath: appConfig.statsApi.basePath,
endpoint: config.features.stats.api.endpoint,
basePath: config.features.stats.api.basePath,
},
// VISUALIZATION
visualize_sol2uml: {
path: '/api/v1/solidity\\:visualize-contracts',
endpoint: appConfig.visualizeApi.endpoint,
basePath: appConfig.visualizeApi.basePath,
endpoint: config.features.sol2uml.api.endpoint,
basePath: config.features.sol2uml.api.basePath,
},
// BLOCKS, TXS
......@@ -345,8 +345,8 @@ export const RESOURCES = {
token_verified_info: {
path: '/api/v1/chains/:chainId/token-infos/:hash',
pathParams: [ 'chainId' as const, 'hash' as const ],
endpoint: appConfig.contractInfoApi.endpoint,
basePath: appConfig.contractInfoApi.basePath,
endpoint: config.features.verifiedTokens.api.endpoint,
basePath: config.features.verifiedTokens.api.basePath,
},
token_counters: {
path: '/api/v2/tokens/:hash/counters',
......
......@@ -4,7 +4,7 @@ import React from 'react';
import type { CsrfData } from 'types/client/account';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import isBodyAllowed from 'lib/api/isBodyAllowed';
import isNeedProxy from 'lib/api/isNeedProxy';
import { getResourceKey } from 'lib/api/useApiQuery';
......@@ -49,7 +49,7 @@ export default function useApiFetch() {
// for user authentication in My account
// for API rate-limits (cannot use in the condition though, but we agreed with devops team that should not be an issue)
// change condition here if something is changed
credentials: appConfig.account.isEnabled ? 'include' : 'same-origin',
credentials: config.features.account.isEnabled ? 'include' : 'same-origin',
headers,
...fetchParams,
},
......
import type CspDev from 'csp-dev';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import { KEY_WORDS } from '../utils';
const MAIN_DOMAINS = [
`*.${ appConfig.app.host }`,
appConfig.app.host,
appConfig.visualizeApi.endpoint,
`*.${ config.app.host }`,
config.app.host,
config.features.sol2uml.api.endpoint,
].filter(Boolean);
// eslint-disable-next-line no-restricted-properties
const REPORT_URI = process.env.SENTRY_CSP_REPORT_URI;
export function app(): CspDev.DirectiveDescriptor {
return {
......@@ -26,18 +25,18 @@ export function app(): CspDev.DirectiveDescriptor {
...MAIN_DOMAINS,
// webpack hmr in safari doesn't recognize localhost as 'self' for some reason
appConfig.isDev ? 'ws://localhost:3000/_next/webpack-hmr' : '',
config.app.isDev ? 'ws://localhost:3000/_next/webpack-hmr' : '',
// API
appConfig.api.endpoint,
appConfig.api.socket,
appConfig.statsApi.endpoint,
appConfig.visualizeApi.endpoint,
appConfig.contractInfoApi.endpoint,
appConfig.adminServiceApi.endpoint,
// APIs
config.api.endpoint,
config.api.socket,
config.features.stats.api.endpoint,
config.features.sol2uml.api.endpoint,
config.features.verifiedTokens.api.endpoint,
config.features.addressVerification.api.endpoint,
// chain RPC server
appConfig.network.rpcUrl,
config.chain.rpcUrl,
'https://infragrid.v.network', // RPC providers
// github (spec for api-docs page)
......@@ -50,7 +49,7 @@ export function app(): CspDev.DirectiveDescriptor {
// next.js generates and rebuilds source maps in dev using eval()
// https://github.com/vercel/next.js/issues/14221#issuecomment-657258278
appConfig.isDev ? KEY_WORDS.UNSAFE_EVAL : '',
config.app.isDev ? KEY_WORDS.UNSAFE_EVAL : '',
// hash of ColorModeScript
'\'sha256-e7MRMmTzLsLQvIy1iizO1lXf7VWYoQ6ysj5fuUzvRwE=\'',
......@@ -109,9 +108,9 @@ export function app(): CspDev.DirectiveDescriptor {
'*',
],
...(REPORT_URI && !appConfig.isDev ? {
...(config.features.sentry.isEnabled && config.features.sentry.cspReportUrl && !config.app.isDev ? {
'report-uri': [
REPORT_URI,
config.features.sentry.cspReportUrl,
],
} : {}),
};
......
import type CspDev from 'csp-dev';
import appConfig from 'configs/app/config';
import config from 'configs/app';
export function googleAnalytics(): CspDev.DirectiveDescriptor {
if (!appConfig.googleAnalytics.propertyId) {
if (!config.features.googleAnalytics.isEnabled) {
return {};
}
......
import type CspDev from 'csp-dev';
import appConfig from 'configs/app/config';
import config from 'configs/app';
export function googleReCaptcha(): CspDev.DirectiveDescriptor {
if (!appConfig.reCaptcha.siteKey) {
if (!config.services.reCaptcha.siteKey) {
return {};
}
......
import type CspDev from 'csp-dev';
import appConfig from 'configs/app/config';
import config from 'configs/app';
export function mixpanel(): CspDev.DirectiveDescriptor {
if (!appConfig.mixpanel.projectToken) {
if (!config.features.mixpanel.isEnabled) {
return {};
}
......
import type CspDev from 'csp-dev';
import appConfig from 'configs/app/config';
import config from 'configs/app';
export function walletConnect(): CspDev.DirectiveDescriptor {
if (!appConfig.walletConnect.projectId || !appConfig.network.rpcUrl) {
if (!config.features.blockchainInteraction.isEnabled) {
return {};
}
......
import appConfig from 'configs/app/config';
import config from 'configs/app';
import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies';
export default function useHasAccount() {
const appProps = useAppContext();
if (!appConfig.account.isEnabled) {
if (!config.features.account.isEnabled) {
return false;
}
......
import React from 'react';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import isBrowser from 'lib/isBrowser';
const base = 'https://github.com/blockscout/blockscout/issues/new/';
const labels = 'new UI';
const title = `${ appConfig.network.name }: <Issue Title>`;
const title = `${ config.chain.name }: <Issue Title>`;
export default function useIssueUrl(backendVersion: string | undefined) {
const [ userAgent, setUserAgent ] = React.useState('');
......@@ -24,7 +24,7 @@ export default function useIssueUrl(backendVersion: string | undefined) {
### Environment
* Backend Version/branch/commit: ${ backendVersion }
* Frontend Version+commit: ${ [ appConfig.footer.frontendVersion, appConfig.footer.frontendCommit ].filter(Boolean).join('+') }
* Frontend Version+commit: ${ [ config.UI.footer.frontendVersion, config.UI.footer.frontendCommit ].filter(Boolean).join('+') }
* User Agent: ${ userAgent }
### Steps to reproduce
......
import { useRouter } from 'next/router';
import { route } from 'nextjs-routes';
import appConfig from 'configs/app/config';
import config from 'configs/app';
export default function useLoginUrl() {
const router = useRouter();
return appConfig.account.authUrl + route({ pathname: '/auth/auth0', query: { path: router.asPath } });
return config.features.account.authUrl + route({ pathname: '/auth/auth0', query: { path: router.asPath } });
}
......@@ -3,7 +3,7 @@ import React from 'react';
import type { NavItemInternal, NavItem, NavGroupItem } from 'types/client/navigation-items';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import abiIcon from 'icons/ABI.svg';
import apiKeysIcon from 'icons/API.svg';
import appsIcon from 'icons/apps.svg';
......@@ -43,9 +43,6 @@ export function isInternalItem(item: NavItem): item is NavItemInternal {
}
export default function useNavItems(): ReturnType {
const isMarketplaceAvailable = Boolean(appConfig.marketplace.configUrl && appConfig.network.rpcUrl);
const hasAPIDocs = appConfig.apiDoc.specUrl;
const router = useRouter();
const pathname = router.pathname;
......@@ -74,7 +71,7 @@ export default function useNavItems(): ReturnType {
// eslint-disable-next-line max-len
{ text: 'Verified contracts', nextRoute: { pathname: '/verified-contracts' as const }, icon: verifiedIcon, isActive: pathname === '/verified-contracts' };
if (appConfig.L2.isL2Network) {
if (config.features.rollup.isEnabled) {
blockchainNavItems = [
[
txs,
......@@ -101,7 +98,7 @@ export default function useNavItems(): ReturnType {
blocks,
topAccounts,
verifiedContracts,
appConfig.beaconChain.hasBeaconChain && {
config.features.beaconChain.isEnabled && {
text: 'Withdrawals',
nextRoute: { pathname: '/withdrawals' as const },
icon: withdrawalsIcon,
......@@ -111,18 +108,18 @@ export default function useNavItems(): ReturnType {
}
const apiNavItems: Array<NavItem> = [
hasAPIDocs ? {
config.features.restApiDocs.isEnabled ? {
text: 'REST API',
nextRoute: { pathname: '/api-docs' as const },
icon: apiDocsIcon,
isActive: pathname === '/api-docs',
} : null,
{
config.features.graphqlApiDocs.isEnabled ? {
text: 'GraphQL',
nextRoute: { pathname: '/graphiql' as const },
icon: graphQLIcon,
isActive: pathname === '/graphiql',
},
} : null,
{
text: 'RPC API',
icon: rpcIcon,
......@@ -148,13 +145,13 @@ export default function useNavItems(): ReturnType {
icon: tokensIcon,
isActive: pathname.startsWith('/token'),
},
isMarketplaceAvailable ? {
config.features.marketplace.isEnabled ? {
text: 'Apps',
nextRoute: { pathname: '/apps' as const },
icon: appsIcon,
isActive: pathname.startsWith('/app'),
} : null,
appConfig.statsApi.endpoint ? {
config.features.stats.isEnabled ? {
text: 'Charts & stats',
nextRoute: { pathname: '/stats' as const },
icon: statsIcon,
......@@ -166,10 +163,10 @@ export default function useNavItems(): ReturnType {
isActive: apiNavItems.some(item => isInternalItem(item) && item.isActive),
subItems: apiNavItems,
},
appConfig.navigation.otherLinks.length > 0 ? {
config.UI.sidebar.otherLinks.length > 0 ? {
text: 'Other',
icon: gearIcon,
subItems: appConfig.navigation.otherLinks,
subItems: config.UI.sidebar.otherLinks,
} : null,
].filter(Boolean);
......@@ -202,7 +199,7 @@ export default function useNavItems(): ReturnType {
icon: abiIcon,
isActive: pathname === '/account/custom-abi',
},
appConfig.contractInfoApi.endpoint && appConfig.adminServiceApi.endpoint && {
config.features.addressVerification.isEnabled && {
text: 'Verified addrs',
nextRoute: { pathname: '/account/verified-addresses' as const },
icon: verifiedIcon,
......@@ -218,5 +215,5 @@ export default function useNavItems(): ReturnType {
};
return { mainNavItems, accountNavItems, profileItem };
}, [ hasAPIDocs, isMarketplaceAvailable, pathname ]);
}, [ pathname ]);
}
......@@ -2,7 +2,7 @@ import type { Route } from 'nextjs-routes';
import type { ApiData, Metadata } from './types';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import compileValue from './compileValue';
......@@ -12,7 +12,7 @@ export default function generate<R extends Route>(route: R, apiData?: ApiData<R>
const params = {
...route.query,
...apiData,
network_name: appConfig.network.name,
network_name: config.chain.name,
network_title: getNetworkTitle(),
};
......
import appConfig from 'configs/app/config';
import config from 'configs/app';
import delay from 'lib/delay';
export default function isGoogleAnalyticsLoaded(retries = 3): Promise<boolean> {
if (!retries || !appConfig.googleAnalytics.propertyId) {
if (!retries || !config.features.googleAnalytics.isEnabled) {
return Promise.resolve(false);
}
return typeof window.ga?.getAll === 'function' ? Promise.resolve(true) : delay(500).then(() => isGoogleAnalyticsLoaded(retries - 1));
......
import mixpanel from 'mixpanel-browser';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import type { EventTypes, EventPayload } from './utils';
......@@ -12,7 +12,7 @@ export default function logEvent<EventType extends EventTypes>(
optionsOrCallback?: TrackFnArgs[2],
callback?: TrackFnArgs[3],
) {
if (!appConfig.mixpanel.projectToken) {
if (!config.features.mixpanel.isEnabled) {
return;
}
mixpanel.track(type, properties, optionsOrCallback, callback);
......
......@@ -5,7 +5,7 @@ import { useRouter } from 'next/router';
import React from 'react';
import { deviceType } from 'react-device-detect';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import * as cookies from 'lib/cookies';
import getQueryParamString from 'lib/router/getQueryParamString';
......@@ -19,21 +19,21 @@ export default function useMixpanelInit() {
React.useEffect(() => {
isGoogleAnalyticsLoaded().then((isGALoaded) => {
if (!appConfig.mixpanel.projectToken) {
if (!config.features.mixpanel.isEnabled) {
return;
}
const debugFlagCookie = cookies.get(cookies.NAMES.MIXPANEL_DEBUG);
const config: Partial<Config> = {
const mixpanelConfig: Partial<Config> = {
debug: Boolean(debugFlagQuery.current || debugFlagCookie),
};
const isAuth = Boolean(cookies.get(cookies.NAMES.API_TOKEN));
mixpanel.init(appConfig.mixpanel.projectToken, config);
mixpanel.init(config.features.mixpanel.projectToken, mixpanelConfig);
mixpanel.register({
'Chain id': appConfig.network.id,
Environment: appConfig.isDev ? 'Dev' : 'Prod',
'Chain id': config.chain.id,
Environment: config.app.isDev ? 'Dev' : 'Prod',
Authorized: isAuth,
'Viewport width': window.innerWidth,
'Viewport height': window.innerHeight,
......
......@@ -2,7 +2,7 @@ import { usePathname } from 'next/navigation';
import { useRouter } from 'next/router';
import React from 'react';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import getQueryParamString from 'lib/router/getQueryParamString';
import getPageType from './getPageType';
......@@ -18,7 +18,7 @@ export default function useLogPageView(isInited: boolean) {
const page = getQueryParamString(router.query.page);
React.useEffect(() => {
if (!appConfig.mixpanel.projectToken || !isInited) {
if (!config.features.mixpanel.isEnabled || !isInited) {
return;
}
......
import appConfig from 'configs/app/config';
import config from 'configs/app';
// TODO delete when page descriptions is refactored
export default function getNetworkTitle() {
return appConfig.network.name + (appConfig.network.shortName ? ` (${ appConfig.network.shortName })` : '') + ' Explorer';
return config.chain.name + (config.chain.shortName ? ` (${ config.chain.shortName })` : '') + ' Explorer';
}
import appConfig from 'configs/app/config';
import config from 'configs/app';
export default function getNetworkValidatorTitle() {
return appConfig.network.verificationType === 'validation' ? 'validator' : 'miner';
return config.chain.verificationType === 'validation' ? 'validator' : 'miner';
}
......@@ -3,7 +3,7 @@ import _mapValues from 'lodash/mapValues';
import type { NetworkExplorer } from 'types/networks';
import appConfig from 'configs/app/config';
import config from 'configs/app';
// for easy .env update
// const NETWORK_EXPLORERS = JSON.stringify([
......@@ -29,7 +29,7 @@ const stripTrailingSlash = (str: string) => str[str.length - 1] === '/' ? str.sl
const addLeadingSlash = (str: string) => str[0] === '/' ? str : '/' + str;
const networkExplorers: Array<NetworkExplorer> = (() => {
return appConfig.network.explorers.map((explorer) => ({
return config.UI.explorers.items.map((explorer) => ({
...explorer,
baseUrl: stripTrailingSlash(explorer.baseUrl),
paths: _mapValues(explorer.paths, _compose(stripTrailingSlash, addLeadingSlash)),
......
import type { GetServerSideProps } from 'next';
import appConfig from 'configs/app/config';
import config from 'configs/app';
export type Props = {
cookies: string;
......@@ -25,7 +25,7 @@ export const base: GetServerSideProps<Props> = async({ req, query }) => {
};
export const account: GetServerSideProps<Props> = async(context) => {
if (!appConfig.account.isEnabled) {
if (!config.features.account.isEnabled) {
return {
notFound: true,
};
......@@ -35,7 +35,7 @@ export const account: GetServerSideProps<Props> = async(context) => {
};
export const verifiedAddresses: GetServerSideProps<Props> = async(context) => {
if (!appConfig.adminServiceApi.endpoint || !appConfig.contractInfoApi.endpoint) {
if (!config.features.addressVerification.isEnabled) {
return {
notFound: true,
};
......@@ -45,7 +45,7 @@ export const verifiedAddresses: GetServerSideProps<Props> = async(context) => {
};
export const beaconChain: GetServerSideProps<Props> = async(context) => {
if (!appConfig.beaconChain.hasBeaconChain) {
if (!config.features.beaconChain.isEnabled) {
return {
notFound: true,
};
......@@ -55,7 +55,7 @@ export const beaconChain: GetServerSideProps<Props> = async(context) => {
};
export const L2: GetServerSideProps<Props> = async(context) => {
if (!appConfig.L2.isL2Network) {
if (!config.features.rollup.isEnabled) {
return {
notFound: true,
};
......@@ -65,7 +65,7 @@ export const L2: GetServerSideProps<Props> = async(context) => {
};
export const marketplace: GetServerSideProps<Props> = async(context) => {
if (!appConfig.marketplace.configUrl || !appConfig.network.rpcUrl) {
if (!config.features.marketplace.isEnabled) {
return {
notFound: true,
};
......@@ -75,7 +75,7 @@ export const marketplace: GetServerSideProps<Props> = async(context) => {
};
export const apiDocs: GetServerSideProps<Props> = async(context) => {
if (!appConfig.apiDoc.specUrl) {
if (!config.features.restApiDocs.isEnabled) {
return {
notFound: true,
};
......@@ -85,7 +85,7 @@ export const apiDocs: GetServerSideProps<Props> = async(context) => {
};
export const csvExport: GetServerSideProps<Props> = async(context) => {
if (!appConfig.reCaptcha.siteKey) {
if (!config.features.csvExport.isEnabled) {
return {
notFound: true,
};
......@@ -95,7 +95,7 @@ export const csvExport: GetServerSideProps<Props> = async(context) => {
};
export const stats: GetServerSideProps<Props> = async(context) => {
if (!appConfig.statsApi.endpoint) {
if (!config.features.stats.isEnabled) {
return {
notFound: true,
};
......
......@@ -2,13 +2,13 @@ import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
import { route } from 'nextjs-routes';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import { httpLogger } from 'lib/api/logger';
import { DAY } from 'lib/consts';
import * as cookies from 'lib/cookies';
export function account(req: NextRequest) {
if (!appConfig.account.isEnabled) {
if (!config.features.account.isEnabled) {
return;
}
......@@ -24,7 +24,7 @@ export function account(req: NextRequest) {
const isProfileRoute = req.nextUrl.pathname.includes('/auth/profile');
if ((isAccountRoute || isProfileRoute)) {
const authUrl = appConfig.account.authUrl + route({ pathname: '/auth/auth0', query: { path: req.nextUrl.pathname } });
const authUrl = config.features.account.authUrl + route({ pathname: '/auth/auth0', query: { path: req.nextUrl.pathname } });
return NextResponse.redirect(authUrl);
}
}
......@@ -35,7 +35,7 @@ export function account(req: NextRequest) {
if (apiTokenCookie) {
// temporary solution
// TODO check app for integrity https://github.com/blockscout/frontend/issues/1028 and make typescript happy here
if (!appConfig.account.logoutUrl) {
if (!config.features.account.logoutUrl) {
httpLogger.logger.error({
message: 'Logout URL is not configured',
});
......@@ -46,7 +46,7 @@ export function account(req: NextRequest) {
// logout URL is always external URL in auth0.com sub-domain
// at least we hope so
const res = NextResponse.redirect(appConfig.account.logoutUrl);
const res = NextResponse.redirect(config.features.account.logoutUrl);
res.cookies.delete(cookies.NAMES.CONFIRM_EMAIL_PAGE_VIEWED); // reset cookie to show email verification page again
return res;
......@@ -55,7 +55,7 @@ export function account(req: NextRequest) {
// if user hasn't seen email verification page, make redirect to it
if (!req.cookies.get(cookies.NAMES.CONFIRM_EMAIL_PAGE_VIEWED)) {
if (!req.nextUrl.pathname.includes('/auth/unverified-email')) {
const url = appConfig.app.baseUrl + route({ pathname: '/auth/unverified-email' });
const url = config.app.baseUrl + route({ pathname: '/auth/unverified-email' });
const res = NextResponse.redirect(url);
res.cookies.set({
name: cookies.NAMES.CONFIRM_EMAIL_PAGE_VIEWED,
......
......@@ -3,13 +3,13 @@ import type { WindowProvider } from 'wagmi';
import 'wagmi/window';
import appConfig from 'configs/app/config';
import config from 'configs/app';
export default function useProvider() {
const [ provider, setProvider ] = React.useState<WindowProvider>();
React.useEffect(() => {
if (!('ethereum' in window && window.ethereum)) {
if (!('ethereum' in window && window.ethereum) || !config.features.web3Wallet.isEnabled) {
return;
}
......@@ -18,11 +18,11 @@ export default function useProvider() {
const providers = Array.isArray(window.ethereum.providers) ? window.ethereum.providers : [ window.ethereum ];
providers.forEach(async(provider) => {
if (appConfig.web3.defaultWallet === 'coinbase' && provider.isCoinbaseWallet) {
if (config.features.web3Wallet.defaultWallet === 'coinbase' && provider.isCoinbaseWallet) {
return setProvider(provider);
}
if (appConfig.web3.defaultWallet === 'metamask' && provider.isMetaMask) {
if (config.features.web3Wallet.defaultWallet === 'metamask' && provider.isMetaMask) {
return setProvider(provider);
}
});
......
......@@ -3,7 +3,7 @@ import type { WalletType, WalletInfo } from 'types/client/wallets';
import coinbaseIcon from 'icons/wallets/coinbase.svg';
import metamaskIcon from 'icons/wallets/metamask.svg';
export const WALLETS_INFO: Record<WalletType, WalletInfo> = {
export const WALLETS_INFO: Record<Exclude<WalletType, 'none'>, WalletInfo> = {
metamask: {
name: 'MetaMask',
icon: metamaskIcon,
......
......@@ -5,7 +5,7 @@ import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import type { AppProps } from 'next/app';
import React, { useState } from 'react';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import { AppContextProvider } from 'lib/contexts/app';
import { ChakraProvider } from 'lib/contexts/chakra';
import { ScrollDirectionProvider } from 'lib/contexts/scrollDirection';
......@@ -85,7 +85,7 @@ function MyApp({ Component, pageProps }: AppProps) {
<AppContextProvider pageProps={ pageProps }>
<QueryClientProvider client={ queryClient }>
<ScrollDirectionProvider>
<SocketProvider url={ `${ appConfig.api.socket }${ appConfig.api.basePath }/socket/v2` }>
<SocketProvider url={ `${ config.api.socket }${ config.api.basePath }/socket/v2` }>
<Component { ...pageProps }/>
</SocketProvider>
</ScrollDirectionProvider>
......
......@@ -3,7 +3,7 @@ import type { DocumentContext } from 'next/document';
import Document, { Html, Head, Main, NextScript } from 'next/document';
import React from 'react';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import * as serverTiming from 'lib/next/serverTiming';
import theme from 'theme';
......@@ -47,11 +47,11 @@ class MyDocument extends Document {
// eslint-disable-next-line max-len
content="Blockscout is the #1 open-source blockchain explorer available today. 100+ chains and counting rely on Blockscout data availability, APIs, and ecosystem tools to support their networks."
/>
<meta property="og:image" content={ appConfig.app.baseUrl + '/static/og.png' }/>
<meta property="og:image" content={ config.app.baseUrl + '/static/og.png' }/>
<meta property="og:site_name" content="Blockscout"/>
<meta property="og:type" content="website"/>
<meta name="twitter:card" content="summary_large_image"/>
<meta property="twitter:image" content={ appConfig.app.baseUrl + '/static/og_twitter.png' }/>
<meta property="twitter:image" content={ config.app.baseUrl + '/static/og_twitter.png' }/>
</Head>
<body>
<ColorModeScript initialColorMode={ theme.config.initialColorMode }/>
......
......@@ -2,7 +2,7 @@ import _pick from 'lodash/pick';
import _pickBy from 'lodash/pickBy';
import type { NextApiRequest, NextApiResponse } from 'next';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import fetchFactory from 'lib/api/nodeFetch';
const handler = async(nextReq: NextApiRequest, nextRes: NextApiResponse) => {
......@@ -13,7 +13,7 @@ const handler = async(nextReq: NextApiRequest, nextRes: NextApiResponse) => {
const url = new URL(
nextReq.url.replace(/^\/node-api\/proxy/, ''),
nextReq.headers['x-endpoint']?.toString() || appConfig.api.endpoint,
nextReq.headers['x-endpoint']?.toString() || config.api.endpoint,
);
const apiRes = await fetchFactory(nextReq)(
url.toString(),
......
export type WalletType = 'metamask' | 'coinbase';
export type WalletType = 'metamask' | 'coinbase' | 'none';
export interface WalletInfo {
name: string;
......
......@@ -7,7 +7,6 @@ export type NextPublicEnvs = {
NEXT_PUBLIC_NETWORK_CURRENCY_NAME?: string;
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL?: string;
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS?: string;
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS?: string;
NEXT_PUBLIC_NETWORK_ASSETS_PATHNAME?: string;
NEXT_PUBLIC_NETWORK_LOGO?: string;
NEXT_PUBLIC_NETWORK_LOGO_DARK?: string;
......@@ -34,18 +33,13 @@ export type NextPublicEnvs = {
NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER?: 'true' | 'false';
NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME?: 'true' | 'false';
// Ads config
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP?: string;
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE?: string;
NEXT_PUBLIC_AD_BANNER_PROVIDER?: 'slise' | 'adbutler' | 'coinzilla' | 'none';
// Text ads config
NEXT_PUBLIC_AD_TEXT_PROVIDER?: 'coinzilla' | 'none';
// App config
NEXT_PUBLIC_APP_INSTANCE?: string;
NEXT_PUBLIC_APP_PROTOCOL?: 'http' | 'https';
NEXT_PUBLIC_APP_HOST: string;
NEXT_PUBLIC_APP_PORT?: string;
NEXT_PUBLIC_APP_ENV?: string;
// API config
NEXT_PUBLIC_API_PROTOCOL?: 'http' | 'https';
......@@ -56,11 +50,8 @@ export type NextPublicEnvs = {
NEXT_PUBLIC_STATS_API_HOST?: string;
NEXT_PUBLIC_VISUALIZE_API_HOST?: string;
NEXT_PUBLIC_CONTRACT_INFO_API_HOST?: string;
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST?: string;
// external services config
NEXT_PUBLIC_SENTRY_DSN?: string;
SENTRY_CSP_REPORT_URI?: string;
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID?: string;
NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY?: string;
NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID?: string;
......@@ -73,7 +64,9 @@ export type NextPublicEnvs = {
& NextPublicEnvsAccount
& NextPublicEnvsMarketplace
& NextPublicEnvsRollup
& NextPublicEnvsBeacon;
& NextPublicEnvsBeacon
& NextPublicEnvsAdsBanner
& NextPublicEnvsSentry;
type NextPublicEnvsAccount =
{
......@@ -87,6 +80,7 @@ type NextPublicEnvsAccount =
NEXT_PUBLIC_AUTH_URL?: string;
NEXT_PUBLIC_LOGOUT_URL: string;
NEXT_PUBLIC_AUTH0_CLIENT_ID: string;
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST?: string;
}
type NextPublicEnvsMarketplace =
......@@ -120,3 +114,30 @@ type NextPublicEnvsBeacon =
NEXT_PUBLIC_HAS_BEACON_CHAIN?: undefined;
NEXT_PUBLIC_BEACON_CHAIN_CURRENCY_SYMBOL?: undefined;
}
type NextPublicEnvsAdsBanner =
{
NEXT_PUBLIC_AD_BANNER_PROVIDER: 'slise' | 'coinzilla' | 'none';
} |
{
NEXT_PUBLIC_AD_BANNER_PROVIDER: 'adbutler';
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP: string;
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE: string;
} |
{
NEXT_PUBLIC_AD_BANNER_PROVIDER?: undefined;
}
type NextPublicEnvsSentry =
{
NEXT_PUBLIC_SENTRY_DSN: string;
SENTRY_CSP_REPORT_URI?: string;
NEXT_PUBLIC_APP_INSTANCE?: string;
NEXT_PUBLIC_APP_ENV?: string;
} |
{
NEXT_PUBLIC_SENTRY_DSN?: undefined;
SENTRY_CSP_REPORT_URI?: undefined;
NEXT_PUBLIC_APP_INSTANCE?: undefined;
NEXT_PUBLIC_APP_ENV?: undefined;
}
......@@ -6,7 +6,7 @@ import React from 'react';
import type { SocketMessage } from 'lib/socket/types';
import type { AddressBlocksValidatedResponse } from 'types/api/address';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import { getResourceKey } from 'lib/api/useApiQuery';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
......@@ -94,7 +94,7 @@ const AddressBlocksValidated = ({ scrollRef }: Props) => {
<Th width="17%">Age</Th>
<Th width="16%">Txn</Th>
<Th width="25%">Gas used</Th>
<Th width="25%" isNumeric>Reward { appConfig.network.currency.symbol }</Th>
<Th width="25%" isNumeric>Reward { config.chain.currency.symbol }</Th>
</Tr>
</Thead>
<Tbody>
......
......@@ -4,7 +4,7 @@ import React from 'react';
import type { CsvExportParams } from 'types/client/address';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import svgFileIcon from 'icons/files/csv.svg';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import useIsMobile from 'lib/hooks/useIsMobile';
......@@ -21,7 +21,7 @@ const AddressCsvExportLink = ({ className, address, params, isLoading }: Props)
const isMobile = useIsMobile();
const isInitialLoading = useIsInitialLoading(isLoading);
if (!appConfig.reCaptcha.siteKey) {
if (!config.features.csvExport.isEnabled) {
return null;
}
......
......@@ -5,7 +5,7 @@ import React from 'react';
import type { Block } from 'types/api/block';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import LinkInternal from 'ui/shared/LinkInternal';
......@@ -48,7 +48,7 @@ const AddressBlocksValidatedListItem = (props: Props) => {
/>
</Flex>
<Flex columnGap={ 2 } w="100%">
<Skeleton isLoaded={ !props.isLoading } fontWeight={ 500 } flexShrink={ 0 }>Reward { appConfig.network.currency.symbol }</Skeleton>
<Skeleton isLoaded={ !props.isLoading } fontWeight={ 500 } flexShrink={ 0 }>Reward { config.chain.currency.symbol }</Skeleton>
<Skeleton isLoaded={ !props.isLoading } color="text_secondary">{ totalReward.toFixed() }</Skeleton>
</Flex>
</ListItemMobile>
......
import BigNumber from 'bignumber.js';
import React from 'react';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery';
import ChartWidget from 'ui/shared/chart/ChartWidget';
......@@ -16,7 +16,7 @@ const AddressCoinBalanceChart = ({ addressHash }: Props) => {
const items = React.useMemo(() => data?.map(({ date, value }) => ({
date: new Date(date),
value: BigNumber(value).div(10 ** appConfig.network.currency.decimals).toNumber(),
value: BigNumber(value).div(10 ** config.chain.currency.decimals).toNumber(),
})), [ data ]);
return (
......@@ -26,7 +26,7 @@ const AddressCoinBalanceChart = ({ addressHash }: Props) => {
items={ items }
isLoading={ isLoading }
h="300px"
units={ appConfig.network.currency.symbol }
units={ config.chain.currency.symbol }
/>
);
};
......
......@@ -5,7 +5,7 @@ import React from 'react';
import type { AddressCoinBalanceHistoryResponse } from 'types/api/address';
import type { PaginationParams } from 'ui/shared/pagination/types';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/pagination/Pagination';
......@@ -31,7 +31,7 @@ const AddressCoinBalanceHistory = ({ query }: Props) => {
<Th width="20%">Block</Th>
<Th width="20%">Txn</Th>
<Th width="20%">Age</Th>
<Th width="20%" isNumeric pr={ 1 }>Balance { appConfig.network.currency.symbol }</Th>
<Th width="20%" isNumeric pr={ 1 }>Balance { config.chain.currency.symbol }</Th>
<Th width="20%" isNumeric>Delta</Th>
</Tr>
</Thead>
......
......@@ -5,7 +5,7 @@ import React from 'react';
import type { AddressCoinBalanceHistoryItem } from 'types/api/address';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import { WEI, ZERO } from 'lib/consts';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address';
......@@ -28,7 +28,7 @@ const AddressCoinBalanceListItem = (props: Props) => {
<ListItemMobile rowGap={ 2 } isAnimated>
<Flex justifyContent="space-between" w="100%">
<Skeleton isLoaded={ !props.isLoading } fontWeight={ 600 }>
{ BigNumber(props.value).div(WEI).dp(8).toFormat() } { appConfig.network.currency.symbol }
{ BigNumber(props.value).div(WEI).dp(8).toFormat() } { config.chain.currency.symbol }
</Skeleton>
<Skeleton isLoaded={ !props.isLoading }>
<Stat flexGrow="0">
......
......@@ -5,7 +5,7 @@ import React from 'react';
import type { SmartContractMethodOutput } from 'types/api/contract';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import { WEI } from 'lib/consts';
import Address from 'ui/shared/address/Address';
import AddressLink from 'ui/shared/address/AddressLink';
......@@ -39,7 +39,7 @@ const ContractMethodStatic = ({ data }: Props) => {
if (event.target.checked) {
setValue(BigNumber(initialValue).div(WEI).toFixed());
setLabel(appConfig.network.currency.symbol || 'ETH');
setLabel(config.chain.currency.symbol || 'ETH');
} else {
setValue(BigNumber(initialValue).toFixed());
setLabel('WEI');
......
......@@ -5,7 +5,7 @@ import { Element } from 'react-scroll';
import type { SmartContractMethod } from 'types/api/contract';
import config from 'configs/app/config';
import config from 'configs/app';
import iconLink from 'icons/link.svg';
import Hint from 'ui/shared/Hint';
......
......@@ -3,7 +3,7 @@ import { useAccount, useWalletClient, useNetwork, useSwitchNetwork } from 'wagmi
import type { SmartContractWriteMethod } from 'types/api/contract';
import config from 'configs/app/config';
import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery';
import ContractMethodsAccordion from 'ui/address/contract/ContractMethodsAccordion';
import ContentLoader from 'ui/shared/ContentLoader';
......@@ -46,8 +46,8 @@ const ContractWrite = ({ addressHash, isProxy, isCustomAbi }: Props) => {
throw new Error('Wallet is not connected');
}
if (chain?.id && String(chain.id) !== config.network.id) {
await switchNetworkAsync?.(Number(config.network.id));
if (chain?.id && String(chain.id) !== config.chain.id) {
await switchNetworkAsync?.(Number(config.chain.id));
}
if (!contractAbi) {
......
......@@ -4,7 +4,7 @@ import React from 'react';
import type { SocketMessage } from 'lib/socket/types';
import type { Address } from 'types/api/address';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import { getResourceKey } from 'lib/api/useApiQuery';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
......@@ -65,15 +65,14 @@ const AddressBalance = ({ data, isLoading }: Props) => {
});
const tokenData = React.useMemo(() => ({
address: appConfig.network.currency.address || '',
name: appConfig.network.currency.name || '',
name: config.chain.currency.name || '',
icon_url: '',
}), [ ]);
return (
<DetailsInfoItem
title="Balance"
hint={ `Address balance in ${ appConfig.network.currency.symbol }. Doesn't include ERC20, ERC721 and ERC1155 tokens` }
hint={ `Address balance in ${ config.chain.currency.symbol }. Doesn't include ERC20, ERC721 and ERC1155 tokens` }
flexWrap="nowrap"
alignItems="flex-start"
isLoading={ isLoading }
......@@ -88,8 +87,8 @@ const AddressBalance = ({ data, isLoading }: Props) => {
<CurrencyValue
value={ data.coin_balance || '0' }
exchangeRate={ data.exchange_rate }
decimals={ String(appConfig.network.currency.decimals) }
currency={ appConfig.network.currency.symbol }
decimals={ String(config.chain.currency.decimals) }
currency={ config.chain.currency.symbol }
accuracyUsd={ 2 }
accuracy={ 8 }
flexWrap="wrap"
......
......@@ -5,7 +5,7 @@ import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import eastArrowIcon from 'icons/arrows/east.svg';
import dayjs from 'lib/date/dayjs';
import Address from 'ui/shared/address/Address';
......@@ -79,9 +79,9 @@ const TxInternalsListItem = ({
) }
</Box>
<HStack spacing={ 3 }>
<Skeleton isLoaded={ !isLoading } fontSize="sm" fontWeight={ 500 }>Value { appConfig.network.currency.symbol }</Skeleton>
<Skeleton isLoaded={ !isLoading } fontSize="sm" fontWeight={ 500 }>Value { config.chain.currency.symbol }</Skeleton>
<Skeleton isLoaded={ !isLoading } fontSize="sm" color="text_secondary" minW={ 6 }>
<span>{ BigNumber(value).div(BigNumber(10 ** appConfig.network.currency.decimals)).toFormat() }</span>
<span>{ BigNumber(value).div(BigNumber(10 ** config.chain.currency.decimals)).toFormat() }</span>
</Skeleton>
</HStack>
</ListItemMobile>
......
......@@ -3,7 +3,7 @@ import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import { default as Thead } from 'ui/shared/TheadSticky';
import AddressIntTxsTableItem from './AddressIntTxsTableItem';
......@@ -26,7 +26,7 @@ const AddressIntTxsTable = ({ data, currentAddress, isLoading }: Props) => {
<Th width="48px" px={ 0 }/>
<Th width="20%">To</Th>
<Th width="20%" isNumeric>
Value { appConfig.network.currency.symbol }
Value { config.chain.currency.symbol }
</Th>
</Tr>
</Thead>
......
......@@ -5,7 +5,7 @@ import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address';
......@@ -105,7 +105,7 @@ const AddressIntTxsTableItem = ({
</Td>
<Td isNumeric verticalAlign="middle">
<Skeleton isLoaded={ !isLoading } display="inline-block" minW={ 6 }>
{ BigNumber(value).div(BigNumber(10 ** appConfig.network.currency.decimals)).toFormat() }
{ BigNumber(value).div(BigNumber(10 ** config.chain.currency.decimals)).toFormat() }
</Skeleton>
</Td>
</Tr>
......
......@@ -2,7 +2,7 @@ import { Flex } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery';
import { ZERO } from 'lib/consts';
import getCurrencyValue from 'lib/getCurrencyValue';
......@@ -34,7 +34,7 @@ const TokenBalances = () => {
accuracy: 8,
accuracyUsd: 2,
exchangeRate: addressData?.exchange_rate,
decimals: String(appConfig.network.currency.decimals),
decimals: String(config.chain.currency.decimals),
});
const tokensInfo = getTokensTotalInfo(tokenQuery.data);
......@@ -52,8 +52,8 @@ const TokenBalances = () => {
isLoading={ addressQuery.isLoading || tokenQuery.isLoading }
/>
<TokenBalancesItem
name={ `${ appConfig.network.currency.symbol } Balance` }
value={ (!nativeUsd.eq(ZERO) ? `$${ nativeUsd.toFormat(2) } USD | ` : '') + `${ nativeValue } ${ appConfig.network.currency.symbol }` }
name={ `${ config.chain.currency.symbol } Balance` }
value={ (!nativeUsd.eq(ZERO) ? `$${ nativeUsd.toFormat(2) } USD | ` : '') + `${ nativeValue } ${ config.chain.currency.symbol }` }
isLoading={ addressQuery.isLoading || tokenQuery.isLoading }
/>
<TokenBalancesItem
......
......@@ -12,7 +12,7 @@ import type {
RootFields,
} from '../types';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources';
import useApiFetch from 'lib/api/useApiFetch';
import LinkInternal from 'ui/shared/LinkInternal';
......@@ -49,7 +49,7 @@ const AddressVerificationStepAddress = ({ defaultAddress, onContinue }: Props) =
};
const response = await apiFetch<'address_verification', AddressCheckResponseSuccess, AddressVerificationResponseError>('address_verification', {
fetchParams: { method: 'POST', body },
pathParams: { chainId: appConfig.network.id, type: ':prepare' },
pathParams: { chainId: config.chain.id, type: ':prepare' },
});
if (response.status !== 'SUCCESS') {
......
......@@ -15,7 +15,7 @@ import type {
} from '../types';
import type { VerifiedAddress } from 'types/api/account';
import appConfig from 'configs/app/config';
import config from 'configs/app';
import useApiFetch from 'lib/api/useApiFetch';
import shortenString from 'lib/shortenString';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
......@@ -62,7 +62,7 @@ const AddressVerificationStepSignature = ({ address, signingMessage, contractCre
const response = await apiFetch<'address_verification', AddressValidationResponseSuccess, AddressVerificationResponseError>('address_verification', {
fetchParams: { method: 'POST', body },
pathParams: { chainId: appConfig.network.id, type: ':verify' },
pathParams: { chainId: config.chain.id, type: ':verify' },
});
if (response.status !== 'SUCCESS') {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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