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 = { ...@@ -211,7 +211,7 @@ module.exports = {
object: 'process', object: 'process',
property: 'env', property: 'env',
// FIXME: restrict the rule only NEXT_PUBLIC variables // 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', 'react/jsx-key': 'error',
...@@ -289,7 +289,7 @@ module.exports = { ...@@ -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: { rules: {
// for configs allow to consume env variables from process.env directly // for configs allow to consume env variables from process.env directly
'no-restricted-properties': [ 0 ], 'no-restricted-properties': [ 0 ],
......
...@@ -12,6 +12,14 @@ COPY package.json yarn.lock ./ ...@@ -12,6 +12,14 @@ COPY package.json yarn.lock ./
RUN apk add git RUN apk add git
RUN yarn --frozen-lockfile 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 ### ENV VARIABLES CHECKER
# Install dependencies # Install dependencies
WORKDIR /envs-validator WORKDIR /envs-validator
...@@ -52,6 +60,13 @@ RUN ./make_envs_template.sh ./docs/ENVS.md ...@@ -52,6 +60,13 @@ RUN ./make_envs_template.sh ./docs/ENVS.md
RUN yarn build 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 ### ENV VARIABLES CHECKER
# Copy dependencies and source code, then build # Copy dependencies and source code, then build
WORKDIR /envs-validator WORKDIR /envs-validator
...@@ -81,6 +96,7 @@ COPY --from=builder /app/next.config.js ./ ...@@ -81,6 +96,7 @@ COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json COPY --from=builder /app/package.json ./package.json
COPY --from=builder /envs-validator/index.js ./envs-validator.js 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 scripts and ENVs file
COPY --chmod=+x ./deploy/scripts/entrypoint.sh . 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 # Set of ENVs for Ethereum network explorer
# https://eth.blockscout.com/ # https://eth.blockscout.com/
# app config # app configuration
NEXT_PUBLIC_APP_PROTOCOL=http NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000 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_NAME=Ethereum
NEXT_PUBLIC_NETWORK_SHORT_NAME=ETH NEXT_PUBLIC_NETWORK_SHORT_NAME=ETH
NEXT_PUBLIC_NETWORK_LOGO=
NEXT_PUBLIC_NETWORK_ICON=
NEXT_PUBLIC_NETWORK_ID=1 NEXT_PUBLIC_NETWORK_ID=1
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 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_VERIFICATION_TYPE=validation
NEXT_PUBLIC_NETWORK_RPC_URL=https://eth.llamarpc.com 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_HOST=eth.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/ 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_STATS_API_HOST=https://stats-eth-main.k8s.blockscout.com
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
......
# Set of ENVs for Goerli testnet network explorer # Set of ENVs for Goerli testnet network explorer
# https://eth-goerli.blockscout.com/ # https://eth-goerli.blockscout.com/
# app config # app configuration
NEXT_PUBLIC_APP_PROTOCOL=http NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000 NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development
# ui config # blockchain parameters
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
NEXT_PUBLIC_NETWORK_NAME=Goerli NEXT_PUBLIC_NETWORK_NAME=Goerli
NEXT_PUBLIC_NETWORK_SHORT_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_ID=5
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 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_VERIFICATION_TYPE=validation
NEXT_PUBLIC_NETWORK_RPC_URL=https://rpc.ankr.com/eth_goerli 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 NEXT_PUBLIC_IS_TESTNET=true
# api config # api configuration
NEXT_PUBLIC_API_HOST=eth-goerli.blockscout.com NEXT_PUBLIC_API_HOST=eth-goerli.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/ 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_STATS_API_HOST=https://stats-goerli.k8s-dev.blockscout.com
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
......
# Set of ENVs for Jest unit tests # Set of ENVs for Jest unit tests
# app config # app configuration
NEXT_PUBLIC_APP_PROTOCOL=http NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000 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 # 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'}}] ## homepage
NEXT_PUBLIC_GIT_TAG=v1.0.11
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap'] NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap']
NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true
NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=true NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=true
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND= NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=
NEXT_PUBLIC_FEATURED_NETWORKS= ## sidebar
NEXT_PUBLIC_FOOTER_LINKS=
NEXT_PUBLIC_NETWORK_LOGO= NEXT_PUBLIC_NETWORK_LOGO=
NEXT_PUBLIC_NETWORK_LOGO_DARK= NEXT_PUBLIC_NETWORK_LOGO_DARK=
NEXT_PUBLIC_NETWORK_ICON= NEXT_PUBLIC_NETWORK_ICON=
NEXT_PUBLIC_NETWORK_ICON_DARK= NEXT_PUBLIC_NETWORK_ICON_DARK=
NEXT_PUBLIC_NETWORK_RPC_URL=https://localhost:1111 NEXT_PUBLIC_FEATURED_NETWORKS=
NEXT_PUBLIC_IS_TESTNET=true ## 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_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_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_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_STATS_API_HOST=https://localhost:3004
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://localhost:3005 NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://localhost:3005
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://localhost:3006 NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://localhost:3006
NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx
NEXT_PUBLIC_API_BASE_PATH=/
...@@ -2,34 +2,40 @@ ...@@ -2,34 +2,40 @@
# frontend app URL - https://localhost:3000/ # frontend app URL - https://localhost:3000/
# API URL - https://localhost:3001/ # API URL - https://localhost:3001/
# app config # app configuration
NEXT_PUBLIC_APP_PROTOCOL=http NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000 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_NAME=POA
NEXT_PUBLIC_NETWORK_SHORT_NAME=POA NEXT_PUBLIC_NETWORK_SHORT_NAME=POA
NEXT_PUBLIC_NETWORK_ID=99 NEXT_PUBLIC_NETWORK_ID=99
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=POA NEXT_PUBLIC_NETWORK_CURRENCY_NAME=POA
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=POA NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=POA
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 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_VERIFICATION_TYPE=validation
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_NETWORK_RPC_URL=https://core.poa.network NEXT_PUBLIC_NETWORK_RPC_URL=https://core.poa.network
# api config # api configuration
NEXT_PUBLIC_API_BASE_PATH=/ NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=localhost NEXT_PUBLIC_API_HOST=localhost
NEXT_PUBLIC_API_PROTOCOL=http NEXT_PUBLIC_API_PROTOCOL=http
NEXT_PUBLIC_API_PORT=3001 NEXT_PUBLIC_API_PORT=3001
\ No newline at end of file
# 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 # Set of ENVs for Develompent network explorer
# https://blockscout-main.k8s-dev.blockscout.com/ # https://blockscout-main.k8s-dev.blockscout.com/
# app config # app configuration
NEXT_PUBLIC_APP_PROTOCOL=http NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000 NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development
# ui config # blockchain parameters
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
NEXT_PUBLIC_NETWORK_NAME=Goerli NEXT_PUBLIC_NETWORK_NAME=Goerli
NEXT_PUBLIC_NETWORK_SHORT_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_ID=5
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 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_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_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_IS_TESTNET=true NEXT_PUBLIC_IS_TESTNET=true
# api config # api configuration
NEXT_PUBLIC_API_HOST=blockscout-main.k8s-dev.blockscout.com NEXT_PUBLIC_API_HOST=blockscout-main.k8s-dev.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/ 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_STATS_API_HOST=https://stats-goerli.k8s-dev.blockscout.com
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.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 NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info-test.k8s-dev.blockscout.com
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs-test.k8s-dev.blockscout.com NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs-test.k8s-dev.blockscout.com
\ No newline at end of file
# Set of ENVs for Develompent L2 network explorer # Set of ENVs for Develompent L2 network explorer
# https://blockscout-optimism-goerli.k8s-dev.blockscout.com/ # https://blockscout-optimism-goerli.k8s-dev.blockscout.com/
# app config # app configuration
NEXT_PUBLIC_APP_PROTOCOL=http NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000 NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development
# ui config # blockchain parameters
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
NEXT_PUBLIC_NETWORK_NAME=Base Göerli NEXT_PUBLIC_NETWORK_NAME=Base Göerli
NEXT_PUBLIC_NETWORK_SHORT_NAME=Base 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_ID=84531
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 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_VERIFICATION_TYPE=validation
NEXT_PUBLIC_NETWORK_RPC_URL=https://goerli.base.org 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 NEXT_PUBLIC_IS_TESTNET=true
# api config # api configuration
NEXT_PUBLIC_API_HOST=blockscout-optimism-goerli.k8s-dev.blockscout.com NEXT_PUBLIC_API_HOST=blockscout-optimism-goerli.k8s-dev.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/ 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_IS_L2_NETWORK=true
NEXT_PUBLIC_L1_BASE_URL=https://blockscout-main.k8s-dev.blockscout.com NEXT_PUBLIC_L1_BASE_URL=https://blockscout-main.k8s-dev.blockscout.com
NEXT_PUBLIC_L2_WITHDRAWAL_URL=https://app.optimism.io/bridge/withdraw NEXT_PUBLIC_L2_WITHDRAWAL_URL=https://app.optimism.io/bridge/withdraw
\ No newline at end of file
# Set of ENVs for POA network explorer # Set of ENVs for POA network explorer
# https://blockscout.com/poa/core/ # https://blockscout.com/poa/core/
# app config # app configuration
NEXT_PUBLIC_APP_PROTOCOL=http NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000 NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development
# ui config # blockchain parameters
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
NEXT_PUBLIC_NETWORK_NAME=POA NEXT_PUBLIC_NETWORK_NAME=POA
NEXT_PUBLIC_NETWORK_SHORT_NAME=POA NEXT_PUBLIC_NETWORK_SHORT_NAME=POA
NEXT_PUBLIC_NETWORK_ID=99 NEXT_PUBLIC_NETWORK_ID=99
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=POA NEXT_PUBLIC_NETWORK_CURRENCY_NAME=POA
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=POA NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=POA
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 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_HOST=blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/poa/core 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 # Set of ENVs for Playwright components tests
# app config # app configuration
NEXT_PUBLIC_APP_PROTOCOL=http NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3100 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 # 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'}}] ## homepage
NEXT_PUBLIC_GIT_TAG=v1.0.11
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap'] NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap']
NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true
NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=true NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=true
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND= NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=
## sidebar
NEXT_PUBLIC_FEATURED_NETWORKS= NEXT_PUBLIC_FEATURED_NETWORKS=
NEXT_PUBLIC_FOOTER_LINKS=
NEXT_PUBLIC_NETWORK_LOGO= NEXT_PUBLIC_NETWORK_LOGO=
NEXT_PUBLIC_NETWORK_LOGO_DARK= NEXT_PUBLIC_NETWORK_LOGO_DARK=
NEXT_PUBLIC_NETWORK_ICON= NEXT_PUBLIC_NETWORK_ICON=
NEXT_PUBLIC_NETWORK_ICON_DARK= NEXT_PUBLIC_NETWORK_ICON_DARK=
NEXT_PUBLIC_NETWORK_RPC_URL=https://localhost:1111 ## footer
NEXT_PUBLIC_IS_TESTNET=true 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_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_IS_L2_NETWORK=false
NEXT_PUBLIC_AD_BANNER_PROVIDER=slise 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_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_STATS_API_HOST=https://localhost:3004
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://localhost:3005 NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://localhost:3005
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://localhost:3006 NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://localhost:3006
NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx
NEXT_PUBLIC_API_BASE_PATH=/
import type * as Sentry from '@sentry/react'; import type * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/tracing'; import { BrowserTracing } from '@sentry/tracing';
import appConfig from 'configs/app';
export const config: Sentry.BrowserOptions = { export const config: Sentry.BrowserOptions = {
environment: process.env.NEXT_PUBLIC_APP_ENV || process.env.NODE_ENV, environment: appConfig.features.sentry.environment,
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, dsn: appConfig.features.sentry.dsn,
release: process.env.NEXT_PUBLIC_GIT_COMMIT_SHA, release: process.env.NEXT_PUBLIC_GIT_TAG || process.env.NEXT_PUBLIC_GIT_COMMIT_SHA,
integrations: [ new BrowserTracing() ], integrations: [ new BrowserTracing() ],
// We recommend adjusting this value in production, or using tracesSampler // We recommend adjusting this value in production, or using tracesSampler
// for finer control // for finer control
...@@ -55,5 +57,5 @@ export const config: Sentry.BrowserOptions = { ...@@ -55,5 +57,5 @@ export const config: Sentry.BrowserOptions = {
}; };
export function configureScope(scope: Sentry.Scope) { 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 ...@@ -9,5 +9,8 @@ fi
# Execute script for replace build-time ENVs placeholders with their values at runtime # Execute script for replace build-time ENVs placeholders with their values at runtime
./replace_envs.sh ./replace_envs.sh
echo "starting Nextjs" # Print list of enabled features
node ./feature-reporter.js
echo "Starting Next.js application"
exec "$@" exec "$@"
\ No newline at end of file
...@@ -64,11 +64,13 @@ async function checkPlaceholdersCongruity(runTimeEnvs: Record<string, string>) { ...@@ -64,11 +64,13 @@ async function checkPlaceholdersCongruity(runTimeEnvs: Record<string, string>) {
} }
if (inconsistencies.length > 0) { 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) => { inconsistencies.forEach((env) => {
console.log(` ${ 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(); 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 IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@discoveryjs/json-ext@^0.5.0":
version "0.5.7"
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
"@jridgewell/gen-mapping@^0.3.0":
version "0.3.3"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098"
integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==
dependencies:
"@jridgewell/set-array" "^1.0.1"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping" "^0.3.9"
"@jridgewell/resolve-uri@^3.1.0":
version "3.1.1"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721"
integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==
"@jridgewell/set-array@^1.0.1":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
"@jridgewell/source-map@^0.3.3":
version "0.3.5"
resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91"
integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==
dependencies:
"@jridgewell/gen-mapping" "^0.3.0"
"@jridgewell/trace-mapping" "^0.3.9"
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
version "1.4.15"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9":
version "0.3.19"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811"
integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==
dependencies:
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
dependencies:
"@nodelib/fs.stat" "2.0.5"
run-parallel "^1.1.9"
"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
"@nodelib/fs.walk@^1.2.3":
version "1.2.8"
resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
dependencies:
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
"@types/eslint-scope@^3.7.3":
version "3.7.4"
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16"
integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==
dependencies:
"@types/eslint" "*"
"@types/estree" "*"
"@types/eslint@*":
version "8.44.2"
resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.2.tgz#0d21c505f98a89b8dd4d37fa162b09da6089199a"
integrity sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg==
dependencies:
"@types/estree" "*"
"@types/json-schema" "*"
"@types/estree@*", "@types/estree@^1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194"
integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==
"@types/json-schema@*", "@types/json-schema@^7.0.8":
version "7.0.12"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==
"@types/node@*":
version "20.4.8"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.8.tgz#b5dda19adaa473a9bf0ab5cbd8f30ec7d43f5c85"
integrity sha512-0mHckf6D2DiIAzh8fM8f3HQCvMKDpK94YQ0DSVkfWTG9BZleYIWudw9cJxX8oCk9bM+vAkDyujDV6dmKHbvQpg==
"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24"
integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==
dependencies:
"@webassemblyjs/helper-numbers" "1.11.6"
"@webassemblyjs/helper-wasm-bytecode" "1.11.6"
"@webassemblyjs/floating-point-hex-parser@1.11.6":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431"
integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==
"@webassemblyjs/helper-api-error@1.11.6":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768"
integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==
"@webassemblyjs/helper-buffer@1.11.6":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093"
integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==
"@webassemblyjs/helper-numbers@1.11.6":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5"
integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==
dependencies:
"@webassemblyjs/floating-point-hex-parser" "1.11.6"
"@webassemblyjs/helper-api-error" "1.11.6"
"@xtuc/long" "4.2.2"
"@webassemblyjs/helper-wasm-bytecode@1.11.6":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9"
integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==
"@webassemblyjs/helper-wasm-section@1.11.6":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577"
integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==
dependencies:
"@webassemblyjs/ast" "1.11.6"
"@webassemblyjs/helper-buffer" "1.11.6"
"@webassemblyjs/helper-wasm-bytecode" "1.11.6"
"@webassemblyjs/wasm-gen" "1.11.6"
"@webassemblyjs/ieee754@1.11.6":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a"
integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==
dependencies:
"@xtuc/ieee754" "^1.2.0"
"@webassemblyjs/leb128@1.11.6":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7"
integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==
dependencies:
"@xtuc/long" "4.2.2"
"@webassemblyjs/utf8@1.11.6":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a"
integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==
"@webassemblyjs/wasm-edit@^1.11.5":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab"
integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==
dependencies:
"@webassemblyjs/ast" "1.11.6"
"@webassemblyjs/helper-buffer" "1.11.6"
"@webassemblyjs/helper-wasm-bytecode" "1.11.6"
"@webassemblyjs/helper-wasm-section" "1.11.6"
"@webassemblyjs/wasm-gen" "1.11.6"
"@webassemblyjs/wasm-opt" "1.11.6"
"@webassemblyjs/wasm-parser" "1.11.6"
"@webassemblyjs/wast-printer" "1.11.6"
"@webassemblyjs/wasm-gen@1.11.6":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268"
integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==
dependencies:
"@webassemblyjs/ast" "1.11.6"
"@webassemblyjs/helper-wasm-bytecode" "1.11.6"
"@webassemblyjs/ieee754" "1.11.6"
"@webassemblyjs/leb128" "1.11.6"
"@webassemblyjs/utf8" "1.11.6"
"@webassemblyjs/wasm-opt@1.11.6":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2"
integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==
dependencies:
"@webassemblyjs/ast" "1.11.6"
"@webassemblyjs/helper-buffer" "1.11.6"
"@webassemblyjs/wasm-gen" "1.11.6"
"@webassemblyjs/wasm-parser" "1.11.6"
"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1"
integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==
dependencies:
"@webassemblyjs/ast" "1.11.6"
"@webassemblyjs/helper-api-error" "1.11.6"
"@webassemblyjs/helper-wasm-bytecode" "1.11.6"
"@webassemblyjs/ieee754" "1.11.6"
"@webassemblyjs/leb128" "1.11.6"
"@webassemblyjs/utf8" "1.11.6"
"@webassemblyjs/wast-printer@1.11.6":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20"
integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==
dependencies:
"@webassemblyjs/ast" "1.11.6"
"@xtuc/long" "4.2.2"
"@webpack-cli/configtest@^2.1.1":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646"
integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==
"@webpack-cli/info@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd"
integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==
"@webpack-cli/serve@^2.0.5":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e"
integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==
"@xtuc/ieee754@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==
"@xtuc/long@4.2.2":
version "4.2.2"
resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
acorn-import-assertions@^1.9.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac"
integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==
acorn@^8.7.1, acorn@^8.8.2:
version "8.10.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5"
integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==
ajv-keywords@^3.5.2:
version "3.5.2"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
ajv@^6.12.5:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
anymatch@~3.1.2:
version "3.1.3"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
dependencies:
normalize-path "^3.0.0"
picomatch "^2.0.4"
array-union@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
binary-extensions@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
braces@^3.0.2, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.0.1"
browserslist@^4.14.5:
version "4.21.10"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0"
integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==
dependencies:
caniuse-lite "^1.0.30001517"
electron-to-chromium "^1.4.477"
node-releases "^2.0.13"
update-browserslist-db "^1.0.11"
buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
caniuse-lite@^1.0.30001517:
version "1.0.30001519"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz#3e7b8b8a7077e78b0eb054d69e6edf5c7df35601"
integrity sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg==
chokidar@^3.5.3:
version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
dependencies:
anymatch "~3.1.2"
braces "~3.0.2"
glob-parent "~5.1.2"
is-binary-path "~2.1.0"
is-glob "~4.0.1"
normalize-path "~3.0.0"
readdirp "~3.6.0"
optionalDependencies:
fsevents "~2.3.2"
chrome-trace-event@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac"
integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==
clone-deep@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
dependencies:
is-plain-object "^2.0.4"
kind-of "^6.0.2"
shallow-clone "^3.0.0"
colorette@^2.0.14:
version "2.0.20"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a"
integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==
commander@^10.0.1:
version "10.0.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
commander@^2.20.0:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
commander@^9.0.0:
version "9.5.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30"
integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==
cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
dependencies:
path-key "^3.1.0"
shebang-command "^2.0.0"
which "^2.0.1"
dir-glob@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
dependencies:
path-type "^4.0.0"
dotenv-cli@^7.2.1:
version "7.2.1"
resolved "https://registry.yarnpkg.com/dotenv-cli/-/dotenv-cli-7.2.1.tgz#e595afd9ebfb721df9da809a435b9aa966c92062"
integrity sha512-ODHbGTskqRtXAzZapDPvgNuDVQApu4oKX8lZW7Y0+9hKA6le1ZJlyRS687oU9FXjOVEDU/VFV6zI125HzhM1UQ==
dependencies:
cross-spawn "^7.0.3"
dotenv "^16.0.0"
dotenv-expand "^10.0.0"
minimist "^1.2.6"
dotenv-expand@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-10.0.0.tgz#12605d00fb0af6d0a592e6558585784032e4ef37"
integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==
dotenv@^16.0.0:
version "16.3.1"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e"
integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==
electron-to-chromium@^1.4.477:
version "1.4.487"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.487.tgz#e2ef8b15f2791bf68fa6f38f2656f1a551d360ae"
integrity sha512-XbCRs/34l31np/p33m+5tdBrdXu9jJkZxSbNxj5I0H1KtV2ZMSB+i/HYqDiRzHaFx2T5EdytjoBRe8QRJE2vQg==
enhanced-resolve@^5.15.0:
version "5.15.0"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35"
integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==
dependencies:
graceful-fs "^4.2.4"
tapable "^2.2.0"
envinfo@^7.7.3:
version "7.10.0"
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.10.0.tgz#55146e3909cc5fe63c22da63fb15b05aeac35b13"
integrity sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw==
es-module-lexer@^1.2.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.3.0.tgz#6be9c9e0b4543a60cd166ff6f8b4e9dae0b0c16f"
integrity sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==
escalade@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
eslint-scope@5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
dependencies:
esrecurse "^4.3.0"
estraverse "^4.1.1"
esrecurse@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
dependencies:
estraverse "^5.2.0"
estraverse@^4.1.1:
version "4.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
estraverse@^5.2.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
events@^3.2.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
fast-deep-equal@^3.1.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-glob@^3.2.9:
version "3.3.1"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4"
integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==
dependencies:
"@nodelib/fs.stat" "^2.0.2"
"@nodelib/fs.walk" "^1.2.3"
glob-parent "^5.1.2"
merge2 "^1.3.0"
micromatch "^4.0.4"
fast-json-stable-stringify@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
fastest-levenshtein@^1.0.12:
version "1.0.16"
resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5"
integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==
fastq@^1.6.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a"
integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==
dependencies:
reusify "^1.0.4"
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
dependencies:
to-regex-range "^5.0.1"
find-up@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
dependencies:
locate-path "^5.0.0"
path-exists "^4.0.0"
fsevents@~2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
glob-parent@^5.1.2, glob-parent@~5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
glob-to-regexp@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
globby@^11.0.4:
version "11.1.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
dependencies:
array-union "^2.1.0"
dir-glob "^3.0.1"
fast-glob "^3.2.9"
ignore "^5.2.0"
merge2 "^1.4.1"
slash "^3.0.0"
graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
version "4.2.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
has-flag@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
has@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
dependencies:
function-bind "^1.1.1"
ignore@^5.2.0:
version "5.2.4"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==
import-local@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4"
integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==
dependencies:
pkg-dir "^4.2.0"
resolve-cwd "^3.0.0"
interpret@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4"
integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==
is-binary-path@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
dependencies:
binary-extensions "^2.0.0"
is-core-module@^2.13.0:
version "2.13.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db"
integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==
dependencies:
has "^1.0.3"
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
is-glob@^4.0.1, is-glob@~4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
dependencies:
is-extglob "^2.1.1"
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
is-plain-object@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
dependencies:
isobject "^3.0.1"
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
isobject@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==
jest-worker@^27.4.5:
version "27.5.1"
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0"
integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==
dependencies:
"@types/node" "*"
merge-stream "^2.0.0"
supports-color "^8.0.0"
json-parse-even-better-errors@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
kind-of@^6.0.2:
version "6.0.3"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
loader-runner@^4.2.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1"
integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==
locate-path@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
dependencies:
p-locate "^4.1.0"
merge-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
merge2@^1.3.0, merge2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
micromatch@^4.0.4:
version "4.0.5"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
dependencies:
braces "^3.0.2"
picomatch "^2.3.1"
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.27:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
minimist@^1.2.6:
version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
mylas@^2.1.9:
version "2.1.13"
resolved "https://registry.yarnpkg.com/mylas/-/mylas-2.1.13.tgz#1e23b37d58fdcc76e15d8a5ed23f9ae9fc0cbdf4"
integrity sha512-+MrqnJRtxdF+xngFfUUkIMQrUUL0KsxbADUkn23Z/4ibGg192Q+z+CQyiYwvWTsYjJygmMR8+w3ZDa98Zh6ESg==
neo-async@^2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
node-releases@^2.0.13:
version "2.0.13"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d"
integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
p-limit@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
dependencies:
p-try "^2.0.0"
p-locate@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
dependencies:
p-limit "^2.2.0"
p-try@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
path-exists@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
path-key@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
path-parse@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
path-type@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
pkg-dir@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
dependencies:
find-up "^4.0.0"
plimit-lit@^1.2.6:
version "1.5.0"
resolved "https://registry.yarnpkg.com/plimit-lit/-/plimit-lit-1.5.0.tgz#f66df8a7041de1e965c4f1c0697ab486968a92a5"
integrity sha512-Eb/MqCb1Iv/ok4m1FqIXqvUKPISufcjZ605hl3KM/n8GaX8zfhtgdLwZU3vKjuHGh2O9Rjog/bHTq8ofIShdng==
dependencies:
queue-lit "^1.5.0"
punycode@^2.1.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
queue-lit@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/queue-lit/-/queue-lit-1.5.0.tgz#8197fdafda1edd615c8a0fc14c48353626e5160a"
integrity sha512-IslToJ4eiCEE9xwMzq3viOO5nH8sUWUCwoElrhNMozzr9IIt2qqvB4I+uHu/zJTQVqc9R5DFwok4ijNK1pU3fA==
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
randombytes@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
dependencies:
safe-buffer "^5.1.0"
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
dependencies:
picomatch "^2.2.1"
rechoir@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22"
integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==
dependencies:
resolve "^1.20.0"
resolve-cwd@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==
dependencies:
resolve-from "^5.0.0"
resolve-from@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
resolve@^1.20.0:
version "1.22.4"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34"
integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==
dependencies:
is-core-module "^2.13.0"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
reusify@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
run-parallel@^1.1.9:
version "1.2.0"
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
dependencies:
queue-microtask "^1.2.2"
safe-buffer@^5.1.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
schema-utils@^3.1.1, schema-utils@^3.2.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe"
integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==
dependencies:
"@types/json-schema" "^7.0.8"
ajv "^6.12.5"
ajv-keywords "^3.5.2"
serialize-javascript@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c"
integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==
dependencies:
randombytes "^2.1.0"
shallow-clone@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
dependencies:
kind-of "^6.0.2"
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
dependencies:
shebang-regex "^3.0.0"
shebang-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
slash@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
source-map-support@~0.5.20:
version "0.5.21"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map@^0.6.0:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
supports-color@^8.0.0:
version "8.1.1"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
dependencies:
has-flag "^4.0.0"
supports-preserve-symlinks-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
tapable@^2.1.1, tapable@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
terser-webpack-plugin@^5.3.7:
version "5.3.9"
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1"
integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==
dependencies:
"@jridgewell/trace-mapping" "^0.3.17"
jest-worker "^27.4.5"
schema-utils "^3.1.1"
serialize-javascript "^6.0.1"
terser "^5.16.8"
terser@^5.16.8:
version "5.19.2"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.19.2.tgz#bdb8017a9a4a8de4663a7983f45c506534f9234e"
integrity sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==
dependencies:
"@jridgewell/source-map" "^0.3.3"
acorn "^8.8.2"
commander "^2.20.0"
source-map-support "~0.5.20"
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
dependencies:
is-number "^7.0.0"
tsc-alias@^1.8.7:
version "1.8.7"
resolved "https://registry.yarnpkg.com/tsc-alias/-/tsc-alias-1.8.7.tgz#4f8721b031a31345fa9f1fa8d3cf209d925abb88"
integrity sha512-59Q/zUQa3miTf99mLbSqaW0hi1jt4WoG8Uhe5hSZJHQpSoFW9eEwvW7jlKMHXWvT+zrzy3SN9PE/YBhQ+WVydA==
dependencies:
chokidar "^3.5.3"
commander "^9.0.0"
globby "^11.0.4"
mylas "^2.1.9"
normalize-path "^3.0.0"
plimit-lit "^1.2.6"
tsc@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/tsc/-/tsc-2.0.4.tgz#5f6499146abea5dca4420b451fa4f2f9345238f5"
integrity sha512-fzoSieZI5KKJVBYGvwbVZs/J5za84f2lSTLPYf6AGiIf43tZ3GNrI1QzTLcjtyDDP4aLxd46RTZq1nQxe7+k5Q==
typescript@5.1:
version "5.1.6"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274"
integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==
update-browserslist-db@^1.0.11:
version "1.0.11"
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940"
integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==
dependencies:
escalade "^3.1.1"
picocolors "^1.0.0"
uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
dependencies:
punycode "^2.1.0"
watchpack@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
dependencies:
glob-to-regexp "^0.4.1"
graceful-fs "^4.1.2"
webpack-cli@^5.1.4:
version "5.1.4"
resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b"
integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==
dependencies:
"@discoveryjs/json-ext" "^0.5.0"
"@webpack-cli/configtest" "^2.1.1"
"@webpack-cli/info" "^2.0.2"
"@webpack-cli/serve" "^2.0.5"
colorette "^2.0.14"
commander "^10.0.1"
cross-spawn "^7.0.3"
envinfo "^7.7.3"
fastest-levenshtein "^1.0.12"
import-local "^3.0.2"
interpret "^3.1.1"
rechoir "^0.8.0"
webpack-merge "^5.7.3"
webpack-merge@^5.7.3:
version "5.9.0"
resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.9.0.tgz#dc160a1c4cf512ceca515cc231669e9ddb133826"
integrity sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==
dependencies:
clone-deep "^4.0.1"
wildcard "^2.0.0"
webpack-sources@^3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
webpack@^5.88.2:
version "5.88.2"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.88.2.tgz#f62b4b842f1c6ff580f3fcb2ed4f0b579f4c210e"
integrity sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==
dependencies:
"@types/eslint-scope" "^3.7.3"
"@types/estree" "^1.0.0"
"@webassemblyjs/ast" "^1.11.5"
"@webassemblyjs/wasm-edit" "^1.11.5"
"@webassemblyjs/wasm-parser" "^1.11.5"
acorn "^8.7.1"
acorn-import-assertions "^1.9.0"
browserslist "^4.14.5"
chrome-trace-event "^1.0.2"
enhanced-resolve "^5.15.0"
es-module-lexer "^1.2.1"
eslint-scope "5.1.1"
events "^3.2.0"
glob-to-regexp "^0.4.1"
graceful-fs "^4.2.9"
json-parse-even-better-errors "^2.3.1"
loader-runner "^4.2.0"
mime-types "^2.1.27"
neo-async "^2.6.2"
schema-utils "^3.2.0"
tapable "^2.1.1"
terser-webpack-plugin "^5.3.7"
watchpack "^2.4.0"
webpack-sources "^3.2.3"
which@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
dependencies:
isexe "^2.0.0"
wildcard@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67"
integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==
...@@ -186,7 +186,6 @@ frontend: ...@@ -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_MARKETPLACE_CONFIG_URL: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/base-goerli.json
NEXT_PUBLIC_NETWORK_EXPLORERS: '' 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_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_NETWORK_RPC_URL: https://goerli.optimism.io
NEXT_PUBLIC_WEB3_DEFAULT_WALLET: coinbase NEXT_PUBLIC_WEB3_DEFAULT_WALLET: coinbase
NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET: true NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET: true
...@@ -199,6 +198,5 @@ frontend: ...@@ -199,6 +198,5 @@ frontend:
NEXT_PUBLIC_L2_WITHDRAWAL_URL: https://app.optimism.io/bridge/withdraw NEXT_PUBLIC_L2_WITHDRAWAL_URL: https://app.optimism.io/bridge/withdraw
NEXT_PUBLIC_GRAPHIQL_TRANSACTION: 0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62 NEXT_PUBLIC_GRAPHIQL_TRANSACTION: 0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62
envFromSecret: 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_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 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: ...@@ -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_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_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_NETWORK_VERIFICATION_TYPE: validation
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM: https://airtable.com/shrqUAcjgGJ4jU88C NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM: https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_APP_ENV: stable NEXT_PUBLIC_APP_ENV: stable
...@@ -160,7 +159,6 @@ frontend: ...@@ -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_MARKETPLACE_CONFIG_URL: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/eth-goerli.json
NEXT_PUBLIC_GRAPHIQL_TRANSACTION: 0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d NEXT_PUBLIC_GRAPHIQL_TRANSACTION: 0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
envFromSecret: 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 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 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 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: ...@@ -107,8 +107,6 @@ frontend:
_default: '' _default: ''
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND: 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%)" _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: NEXT_PUBLIC_NETWORK_RPC_URL:
_default: https://goerli.optimism.io _default: https://goerli.optimism.io
NEXT_PUBLIC_WEB3_DEFAULT_WALLET: NEXT_PUBLIC_WEB3_DEFAULT_WALLET:
...@@ -133,8 +131,6 @@ frontend: ...@@ -133,8 +131,6 @@ frontend:
_default: https://app.optimism.io/bridge/withdraw _default: https://app.optimism.io/bridge/withdraw
NEXT_PUBLIC_GRAPHIQL_TRANSACTION: NEXT_PUBLIC_GRAPHIQL_TRANSACTION:
_default: 0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62 _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: 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 _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: SENTRY_CSP_REPORT_URI:
......
...@@ -111,8 +111,6 @@ frontend: ...@@ -111,8 +111,6 @@ frontend:
_default: true _default: true
NEXT_PUBLIC_GRAPHIQL_TRANSACTION: NEXT_PUBLIC_GRAPHIQL_TRANSACTION:
_default: 0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d _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: 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 _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: SENTRY_CSP_REPORT_URI:
......
...@@ -59,7 +59,7 @@ For all types of dependencies: ...@@ -59,7 +59,7 @@ For all types of dependencies:
These are the steps that you have to follow to make everything work: 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 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 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 4. Add the variable to CI configs where it is needed
- `deploy/values/review/values.yaml.gotmpl` - review development environment - `deploy/values/review/values.yaml.gotmpl` - review development environment
......
# Run-time environment variables # Run-time environment variables
The app instance could be customized by passing following variables to NodeJS environment at runtime. See their list below. The app instance could be customized by passing following variables to NodeJS environment at run-time. See their list below.
**IMPORTANT NOTE!** For _production_ build purposes all json-like values should be single-quoted. If it contains a hash (`#`) or a dollar-sign (`$`) the whole value should be wrapped in single quotes as well (see `dotenv` [readme](https://github.com/bkeepers/dotenv#variable-substitution) for the reference) **IMPORTANT NOTE!** For _production_ build purposes all json-like values should be single-quoted. If it contains a hash (`#`) or a dollar-sign (`$`) the whole value should be wrapped in single quotes as well (see `dotenv` [readme](https://github.com/bkeepers/dotenv#variable-substitution) for the reference)
## Network configuration ## Table of contents
- [App configuration](ENVS.md#app-configuration)
- [Blockchain parameters](ENVS.md#blockchain-parameters)
- [API configuration](ENVS.md#api-configuration)
- [UI configuration](ENVS.md#ui-configuration)
- [Homepage](ENVS.md#homepage)
- [Sidebar](ENVS.md#sidebar)
- [Footer](ENVS.md#footer)
- [Misc](ENVS.md#misc)
- [App features](ENVS.md#app-features)
- [My account](ENVS.md#my-account)
- [Address verification](ENVS.md#address-verification-in-my-account) in "My account"
- [Blockchain interaction](ENVS.md#blockchain-interaction-writing-to-contract-etc) (writing to contract, etc.)
- [Banner ads](ENVS.md#banner-ads)
- [Text ads](ENVS.md#text-ads)
- [Beacon chain](ENVS.md#beacon-chain)
- [Rollup (L2) chain](ENVS.md#rollup-l2-chain)
- [Export data to CSV file](ENVS.md#export-data-to-csv-file)
- [Google analytics](ENVS.md#google-analytics)
- [Mixpanel analytics](ENVS.md#mixpanel-analytics)
- [GraphQL API documentation](ENVS.md#graphql-api-documentation)
- [REST API documentation](ENVS.md#rest-api-documentation)
- [Marketplace](ENVS.md#marketplace)
- [Solidity to UML diagrams](ENVS.md#solidity-to-uml-diagrams)
- [Blockchain statistics](ENVS.md#blockchain-statistics)
- [Web3 wallet integration](ENVS.md#web3-wallet-integration-add-token-or-network-to-the-wallet) (add token or network to the wallet)
- [Verified tokens info](ENVS.md#verified-tokens-info)
- [Sentry error monitoring](ENVS.md#sentry-error-monitoring)
- [3rd party services configuration](ENVS.md#external-services-configuration)
| Variable | Type| Description | Is required | Default value | Example value | &nbsp;
## App configuration
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_APP_PROTOCOL | `http \| https` | App url schema | - | `https` | `http` |
| NEXT_PUBLIC_APP_HOST | `string` | App host | Required | - | `blockscout.com` |
| NEXT_PUBLIC_APP_PORT | `number` | Port where app is running | - | `3000` | `3001` |
| NEXT_PUBLIC_USE_NEXT_JS_PROXY | `boolean` | Tells the app to proxy all APIs request through the NextJS app. **We strongly advise not to use it in the production environment**, since it can lead to performance issues of the NodeJS server | - | `false` | `true` |
&nbsp;
## Blockchain parameters
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_NETWORK_NAME | `string` | Displayed name of the network | yes | - | `Gnosis Chain` | | NEXT_PUBLIC_NETWORK_NAME | `string` | Displayed name of the network | Required | - | `Gnosis Chain` |
| NEXT_PUBLIC_NETWORK_SHORT_NAME | `string` | Used for SEO attributes (page title and description) | - | - | `OoG` | | NEXT_PUBLIC_NETWORK_SHORT_NAME | `string` | Used for SEO attributes (e.g, page description) | - | - | `OoG` |
| NEXT_PUBLIC_NETWORK_ID | `number` | Chain id, see [https://chainlist.org](https://chainlist.org) for the reference | yes | - | `99` | | NEXT_PUBLIC_NETWORK_ID | `number` | Chain id, see [https://chainlist.org](https://chainlist.org) for the reference | Required | - | `99` |
| NEXT_PUBLIC_NETWORK_RPC_URL | `string` | Chain server RPC url, see [https://chainlist.org](https://chainlist.org) for the reference. If not provided, some functionality of the explorer, related to smart contracts interaction and third-party apps integration, will be unavailable | - | - | `https://core.poa.network` | | NEXT_PUBLIC_NETWORK_RPC_URL | `string` | Chain public RPC server url, see [https://chainlist.org](https://chainlist.org) for the reference | - | - | `https://core.poa.network` |
| NEXT_PUBLIC_NETWORK_CURRENCY_NAME | `string` | Network currency name | - | - | `Ether` | | NEXT_PUBLIC_NETWORK_CURRENCY_NAME | `string` | Network currency name | - | - | `Ether` |
| NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL | `string` | Network currency symbol | - | - | `ETH` | | NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL | `string` | Network currency symbol | - | - | `ETH` |
| NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS | `string` | Network currency decimals | - | `18` | `6` | | NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS | `string` | Network currency decimals | - | `18` | `6` |
| NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS | `string` | Address of network's native token | - | - | `0x029a799563238d0e75e20be2f4bda0ea68d00172` | | NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE | `validation` or `mining` | Verification type in the network | - | `mining` | `validation` |
| NEXT_PUBLIC_NETWORK_LOGO | `string` | Network logo; if not provided, placeholder will be shown; *Note* the logo height should be 20px and width less than 120px | - | - | `https://placekitten.com/240/40` |
| NEXT_PUBLIC_NETWORK_LOGO_DARK | `string` | Network logo for dark color mode; if not provided, **inverted** regular logo will be used instead | - | - | `https://placekitten.com/240/40` |
| NEXT_PUBLIC_NETWORK_ICON | `string` | Network icon; used as a replacement for regular network logo when nav bar is collapsed; if not provided, placeholder will be shown; *Note* the icon size should be at least 60px by 60px | - | - | `https://placekitten.com/60/60` |
| NEXT_PUBLIC_NETWORK_ICON_DARK | `string` | Network icon for dark color mode; if not provided, **inverted** regular icon will be used instead | - | - | `https://placekitten.com/60/60` |
| NEXT_PUBLIC_IS_TESTNET | `boolean`| Set to true if network is testnet | - | `false` | `true` | | NEXT_PUBLIC_IS_TESTNET | `boolean`| Set to true if network is testnet | - | `false` | `true` |
&nbsp;
## API configuration
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_API_PROTOCOL | `http \| https` | Main API protocol | - | `https` | `http` |
| NEXT_PUBLIC_API_HOST | `string` | Main API host | Required | - | `blockscout.com` |
| NEXT_PUBLIC_API_PORT | `number` | Port where API is running on the host | - | - | `3001` |
| NEXT_PUBLIC_API_BASE_PATH | `string` | Base path for Main API endpoint url | - | - | `/poa/core` |
| NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL | `ws \| wss` | Main API websocket protocol | - | `wss` | `ws` |
&nbsp;
## UI configuration ## UI configuration
| Variable | Type| Description | Is required | Default value | Example value | ### Homepage
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_HOMEPAGE_CHARTS | `Array<'daily_txs' \| 'coin_price' \| 'market_cap'>` | List of charts displayed on the home page | - | - | `['daily_txs','coin_price','market_cap']` |
| NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR | `string` | Text color of the hero plate on the homepage (escape "#" symbol if you use HEX color codes or use rgba-value instead) | - | `white` | `\#DCFE76` |
| NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND | `string` | Background css value for hero plate on the homepage (escape "#" symbol if you use HEX color codes or use rgba-value instead) | - | `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)` | `radial-gradient(at 15% 86%, hsla(350,65%,70%,1) 0px, transparent 50%)` \| `no-repeat bottom 20% right 0px/100% url(https://placekitten/1400/200)` |
| NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER | `boolean` | Set to false if network doesn't have gas tracker | - | `true` | `false` |
| NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME | `boolean` | Set to false if average block time is useless for the network | - | `true` | `false` |
&nbsp;
### Sidebar
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_NETWORK_LOGO | `string` | Network logo; if not provided, placeholder will be shown; *Note* the logo height should be 20px and width less than 120px | - | - | `https://placekitten.com/240/40` |
| NEXT_PUBLIC_NETWORK_LOGO_DARK | `string` | Network logo for dark color mode; if not provided, **inverted** regular logo will be used instead | - | - | `https://placekitten.com/240/40` |
| NEXT_PUBLIC_NETWORK_ICON | `string` | Network icon; used as a replacement for regular network logo when nav bar is collapsed; if not provided, placeholder will be shown; *Note* the icon size should be at least 60px by 60px | - | - | `https://placekitten.com/60/60` |
| NEXT_PUBLIC_NETWORK_ICON_DARK | `string` | Network icon for dark color mode; if not provided, **inverted** regular icon will be used instead | - | - | `https://placekitten.com/60/60` |
| NEXT_PUBLIC_FEATURED_NETWORKS | `string` | URL of configuration file (`.json` format only) which contains list of featured networks that will be shown in the network menu. See [below](#featured-network-configuration-properties) list of available properties for particular network | - | - | `https://example.com/featured_networks_config.json` | | NEXT_PUBLIC_FEATURED_NETWORKS | `string` | URL of configuration file (`.json` format only) which contains list of featured networks that will be shown in the network menu. See [below](#featured-network-configuration-properties) list of available properties for particular network | - | - | `https://example.com/featured_networks_config.json` |
| NEXT_PUBLIC_OTHER_LINKS | `Array<{url: string; text: string}>` | List of links for the "Other" navigation menu | - | - | `[{'url':'https://blockscout.com','text':'Blockscout'}]` | | NEXT_PUBLIC_OTHER_LINKS | `Array<{url: string; text: string}>` | List of links for the "Other" navigation menu | - | - | `[{'url':'https://blockscout.com','text':'Blockscout'}]` |
#### Featured network configuration properties
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| title | `string` | Displayed name of the network | Required | - | `Gnosis Chain` |
| url | `string` | Network explorer main page url | Required | - | `https://blockscout.com/xdai/mainnet` |
| group | `Mainnets \| Testnets \| Other` | Indicates in which tab network appears in the menu | Required | - | `Mainnets` |
| icon | `string` | Network icon; if not provided, the common placeholder will be shown; *Note* that icon size should be at least 60px by 60px | - | - | `https://placekitten.com/60/60` |
| isActive | `boolean` | Pass `true` if item should be shonw as active in the menu | - | - | `true` |
| invertIconInDarkMode | `boolean` | Pass `true` if icon colors should be inverted in dark mode | - | - | `true` |
&nbsp;
### Footer
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_FOOTER_LINKS | `string` | URL of configuration file (`.json` format only) which contains list of link groups to be displayed in the footer. See [below](#footer-links-configuration-properties) list of available properties for particular group | - | - | `https://example.com/footer_links_config.json` | | NEXT_PUBLIC_FOOTER_LINKS | `string` | URL of configuration file (`.json` format only) which contains list of link groups to be displayed in the footer. See [below](#footer-links-configuration-properties) list of available properties for particular group | - | - | `https://example.com/footer_links_config.json` |
| NEXT_PUBLIC_MARKETPLACE_CONFIG_URL | `string` | URL of configuration file (`.json` format only) which contains list of apps that will be shown on the marketplace page. See [below](#marketplace-app-configuration-properties) list of available properties for an app | - | - | `https://example.com/marketplace_config.json` |
| NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM | `string` | Link to form where authors can submit their dapps to the marketplace | - | - | `https://airtable.com/shrqUAcjgGJ4jU88C` | The app version shown in the footer is derived from build-time ENV variables `NEXT_PUBLIC_GIT_TAG` and `NEXT_PUBLIC_GIT_COMMIT_SHA` and cannot be overwritten at run-time.
#### Footer links configuration properties
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| title | `string` | Title of link group | Required | - | `Company` |
| links | `Array<{'text':string;'url':string;}>` | list of links | Required | - | `[{'text':'Homepage','url':'https://www.blockscout.com'}]` |
&nbsp;
### Misc
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_NETWORK_EXPLORERS | `Array<NetworkExplorer>` where `NetworkExplorer` can have following [properties](#network-explorer-configuration-properties) | Used to build up links to transactions, blocks, addresses in other chain explorers. | - | - | `[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/poa/core/tx'}}]` | | NEXT_PUBLIC_NETWORK_EXPLORERS | `Array<NetworkExplorer>` where `NetworkExplorer` can have following [properties](#network-explorer-configuration-properties) | Used to build up links to transactions, blocks, addresses in other chain explorers. | - | - | `[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/poa/core/tx'}}]` |
| NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE | `validation` or `mining` | Verification type in the network | - | `mining` | `validation` | | NEXT_PUBLIC_HIDE_INDEXING_ALERT | `boolean` | Set to `true` to hide indexing alert, if the chain indexing isn't completed | - | `false` | `true` |
| NEXT_PUBLIC_HOMEPAGE_CHARTS | `Array<'daily_txs' \| 'coin_price' \| 'market_cap'>` | List of charts displayed on the home page | - | - | `['daily_txs','coin_price','market_cap']` |
| NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR | `string` | Text color of the hero plate on the homepage (escape "#" symbol if you use HEX color codes) | `\#FFFFFF \| rgb(220, 254, 118)` | `\#DCFE76` | #### Network explorer configuration properties
| NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND | `string` | Background css value for hero plate on the homepage (escape "#" symbol if you use HEX color codes) | - | `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)` | `radial-gradient(at 15% 86%, hsla(350,65%,70%,1) 0px, transparent 50%)` \| `no-repeat bottom 20% right 0px/100% url(https://placekitten/1400/200)` |
| NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER | `boolean` | Set to false if network doesn't have gas tracker | - | `true` | `false` | | Variable | Type| Description | Compulsoriness | Default value | Example value |
| NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME | `boolean` | Set to false if average block time is useless for the network | - | `true` | `false` | | --- | --- | --- | --- | --- | --- |
| title | `string` | Displayed name of the explorer | Required | - | `Anyblock` |
| baseUrl | `string` | Base url of the explorer | Required | - | `https://explorer.anyblock.tools` |
| paths | `Record<'tx' \| 'block' \| 'address' \| 'token', string>` | Map of explorer entities and their paths | Required | - | `{'tx':'/ethereum/poa/core/tx'}` |
*Note* The url of an entity will be constructed as `<baseUrl><paths[<entity-type>]><entity-id>`, e.g `https://explorer.anyblock.tools/ethereum/poa/core/tx/<tx-id>`
&nbsp;
## App features
*Note* The variables which are marked as required should be passed as described in order to enable the particular feature, but there are not required in the whole app context.
### My account
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED | `boolean` | Set to true if network has account feature | Required | - | `true` |
| NEXT_PUBLIC_AUTH0_CLIENT_ID | `string` | Client id for [Auth0](https://auth0.com/) provider | Required | - | `<your-secret>` |
| NEXT_PUBLIC_AUTH_URL | `string` | Account auth base url; it is used for building login URL (`${ NEXT_PUBLIC_AUTH_URL }/auth/auth0`) and logout return URL (`${ NEXT_PUBLIC_AUTH_URL }/auth/logout`); if not provided the base app URL will be used instead | Required | - | `https://blockscout.com` |
| NEXT_PUBLIC_LOGOUT_URL | `string` | Account logout url. Required if account is supported for the app instance. | Required | - | `https://blockscoutcom.us.auth0.com/v2/logout` |
&nbsp;
### Address verification in "My account"
*Note* all ENV variables required for [My account](ENVS.md#my-account) feature should be passed along side with the following ones:
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_CONTRACT_INFO_API_HOST | `string` | Contract Info API endpoint url | Required | - | `https://contracts-info.services.blockscout.com` |
| NEXT_PUBLIC_ADMIN_SERVICE_API_HOST | `string` | Admin Service API endpoint url | Required | - | `https://admin-rs.services.blockscout.com` |
&nbsp;
### Blockchain interaction (writing to contract, etc.)
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID | `string` | Project id for [WalletConnect](https://docs.walletconnect.com/2.0/web3modal/react/installation#obtain-project-id) integration | Required | - | `<your-secret>` |
| NEXT_PUBLIC_NETWORK_RPC_URL | `string` | See in [Blockchain parameters](ENVS.md#blockchain-parameters) section | Required | - | `https://core.poa.network` |
| NEXT_PUBLIC_NETWORK_NAME | `string` | See in [Blockchain parameters](ENVS.md#blockchain-parameters) section | Required | - | `Gnosis Chain` |
| NEXT_PUBLIC_NETWORK_ID | `number` | See in [Blockchain parameters](ENVS.md#blockchain-parameters) section | Required | - | `99` |
| NEXT_PUBLIC_NETWORK_CURRENCY_NAME | `string` | See in [Blockchain parameters](ENVS.md#blockchain-parameters) section | Required | - | `Ether` |
| NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL | `string` | See in [Blockchain parameters](ENVS.md#blockchain-parameters) section | Required | - | `ETH` |
| NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS | `string` | See in [Blockchain parameters](ENVS.md#blockchain-parameters) section | - | `18` | `6` |
&nbsp;
### Banner ads
This feature is **enabled by default** with the `slise` ads provider. To switch it off pass `NEXT_PUBLIC_AD_BANNER_PROVIDER=none`.
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_AD_BANNER_PROVIDER | `slise` \| `adbutler` \| `coinzilla` \| `none` | Ads provider | - | `slise` | `coinzilla` |
| NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP | `{ id: string; width: string; height: string }` | Placement config for desktop Adbutler banner | - | - | `{'id':'123456','width':'728','height':'90'}` | | NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP | `{ id: string; width: string; height: string }` | Placement config for desktop Adbutler banner | - | - | `{'id':'123456','width':'728','height':'90'}` |
| NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE | `{ id: string; width: number; height: number }` | Placement config for mobile Adbutler banner | - | - | `{'id':'654321','width':'300','height':'100'}` | | NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE | `{ id: string; width: number; height: number }` | Placement config for mobile Adbutler banner | - | - | `{'id':'654321','width':'300','height':'100'}` |
| NEXT_PUBLIC_AD_BANNER_PROVIDER | `slise` \| `adbutler` \| `coinzilla` \| `none` | Banner ad provider. Set to `none` to disable ad banners | - | `slise` | `coinzilla` |
| NEXT_PUBLIC_AD_TEXT_PROVIDER | `coinzilla` \| `none` | Text ad provider. Set to `none` to disable text ad | - | `coinzilla` | `none` | &nbsp;
| NEXT_PUBLIC_API_SPEC_URL | `string` | Spec to be displayed on api-docs page | - | - | `https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml` |
| NEXT_PUBLIC_GRAPHIQL_TRANSACTION | `string` | Txn hash for default query at GraphQl playground page | - | - | `0x69e3923eef50eada197c3336d546936d0c994211492c9f947a24c02827568f9f` | ### Text ads
| NEXT_PUBLIC_WEB3_DEFAULT_WALLET | `metamask` \| `coinbase`| Type of Web3 wallet which will be used by default to add tokens or chains to | - | `metamask` | `coinbase` |
| NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET | `boolean`| Set to `true` to hide icon "Add to your wallet" next to token addresses | - | `false` | `true` | This feature is **enabled by default** with the `coinzilla` ads provider. To switch it off pass `NEXT_PUBLIC_AD_TEXT_PROVIDER=none`.
| NEXT_PUBLIC_HIDE_INDEXING_ALERT | `boolean` | Set to `true` to hide indexing alert, if the chain indexing isn't completed | - | `false` | `true` |
### Marketplace app configuration properties | Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| Property | Type | Description | Example value | NEXT_PUBLIC_AD_TEXT_PROVIDER | `coinzilla` \| `none` | Ads provider | - | `coinzilla` | `none` |
| --- | --- | --- | --- |
| id | `string` | Used as slug for the app. Must be unique in the app list. | `'app'` | &nbsp;
| external | `boolean` | If true means that the application opens in a new window, but not in an iframe. | `true` |
| title | `string` | Displayed title of the app. | `'The App'` | ### Beacon chain
| logo | `string` | URL to logo file. Should be at least 288x288. | `'https://foo.app/icon.png'` |
| shortDescription | `string` | Displayed only in the app list. | `'Awesome app'` | | Variable | Type| Description | Compulsoriness | Default value | Example value |
| categories | `Array<MarketplaceCategoryId>` | Displayed category. Select one of the following bellow. | `['security', 'tools']` | | --- | --- | --- | --- | --- | --- |
| author | `string` | Displayed author of the app | `'Bob'` | | NEXT_PUBLIC_HAS_BEACON_CHAIN | `boolean` | Set to true for networks with the beacon chain | Required | - | `true` |
| url | `string` | URL of the app which will be launched in the iframe. | `'https://foo.app/launch'` | | NEXT_PUBLIC_BEACON_CHAIN_CURRENCY_SYMBOL | `string` | Beacon network currency symbol | - | `NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL` | `ETH` |
| description | `string` | Displayed only in the modal dialog with additional info about the app. | `'The best app'` |
| site | `string` *(optional)* | Displayed site link | `'https://blockscout.com'` | &nbsp;
| twitter | `string` *(optional)* | Displayed twitter link | `'https://twitter.com/blockscoutcom'` |
| telegram | `string` *(optional)* | Displayed telegram link | `'https://t.me/poa_network'` | ### Rollup (L2) chain
| github | `string` *(optional)* | Displayed github link | `'https://github.com/blockscout'` |
| Variable | Type| Description | Compulsoriness | Default value | Example value |
### Marketplace categories ids | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_IS_L2_NETWORK | `boolean` | Set to true for L2 solutions | Required | - | `true` |
| NEXT_PUBLIC_L1_BASE_URL | `string` | Blockscout base URL for L1 network | Required | - | `'http://eth-goerli.blockscout.com'` |
| NEXT_PUBLIC_L2_WITHDRAWAL_URL | `string` | URL for L2 -> L1 withdrawals | Required | - | `https://app.optimism.io/bridge/withdraw` |
&nbsp;
### Export data to CSV file
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY | `string` | See [below](ENVS.md#google-recaptcha) | true | - | `<your-secret>` |
&nbsp;
### Google analytics
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID | `string` | Property ID for [Google Analytics](https://analytics.google.com/) service | true | - | `UA-XXXXXX-X` |
&nbsp;
### Mixpanel analytics
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN | `string` | Project token for [Mixpanel](https://mixpanel.com/) analytics service | true | - | `<your-secret>` |
&nbsp;
### GraphQL API documentation
This feature is **always enabled**, but you can configure its behavior by passing the following variables.
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_GRAPHIQL_TRANSACTION | `string` | Txn hash for default query at GraphQl playground page | - | - | `0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62` |
&nbsp;
### REST API documentation
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_API_SPEC_URL | `string` | Spec to be displayed on `/api-docs` page | Required | - | `https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml` |
&nbsp;
### Marketplace
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_MARKETPLACE_CONFIG_URL | `string` | URL of configuration file (`.json` format only) which contains list of apps that will be shown on the marketplace page. See [below](#marketplace-app-configuration-properties) list of available properties for an app | Required | - | `https://example.com/marketplace_config.json` |
| NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM | `string` | Link to form where authors can submit their dapps to the marketplace | Required | - | `https://airtable.com/shrqUAcjgGJ4jU88C` |
| NEXT_PUBLIC_NETWORK_RPC_URL | `string` | See in [Blockchain parameters](ENVS.md#blockchain-parameters) section | Required | - | `https://core.poa.network` |
#### Marketplace app configuration properties
| Property | Type | Description | Compulsoriness | Example value
| --- | --- | --- | --- | --- |
| id | `string` | Used as slug for the app. Must be unique in the app list. | Required | `'app'` |
| external | `boolean` | `true` means that the application opens in a new window, but not in an iframe. | - | `true` |
| title | `string` | Displayed title of the app. | Required | `'The App'` |
| logo | `string` | URL to logo file. Should be at least 288x288. | Required | `'https://foo.app/icon.png'` |
| shortDescription | `string` | Displayed only in the app list. | Required | `'Awesome app'` |
| categories | `Array<MarketplaceCategoryId>` | Displayed category. Select one of the following bellow. | Required | `['security', 'tools']` |
| author | `string` | Displayed author of the app | Required | `'Bob'` |
| url | `string` | URL of the app which will be launched in the iframe. | Required | `'https://foo.app/launch'` |
| description | `string` | Displayed only in the modal dialog with additional info about the app. | Required | `'The best app'` |
| site | `string` | Displayed site link | - | `'https://blockscout.com'` |
| twitter | `string` | Displayed twitter link | - | `'https://twitter.com/blockscoutcom'` |
| telegram | `string` | Displayed telegram link | - | `'https://t.me/poa_network'` |
| github | `string` | Displayed github link | - | `'https://github.com/blockscout'` |
#### Marketplace categories ids
For each application, you need to specify the `MarketplaceCategoryId` to which it belongs. Select one of the following: For each application, you need to specify the `MarketplaceCategoryId` to which it belongs. Select one of the following:
...@@ -80,94 +313,60 @@ For each application, you need to specify the `MarketplaceCategoryId` to which i ...@@ -80,94 +313,60 @@ For each application, you need to specify the `MarketplaceCategoryId` to which i
- `tools` - `tools`
- `yieldFarming` - `yieldFarming`
### Featured network configuration properties &nbsp;
| Variable | Type| Description | Is required | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| title | `string` | Displayed name of the network | yes | - | `Gnosis Chain` |
| url | `string` | Network explorer main page url | yes | - | `https://blockscout.com/xdai/mainnet` |
| group | `Mainnets \| Testnets \| Other` | Indicates in which tab network appears in the menu | yes | - | `Mainnets` |
| icon | `string` | Network icon; if not provided, the common placeholder will be shown; *Note* that icon size should be at least 60px by 60px | - | - | `https://placekitten.com/60/60` |
| isActive | `boolean` | Pass `true` if item should be shonw as active in the menu | - | - | `true` |
| invertIconInDarkMode | `boolean` | Pass `true` if icon colors should be inverted in dark mode | - | - | `true` |
### Network explorer configuration properties ### Solidity to UML diagrams
| Variable | Type| Description | Is required | Default value | Example value | | Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| title | `string` | Displayed name of the explorer | yes | - | `Anyblock` | | NEXT_PUBLIC_VISUALIZE_API_HOST | `string` | Visualize API endpoint url | Required | - | `https://visualizer.services.blockscout.com` |
| baseUrl | `string` | Base url of the explorer | yes | - | `https://explorer.anyblock.tools` |
| paths | `Record<'tx' \| 'block' \| 'address' \| 'token', string>` | Map of explorer entities and their paths | yes | - | `{'tx':'/ethereum/poa/core/tx'}` |
*Note* The url of an entity will be constructed as `<baseUrl><paths[<entity-type>]><entity-id>`, e.g `https://explorer.anyblock.tools/ethereum/poa/core/tx/<tx-id>` &nbsp;
### Footer links configuration properties ### Blockchain statistics
| Variable | Type| Description | Is required | Default value | Example value | | Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| title | `string` | Title of link group | yes | - | `Company` | | NEXT_PUBLIC_STATS_API_HOST | `string` | API endpoint url | Required | - | `https://stats.services.blockscout.com` |
| links | `Array<{'text':string;'url':string;}>` | list of links | yes | - | `[{'text':'Homepage','url':'https://www.blockscout.com'}]` |
## Account configuration &nbsp;
In order to enable "My Account" feature you have to configure following set of variables. All variables are **required**. ### Web3 wallet integration (add token or network to the wallet)
| Variable | Type| Description | Is required | Default value | Example value | This feature is **enabled by default** with the `metamask` wallet type. To switch it off pass `NEXT_PUBLIC_WEB3_DEFAULT_WALLET=none`.
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED | `boolean` | Set to true if network has account feature | yes | `false` | `true` |
| NEXT_PUBLIC_AUTH0_CLIENT_ID | `string` | Client id for [Auth0](https://auth0.com/) provider | yes | - | `<secret>` |
| NEXT_PUBLIC_AUTH_URL | `string` | Account auth base url; it is used for building login URL (`${ NEXT_PUBLIC_AUTH_URL }/auth/auth0`) and logout return URL (`${ NEXT_PUBLIC_AUTH_URL }/auth/logout`); if not provided the base app URL will be used instead | yes | - | `https://blockscout.com` |
| NEXT_PUBLIC_LOGOUT_URL | `string` | Account logout url. Required if account is supported for the app instance. | yes | - | `https://blockscoutcom.us.auth0.com/v2/logout` |
## App configuration | Variable | Type| Description | Compulsoriness | Default value | Example value |
| Variable | Type| Description | Is required | Default value | Example value |
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_APP_INSTANCE | `string` | Name of app instance | - | - | `wonderful_kepler` | | NEXT_PUBLIC_WEB3_DEFAULT_WALLET | `metamask` \| `coinbase` \| `none` | Type of Web3 wallet which will be used by default to add tokens or chains to | - | `metamask` | `coinbase` |
| NEXT_PUBLIC_APP_PROTOCOL | `http \| https` | App url schema | - | `https` | `http` | | NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET | `boolean`| Set to `true` to hide icon "Add to your wallet" next to token addresses | - | - | `true` |
| NEXT_PUBLIC_APP_HOST | `string` | App host | yes | - | `blockscout.com` |
| NEXT_PUBLIC_APP_PORT | `number` | Port where app is running | - | `3000` | `3001` |
| NEXT_PUBLIC_APP_ENV | `string` | Current app env (e.g development, review or production). Used for Sentry.io configuration | - | equals to `process.env.NODE_ENV` | `production` |
| NEXT_PUBLIC_USE_NEXT_JS_PROXY | `boolean` | Tells the app to proxy all APIs request through the NextJs app. **We strongly advise not to use it in the production environment** | - | `false` | `true` |
## API configuration &nbsp;
| Variable | Type| Description | Is required | Default value | Example value | ### Verified tokens info
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_API_PROTOCOL | `http \| https` | Main API protocol | - | `https` | `http` | | NEXT_PUBLIC_CONTRACT_INFO_API_HOST | `string` | Contract Info API endpoint url | Required | - | `https://contracts-info.services.blockscout.com` |
| NEXT_PUBLIC_API_HOST | `string` | Main API host | yes | - | `blockscout.com` |
| NEXT_PUBLIC_API_PORT | `number` | Port where API is running on the host | - | - | `3001` |
| NEXT_PUBLIC_API_BASE_PATH | `string` | Base path for Main API endpoint url | - | - | `/poa/core` |
| NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL | `ws \| wss` | Main API websocket protocol | - | `wss` | `ws` |
| NEXT_PUBLIC_STATS_API_HOST | `string` | Stats API endpoint url | - | - | `https://stats.services.blockscout.com` |
| NEXT_PUBLIC_VISUALIZE_API_HOST | `string` | Visualize API endpoint url | - | - | `https://visualizer.services.blockscout.com` |
| NEXT_PUBLIC_CONTRACT_INFO_API_HOST | `string` | Contract Info API endpoint url | - | - | `https://contracts-info.services.blockscout.com` |
| NEXT_PUBLIC_ADMIN_SERVICE_API_HOST | `string` | Admin Service API endpoint url | - | - | `https://admin-rs.services.blockscout.com` |
&nbsp;
## External services configuration ### Sentry error monitoring
| Variable | Type| Description | Is required | Default value | Example value | | Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_SENTRY_DSN | `string` | Client key for your Sentry.io app | - | - | `<secret>` | | NEXT_PUBLIC_SENTRY_DSN | `string` | Client key for your Sentry.io app | Required | - | `<your-secret>` |
| SENTRY_CSP_REPORT_URI | `string` | URL for sending CSP-reports to your Sentry.io app | - | - | `<secret>` | | SENTRY_CSP_REPORT_URI | `string` | URL for sending CSP-reports to your Sentry.io app | - | - | `<your-secret>` |
| NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID | `string` | Project id for [WalletConnect](https://docs.walletconnect.com/2.0/web3modal/react/installation#obtain-project-id) integration | - | - | `<secret>` | | NEXT_PUBLIC_APP_ENV | `string` | Current app env (e.g development, review or production). Passed as `environment` property to Sentry config | - | `process.env.NODE_ENV` | `production` |
| NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY | `string` | Site key for [reCAPTCHA](https://developers.google.com/recaptcha) service | - | - | `<secret>` | | NEXT_PUBLIC_APP_INSTANCE | `string` | Name of app instance. Used as custom tag `app_instance` value in the main Sentry scope | - | - | `wonderful_kepler` |
| NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID | `string` | Property ID for [Google Analytics](https://analytics.google.com/) service | - | - | `UA-XXXXXX-X` |
| NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN | `string` | Project token for [Mixpanel](https://mixpanel.com/) analytics service | - | - | `<secret>` |
## L2 configuration &nbsp;
*Note* All variables are required only for roll-up instances
| Variable | Type| Description | Is required | Default value | Example value | ## External services configuration
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_IS_L2_NETWORK | `boolean` | Set to true for L2 solutions (Optimism Bedrock based) | yes | - | `true` | ### Google ReCaptcha
| NEXT_PUBLIC_L1_BASE_URL | `string` | Base Blockscout URL for L1 network | yes | - | `'http://eth-goerli.blockscout.com'` |
| NEXT_PUBLIC_L2_WITHDRAWAL_URL | `string` | URL for L2 -> L1 withdrawals | yes | - | `https://app.optimism.io/bridge/withdraw` |
## Beacon chain configuration For obtaining the variables values please refer to [reCAPTCHA documentation](https://developers.google.com/recaptcha).
| Variable | Type| Description | Is required | Default value | Example value | | Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_HAS_BEACON_CHAIN | `boolean` | Set to true for networks with the beacon chain | - | - | `true` | | NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY | `string` | Site key | - | - | `<your-secret>` |
| NEXT_PUBLIC_BEACON_CHAIN_CURRENCY_SYMBOL | `string` | Beacon network currency symbol | - | NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL | `ETH` |
import { compile } from 'path-to-regexp'; import { compile } from 'path-to-regexp';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import isNeedProxy from './isNeedProxy'; import isNeedProxy from './isNeedProxy';
import { RESOURCES } from './resources'; import { RESOURCES } from './resources';
...@@ -12,8 +12,8 @@ export default function buildUrl<R extends ResourceName>( ...@@ -12,8 +12,8 @@ export default function buildUrl<R extends ResourceName>(
queryParams?: Record<string, string | Array<string> | number | null | undefined>, queryParams?: Record<string, string | Array<string> | number | null | undefined>,
): string { ): string {
const resource: ApiResource = RESOURCES[resourceName]; const resource: ApiResource = RESOURCES[resourceName];
const baseUrl = isNeedProxy() ? appConfig.app.baseUrl : (resource.endpoint || appConfig.api.endpoint); const baseUrl = isNeedProxy() ? config.app.baseUrl : (resource.endpoint || config.api.endpoint);
const basePath = resource.basePath !== undefined ? resource.basePath : appConfig.api.basePath; const basePath = resource.basePath !== undefined ? resource.basePath : config.api.basePath;
const path = isNeedProxy() ? '/node-api/proxy' + basePath + resource.path : basePath + resource.path; const path = isNeedProxy() ? '/node-api/proxy' + basePath + resource.path : basePath + resource.path;
const url = new URL(compile(path)(pathParams), baseUrl); const url = new URL(compile(path)(pathParams), baseUrl);
......
import { compile } from 'path-to-regexp'; import { compile } from 'path-to-regexp';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import { RESOURCES } from './resources'; import { RESOURCES } from './resources';
import type { ApiResource, ResourceName } from './resources'; import type { ApiResource, ResourceName } from './resources';
...@@ -11,8 +11,8 @@ export default function buildUrlNode( ...@@ -11,8 +11,8 @@ export default function buildUrlNode(
queryParams?: Record<string, string | number | undefined>, queryParams?: Record<string, string | number | undefined>,
) { ) {
const resource: ApiResource = typeof _resource === 'string' ? RESOURCES[_resource] : _resource; const resource: ApiResource = typeof _resource === 'string' ? RESOURCES[_resource] : _resource;
const baseUrl = resource.endpoint || appConfig.api.endpoint; const baseUrl = resource.endpoint || config.api.endpoint;
const basePath = resource.basePath !== undefined ? resource.basePath : appConfig.api.basePath; const basePath = resource.basePath !== undefined ? resource.basePath : config.api.basePath;
const path = basePath + resource.path; const path = basePath + resource.path;
const url = new URL(compile(path)(pathParams), baseUrl); const url = new URL(compile(path)(pathParams), baseUrl);
......
import appConfig from 'configs/app/config'; import config from 'configs/app';
// FIXME // FIXME
// I was not able to figure out how to send CORS with credentials from localhost // I was not able to figure out how to send CORS with credentials from localhost
// unsuccessfully tried different ways, even custom local dev domain // unsuccessfully tried different ways, even custom local dev domain
// so for local development we have to use next.js api as proxy server // so for local development we have to use next.js api as proxy server
export default function isNeedProxy() { export default function isNeedProxy() {
if (appConfig.app.useNextJsProxy) { if (config.app.useProxy) {
return true; 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'; ...@@ -59,7 +59,7 @@ import type { VisualizedContract } from 'types/api/visualization';
import type { WithdrawalsResponse, WithdrawalsCounters } from 'types/api/withdrawals'; import type { WithdrawalsResponse, WithdrawalsCounters } from 'types/api/withdrawals';
import type { ArrayElement } from 'types/utils'; import type { ArrayElement } from 'types/utils';
import appConfig from 'configs/app/config'; import config from 'configs/app';
export interface ApiResource { export interface ApiResource {
path: ResourcePath; path: ResourcePath;
...@@ -111,58 +111,58 @@ export const RESOURCES = { ...@@ -111,58 +111,58 @@ export const RESOURCES = {
address_verification: { address_verification: {
path: '/api/v1/chains/:chainId/verified-addresses:type', path: '/api/v1/chains/:chainId/verified-addresses:type',
pathParams: [ 'chainId' as const, 'type' as const ], pathParams: [ 'chainId' as const, 'type' as const ],
endpoint: appConfig.contractInfoApi.endpoint, endpoint: config.features.verifiedTokens.api.endpoint,
basePath: appConfig.contractInfoApi.basePath, basePath: config.features.verifiedTokens.api.basePath,
needAuth: true, needAuth: true,
}, },
verified_addresses: { verified_addresses: {
path: '/api/v1/chains/:chainId/verified-addresses', path: '/api/v1/chains/:chainId/verified-addresses',
pathParams: [ 'chainId' as const ], pathParams: [ 'chainId' as const ],
endpoint: appConfig.contractInfoApi.endpoint, endpoint: config.features.verifiedTokens.api.endpoint,
basePath: appConfig.contractInfoApi.basePath, basePath: config.features.verifiedTokens.api.basePath,
needAuth: true, needAuth: true,
}, },
token_info_applications_config: { token_info_applications_config: {
path: '/api/v1/chains/:chainId/token-info-submissions/selectors', path: '/api/v1/chains/:chainId/token-info-submissions/selectors',
pathParams: [ 'chainId' as const ], pathParams: [ 'chainId' as const ],
endpoint: appConfig.adminServiceApi.endpoint, endpoint: config.features.addressVerification.api.endpoint,
basePath: appConfig.adminServiceApi.basePath, basePath: config.features.addressVerification.api.basePath,
needAuth: true, needAuth: true,
}, },
token_info_applications: { token_info_applications: {
path: '/api/v1/chains/:chainId/token-info-submissions/:id?', path: '/api/v1/chains/:chainId/token-info-submissions/:id?',
pathParams: [ 'chainId' as const, 'id' as const ], pathParams: [ 'chainId' as const, 'id' as const ],
endpoint: appConfig.adminServiceApi.endpoint, endpoint: config.features.addressVerification.api.endpoint,
basePath: appConfig.adminServiceApi.basePath, basePath: config.features.addressVerification.api.basePath,
needAuth: true, needAuth: true,
}, },
// STATS // STATS
stats_counters: { stats_counters: {
path: '/api/v1/counters', path: '/api/v1/counters',
endpoint: appConfig.statsApi.endpoint, endpoint: config.features.stats.api.endpoint,
basePath: appConfig.statsApi.basePath, basePath: config.features.stats.api.basePath,
}, },
stats_lines: { stats_lines: {
path: '/api/v1/lines', path: '/api/v1/lines',
endpoint: appConfig.statsApi.endpoint, endpoint: config.features.stats.api.endpoint,
basePath: appConfig.statsApi.basePath, basePath: config.features.stats.api.basePath,
}, },
stats_line: { stats_line: {
path: '/api/v1/lines/:id', path: '/api/v1/lines/:id',
pathParams: [ 'id' as const ], pathParams: [ 'id' as const ],
endpoint: appConfig.statsApi.endpoint, endpoint: config.features.stats.api.endpoint,
basePath: appConfig.statsApi.basePath, basePath: config.features.stats.api.basePath,
}, },
// VISUALIZATION // VISUALIZATION
visualize_sol2uml: { visualize_sol2uml: {
path: '/api/v1/solidity\\:visualize-contracts', path: '/api/v1/solidity\\:visualize-contracts',
endpoint: appConfig.visualizeApi.endpoint, endpoint: config.features.sol2uml.api.endpoint,
basePath: appConfig.visualizeApi.basePath, basePath: config.features.sol2uml.api.basePath,
}, },
// BLOCKS, TXS // BLOCKS, TXS
...@@ -345,8 +345,8 @@ export const RESOURCES = { ...@@ -345,8 +345,8 @@ export const RESOURCES = {
token_verified_info: { token_verified_info: {
path: '/api/v1/chains/:chainId/token-infos/:hash', path: '/api/v1/chains/:chainId/token-infos/:hash',
pathParams: [ 'chainId' as const, 'hash' as const ], pathParams: [ 'chainId' as const, 'hash' as const ],
endpoint: appConfig.contractInfoApi.endpoint, endpoint: config.features.verifiedTokens.api.endpoint,
basePath: appConfig.contractInfoApi.basePath, basePath: config.features.verifiedTokens.api.basePath,
}, },
token_counters: { token_counters: {
path: '/api/v2/tokens/:hash/counters', path: '/api/v2/tokens/:hash/counters',
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { CsrfData } from 'types/client/account'; 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 isBodyAllowed from 'lib/api/isBodyAllowed';
import isNeedProxy from 'lib/api/isNeedProxy'; import isNeedProxy from 'lib/api/isNeedProxy';
import { getResourceKey } from 'lib/api/useApiQuery'; import { getResourceKey } from 'lib/api/useApiQuery';
...@@ -49,7 +49,7 @@ export default function useApiFetch() { ...@@ -49,7 +49,7 @@ export default function useApiFetch() {
// for user authentication in My account // 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) // 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 // change condition here if something is changed
credentials: appConfig.account.isEnabled ? 'include' : 'same-origin', credentials: config.features.account.isEnabled ? 'include' : 'same-origin',
headers, headers,
...fetchParams, ...fetchParams,
}, },
......
import type CspDev from 'csp-dev'; import type CspDev from 'csp-dev';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import { KEY_WORDS } from '../utils'; import { KEY_WORDS } from '../utils';
const MAIN_DOMAINS = [ const MAIN_DOMAINS = [
`*.${ appConfig.app.host }`, `*.${ config.app.host }`,
appConfig.app.host, config.app.host,
appConfig.visualizeApi.endpoint, config.features.sol2uml.api.endpoint,
].filter(Boolean); ].filter(Boolean);
// eslint-disable-next-line no-restricted-properties // eslint-disable-next-line no-restricted-properties
const REPORT_URI = process.env.SENTRY_CSP_REPORT_URI;
export function app(): CspDev.DirectiveDescriptor { export function app(): CspDev.DirectiveDescriptor {
return { return {
...@@ -26,18 +25,18 @@ export function app(): CspDev.DirectiveDescriptor { ...@@ -26,18 +25,18 @@ export function app(): CspDev.DirectiveDescriptor {
...MAIN_DOMAINS, ...MAIN_DOMAINS,
// webpack hmr in safari doesn't recognize localhost as 'self' for some reason // 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 // APIs
appConfig.api.endpoint, config.api.endpoint,
appConfig.api.socket, config.api.socket,
appConfig.statsApi.endpoint, config.features.stats.api.endpoint,
appConfig.visualizeApi.endpoint, config.features.sol2uml.api.endpoint,
appConfig.contractInfoApi.endpoint, config.features.verifiedTokens.api.endpoint,
appConfig.adminServiceApi.endpoint, config.features.addressVerification.api.endpoint,
// chain RPC server // chain RPC server
appConfig.network.rpcUrl, config.chain.rpcUrl,
'https://infragrid.v.network', // RPC providers 'https://infragrid.v.network', // RPC providers
// github (spec for api-docs page) // github (spec for api-docs page)
...@@ -50,7 +49,7 @@ export function app(): CspDev.DirectiveDescriptor { ...@@ -50,7 +49,7 @@ export function app(): CspDev.DirectiveDescriptor {
// next.js generates and rebuilds source maps in dev using eval() // next.js generates and rebuilds source maps in dev using eval()
// https://github.com/vercel/next.js/issues/14221#issuecomment-657258278 // 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 // hash of ColorModeScript
'\'sha256-e7MRMmTzLsLQvIy1iizO1lXf7VWYoQ6ysj5fuUzvRwE=\'', '\'sha256-e7MRMmTzLsLQvIy1iizO1lXf7VWYoQ6ysj5fuUzvRwE=\'',
...@@ -109,9 +108,9 @@ export function app(): CspDev.DirectiveDescriptor { ...@@ -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': [
REPORT_URI, config.features.sentry.cspReportUrl,
], ],
} : {}), } : {}),
}; };
......
import type CspDev from 'csp-dev'; import type CspDev from 'csp-dev';
import appConfig from 'configs/app/config'; import config from 'configs/app';
export function googleAnalytics(): CspDev.DirectiveDescriptor { export function googleAnalytics(): CspDev.DirectiveDescriptor {
if (!appConfig.googleAnalytics.propertyId) { if (!config.features.googleAnalytics.isEnabled) {
return {}; return {};
} }
......
import type CspDev from 'csp-dev'; import type CspDev from 'csp-dev';
import appConfig from 'configs/app/config'; import config from 'configs/app';
export function googleReCaptcha(): CspDev.DirectiveDescriptor { export function googleReCaptcha(): CspDev.DirectiveDescriptor {
if (!appConfig.reCaptcha.siteKey) { if (!config.services.reCaptcha.siteKey) {
return {}; return {};
} }
......
import type CspDev from 'csp-dev'; import type CspDev from 'csp-dev';
import appConfig from 'configs/app/config'; import config from 'configs/app';
export function mixpanel(): CspDev.DirectiveDescriptor { export function mixpanel(): CspDev.DirectiveDescriptor {
if (!appConfig.mixpanel.projectToken) { if (!config.features.mixpanel.isEnabled) {
return {}; return {};
} }
......
import type CspDev from 'csp-dev'; import type CspDev from 'csp-dev';
import appConfig from 'configs/app/config'; import config from 'configs/app';
export function walletConnect(): CspDev.DirectiveDescriptor { export function walletConnect(): CspDev.DirectiveDescriptor {
if (!appConfig.walletConnect.projectId || !appConfig.network.rpcUrl) { if (!config.features.blockchainInteraction.isEnabled) {
return {}; return {};
} }
......
import appConfig from 'configs/app/config'; import config from 'configs/app';
import { useAppContext } from 'lib/contexts/app'; import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
export default function useHasAccount() { export default function useHasAccount() {
const appProps = useAppContext(); const appProps = useAppContext();
if (!appConfig.account.isEnabled) { if (!config.features.account.isEnabled) {
return false; return false;
} }
......
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import isBrowser from 'lib/isBrowser'; import isBrowser from 'lib/isBrowser';
const base = 'https://github.com/blockscout/blockscout/issues/new/'; const base = 'https://github.com/blockscout/blockscout/issues/new/';
const labels = 'new UI'; 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) { export default function useIssueUrl(backendVersion: string | undefined) {
const [ userAgent, setUserAgent ] = React.useState(''); const [ userAgent, setUserAgent ] = React.useState('');
...@@ -24,7 +24,7 @@ export default function useIssueUrl(backendVersion: string | undefined) { ...@@ -24,7 +24,7 @@ export default function useIssueUrl(backendVersion: string | undefined) {
### Environment ### Environment
* Backend Version/branch/commit: ${ backendVersion } * 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 } * User Agent: ${ userAgent }
### Steps to reproduce ### Steps to reproduce
......
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import appConfig from 'configs/app/config'; import config from 'configs/app';
export default function useLoginUrl() { export default function useLoginUrl() {
const router = useRouter(); 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'; ...@@ -3,7 +3,7 @@ import React from 'react';
import type { NavItemInternal, NavItem, NavGroupItem } from 'types/client/navigation-items'; 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 abiIcon from 'icons/ABI.svg';
import apiKeysIcon from 'icons/API.svg'; import apiKeysIcon from 'icons/API.svg';
import appsIcon from 'icons/apps.svg'; import appsIcon from 'icons/apps.svg';
...@@ -43,9 +43,6 @@ export function isInternalItem(item: NavItem): item is NavItemInternal { ...@@ -43,9 +43,6 @@ export function isInternalItem(item: NavItem): item is NavItemInternal {
} }
export default function useNavItems(): ReturnType { export default function useNavItems(): ReturnType {
const isMarketplaceAvailable = Boolean(appConfig.marketplace.configUrl && appConfig.network.rpcUrl);
const hasAPIDocs = appConfig.apiDoc.specUrl;
const router = useRouter(); const router = useRouter();
const pathname = router.pathname; const pathname = router.pathname;
...@@ -74,7 +71,7 @@ export default function useNavItems(): ReturnType { ...@@ -74,7 +71,7 @@ export default function useNavItems(): ReturnType {
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
{ text: 'Verified contracts', nextRoute: { pathname: '/verified-contracts' as const }, icon: verifiedIcon, isActive: pathname === '/verified-contracts' }; { 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 = [ blockchainNavItems = [
[ [
txs, txs,
...@@ -101,7 +98,7 @@ export default function useNavItems(): ReturnType { ...@@ -101,7 +98,7 @@ export default function useNavItems(): ReturnType {
blocks, blocks,
topAccounts, topAccounts,
verifiedContracts, verifiedContracts,
appConfig.beaconChain.hasBeaconChain && { config.features.beaconChain.isEnabled && {
text: 'Withdrawals', text: 'Withdrawals',
nextRoute: { pathname: '/withdrawals' as const }, nextRoute: { pathname: '/withdrawals' as const },
icon: withdrawalsIcon, icon: withdrawalsIcon,
...@@ -111,18 +108,18 @@ export default function useNavItems(): ReturnType { ...@@ -111,18 +108,18 @@ export default function useNavItems(): ReturnType {
} }
const apiNavItems: Array<NavItem> = [ const apiNavItems: Array<NavItem> = [
hasAPIDocs ? { config.features.restApiDocs.isEnabled ? {
text: 'REST API', text: 'REST API',
nextRoute: { pathname: '/api-docs' as const }, nextRoute: { pathname: '/api-docs' as const },
icon: apiDocsIcon, icon: apiDocsIcon,
isActive: pathname === '/api-docs', isActive: pathname === '/api-docs',
} : null, } : null,
{ config.features.graphqlApiDocs.isEnabled ? {
text: 'GraphQL', text: 'GraphQL',
nextRoute: { pathname: '/graphiql' as const }, nextRoute: { pathname: '/graphiql' as const },
icon: graphQLIcon, icon: graphQLIcon,
isActive: pathname === '/graphiql', isActive: pathname === '/graphiql',
}, } : null,
{ {
text: 'RPC API', text: 'RPC API',
icon: rpcIcon, icon: rpcIcon,
...@@ -148,13 +145,13 @@ export default function useNavItems(): ReturnType { ...@@ -148,13 +145,13 @@ export default function useNavItems(): ReturnType {
icon: tokensIcon, icon: tokensIcon,
isActive: pathname.startsWith('/token'), isActive: pathname.startsWith('/token'),
}, },
isMarketplaceAvailable ? { config.features.marketplace.isEnabled ? {
text: 'Apps', text: 'Apps',
nextRoute: { pathname: '/apps' as const }, nextRoute: { pathname: '/apps' as const },
icon: appsIcon, icon: appsIcon,
isActive: pathname.startsWith('/app'), isActive: pathname.startsWith('/app'),
} : null, } : null,
appConfig.statsApi.endpoint ? { config.features.stats.isEnabled ? {
text: 'Charts & stats', text: 'Charts & stats',
nextRoute: { pathname: '/stats' as const }, nextRoute: { pathname: '/stats' as const },
icon: statsIcon, icon: statsIcon,
...@@ -166,10 +163,10 @@ export default function useNavItems(): ReturnType { ...@@ -166,10 +163,10 @@ export default function useNavItems(): ReturnType {
isActive: apiNavItems.some(item => isInternalItem(item) && item.isActive), isActive: apiNavItems.some(item => isInternalItem(item) && item.isActive),
subItems: apiNavItems, subItems: apiNavItems,
}, },
appConfig.navigation.otherLinks.length > 0 ? { config.UI.sidebar.otherLinks.length > 0 ? {
text: 'Other', text: 'Other',
icon: gearIcon, icon: gearIcon,
subItems: appConfig.navigation.otherLinks, subItems: config.UI.sidebar.otherLinks,
} : null, } : null,
].filter(Boolean); ].filter(Boolean);
...@@ -202,7 +199,7 @@ export default function useNavItems(): ReturnType { ...@@ -202,7 +199,7 @@ export default function useNavItems(): ReturnType {
icon: abiIcon, icon: abiIcon,
isActive: pathname === '/account/custom-abi', isActive: pathname === '/account/custom-abi',
}, },
appConfig.contractInfoApi.endpoint && appConfig.adminServiceApi.endpoint && { config.features.addressVerification.isEnabled && {
text: 'Verified addrs', text: 'Verified addrs',
nextRoute: { pathname: '/account/verified-addresses' as const }, nextRoute: { pathname: '/account/verified-addresses' as const },
icon: verifiedIcon, icon: verifiedIcon,
...@@ -218,5 +215,5 @@ export default function useNavItems(): ReturnType { ...@@ -218,5 +215,5 @@ export default function useNavItems(): ReturnType {
}; };
return { mainNavItems, accountNavItems, profileItem }; return { mainNavItems, accountNavItems, profileItem };
}, [ hasAPIDocs, isMarketplaceAvailable, pathname ]); }, [ pathname ]);
} }
...@@ -2,7 +2,7 @@ import type { Route } from 'nextjs-routes'; ...@@ -2,7 +2,7 @@ import type { Route } from 'nextjs-routes';
import type { ApiData, Metadata } from './types'; import type { ApiData, Metadata } from './types';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import getNetworkTitle from 'lib/networks/getNetworkTitle';
import compileValue from './compileValue'; import compileValue from './compileValue';
...@@ -12,7 +12,7 @@ export default function generate<R extends Route>(route: R, apiData?: ApiData<R> ...@@ -12,7 +12,7 @@ export default function generate<R extends Route>(route: R, apiData?: ApiData<R>
const params = { const params = {
...route.query, ...route.query,
...apiData, ...apiData,
network_name: appConfig.network.name, network_name: config.chain.name,
network_title: getNetworkTitle(), network_title: getNetworkTitle(),
}; };
......
import appConfig from 'configs/app/config'; import config from 'configs/app';
import delay from 'lib/delay'; import delay from 'lib/delay';
export default function isGoogleAnalyticsLoaded(retries = 3): Promise<boolean> { export default function isGoogleAnalyticsLoaded(retries = 3): Promise<boolean> {
if (!retries || !appConfig.googleAnalytics.propertyId) { if (!retries || !config.features.googleAnalytics.isEnabled) {
return Promise.resolve(false); return Promise.resolve(false);
} }
return typeof window.ga?.getAll === 'function' ? Promise.resolve(true) : delay(500).then(() => isGoogleAnalyticsLoaded(retries - 1)); return typeof window.ga?.getAll === 'function' ? Promise.resolve(true) : delay(500).then(() => isGoogleAnalyticsLoaded(retries - 1));
......
import mixpanel from 'mixpanel-browser'; import mixpanel from 'mixpanel-browser';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import type { EventTypes, EventPayload } from './utils'; import type { EventTypes, EventPayload } from './utils';
...@@ -12,7 +12,7 @@ export default function logEvent<EventType extends EventTypes>( ...@@ -12,7 +12,7 @@ export default function logEvent<EventType extends EventTypes>(
optionsOrCallback?: TrackFnArgs[2], optionsOrCallback?: TrackFnArgs[2],
callback?: TrackFnArgs[3], callback?: TrackFnArgs[3],
) { ) {
if (!appConfig.mixpanel.projectToken) { if (!config.features.mixpanel.isEnabled) {
return; return;
} }
mixpanel.track(type, properties, optionsOrCallback, callback); mixpanel.track(type, properties, optionsOrCallback, callback);
......
...@@ -5,7 +5,7 @@ import { useRouter } from 'next/router'; ...@@ -5,7 +5,7 @@ import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import { deviceType } from 'react-device-detect'; import { deviceType } from 'react-device-detect';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
...@@ -19,21 +19,21 @@ export default function useMixpanelInit() { ...@@ -19,21 +19,21 @@ export default function useMixpanelInit() {
React.useEffect(() => { React.useEffect(() => {
isGoogleAnalyticsLoaded().then((isGALoaded) => { isGoogleAnalyticsLoaded().then((isGALoaded) => {
if (!appConfig.mixpanel.projectToken) { if (!config.features.mixpanel.isEnabled) {
return; return;
} }
const debugFlagCookie = cookies.get(cookies.NAMES.MIXPANEL_DEBUG); const debugFlagCookie = cookies.get(cookies.NAMES.MIXPANEL_DEBUG);
const config: Partial<Config> = { const mixpanelConfig: Partial<Config> = {
debug: Boolean(debugFlagQuery.current || debugFlagCookie), debug: Boolean(debugFlagQuery.current || debugFlagCookie),
}; };
const isAuth = Boolean(cookies.get(cookies.NAMES.API_TOKEN)); const isAuth = Boolean(cookies.get(cookies.NAMES.API_TOKEN));
mixpanel.init(appConfig.mixpanel.projectToken, config); mixpanel.init(config.features.mixpanel.projectToken, mixpanelConfig);
mixpanel.register({ mixpanel.register({
'Chain id': appConfig.network.id, 'Chain id': config.chain.id,
Environment: appConfig.isDev ? 'Dev' : 'Prod', Environment: config.app.isDev ? 'Dev' : 'Prod',
Authorized: isAuth, Authorized: isAuth,
'Viewport width': window.innerWidth, 'Viewport width': window.innerWidth,
'Viewport height': window.innerHeight, 'Viewport height': window.innerHeight,
......
...@@ -2,7 +2,7 @@ import { usePathname } from 'next/navigation'; ...@@ -2,7 +2,7 @@ import { usePathname } from 'next/navigation';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import getPageType from './getPageType'; import getPageType from './getPageType';
...@@ -18,7 +18,7 @@ export default function useLogPageView(isInited: boolean) { ...@@ -18,7 +18,7 @@ export default function useLogPageView(isInited: boolean) {
const page = getQueryParamString(router.query.page); const page = getQueryParamString(router.query.page);
React.useEffect(() => { React.useEffect(() => {
if (!appConfig.mixpanel.projectToken || !isInited) { if (!config.features.mixpanel.isEnabled || !isInited) {
return; return;
} }
......
import appConfig from 'configs/app/config'; import config from 'configs/app';
// TODO delete when page descriptions is refactored // TODO delete when page descriptions is refactored
export default function getNetworkTitle() { 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() { 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'; ...@@ -3,7 +3,7 @@ import _mapValues from 'lodash/mapValues';
import type { NetworkExplorer } from 'types/networks'; import type { NetworkExplorer } from 'types/networks';
import appConfig from 'configs/app/config'; import config from 'configs/app';
// for easy .env update // for easy .env update
// const NETWORK_EXPLORERS = JSON.stringify([ // const NETWORK_EXPLORERS = JSON.stringify([
...@@ -29,7 +29,7 @@ const stripTrailingSlash = (str: string) => str[str.length - 1] === '/' ? str.sl ...@@ -29,7 +29,7 @@ const stripTrailingSlash = (str: string) => str[str.length - 1] === '/' ? str.sl
const addLeadingSlash = (str: string) => str[0] === '/' ? str : '/' + str; const addLeadingSlash = (str: string) => str[0] === '/' ? str : '/' + str;
const networkExplorers: Array<NetworkExplorer> = (() => { const networkExplorers: Array<NetworkExplorer> = (() => {
return appConfig.network.explorers.map((explorer) => ({ return config.UI.explorers.items.map((explorer) => ({
...explorer, ...explorer,
baseUrl: stripTrailingSlash(explorer.baseUrl), baseUrl: stripTrailingSlash(explorer.baseUrl),
paths: _mapValues(explorer.paths, _compose(stripTrailingSlash, addLeadingSlash)), paths: _mapValues(explorer.paths, _compose(stripTrailingSlash, addLeadingSlash)),
......
import type { GetServerSideProps } from 'next'; import type { GetServerSideProps } from 'next';
import appConfig from 'configs/app/config'; import config from 'configs/app';
export type Props = { export type Props = {
cookies: string; cookies: string;
...@@ -25,7 +25,7 @@ export const base: GetServerSideProps<Props> = async({ req, query }) => { ...@@ -25,7 +25,7 @@ export const base: GetServerSideProps<Props> = async({ req, query }) => {
}; };
export const account: GetServerSideProps<Props> = async(context) => { export const account: GetServerSideProps<Props> = async(context) => {
if (!appConfig.account.isEnabled) { if (!config.features.account.isEnabled) {
return { return {
notFound: true, notFound: true,
}; };
...@@ -35,7 +35,7 @@ export const account: GetServerSideProps<Props> = async(context) => { ...@@ -35,7 +35,7 @@ export const account: GetServerSideProps<Props> = async(context) => {
}; };
export const verifiedAddresses: GetServerSideProps<Props> = async(context) => { export const verifiedAddresses: GetServerSideProps<Props> = async(context) => {
if (!appConfig.adminServiceApi.endpoint || !appConfig.contractInfoApi.endpoint) { if (!config.features.addressVerification.isEnabled) {
return { return {
notFound: true, notFound: true,
}; };
...@@ -45,7 +45,7 @@ export const verifiedAddresses: GetServerSideProps<Props> = async(context) => { ...@@ -45,7 +45,7 @@ export const verifiedAddresses: GetServerSideProps<Props> = async(context) => {
}; };
export const beaconChain: GetServerSideProps<Props> = async(context) => { export const beaconChain: GetServerSideProps<Props> = async(context) => {
if (!appConfig.beaconChain.hasBeaconChain) { if (!config.features.beaconChain.isEnabled) {
return { return {
notFound: true, notFound: true,
}; };
...@@ -55,7 +55,7 @@ export const beaconChain: GetServerSideProps<Props> = async(context) => { ...@@ -55,7 +55,7 @@ export const beaconChain: GetServerSideProps<Props> = async(context) => {
}; };
export const L2: GetServerSideProps<Props> = async(context) => { export const L2: GetServerSideProps<Props> = async(context) => {
if (!appConfig.L2.isL2Network) { if (!config.features.rollup.isEnabled) {
return { return {
notFound: true, notFound: true,
}; };
...@@ -65,7 +65,7 @@ export const L2: GetServerSideProps<Props> = async(context) => { ...@@ -65,7 +65,7 @@ export const L2: GetServerSideProps<Props> = async(context) => {
}; };
export const marketplace: GetServerSideProps<Props> = async(context) => { export const marketplace: GetServerSideProps<Props> = async(context) => {
if (!appConfig.marketplace.configUrl || !appConfig.network.rpcUrl) { if (!config.features.marketplace.isEnabled) {
return { return {
notFound: true, notFound: true,
}; };
...@@ -75,7 +75,7 @@ export const marketplace: GetServerSideProps<Props> = async(context) => { ...@@ -75,7 +75,7 @@ export const marketplace: GetServerSideProps<Props> = async(context) => {
}; };
export const apiDocs: GetServerSideProps<Props> = async(context) => { export const apiDocs: GetServerSideProps<Props> = async(context) => {
if (!appConfig.apiDoc.specUrl) { if (!config.features.restApiDocs.isEnabled) {
return { return {
notFound: true, notFound: true,
}; };
...@@ -85,7 +85,7 @@ export const apiDocs: GetServerSideProps<Props> = async(context) => { ...@@ -85,7 +85,7 @@ export const apiDocs: GetServerSideProps<Props> = async(context) => {
}; };
export const csvExport: GetServerSideProps<Props> = async(context) => { export const csvExport: GetServerSideProps<Props> = async(context) => {
if (!appConfig.reCaptcha.siteKey) { if (!config.features.csvExport.isEnabled) {
return { return {
notFound: true, notFound: true,
}; };
...@@ -95,7 +95,7 @@ export const csvExport: GetServerSideProps<Props> = async(context) => { ...@@ -95,7 +95,7 @@ export const csvExport: GetServerSideProps<Props> = async(context) => {
}; };
export const stats: GetServerSideProps<Props> = async(context) => { export const stats: GetServerSideProps<Props> = async(context) => {
if (!appConfig.statsApi.endpoint) { if (!config.features.stats.isEnabled) {
return { return {
notFound: true, notFound: true,
}; };
......
...@@ -2,13 +2,13 @@ import type { NextRequest } from 'next/server'; ...@@ -2,13 +2,13 @@ import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server'; import { NextResponse } from 'next/server';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import { httpLogger } from 'lib/api/logger'; import { httpLogger } from 'lib/api/logger';
import { DAY } from 'lib/consts'; import { DAY } from 'lib/consts';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
export function account(req: NextRequest) { export function account(req: NextRequest) {
if (!appConfig.account.isEnabled) { if (!config.features.account.isEnabled) {
return; return;
} }
...@@ -24,7 +24,7 @@ export function account(req: NextRequest) { ...@@ -24,7 +24,7 @@ export function account(req: NextRequest) {
const isProfileRoute = req.nextUrl.pathname.includes('/auth/profile'); const isProfileRoute = req.nextUrl.pathname.includes('/auth/profile');
if ((isAccountRoute || isProfileRoute)) { 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); return NextResponse.redirect(authUrl);
} }
} }
...@@ -35,7 +35,7 @@ export function account(req: NextRequest) { ...@@ -35,7 +35,7 @@ export function account(req: NextRequest) {
if (apiTokenCookie) { if (apiTokenCookie) {
// temporary solution // temporary solution
// TODO check app for integrity https://github.com/blockscout/frontend/issues/1028 and make typescript happy here // 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({ httpLogger.logger.error({
message: 'Logout URL is not configured', message: 'Logout URL is not configured',
}); });
...@@ -46,7 +46,7 @@ export function account(req: NextRequest) { ...@@ -46,7 +46,7 @@ export function account(req: NextRequest) {
// logout URL is always external URL in auth0.com sub-domain // logout URL is always external URL in auth0.com sub-domain
// at least we hope so // 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 res.cookies.delete(cookies.NAMES.CONFIRM_EMAIL_PAGE_VIEWED); // reset cookie to show email verification page again
return res; return res;
...@@ -55,7 +55,7 @@ export function account(req: NextRequest) { ...@@ -55,7 +55,7 @@ export function account(req: NextRequest) {
// if user hasn't seen email verification page, make redirect to it // if user hasn't seen email verification page, make redirect to it
if (!req.cookies.get(cookies.NAMES.CONFIRM_EMAIL_PAGE_VIEWED)) { if (!req.cookies.get(cookies.NAMES.CONFIRM_EMAIL_PAGE_VIEWED)) {
if (!req.nextUrl.pathname.includes('/auth/unverified-email')) { 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); const res = NextResponse.redirect(url);
res.cookies.set({ res.cookies.set({
name: cookies.NAMES.CONFIRM_EMAIL_PAGE_VIEWED, name: cookies.NAMES.CONFIRM_EMAIL_PAGE_VIEWED,
......
...@@ -3,13 +3,13 @@ import type { WindowProvider } from 'wagmi'; ...@@ -3,13 +3,13 @@ import type { WindowProvider } from 'wagmi';
import 'wagmi/window'; import 'wagmi/window';
import appConfig from 'configs/app/config'; import config from 'configs/app';
export default function useProvider() { export default function useProvider() {
const [ provider, setProvider ] = React.useState<WindowProvider>(); const [ provider, setProvider ] = React.useState<WindowProvider>();
React.useEffect(() => { React.useEffect(() => {
if (!('ethereum' in window && window.ethereum)) { if (!('ethereum' in window && window.ethereum) || !config.features.web3Wallet.isEnabled) {
return; return;
} }
...@@ -18,11 +18,11 @@ export default function useProvider() { ...@@ -18,11 +18,11 @@ export default function useProvider() {
const providers = Array.isArray(window.ethereum.providers) ? window.ethereum.providers : [ window.ethereum ]; const providers = Array.isArray(window.ethereum.providers) ? window.ethereum.providers : [ window.ethereum ];
providers.forEach(async(provider) => { providers.forEach(async(provider) => {
if (appConfig.web3.defaultWallet === 'coinbase' && provider.isCoinbaseWallet) { if (config.features.web3Wallet.defaultWallet === 'coinbase' && provider.isCoinbaseWallet) {
return setProvider(provider); return setProvider(provider);
} }
if (appConfig.web3.defaultWallet === 'metamask' && provider.isMetaMask) { if (config.features.web3Wallet.defaultWallet === 'metamask' && provider.isMetaMask) {
return setProvider(provider); return setProvider(provider);
} }
}); });
......
...@@ -3,7 +3,7 @@ import type { WalletType, WalletInfo } from 'types/client/wallets'; ...@@ -3,7 +3,7 @@ import type { WalletType, WalletInfo } from 'types/client/wallets';
import coinbaseIcon from 'icons/wallets/coinbase.svg'; import coinbaseIcon from 'icons/wallets/coinbase.svg';
import metamaskIcon from 'icons/wallets/metamask.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: { metamask: {
name: 'MetaMask', name: 'MetaMask',
icon: metamaskIcon, icon: metamaskIcon,
......
...@@ -5,7 +5,7 @@ import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; ...@@ -5,7 +5,7 @@ import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import type { AppProps } from 'next/app'; import type { AppProps } from 'next/app';
import React, { useState } from 'react'; import React, { useState } from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import { AppContextProvider } from 'lib/contexts/app'; import { AppContextProvider } from 'lib/contexts/app';
import { ChakraProvider } from 'lib/contexts/chakra'; import { ChakraProvider } from 'lib/contexts/chakra';
import { ScrollDirectionProvider } from 'lib/contexts/scrollDirection'; import { ScrollDirectionProvider } from 'lib/contexts/scrollDirection';
...@@ -85,7 +85,7 @@ function MyApp({ Component, pageProps }: AppProps) { ...@@ -85,7 +85,7 @@ function MyApp({ Component, pageProps }: AppProps) {
<AppContextProvider pageProps={ pageProps }> <AppContextProvider pageProps={ pageProps }>
<QueryClientProvider client={ queryClient }> <QueryClientProvider client={ queryClient }>
<ScrollDirectionProvider> <ScrollDirectionProvider>
<SocketProvider url={ `${ appConfig.api.socket }${ appConfig.api.basePath }/socket/v2` }> <SocketProvider url={ `${ config.api.socket }${ config.api.basePath }/socket/v2` }>
<Component { ...pageProps }/> <Component { ...pageProps }/>
</SocketProvider> </SocketProvider>
</ScrollDirectionProvider> </ScrollDirectionProvider>
......
...@@ -3,7 +3,7 @@ import type { DocumentContext } from 'next/document'; ...@@ -3,7 +3,7 @@ import type { DocumentContext } from 'next/document';
import Document, { Html, Head, Main, NextScript } from 'next/document'; import Document, { Html, Head, Main, NextScript } from 'next/document';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import * as serverTiming from 'lib/next/serverTiming'; import * as serverTiming from 'lib/next/serverTiming';
import theme from 'theme'; import theme from 'theme';
...@@ -47,11 +47,11 @@ class MyDocument extends Document { ...@@ -47,11 +47,11 @@ class MyDocument extends Document {
// eslint-disable-next-line max-len // 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." 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:site_name" content="Blockscout"/>
<meta property="og:type" content="website"/> <meta property="og:type" content="website"/>
<meta name="twitter:card" content="summary_large_image"/> <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> </Head>
<body> <body>
<ColorModeScript initialColorMode={ theme.config.initialColorMode }/> <ColorModeScript initialColorMode={ theme.config.initialColorMode }/>
......
...@@ -2,7 +2,7 @@ import _pick from 'lodash/pick'; ...@@ -2,7 +2,7 @@ import _pick from 'lodash/pick';
import _pickBy from 'lodash/pickBy'; import _pickBy from 'lodash/pickBy';
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import fetchFactory from 'lib/api/nodeFetch'; import fetchFactory from 'lib/api/nodeFetch';
const handler = async(nextReq: NextApiRequest, nextRes: NextApiResponse) => { const handler = async(nextReq: NextApiRequest, nextRes: NextApiResponse) => {
...@@ -13,7 +13,7 @@ const handler = async(nextReq: NextApiRequest, nextRes: NextApiResponse) => { ...@@ -13,7 +13,7 @@ const handler = async(nextReq: NextApiRequest, nextRes: NextApiResponse) => {
const url = new URL( const url = new URL(
nextReq.url.replace(/^\/node-api\/proxy/, ''), 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)( const apiRes = await fetchFactory(nextReq)(
url.toString(), url.toString(),
......
export type WalletType = 'metamask' | 'coinbase'; export type WalletType = 'metamask' | 'coinbase' | 'none';
export interface WalletInfo { export interface WalletInfo {
name: string; name: string;
......
...@@ -7,7 +7,6 @@ export type NextPublicEnvs = { ...@@ -7,7 +7,6 @@ export type NextPublicEnvs = {
NEXT_PUBLIC_NETWORK_CURRENCY_NAME?: string; NEXT_PUBLIC_NETWORK_CURRENCY_NAME?: string;
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL?: string; NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL?: string;
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS?: string; NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS?: string;
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS?: string;
NEXT_PUBLIC_NETWORK_ASSETS_PATHNAME?: string; NEXT_PUBLIC_NETWORK_ASSETS_PATHNAME?: string;
NEXT_PUBLIC_NETWORK_LOGO?: string; NEXT_PUBLIC_NETWORK_LOGO?: string;
NEXT_PUBLIC_NETWORK_LOGO_DARK?: string; NEXT_PUBLIC_NETWORK_LOGO_DARK?: string;
...@@ -34,18 +33,13 @@ export type NextPublicEnvs = { ...@@ -34,18 +33,13 @@ export type NextPublicEnvs = {
NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER?: 'true' | 'false'; NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER?: 'true' | 'false';
NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME?: 'true' | 'false'; NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME?: 'true' | 'false';
// Ads config // Text 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';
NEXT_PUBLIC_AD_TEXT_PROVIDER?: 'coinzilla' | 'none'; NEXT_PUBLIC_AD_TEXT_PROVIDER?: 'coinzilla' | 'none';
// App config // App config
NEXT_PUBLIC_APP_INSTANCE?: string;
NEXT_PUBLIC_APP_PROTOCOL?: 'http' | 'https'; NEXT_PUBLIC_APP_PROTOCOL?: 'http' | 'https';
NEXT_PUBLIC_APP_HOST: string; NEXT_PUBLIC_APP_HOST: string;
NEXT_PUBLIC_APP_PORT?: string; NEXT_PUBLIC_APP_PORT?: string;
NEXT_PUBLIC_APP_ENV?: string;
// API config // API config
NEXT_PUBLIC_API_PROTOCOL?: 'http' | 'https'; NEXT_PUBLIC_API_PROTOCOL?: 'http' | 'https';
...@@ -56,11 +50,8 @@ export type NextPublicEnvs = { ...@@ -56,11 +50,8 @@ export type NextPublicEnvs = {
NEXT_PUBLIC_STATS_API_HOST?: string; NEXT_PUBLIC_STATS_API_HOST?: string;
NEXT_PUBLIC_VISUALIZE_API_HOST?: string; NEXT_PUBLIC_VISUALIZE_API_HOST?: string;
NEXT_PUBLIC_CONTRACT_INFO_API_HOST?: string; NEXT_PUBLIC_CONTRACT_INFO_API_HOST?: string;
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST?: string;
// external services config // external services config
NEXT_PUBLIC_SENTRY_DSN?: string;
SENTRY_CSP_REPORT_URI?: string;
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID?: string; NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID?: string;
NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY?: string; NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY?: string;
NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID?: string; NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID?: string;
...@@ -73,7 +64,9 @@ export type NextPublicEnvs = { ...@@ -73,7 +64,9 @@ export type NextPublicEnvs = {
& NextPublicEnvsAccount & NextPublicEnvsAccount
& NextPublicEnvsMarketplace & NextPublicEnvsMarketplace
& NextPublicEnvsRollup & NextPublicEnvsRollup
& NextPublicEnvsBeacon; & NextPublicEnvsBeacon
& NextPublicEnvsAdsBanner
& NextPublicEnvsSentry;
type NextPublicEnvsAccount = type NextPublicEnvsAccount =
{ {
...@@ -87,6 +80,7 @@ type NextPublicEnvsAccount = ...@@ -87,6 +80,7 @@ type NextPublicEnvsAccount =
NEXT_PUBLIC_AUTH_URL?: string; NEXT_PUBLIC_AUTH_URL?: string;
NEXT_PUBLIC_LOGOUT_URL: string; NEXT_PUBLIC_LOGOUT_URL: string;
NEXT_PUBLIC_AUTH0_CLIENT_ID: string; NEXT_PUBLIC_AUTH0_CLIENT_ID: string;
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST?: string;
} }
type NextPublicEnvsMarketplace = type NextPublicEnvsMarketplace =
...@@ -120,3 +114,30 @@ type NextPublicEnvsBeacon = ...@@ -120,3 +114,30 @@ type NextPublicEnvsBeacon =
NEXT_PUBLIC_HAS_BEACON_CHAIN?: undefined; NEXT_PUBLIC_HAS_BEACON_CHAIN?: undefined;
NEXT_PUBLIC_BEACON_CHAIN_CURRENCY_SYMBOL?: 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'; ...@@ -6,7 +6,7 @@ import React from 'react';
import type { SocketMessage } from 'lib/socket/types'; import type { SocketMessage } from 'lib/socket/types';
import type { AddressBlocksValidatedResponse } from 'types/api/address'; 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 { getResourceKey } from 'lib/api/useApiQuery';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
...@@ -94,7 +94,7 @@ const AddressBlocksValidated = ({ scrollRef }: Props) => { ...@@ -94,7 +94,7 @@ const AddressBlocksValidated = ({ scrollRef }: Props) => {
<Th width="17%">Age</Th> <Th width="17%">Age</Th>
<Th width="16%">Txn</Th> <Th width="16%">Txn</Th>
<Th width="25%">Gas used</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> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { CsvExportParams } from 'types/client/address'; 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 svgFileIcon from 'icons/files/csv.svg';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
...@@ -21,7 +21,7 @@ const AddressCsvExportLink = ({ className, address, params, isLoading }: Props) ...@@ -21,7 +21,7 @@ const AddressCsvExportLink = ({ className, address, params, isLoading }: Props)
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const isInitialLoading = useIsInitialLoading(isLoading); const isInitialLoading = useIsInitialLoading(isLoading);
if (!appConfig.reCaptcha.siteKey) { if (!config.features.csvExport.isEnabled) {
return null; return null;
} }
......
...@@ -5,7 +5,7 @@ import React from 'react'; ...@@ -5,7 +5,7 @@ import React from 'react';
import type { Block } from 'types/api/block'; 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 getBlockTotalReward from 'lib/block/getBlockTotalReward';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
...@@ -48,7 +48,7 @@ const AddressBlocksValidatedListItem = (props: Props) => { ...@@ -48,7 +48,7 @@ const AddressBlocksValidatedListItem = (props: Props) => {
/> />
</Flex> </Flex>
<Flex columnGap={ 2 } w="100%"> <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> <Skeleton isLoaded={ !props.isLoading } color="text_secondary">{ totalReward.toFixed() }</Skeleton>
</Flex> </Flex>
</ListItemMobile> </ListItemMobile>
......
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import ChartWidget from 'ui/shared/chart/ChartWidget'; import ChartWidget from 'ui/shared/chart/ChartWidget';
...@@ -16,7 +16,7 @@ const AddressCoinBalanceChart = ({ addressHash }: Props) => { ...@@ -16,7 +16,7 @@ const AddressCoinBalanceChart = ({ addressHash }: Props) => {
const items = React.useMemo(() => data?.map(({ date, value }) => ({ const items = React.useMemo(() => data?.map(({ date, value }) => ({
date: new Date(date), 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 ]); })), [ data ]);
return ( return (
...@@ -26,7 +26,7 @@ const AddressCoinBalanceChart = ({ addressHash }: Props) => { ...@@ -26,7 +26,7 @@ const AddressCoinBalanceChart = ({ addressHash }: Props) => {
items={ items } items={ items }
isLoading={ isLoading } isLoading={ isLoading }
h="300px" h="300px"
units={ appConfig.network.currency.symbol } units={ config.chain.currency.symbol }
/> />
); );
}; };
......
...@@ -5,7 +5,7 @@ import React from 'react'; ...@@ -5,7 +5,7 @@ import React from 'react';
import type { AddressCoinBalanceHistoryResponse } from 'types/api/address'; import type { AddressCoinBalanceHistoryResponse } from 'types/api/address';
import type { PaginationParams } from 'ui/shared/pagination/types'; 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 ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
...@@ -31,7 +31,7 @@ const AddressCoinBalanceHistory = ({ query }: Props) => { ...@@ -31,7 +31,7 @@ const AddressCoinBalanceHistory = ({ query }: Props) => {
<Th width="20%">Block</Th> <Th width="20%">Block</Th>
<Th width="20%">Txn</Th> <Th width="20%">Txn</Th>
<Th width="20%">Age</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> <Th width="20%" isNumeric>Delta</Th>
</Tr> </Tr>
</Thead> </Thead>
......
...@@ -5,7 +5,7 @@ import React from 'react'; ...@@ -5,7 +5,7 @@ import React from 'react';
import type { AddressCoinBalanceHistoryItem } from 'types/api/address'; 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 { WEI, ZERO } from 'lib/consts';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
...@@ -28,7 +28,7 @@ const AddressCoinBalanceListItem = (props: Props) => { ...@@ -28,7 +28,7 @@ const AddressCoinBalanceListItem = (props: Props) => {
<ListItemMobile rowGap={ 2 } isAnimated> <ListItemMobile rowGap={ 2 } isAnimated>
<Flex justifyContent="space-between" w="100%"> <Flex justifyContent="space-between" w="100%">
<Skeleton isLoaded={ !props.isLoading } fontWeight={ 600 }> <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>
<Skeleton isLoaded={ !props.isLoading }> <Skeleton isLoaded={ !props.isLoading }>
<Stat flexGrow="0"> <Stat flexGrow="0">
......
...@@ -5,7 +5,7 @@ import React from 'react'; ...@@ -5,7 +5,7 @@ import React from 'react';
import type { SmartContractMethodOutput } from 'types/api/contract'; import type { SmartContractMethodOutput } from 'types/api/contract';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import { WEI } from 'lib/consts'; import { WEI } from 'lib/consts';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -39,7 +39,7 @@ const ContractMethodStatic = ({ data }: Props) => { ...@@ -39,7 +39,7 @@ const ContractMethodStatic = ({ data }: Props) => {
if (event.target.checked) { if (event.target.checked) {
setValue(BigNumber(initialValue).div(WEI).toFixed()); setValue(BigNumber(initialValue).div(WEI).toFixed());
setLabel(appConfig.network.currency.symbol || 'ETH'); setLabel(config.chain.currency.symbol || 'ETH');
} else { } else {
setValue(BigNumber(initialValue).toFixed()); setValue(BigNumber(initialValue).toFixed());
setLabel('WEI'); setLabel('WEI');
......
...@@ -5,7 +5,7 @@ import { Element } from 'react-scroll'; ...@@ -5,7 +5,7 @@ import { Element } from 'react-scroll';
import type { SmartContractMethod } from 'types/api/contract'; 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 iconLink from 'icons/link.svg';
import Hint from 'ui/shared/Hint'; import Hint from 'ui/shared/Hint';
......
...@@ -3,7 +3,7 @@ import { useAccount, useWalletClient, useNetwork, useSwitchNetwork } from 'wagmi ...@@ -3,7 +3,7 @@ import { useAccount, useWalletClient, useNetwork, useSwitchNetwork } from 'wagmi
import type { SmartContractWriteMethod } from 'types/api/contract'; 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 useApiQuery from 'lib/api/useApiQuery';
import ContractMethodsAccordion from 'ui/address/contract/ContractMethodsAccordion'; import ContractMethodsAccordion from 'ui/address/contract/ContractMethodsAccordion';
import ContentLoader from 'ui/shared/ContentLoader'; import ContentLoader from 'ui/shared/ContentLoader';
...@@ -46,8 +46,8 @@ const ContractWrite = ({ addressHash, isProxy, isCustomAbi }: Props) => { ...@@ -46,8 +46,8 @@ const ContractWrite = ({ addressHash, isProxy, isCustomAbi }: Props) => {
throw new Error('Wallet is not connected'); throw new Error('Wallet is not connected');
} }
if (chain?.id && String(chain.id) !== config.network.id) { if (chain?.id && String(chain.id) !== config.chain.id) {
await switchNetworkAsync?.(Number(config.network.id)); await switchNetworkAsync?.(Number(config.chain.id));
} }
if (!contractAbi) { if (!contractAbi) {
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { SocketMessage } from 'lib/socket/types'; import type { SocketMessage } from 'lib/socket/types';
import type { Address } from 'types/api/address'; 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 { getResourceKey } from 'lib/api/useApiQuery';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
...@@ -65,15 +65,14 @@ const AddressBalance = ({ data, isLoading }: Props) => { ...@@ -65,15 +65,14 @@ const AddressBalance = ({ data, isLoading }: Props) => {
}); });
const tokenData = React.useMemo(() => ({ const tokenData = React.useMemo(() => ({
address: appConfig.network.currency.address || '', name: config.chain.currency.name || '',
name: appConfig.network.currency.name || '',
icon_url: '', icon_url: '',
}), [ ]); }), [ ]);
return ( return (
<DetailsInfoItem <DetailsInfoItem
title="Balance" 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" flexWrap="nowrap"
alignItems="flex-start" alignItems="flex-start"
isLoading={ isLoading } isLoading={ isLoading }
...@@ -88,8 +87,8 @@ const AddressBalance = ({ data, isLoading }: Props) => { ...@@ -88,8 +87,8 @@ const AddressBalance = ({ data, isLoading }: Props) => {
<CurrencyValue <CurrencyValue
value={ data.coin_balance || '0' } value={ data.coin_balance || '0' }
exchangeRate={ data.exchange_rate } exchangeRate={ data.exchange_rate }
decimals={ String(appConfig.network.currency.decimals) } decimals={ String(config.chain.currency.decimals) }
currency={ appConfig.network.currency.symbol } currency={ config.chain.currency.symbol }
accuracyUsd={ 2 } accuracyUsd={ 2 }
accuracy={ 8 } accuracy={ 8 }
flexWrap="wrap" flexWrap="wrap"
......
...@@ -5,7 +5,7 @@ import React from 'react'; ...@@ -5,7 +5,7 @@ import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction'; 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 eastArrowIcon from 'icons/arrows/east.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
...@@ -79,9 +79,9 @@ const TxInternalsListItem = ({ ...@@ -79,9 +79,9 @@ const TxInternalsListItem = ({
) } ) }
</Box> </Box>
<HStack spacing={ 3 }> <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 }> <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> </Skeleton>
</HStack> </HStack>
</ListItemMobile> </ListItemMobile>
......
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction'; 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 { default as Thead } from 'ui/shared/TheadSticky';
import AddressIntTxsTableItem from './AddressIntTxsTableItem'; import AddressIntTxsTableItem from './AddressIntTxsTableItem';
...@@ -26,7 +26,7 @@ const AddressIntTxsTable = ({ data, currentAddress, isLoading }: Props) => { ...@@ -26,7 +26,7 @@ const AddressIntTxsTable = ({ data, currentAddress, isLoading }: Props) => {
<Th width="48px" px={ 0 }/> <Th width="48px" px={ 0 }/>
<Th width="20%">To</Th> <Th width="20%">To</Th>
<Th width="20%" isNumeric> <Th width="20%" isNumeric>
Value { appConfig.network.currency.symbol } Value { config.chain.currency.symbol }
</Th> </Th>
</Tr> </Tr>
</Thead> </Thead>
......
...@@ -5,7 +5,7 @@ import React from 'react'; ...@@ -5,7 +5,7 @@ import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction'; 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 rightArrowIcon from 'icons/arrows/east.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
...@@ -105,7 +105,7 @@ const AddressIntTxsTableItem = ({ ...@@ -105,7 +105,7 @@ const AddressIntTxsTableItem = ({
</Td> </Td>
<Td isNumeric verticalAlign="middle"> <Td isNumeric verticalAlign="middle">
<Skeleton isLoaded={ !isLoading } display="inline-block" minW={ 6 }> <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> </Skeleton>
</Td> </Td>
</Tr> </Tr>
......
...@@ -2,7 +2,7 @@ import { Flex } from '@chakra-ui/react'; ...@@ -2,7 +2,7 @@ import { Flex } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { ZERO } from 'lib/consts'; import { ZERO } from 'lib/consts';
import getCurrencyValue from 'lib/getCurrencyValue'; import getCurrencyValue from 'lib/getCurrencyValue';
...@@ -34,7 +34,7 @@ const TokenBalances = () => { ...@@ -34,7 +34,7 @@ const TokenBalances = () => {
accuracy: 8, accuracy: 8,
accuracyUsd: 2, accuracyUsd: 2,
exchangeRate: addressData?.exchange_rate, exchangeRate: addressData?.exchange_rate,
decimals: String(appConfig.network.currency.decimals), decimals: String(config.chain.currency.decimals),
}); });
const tokensInfo = getTokensTotalInfo(tokenQuery.data); const tokensInfo = getTokensTotalInfo(tokenQuery.data);
...@@ -52,8 +52,8 @@ const TokenBalances = () => { ...@@ -52,8 +52,8 @@ const TokenBalances = () => {
isLoading={ addressQuery.isLoading || tokenQuery.isLoading } isLoading={ addressQuery.isLoading || tokenQuery.isLoading }
/> />
<TokenBalancesItem <TokenBalancesItem
name={ `${ appConfig.network.currency.symbol } Balance` } name={ `${ config.chain.currency.symbol } Balance` }
value={ (!nativeUsd.eq(ZERO) ? `$${ nativeUsd.toFormat(2) } USD | ` : '') + `${ nativeValue } ${ appConfig.network.currency.symbol }` } value={ (!nativeUsd.eq(ZERO) ? `$${ nativeUsd.toFormat(2) } USD | ` : '') + `${ nativeValue } ${ config.chain.currency.symbol }` }
isLoading={ addressQuery.isLoading || tokenQuery.isLoading } isLoading={ addressQuery.isLoading || tokenQuery.isLoading }
/> />
<TokenBalancesItem <TokenBalancesItem
......
...@@ -12,7 +12,7 @@ import type { ...@@ -12,7 +12,7 @@ import type {
RootFields, RootFields,
} from '../types'; } from '../types';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
...@@ -49,7 +49,7 @@ const AddressVerificationStepAddress = ({ defaultAddress, onContinue }: Props) = ...@@ -49,7 +49,7 @@ const AddressVerificationStepAddress = ({ defaultAddress, onContinue }: Props) =
}; };
const response = await apiFetch<'address_verification', AddressCheckResponseSuccess, AddressVerificationResponseError>('address_verification', { const response = await apiFetch<'address_verification', AddressCheckResponseSuccess, AddressVerificationResponseError>('address_verification', {
fetchParams: { method: 'POST', body }, fetchParams: { method: 'POST', body },
pathParams: { chainId: appConfig.network.id, type: ':prepare' }, pathParams: { chainId: config.chain.id, type: ':prepare' },
}); });
if (response.status !== 'SUCCESS') { if (response.status !== 'SUCCESS') {
......
...@@ -15,7 +15,7 @@ import type { ...@@ -15,7 +15,7 @@ import type {
} from '../types'; } from '../types';
import type { VerifiedAddress } from 'types/api/account'; 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 useApiFetch from 'lib/api/useApiFetch';
import shortenString from 'lib/shortenString'; import shortenString from 'lib/shortenString';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
...@@ -62,7 +62,7 @@ const AddressVerificationStepSignature = ({ address, signingMessage, contractCre ...@@ -62,7 +62,7 @@ const AddressVerificationStepSignature = ({ address, signingMessage, contractCre
const response = await apiFetch<'address_verification', AddressValidationResponseSuccess, AddressVerificationResponseError>('address_verification', { const response = await apiFetch<'address_verification', AddressValidationResponseSuccess, AddressVerificationResponseError>('address_verification', {
fetchParams: { method: 'POST', body }, fetchParams: { method: 'POST', body },
pathParams: { chainId: appConfig.network.id, type: ':verify' }, pathParams: { chainId: config.chain.id, type: ':verify' },
}); });
if (response.status !== 'SUCCESS') { if (response.status !== 'SUCCESS') {
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { AddressesItem } from 'types/api/addresses'; import type { AddressesItem } from 'types/api/addresses';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -26,7 +26,7 @@ const AddressesListItem = ({ ...@@ -26,7 +26,7 @@ const AddressesListItem = ({
isLoading, isLoading,
}: Props) => { }: Props) => {
const addressBalance = BigNumber(item.coin_balance).div(BigNumber(10 ** appConfig.network.currency.decimals)); const addressBalance = BigNumber(item.coin_balance).div(BigNumber(10 ** config.chain.currency.decimals));
return ( return (
<ListItemMobile rowGap={ 3 }> <ListItemMobile rowGap={ 3 }>
...@@ -52,7 +52,7 @@ const AddressesListItem = ({ ...@@ -52,7 +52,7 @@ const AddressesListItem = ({
<Tag key={ tag.label } isLoading={ isLoading }>{ tag.display_name }</Tag> <Tag key={ tag.label } isLoading={ isLoading }>{ tag.display_name }</Tag>
)) } )) }
<HStack spacing={ 3 }> <HStack spacing={ 3 }>
<Skeleton isLoaded={ !isLoading } fontSize="sm" fontWeight={ 500 }>{ `Balance ${ appConfig.network.currency.symbol }` }</Skeleton> <Skeleton isLoaded={ !isLoading } fontSize="sm" fontWeight={ 500 }>{ `Balance ${ config.chain.currency.symbol }` }</Skeleton>
<Skeleton isLoaded={ !isLoading } fontSize="sm" color="text_secondary"> <Skeleton isLoaded={ !isLoading } fontSize="sm" color="text_secondary">
<span>{ addressBalance.dp(8).toFormat() }</span> <span>{ addressBalance.dp(8).toFormat() }</span>
</Skeleton> </Skeleton>
......
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import type { AddressesItem } from 'types/api/addresses'; import type { AddressesItem } from 'types/api/addresses';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import { default as Thead } from 'ui/shared/TheadSticky'; import { default as Thead } from 'ui/shared/TheadSticky';
import AddressesTableItem from './AddressesTableItem'; import AddressesTableItem from './AddressesTableItem';
...@@ -25,7 +25,7 @@ const AddressesTable = ({ items, totalSupply, pageStartIndex, top, isLoading }: ...@@ -25,7 +25,7 @@ const AddressesTable = ({ items, totalSupply, pageStartIndex, top, isLoading }:
<Th width="64px">Rank</Th> <Th width="64px">Rank</Th>
<Th width={ hasPercentage ? '30%' : '40%' }>Address</Th> <Th width={ hasPercentage ? '30%' : '40%' }>Address</Th>
<Th width="20%" pl={ 10 }>Public tag</Th> <Th width="20%" pl={ 10 }>Public tag</Th>
<Th width={ hasPercentage ? '20%' : '25%' } isNumeric>{ `Balance ${ appConfig.network.currency.symbol }` }</Th> <Th width={ hasPercentage ? '20%' : '25%' } isNumeric>{ `Balance ${ config.chain.currency.symbol }` }</Th>
{ hasPercentage && <Th width="15%" isNumeric>Percentage</Th> } { hasPercentage && <Th width="15%" isNumeric>Percentage</Th> }
<Th width="15%" isNumeric>Txn count</Th> <Th width="15%" isNumeric>Txn count</Th>
</Tr> </Tr>
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { AddressesItem } from 'types/api/addresses'; import type { AddressesItem } from 'types/api/addresses';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -27,7 +27,7 @@ const AddressesTableItem = ({ ...@@ -27,7 +27,7 @@ const AddressesTableItem = ({
isLoading, isLoading,
}: Props) => { }: Props) => {
const addressBalance = BigNumber(item.coin_balance).div(BigNumber(10 ** appConfig.network.currency.decimals)); const addressBalance = BigNumber(item.coin_balance).div(BigNumber(10 ** config.chain.currency.decimals));
const addressBalanceChunks = addressBalance.dp(8).toFormat().split('.'); const addressBalanceChunks = addressBalance.dp(8).toFormat().split('.');
return ( return (
......
...@@ -8,7 +8,7 @@ import { Box, useColorModeValue } from '@chakra-ui/react'; ...@@ -8,7 +8,7 @@ import { Box, useColorModeValue } from '@chakra-ui/react';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import ContentLoader from 'ui/shared/ContentLoader'; import ContentLoader from 'ui/shared/ContentLoader';
import 'swagger-ui-react/swagger-ui.css'; import 'swagger-ui-react/swagger-ui.css';
...@@ -57,7 +57,7 @@ const SwaggerUI = () => { ...@@ -57,7 +57,7 @@ const SwaggerUI = () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const reqInterceptor = React.useCallback((req: any) => { const reqInterceptor = React.useCallback((req: any) => {
if (!req.loadSpec) { if (!req.loadSpec) {
req.url = req.url.replace(DEFAULT_SERVER, appConfig.api.host); req.url = req.url.replace(DEFAULT_SERVER, config.api.host);
const url = new URL(req.url); const url = new URL(req.url);
url.protocol = 'https:'; url.protocol = 'https:';
req.url = url.toString(); req.url = url.toString();
...@@ -68,7 +68,7 @@ const SwaggerUI = () => { ...@@ -68,7 +68,7 @@ const SwaggerUI = () => {
return ( return (
<Box sx={ swaggerStyle }> <Box sx={ swaggerStyle }>
<SwaggerUIReact <SwaggerUIReact
url={ appConfig.apiDoc.specUrl } url={ config.features.restApiDocs.specUrl }
plugins={ [ NeverShowInfoPlugin ] } plugins={ [ NeverShowInfoPlugin ] }
requestInterceptor={ reqInterceptor } requestInterceptor={ reqInterceptor }
/> />
......
...@@ -9,7 +9,7 @@ import { scroller, Element } from 'react-scroll'; ...@@ -9,7 +9,7 @@ import { scroller, Element } from 'react-scroll';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import clockIcon from 'icons/clock.svg'; import clockIcon from 'icons/clock.svg';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.svg';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
...@@ -94,7 +94,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -94,7 +94,7 @@ const BlockDetails = ({ query }: Props) => {
const validatorTitle = getNetworkValidatorTitle(); const validatorTitle = getNetworkValidatorTitle();
const rewardBreakDown = (() => { const rewardBreakDown = (() => {
if (appConfig.L2.isL2Network || totalReward.isEqualTo(ZERO) || txFees.isEqualTo(ZERO) || burntFees.isEqualTo(ZERO)) { if (config.features.rollup.isEnabled || totalReward.isEqualTo(ZERO) || txFees.isEqualTo(ZERO) || burntFees.isEqualTo(ZERO)) {
return null; return null;
} }
...@@ -186,7 +186,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -186,7 +186,7 @@ const BlockDetails = ({ query }: Props) => {
</LinkInternal> </LinkInternal>
</Skeleton> </Skeleton>
</DetailsInfoItem> </DetailsInfoItem>
{ appConfig.beaconChain.hasBeaconChain && Boolean(data.withdrawals_count) && ( { config.features.beaconChain.isEnabled && Boolean(data.withdrawals_count) && (
<DetailsInfoItem <DetailsInfoItem
title="Withdrawals" title="Withdrawals"
hint="The number of beacon withdrawals in the block" hint="The number of beacon withdrawals in the block"
...@@ -200,7 +200,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -200,7 +200,7 @@ const BlockDetails = ({ query }: Props) => {
</DetailsInfoItem> </DetailsInfoItem>
) } ) }
<DetailsInfoItem <DetailsInfoItem
title={ appConfig.network.verificationType === 'validation' ? 'Validated by' : 'Mined by' } title={ config.chain.verificationType === 'validation' ? 'Validated by' : 'Mined by' }
hint="A block producer who successfully included the block onto the blockchain" hint="A block producer who successfully included the block onto the blockchain"
columnGap={ 1 } columnGap={ 1 }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
...@@ -210,18 +210,18 @@ const BlockDetails = ({ query }: Props) => { ...@@ -210,18 +210,18 @@ const BlockDetails = ({ query }: Props) => {
{ /* api doesn't return the block processing time yet */ } { /* api doesn't return the block processing time yet */ }
{ /* <Text>{ dayjs.duration(block.minedIn, 'second').humanize(true) }</Text> */ } { /* <Text>{ dayjs.duration(block.minedIn, 'second').humanize(true) }</Text> */ }
</DetailsInfoItem> </DetailsInfoItem>
{ !appConfig.L2.isL2Network && !totalReward.isEqualTo(ZERO) && ( { !config.features.rollup.isEnabled && !totalReward.isEqualTo(ZERO) && (
<DetailsInfoItem <DetailsInfoItem
title="Block reward" title="Block reward"
hint={ hint={
`For each block, the ${ validatorTitle } is rewarded with a finite amount of ${ appConfig.network.currency.symbol || 'native token' } `For each block, the ${ validatorTitle } is rewarded with a finite amount of ${ config.chain.currency.symbol || 'native token' }
on top of the fees paid for all transactions in the block` on top of the fees paid for all transactions in the block`
} }
columnGap={ 1 } columnGap={ 1 }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton isLoaded={ !isPlaceholderData }>
{ totalReward.dividedBy(WEI).toFixed() } { appConfig.network.currency.symbol } { totalReward.dividedBy(WEI).toFixed() } { config.chain.currency.symbol }
</Skeleton> </Skeleton>
{ rewardBreakDown } { rewardBreakDown }
</DetailsInfoItem> </DetailsInfoItem>
...@@ -235,7 +235,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -235,7 +235,7 @@ const BlockDetails = ({ query }: Props) => {
// is this text correct for validators? // is this text correct for validators?
hint={ `Amount of distributed reward. ${ capitalize(validatorTitle) }s receive a static block reward + Tx fees + uncle fees` } hint={ `Amount of distributed reward. ${ capitalize(validatorTitle) }s receive a static block reward + Tx fees + uncle fees` }
> >
{ BigNumber(reward).dividedBy(WEI).toFixed() } { appConfig.network.currency.symbol } { BigNumber(reward).dividedBy(WEI).toFixed() } { config.chain.currency.symbol }
</DetailsInfoItem> </DetailsInfoItem>
)) ))
} }
...@@ -293,7 +293,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -293,7 +293,7 @@ const BlockDetails = ({ query }: Props) => {
<Skeleton isLoaded={ !isPlaceholderData } h="20px" maxW="380px" w="100%"/> <Skeleton isLoaded={ !isPlaceholderData } h="20px" maxW="380px" w="100%"/>
) : ( ) : (
<> <>
<Text>{ BigNumber(data.base_fee_per_gas).dividedBy(WEI).toFixed() } { appConfig.network.currency.symbol } </Text> <Text>{ BigNumber(data.base_fee_per_gas).dividedBy(WEI).toFixed() } { config.chain.currency.symbol } </Text>
<Text variant="secondary" whiteSpace="pre"> <Text variant="secondary" whiteSpace="pre">
{ space }({ BigNumber(data.base_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() } Gwei) { space }({ BigNumber(data.base_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() } Gwei)
</Text> </Text>
...@@ -304,7 +304,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -304,7 +304,7 @@ const BlockDetails = ({ query }: Props) => {
<DetailsInfoItem <DetailsInfoItem
title="Burnt fees" title="Burnt fees"
hint={ hint={
`Amount of ${ appConfig.network.currency.symbol || 'native token' } burned from transactions included in the block. `Amount of ${ config.chain.currency.symbol || 'native token' } burned from transactions included in the block.
Equals Block Base Fee per Gas * Gas Used` Equals Block Base Fee per Gas * Gas Used`
} }
...@@ -312,7 +312,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -312,7 +312,7 @@ const BlockDetails = ({ query }: Props) => {
> >
<Icon as={ flameIcon } boxSize={ 5 } color="gray.500" isLoading={ isPlaceholderData }/> <Icon as={ flameIcon } boxSize={ 5 } color="gray.500" isLoading={ isPlaceholderData }/>
<Skeleton isLoaded={ !isPlaceholderData } ml={ 1 }> <Skeleton isLoaded={ !isPlaceholderData } ml={ 1 }>
{ burntFees.dividedBy(WEI).toFixed() } { appConfig.network.currency.symbol } { burntFees.dividedBy(WEI).toFixed() } { config.chain.currency.symbol }
</Skeleton> </Skeleton>
{ !txFees.isEqualTo(ZERO) && ( { !txFees.isEqualTo(ZERO) && (
<Tooltip label="Burnt fees / Txn fees * 100%"> <Tooltip label="Burnt fees / Txn fees * 100%">
...@@ -333,7 +333,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -333,7 +333,7 @@ const BlockDetails = ({ query }: Props) => {
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton isLoaded={ !isPlaceholderData }>
{ BigNumber(data.priority_fee).dividedBy(WEI).toFixed() } { appConfig.network.currency.symbol } { BigNumber(data.priority_fee).dividedBy(WEI).toFixed() } { config.chain.currency.symbol }
</Skeleton> </Skeleton>
</DetailsInfoItem> </DetailsInfoItem>
) } ) }
......
...@@ -6,7 +6,7 @@ import React from 'react'; ...@@ -6,7 +6,7 @@ import React from 'react';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.svg';
import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { WEI } from 'lib/consts'; import { WEI } from 'lib/consts';
...@@ -88,15 +88,15 @@ const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => { ...@@ -88,15 +88,15 @@ const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
) } ) }
</Flex> </Flex>
</Box> </Box>
{ !appConfig.L2.isL2Network && ( { !config.features.rollup.isEnabled && (
<Flex columnGap={ 2 }> <Flex columnGap={ 2 }>
<Text fontWeight={ 500 }>Reward { appConfig.network.currency.symbol }</Text> <Text fontWeight={ 500 }>Reward { config.chain.currency.symbol }</Text>
<Skeleton isLoaded={ !isLoading } display="inline-block" color="text_secondary"> <Skeleton isLoaded={ !isLoading } display="inline-block" color="text_secondary">
<span>{ totalReward.toFixed() }</span> <span>{ totalReward.toFixed() }</span>
</Skeleton> </Skeleton>
</Flex> </Flex>
) } ) }
{ !appConfig.L2.isL2Network && ( { !config.features.rollup.isEnabled && (
<Box> <Box>
<Text fontWeight={ 500 }>Burnt fees</Text> <Text fontWeight={ 500 }>Burnt fees</Text>
<Flex columnGap={ 4 } mt={ 2 }> <Flex columnGap={ 4 } mt={ 2 }>
......
...@@ -5,7 +5,7 @@ import React from 'react'; ...@@ -5,7 +5,7 @@ import React from 'react';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import BlocksTableItem from 'ui/blocks/BlocksTableItem'; import BlocksTableItem from 'ui/blocks/BlocksTableItem';
import { default as Thead } from 'ui/shared/TheadSticky'; import { default as Thead } from 'ui/shared/TheadSticky';
...@@ -25,11 +25,11 @@ const BlocksTable = ({ data, isLoading, top, page }: Props) => { ...@@ -25,11 +25,11 @@ const BlocksTable = ({ data, isLoading, top, page }: Props) => {
<Tr> <Tr>
<Th width="125px">Block</Th> <Th width="125px">Block</Th>
<Th width="120px">Size, bytes</Th> <Th width="120px">Size, bytes</Th>
<Th width={ appConfig.L2.isL2Network ? '37%' : '21%' } minW="144px">{ capitalize(getNetworkValidatorTitle()) }</Th> <Th width={ config.features.rollup.isEnabled ? '37%' : '21%' } minW="144px">{ capitalize(getNetworkValidatorTitle()) }</Th>
<Th width="64px" isNumeric>Txn</Th> <Th width="64px" isNumeric>Txn</Th>
<Th width={ appConfig.L2.isL2Network ? '63%' : '35%' }>Gas used</Th> <Th width={ config.features.rollup.isEnabled ? '63%' : '35%' }>Gas used</Th>
{ !appConfig.L2.isL2Network && <Th width="22%">Reward { appConfig.network.currency.symbol }</Th> } { !config.features.rollup.isEnabled && <Th width="22%">Reward { config.chain.currency.symbol }</Th> }
{ !appConfig.L2.isL2Network && <Th width="22%">Burnt fees { appConfig.network.currency.symbol }</Th> } { !config.features.rollup.isEnabled && <Th width="22%">Burnt fees { config.chain.currency.symbol }</Th> }
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
......
...@@ -6,7 +6,7 @@ import React from 'react'; ...@@ -6,7 +6,7 @@ import React from 'react';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.svg';
import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { WEI } from 'lib/consts'; import { WEI } from 'lib/consts';
...@@ -87,7 +87,7 @@ const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => { ...@@ -87,7 +87,7 @@ const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
</Skeleton> </Skeleton>
) : data.tx_count } ) : data.tx_count }
</Td> </Td>
{ !appConfig.L2.isL2Network && ( { !config.features.rollup.isEnabled && (
<Td fontSize="sm"> <Td fontSize="sm">
<Skeleton isLoaded={ !isLoading } display="inline-block">{ BigNumber(data.gas_used || 0).toFormat() }</Skeleton> <Skeleton isLoaded={ !isLoading } display="inline-block">{ BigNumber(data.gas_used || 0).toFormat() }</Skeleton>
<Flex mt={ 2 }> <Flex mt={ 2 }>
...@@ -114,7 +114,7 @@ const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => { ...@@ -114,7 +114,7 @@ const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
{ totalReward.toFixed(8) } { totalReward.toFixed(8) }
</Skeleton> </Skeleton>
</Td> </Td>
{ !appConfig.L2.isL2Network && ( { !config.features.rollup.isEnabled && (
<Td fontSize="sm"> <Td fontSize="sm">
<Flex alignItems="center" columnGap={ 1 }> <Flex alignItems="center" columnGap={ 1 }>
<Icon as={ flameIcon } boxSize={ 5 } color={ burntFeesIconColor } isLoading={ isLoading }/> <Icon as={ flameIcon } boxSize={ 5 } color={ burntFeesIconColor } isLoading={ isLoading }/>
......
...@@ -5,7 +5,7 @@ import type { UseFormReturn } from 'react-hook-form'; ...@@ -5,7 +5,7 @@ import type { UseFormReturn } from 'react-hook-form';
import type { FormFields } from './types'; import type { FormFields } from './types';
import appConfig from 'configs/app/config'; import config from 'configs/app';
interface Props { interface Props {
formApi: UseFormReturn<FormFields>; formApi: UseFormReturn<FormFields>;
...@@ -42,7 +42,7 @@ const CsvExportFormReCaptcha = ({ formApi }: Props) => { ...@@ -42,7 +42,7 @@ const CsvExportFormReCaptcha = ({ formApi }: Props) => {
formApi.setError('reCaptcha', { type: 'required' }); formApi.setError('reCaptcha', { type: 'required' });
}, [ formApi ]); }, [ formApi ]);
if (!appConfig.reCaptcha.siteKey) { if (!config.features.csvExport.isEnabled) {
return ( return (
<Alert status="error"> <Alert status="error">
CSV export is not available at the moment since reCaptcha is not configured for this application. CSV export is not available at the moment since reCaptcha is not configured for this application.
...@@ -55,7 +55,7 @@ const CsvExportFormReCaptcha = ({ formApi }: Props) => { ...@@ -55,7 +55,7 @@ const CsvExportFormReCaptcha = ({ formApi }: Props) => {
<ReCaptcha <ReCaptcha
className="recaptcha" className="recaptcha"
ref={ ref } ref={ ref }
sitekey={ appConfig.reCaptcha.siteKey } sitekey={ config.features.csvExport.reCaptcha.siteKey }
onChange={ handleReCaptchaChange } onChange={ handleReCaptchaChange }
onExpired={ handleReCaptchaExpire } onExpired={ handleReCaptchaExpire }
/> />
......
...@@ -3,7 +3,7 @@ import { createGraphiQLFetcher } from '@graphiql/toolkit'; ...@@ -3,7 +3,7 @@ import { createGraphiQLFetcher } from '@graphiql/toolkit';
import { GraphiQL } from 'graphiql'; import { GraphiQL } from 'graphiql';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import buildUrl from 'lib/api/buildUrl'; import buildUrl from 'lib/api/buildUrl';
import 'graphiql/graphiql.css'; import 'graphiql/graphiql.css';
import isBrowser from 'lib/isBrowser'; import isBrowser from 'lib/isBrowser';
...@@ -33,7 +33,7 @@ const GraphQL = () => { ...@@ -33,7 +33,7 @@ const GraphQL = () => {
const initialQuery = `{ const initialQuery = `{
transaction( transaction(
hash: "${ appConfig.graphQL.defaultTxnHash }" hash: "${ config.features.graphqlApiDocs.defaultTxHash }"
) { ) {
hash hash
blockNumber blockNumber
...@@ -50,7 +50,7 @@ const GraphQL = () => { ...@@ -50,7 +50,7 @@ const GraphQL = () => {
// or the older one subscriptions-transport-ws // or the older one subscriptions-transport-ws
// so we (isstuev & vbaranov) decided to configure playground without subscriptions // so we (isstuev & vbaranov) decided to configure playground without subscriptions
// in case of any complaint consider reconfigure the graphql ws server with absinthe_graphql_ws package // in case of any complaint consider reconfigure the graphql ws server with absinthe_graphql_ws package
// subscriptionUrl: `wss://${appConfig.app.host}/socket/`, // subscriptionUrl: `wss://${config.app.host}/socket/`,
}); });
return ( return (
......
...@@ -7,7 +7,7 @@ import React from 'react'; ...@@ -7,7 +7,7 @@ import React from 'react';
import type { SocketMessage } from 'lib/socket/types'; import type { SocketMessage } from 'lib/socket/types';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import { nbsp } from 'lib/html-entities'; import { nbsp } from 'lib/html-entities';
...@@ -24,11 +24,11 @@ const BLOCK_HEIGHT_L2 = 112; ...@@ -24,11 +24,11 @@ const BLOCK_HEIGHT_L2 = 112;
const BLOCK_MARGIN = 12; const BLOCK_MARGIN = 12;
const LatestBlocks = () => { const LatestBlocks = () => {
const blockHeight = appConfig.L2.isL2Network ? BLOCK_HEIGHT_L2 : BLOCK_HEIGHT_L1; const blockHeight = config.features.rollup.isEnabled ? BLOCK_HEIGHT_L2 : BLOCK_HEIGHT_L1;
const isMobile = useIsMobile(); const isMobile = useIsMobile();
// const blocksMaxCount = isMobile ? 2 : 3; // const blocksMaxCount = isMobile ? 2 : 3;
let blocksMaxCount: number; let blocksMaxCount: number;
if (appConfig.L2.isL2Network) { if (config.features.rollup.isEnabled) {
blocksMaxCount = isMobile ? 4 : 5; blocksMaxCount = isMobile ? 4 : 5;
} else { } else {
blocksMaxCount = isMobile ? 2 : 3; blocksMaxCount = isMobile ? 2 : 3;
......
...@@ -9,7 +9,7 @@ import React from 'react'; ...@@ -9,7 +9,7 @@ import React from 'react';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import blockIcon from 'icons/block.svg'; import blockIcon from 'icons/block.svg';
import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
...@@ -65,7 +65,7 @@ const LatestBlocksItem = ({ block, h, isLoading }: Props) => { ...@@ -65,7 +65,7 @@ const LatestBlocksItem = ({ block, h, isLoading }: Props) => {
<Grid gridGap={ 2 } templateColumns="auto minmax(0, 1fr)" fontSize="sm"> <Grid gridGap={ 2 } templateColumns="auto minmax(0, 1fr)" fontSize="sm">
<Skeleton isLoaded={ !isLoading }>Txn</Skeleton> <Skeleton isLoaded={ !isLoading }>Txn</Skeleton>
<Skeleton isLoaded={ !isLoading } color="text_secondary"><span>{ block.tx_count }</span></Skeleton> <Skeleton isLoaded={ !isLoading } color="text_secondary"><span>{ block.tx_count }</span></Skeleton>
{ !appConfig.L2.isL2Network && ( { !config.features.rollup.isEnabled && (
<> <>
<Skeleton isLoaded={ !isLoading }>Reward</Skeleton> <Skeleton isLoaded={ !isLoading }>Reward</Skeleton>
<Skeleton isLoaded={ !isLoading } color="text_secondary"><span>{ totalReward.toFixed() }</span></Skeleton> <Skeleton isLoaded={ !isLoading } color="text_secondary"><span>{ totalReward.toFixed() }</span></Skeleton>
......
...@@ -9,7 +9,7 @@ import React from 'react'; ...@@ -9,7 +9,7 @@ import React from 'react';
import type { L2DepositsItem } from 'types/api/l2Deposits'; import type { L2DepositsItem } from 'types/api/l2Deposits';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import blockIcon from 'icons/block.svg'; import blockIcon from 'icons/block.svg';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
...@@ -30,7 +30,7 @@ const LatestTxsItem = ({ item, isLoading }: Props) => { ...@@ -30,7 +30,7 @@ const LatestTxsItem = ({ item, isLoading }: Props) => {
const l1BlockLink = ( const l1BlockLink = (
<LinkExternal <LinkExternal
href={ appConfig.L2.L1BaseUrl + href={ config.features.rollup.L1BaseUrl +
route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: item.l1_block_number.toString() } }) route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: item.l1_block_number.toString() } })
} }
fontWeight={ 700 } fontWeight={ 700 }
...@@ -45,7 +45,7 @@ const LatestTxsItem = ({ item, isLoading }: Props) => { ...@@ -45,7 +45,7 @@ const LatestTxsItem = ({ item, isLoading }: Props) => {
const l1TxLink = ( const l1TxLink = (
<LinkExternal <LinkExternal
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: item.l1_tx_hash } }) } href={ config.features.rollup.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: item.l1_tx_hash } }) }
maxW="100%" maxW="100%"
display="inline-flex" display="inline-flex"
alignItems="center" alignItems="center"
......
...@@ -10,7 +10,7 @@ import React from 'react'; ...@@ -10,7 +10,7 @@ import React from 'react';
import type { Transaction } from 'types/api/transaction'; import type { Transaction } from 'types/api/transaction';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import transactionIcon from 'icons/transactions.svg'; import transactionIcon from 'icons/transactions.svg';
import getValueWithUnit from 'lib/getValueWithUnit'; import getValueWithUnit from 'lib/getValueWithUnit';
...@@ -117,7 +117,7 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => { ...@@ -117,7 +117,7 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
</Grid> </Grid>
<Box> <Box>
<Skeleton isLoaded={ !isLoading } mb={ 2 }> <Skeleton isLoaded={ !isLoading } mb={ 2 }>
<Text as="span" whiteSpace="pre">{ appConfig.network.currency.symbol } </Text> <Text as="span" whiteSpace="pre">{ config.chain.currency.symbol } </Text>
<Text as="span" variant="secondary">{ getValueWithUnit(tx.value).dp(5).toFormat() }</Text> <Text as="span" variant="secondary">{ getValueWithUnit(tx.value).dp(5).toFormat() }</Text>
</Skeleton> </Skeleton>
<Skeleton isLoaded={ !isLoading }> <Skeleton isLoaded={ !isLoading }>
......
...@@ -9,7 +9,7 @@ import React from 'react'; ...@@ -9,7 +9,7 @@ import React from 'react';
import type { Transaction } from 'types/api/transaction'; import type { Transaction } from 'types/api/transaction';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import transactionIcon from 'icons/transactions.svg'; import transactionIcon from 'icons/transactions.svg';
import getValueWithUnit from 'lib/getValueWithUnit'; import getValueWithUnit from 'lib/getValueWithUnit';
...@@ -115,11 +115,11 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => { ...@@ -115,11 +115,11 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
) } ) }
</Flex> </Flex>
<Skeleton isLoaded={ !isLoading } mb={ 2 } fontSize="sm" w="fit-content"> <Skeleton isLoaded={ !isLoading } mb={ 2 } fontSize="sm" w="fit-content">
<Text as="span">Value { appConfig.network.currency.symbol } </Text> <Text as="span">Value { config.chain.currency.symbol } </Text>
<Text as="span" variant="secondary">{ getValueWithUnit(tx.value).dp(5).toFormat() }</Text> <Text as="span" variant="secondary">{ getValueWithUnit(tx.value).dp(5).toFormat() }</Text>
</Skeleton> </Skeleton>
<Skeleton isLoaded={ !isLoading } fontSize="sm" w="fit-content"> <Skeleton isLoaded={ !isLoading } fontSize="sm" w="fit-content">
<Text as="span">Fee { appConfig.network.currency.symbol } </Text> <Text as="span">Fee { config.chain.currency.symbol } </Text>
<Text as="span" variant="secondary">{ getValueWithUnit(tx.fee.value).dp(5).toFormat() }</Text> <Text as="span" variant="secondary">{ getValueWithUnit(tx.fee.value).dp(5).toFormat() }</Text>
</Skeleton> </Skeleton>
</Box> </Box>
......
...@@ -2,7 +2,7 @@ import { Grid } from '@chakra-ui/react'; ...@@ -2,7 +2,7 @@ import { Grid } from '@chakra-ui/react';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import blockIcon from 'icons/block.svg'; import blockIcon from 'icons/block.svg';
import clockIcon from 'icons/clock-light.svg'; import clockIcon from 'icons/clock-light.svg';
import gasIcon from 'icons/gas.svg'; import gasIcon from 'icons/gas.svg';
...@@ -14,8 +14,8 @@ import { HOMEPAGE_STATS } from 'stubs/stats'; ...@@ -14,8 +14,8 @@ import { HOMEPAGE_STATS } from 'stubs/stats';
import StatsGasPrices from './StatsGasPrices'; import StatsGasPrices from './StatsGasPrices';
import StatsItem from './StatsItem'; import StatsItem from './StatsItem';
const hasGasTracker = appConfig.homepage.showGasTracker; const hasGasTracker = config.UI.homepage.showGasTracker;
const hasAvgBlockTime = appConfig.homepage.showAvgBlockTime; const hasAvgBlockTime = config.UI.homepage.showAvgBlockTime;
let itemsCount = 5; let itemsCount = 5;
!hasGasTracker && itemsCount--; !hasGasTracker && itemsCount--;
......
import { Heading } from '@chakra-ui/react'; import { Heading } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import useHasAccount from 'lib/hooks/useHasAccount'; import useHasAccount from 'lib/hooks/useHasAccount';
import LatestDeposits from 'ui/home/LatestDeposits'; import LatestDeposits from 'ui/home/LatestDeposits';
import LatestTxs from 'ui/home/LatestTxs'; import LatestTxs from 'ui/home/LatestTxs';
...@@ -10,10 +10,10 @@ import TabsWithScroll from 'ui/shared/Tabs/TabsWithScroll'; ...@@ -10,10 +10,10 @@ import TabsWithScroll from 'ui/shared/Tabs/TabsWithScroll';
const TransactionsHome = () => { const TransactionsHome = () => {
const hasAccount = useHasAccount(); const hasAccount = useHasAccount();
if (appConfig.L2.isL2Network || hasAccount) { if (config.features.rollup.isEnabled || hasAccount) {
const tabs = [ const tabs = [
{ id: 'txn', title: 'Latest txn', component: <LatestTxs/> }, { id: 'txn', title: 'Latest txn', component: <LatestTxs/> },
appConfig.L2.isL2Network && { id: 'deposits', title: 'Deposits (L1→L2 txn)', component: <LatestDeposits/> }, config.features.rollup.isEnabled && { id: 'deposits', title: 'Deposits (L1→L2 txn)', component: <LatestDeposits/> },
hasAccount && { id: 'watchlist', title: 'Watch list', component: <LatestWatchlistTxs/> }, hasAccount && { id: 'watchlist', title: 'Watch list', component: <LatestWatchlistTxs/> },
].filter(Boolean); ].filter(Boolean);
return ( return (
......
import { Flex, Skeleton, Text, useColorModeValue } from '@chakra-ui/react'; import { Flex, Skeleton, Text, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import Hint from 'ui/shared/Hint'; import Hint from 'ui/shared/Hint';
...@@ -11,13 +11,13 @@ import useFetchChartData from './useFetchChartData'; ...@@ -11,13 +11,13 @@ import useFetchChartData from './useFetchChartData';
import INDICATORS from './utils/indicators'; import INDICATORS from './utils/indicators';
const indicators = INDICATORS const indicators = INDICATORS
.filter(({ id }) => appConfig.homepage.charts.includes(id)) .filter(({ id }) => config.UI.homepage.charts.includes(id))
.sort((a, b) => { .sort((a, b) => {
if (appConfig.homepage.charts.indexOf(a.id) > appConfig.homepage.charts.indexOf(b.id)) { if (config.UI.homepage.charts.indexOf(a.id) > config.UI.homepage.charts.indexOf(b.id)) {
return 1; return 1;
} }
if (appConfig.homepage.charts.indexOf(a.id) < appConfig.homepage.charts.indexOf(b.id)) { if (config.UI.homepage.charts.indexOf(a.id) < config.UI.homepage.charts.indexOf(b.id)) {
return -1; return -1;
} }
......
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import type { TChainIndicator } from '../types'; import type { TChainIndicator } from '../types';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import globeIcon from 'icons/globe.svg'; import globeIcon from 'icons/globe.svg';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import { sortByDateDesc } from 'ui/shared/chart/utils/sorts'; import { sortByDateDesc } from 'ui/shared/chart/utils/sorts';
...@@ -28,24 +28,23 @@ const dailyTxsIndicator: TChainIndicator<'homepage_chart_txs'> = { ...@@ -28,24 +28,23 @@ const dailyTxsIndicator: TChainIndicator<'homepage_chart_txs'> = {
}; };
const nativeTokenData = { const nativeTokenData = {
address: appConfig.network.currency.address || '', name: config.chain.currency.name || '',
name: appConfig.network.currency.name || '',
icon_url: '', icon_url: '',
}; };
const coinPriceIndicator: TChainIndicator<'homepage_chart_market'> = { const coinPriceIndicator: TChainIndicator<'homepage_chart_market'> = {
id: 'coin_price', id: 'coin_price',
title: `${ appConfig.network.currency.symbol } price`, title: `${ config.chain.currency.symbol } price`,
value: (stats) => '$' + Number(stats.coin_price).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }), value: (stats) => '$' + Number(stats.coin_price).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }),
icon: <TokenLogo data={ nativeTokenData } boxSize={ 6 }/>, icon: <TokenLogo data={ nativeTokenData } boxSize={ 6 }/>,
hint: `${ appConfig.network.currency.symbol } token daily price in USD.`, hint: `${ config.chain.currency.symbol } token daily price in USD.`,
api: { api: {
resourceName: 'homepage_chart_market', resourceName: 'homepage_chart_market',
dataFn: (response) => ([ { dataFn: (response) => ([ {
items: response.chart_data items: response.chart_data
.map((item) => ({ date: new Date(item.date), value: Number(item.closing_price) })) .map((item) => ({ date: new Date(item.date), value: Number(item.closing_price) }))
.sort(sortByDateDesc), .sort(sortByDateDesc),
name: `${ appConfig.network.currency.symbol } price`, name: `${ config.chain.currency.symbol } price`,
valueFormatter: (x: number) => '$' + x.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }), valueFormatter: (x: number) => '$' + x.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }),
} ]), } ]),
}, },
......
...@@ -5,7 +5,7 @@ import React from 'react'; ...@@ -5,7 +5,7 @@ import React from 'react';
import type { L2DepositsItem } from 'types/api/l2Deposits'; import type { L2DepositsItem } from 'types/api/l2Deposits';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import blockIcon from 'icons/block.svg'; import blockIcon from 'icons/block.svg';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
...@@ -27,7 +27,7 @@ const DepositsListItem = ({ item, isLoading }: Props) => { ...@@ -27,7 +27,7 @@ const DepositsListItem = ({ item, isLoading }: Props) => {
<ListItemMobileGrid.Label isLoading={ isLoading }>L1 block No</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>L1 block No</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px"> <ListItemMobileGrid.Value py="3px">
<LinkExternal <LinkExternal
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: item.l1_block_number.toString() } }) } href={ config.features.rollup.L1BaseUrl + route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: item.l1_block_number.toString() } }) }
fontWeight={ 600 } fontWeight={ 600 }
display="flex" display="flex"
isLoading={ isLoading } isLoading={ isLoading }
...@@ -63,7 +63,7 @@ const DepositsListItem = ({ item, isLoading }: Props) => { ...@@ -63,7 +63,7 @@ const DepositsListItem = ({ item, isLoading }: Props) => {
<ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn hash</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px"> <ListItemMobileGrid.Value py="3px">
<LinkExternal <LinkExternal
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: item.l1_tx_hash } }) } href={ config.features.rollup.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: item.l1_tx_hash } }) }
maxW="100%" maxW="100%"
display="flex" display="flex"
overflow="hidden" overflow="hidden"
...@@ -79,7 +79,7 @@ const DepositsListItem = ({ item, isLoading }: Props) => { ...@@ -79,7 +79,7 @@ const DepositsListItem = ({ item, isLoading }: Props) => {
<ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn origin</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn origin</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px"> <ListItemMobileGrid.Value py="3px">
<LinkExternal <LinkExternal
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/address/[hash]', query: { hash: item.l1_tx_origin } }) } href={ config.features.rollup.L1BaseUrl + route({ pathname: '/address/[hash]', query: { hash: item.l1_tx_origin } }) }
maxW="100%" maxW="100%"
display="flex" display="flex"
overflow="hidden" overflow="hidden"
......
...@@ -5,7 +5,7 @@ import React from 'react'; ...@@ -5,7 +5,7 @@ import React from 'react';
import type { L2DepositsItem } from 'types/api/l2Deposits'; import type { L2DepositsItem } from 'types/api/l2Deposits';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import blockIcon from 'icons/block.svg'; import blockIcon from 'icons/block.svg';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
...@@ -24,7 +24,7 @@ const WithdrawalsTableItem = ({ item, isLoading }: Props) => { ...@@ -24,7 +24,7 @@ const WithdrawalsTableItem = ({ item, isLoading }: Props) => {
<Tr> <Tr>
<Td verticalAlign="middle" fontWeight={ 600 }> <Td verticalAlign="middle" fontWeight={ 600 }>
<LinkExternal <LinkExternal
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: item.l1_block_number.toString() } }) } href={ config.features.rollup.L1BaseUrl + route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: item.l1_block_number.toString() } }) }
fontWeight={ 600 } fontWeight={ 600 }
display="inline-flex" display="inline-flex"
isLoading={ isLoading } isLoading={ isLoading }
...@@ -56,7 +56,7 @@ const WithdrawalsTableItem = ({ item, isLoading }: Props) => { ...@@ -56,7 +56,7 @@ const WithdrawalsTableItem = ({ item, isLoading }: Props) => {
</Td> </Td>
<Td verticalAlign="middle"> <Td verticalAlign="middle">
<LinkExternal <LinkExternal
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: item.l1_tx_hash } }) } href={ config.features.rollup.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: item.l1_tx_hash } }) }
maxW="100%" maxW="100%"
display="inline-flex" display="inline-flex"
overflow="hidden" overflow="hidden"
...@@ -70,7 +70,7 @@ const WithdrawalsTableItem = ({ item, isLoading }: Props) => { ...@@ -70,7 +70,7 @@ const WithdrawalsTableItem = ({ item, isLoading }: Props) => {
</Td> </Td>
<Td verticalAlign="middle"> <Td verticalAlign="middle">
<LinkExternal <LinkExternal
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/address/[hash]', query: { hash: item.l1_tx_origin } }) } href={ config.features.rollup.L1BaseUrl + route({ pathname: '/address/[hash]', query: { hash: item.l1_tx_origin } }) }
maxW="100%" maxW="100%"
display="inline-flex" display="inline-flex"
overflow="hidden" overflow="hidden"
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { L2OutputRootsItem } from 'types/api/l2OutputRoots'; import type { L2OutputRootsItem } from 'types/api/l2OutputRoots';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
...@@ -53,7 +53,7 @@ const OutputRootsListItem = ({ item, isLoading }: Props) => { ...@@ -53,7 +53,7 @@ const OutputRootsListItem = ({ item, isLoading }: Props) => {
maxW="100%" maxW="100%"
display="flex" display="flex"
overflow="hidden" overflow="hidden"
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: item.l1_tx_hash } }) } href={ config.features.rollup.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: item.l1_tx_hash } }) }
isLoading={ isLoading } isLoading={ isLoading }
> >
<Icon as={ txIcon } boxSize={ 6 } isLoading={ isLoading }/> <Icon as={ txIcon } boxSize={ 6 } isLoading={ isLoading }/>
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { L2OutputRootsItem } from 'types/api/l2OutputRoots'; import type { L2OutputRootsItem } from 'types/api/l2OutputRoots';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import txBatchIcon from 'icons/txBatch.svg'; import txBatchIcon from 'icons/txBatch.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
...@@ -47,7 +47,7 @@ const OutputRootsTableItem = ({ item, isLoading }: Props) => { ...@@ -47,7 +47,7 @@ const OutputRootsTableItem = ({ item, isLoading }: Props) => {
<LinkExternal <LinkExternal
maxW="100%" maxW="100%"
display="inline-flex" display="inline-flex"
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: item.l1_tx_hash } }) } href={ config.features.rollup.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: item.l1_tx_hash } }) }
isLoading={ isLoading } isLoading={ isLoading }
> >
<Icon as={ txIcon } boxSize={ 6 } isLoading={ isLoading }/> <Icon as={ txIcon } boxSize={ 6 } isLoading={ isLoading }/>
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { L2TxnBatchesItem } from 'types/api/l2TxnBatches'; import type { L2TxnBatchesItem } from 'types/api/l2TxnBatches';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import txBatchIcon from 'icons/txBatch.svg'; import txBatchIcon from 'icons/txBatch.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
...@@ -56,7 +56,7 @@ const TxnBatchesListItem = ({ item, isLoading }: Props) => { ...@@ -56,7 +56,7 @@ const TxnBatchesListItem = ({ item, isLoading }: Props) => {
<LinkExternal <LinkExternal
fontWeight={ 600 } fontWeight={ 600 }
display="inline-flex" display="inline-flex"
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: item.epoch_number.toString() } }) } href={ config.features.rollup.L1BaseUrl + route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: item.epoch_number.toString() } }) }
isLoading={ isLoading } isLoading={ isLoading }
> >
<Skeleton isLoaded={ !isLoading }> <Skeleton isLoaded={ !isLoading }>
...@@ -72,7 +72,7 @@ const TxnBatchesListItem = ({ item, isLoading }: Props) => { ...@@ -72,7 +72,7 @@ const TxnBatchesListItem = ({ item, isLoading }: Props) => {
<LinkExternal <LinkExternal
maxW="100%" maxW="100%"
display="inline-flex" display="inline-flex"
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: hash } }) } href={ config.features.rollup.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: hash } }) }
key={ hash } key={ hash }
isLoading={ isLoading } isLoading={ isLoading }
> >
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { L2TxnBatchesItem } from 'types/api/l2TxnBatches'; import type { L2TxnBatchesItem } from 'types/api/l2TxnBatches';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import txBatchIcon from 'icons/txBatch.svg'; import txBatchIcon from 'icons/txBatch.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
...@@ -47,7 +47,7 @@ const TxnBatchesTableItem = ({ item, isLoading }: Props) => { ...@@ -47,7 +47,7 @@ const TxnBatchesTableItem = ({ item, isLoading }: Props) => {
</Td> </Td>
<Td> <Td>
<LinkExternal <LinkExternal
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: item.epoch_number.toString() } }) } href={ config.features.rollup.L1BaseUrl + route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: item.epoch_number.toString() } }) }
fontWeight={ 600 } fontWeight={ 600 }
display="inline-flex" display="inline-flex"
isLoading={ isLoading } isLoading={ isLoading }
...@@ -65,7 +65,7 @@ const TxnBatchesTableItem = ({ item, isLoading }: Props) => { ...@@ -65,7 +65,7 @@ const TxnBatchesTableItem = ({ item, isLoading }: Props) => {
maxW="100%" maxW="100%"
display="inline-flex" display="inline-flex"
key={ hash } key={ hash }
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: hash } }) } href={ config.features.rollup.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: hash } }) }
isLoading={ isLoading } isLoading={ isLoading }
> >
<Icon as={ txIcon } boxSize={ 6 } isLoading={ isLoading }/> <Icon as={ txIcon } boxSize={ 6 } isLoading={ isLoading }/>
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { L2WithdrawalsItem } from 'types/api/l2Withdrawals'; import type { L2WithdrawalsItem } from 'types/api/l2Withdrawals';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
...@@ -75,7 +75,7 @@ const WithdrawalsListItem = ({ item, isLoading }: Props) => { ...@@ -75,7 +75,7 @@ const WithdrawalsListItem = ({ item, isLoading }: Props) => {
<ListItemMobileGrid.Label isLoading={ isLoading }>Status</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>Status</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value> <ListItemMobileGrid.Value>
{ item.status === 'Ready for relay' ? { item.status === 'Ready for relay' ?
<LinkExternal href={ appConfig.L2.withdrawalUrl }>{ item.status }</LinkExternal> : <LinkExternal href={ config.features.rollup.withdrawalUrl }>{ item.status }</LinkExternal> :
<Skeleton isLoaded={ !isLoading } display="inline-block">{ item.status }</Skeleton> } <Skeleton isLoaded={ !isLoading } display="inline-block">{ item.status }</Skeleton> }
</ListItemMobileGrid.Value> </ListItemMobileGrid.Value>
...@@ -84,7 +84,7 @@ const WithdrawalsListItem = ({ item, isLoading }: Props) => { ...@@ -84,7 +84,7 @@ const WithdrawalsListItem = ({ item, isLoading }: Props) => {
<ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn hash</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px"> <ListItemMobileGrid.Value py="3px">
<LinkExternal <LinkExternal
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: item.l1_tx_hash } }) } href={ config.features.rollup.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: item.l1_tx_hash } }) }
maxW="100%" maxW="100%"
display="inline-flex" display="inline-flex"
overflow="hidden" overflow="hidden"
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { L2WithdrawalsItem } from 'types/api/l2Withdrawals'; import type { L2WithdrawalsItem } from 'types/api/l2Withdrawals';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
...@@ -55,14 +55,14 @@ const WithdrawalsTableItem = ({ item, isLoading }: Props) => { ...@@ -55,14 +55,14 @@ const WithdrawalsTableItem = ({ item, isLoading }: Props) => {
</Td> </Td>
<Td verticalAlign="middle"> <Td verticalAlign="middle">
{ item.status === 'Ready for relay' ? { item.status === 'Ready for relay' ?
<LinkExternal href={ appConfig.L2.withdrawalUrl }>{ item.status }</LinkExternal> : <LinkExternal href={ config.features.rollup.withdrawalUrl }>{ item.status }</LinkExternal> :
<Skeleton isLoaded={ !isLoading } display="inline-block">{ item.status }</Skeleton> <Skeleton isLoaded={ !isLoading } display="inline-block">{ item.status }</Skeleton>
} }
</Td> </Td>
<Td verticalAlign="middle"> <Td verticalAlign="middle">
{ item.l1_tx_hash ? ( { item.l1_tx_hash ? (
<LinkExternal <LinkExternal
href={ appConfig.L2.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: item.l1_tx_hash } }) } href={ config.features.rollup.L1BaseUrl + route({ pathname: '/tx/[hash]', query: { hash: item.l1_tx_hash } }) }
isLoading={ isLoading } isLoading={ isLoading }
display="inline-flex" display="inline-flex"
> >
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { MarketplaceAppOverview } from 'types/client/marketplace'; import type { MarketplaceAppOverview } from 'types/client/marketplace';
import { MarketplaceCategory } from 'types/client/marketplace'; import { MarketplaceCategory } from 'types/client/marketplace';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import useApiFetch from 'lib/hooks/useFetch'; import useApiFetch from 'lib/hooks/useFetch';
import { MARKETPLACE_APP } from 'stubs/marketplace'; import { MARKETPLACE_APP } from 'stubs/marketplace';
...@@ -23,7 +23,7 @@ export default function useMarketplaceApps(filter: string, selectedCategoryId: s ...@@ -23,7 +23,7 @@ export default function useMarketplaceApps(filter: string, selectedCategoryId: s
const apiFetch = useApiFetch(); const apiFetch = useApiFetch();
const { isPlaceholderData, isError, error, data } = useQuery<unknown, ResourceError<unknown>, Array<MarketplaceAppOverview>>( const { isPlaceholderData, isError, error, data } = useQuery<unknown, ResourceError<unknown>, Array<MarketplaceAppOverview>>(
[ 'marketplace-apps' ], [ 'marketplace-apps' ],
async() => apiFetch(appConfig.marketplace.configUrl || ''), async() => apiFetch(config.features.marketplace.configUrl || ''),
{ {
select: (data) => (data as Array<MarketplaceAppOverview>).sort((a, b) => a.title.localeCompare(b.title)), select: (data) => (data as Array<MarketplaceAppOverview>).sort((a, b) => a.title.localeCompare(b.title)),
placeholderData: Array(9).fill(MARKETPLACE_APP), placeholderData: Array(9).fill(MARKETPLACE_APP),
......
...@@ -5,7 +5,7 @@ import React from 'react'; ...@@ -5,7 +5,7 @@ import React from 'react';
import type { TokenType } from 'types/api/token'; import type { TokenType } from 'types/api/token';
import type { RoutedTab } from 'ui/shared/Tabs/types'; import type { RoutedTab } from 'ui/shared/Tabs/types';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import iconSuccess from 'icons/status/success.svg'; import iconSuccess from 'icons/status/success.svg';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/contexts/app'; import { useAppContext } from 'lib/contexts/app';
...@@ -59,7 +59,7 @@ const AddressPageContent = () => { ...@@ -59,7 +59,7 @@ const AddressPageContent = () => {
const tabs: Array<RoutedTab> = React.useMemo(() => { const tabs: Array<RoutedTab> = React.useMemo(() => {
return [ return [
{ id: 'txs', title: 'Transactions', component: <AddressTxs scrollRef={ tabsScrollRef }/> }, { id: 'txs', title: 'Transactions', component: <AddressTxs scrollRef={ tabsScrollRef }/> },
appConfig.beaconChain.hasBeaconChain && addressQuery.data?.has_beacon_chain_withdrawals ? config.features.beaconChain.isEnabled && addressQuery.data?.has_beacon_chain_withdrawals ?
{ id: 'withdrawals', title: 'Withdrawals', component: <AddressWithdrawals scrollRef={ tabsScrollRef }/> } : { id: 'withdrawals', title: 'Withdrawals', component: <AddressWithdrawals scrollRef={ tabsScrollRef }/> } :
undefined, undefined,
addressQuery.data?.has_token_transfers ? addressQuery.data?.has_token_transfers ?
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { PaginationParams } from 'ui/shared/pagination/types'; import type { PaginationParams } from 'ui/shared/pagination/types';
import type { RoutedTab } from 'ui/shared/Tabs/types'; import type { RoutedTab } from 'ui/shared/Tabs/types';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/contexts/app'; import { useAppContext } from 'lib/contexts/app';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
...@@ -62,7 +62,7 @@ const BlockPageContent = () => { ...@@ -62,7 +62,7 @@ const BlockPageContent = () => {
resourceName: 'block_withdrawals', resourceName: 'block_withdrawals',
pathParams: { height_or_hash: heightOrHash }, pathParams: { height_or_hash: heightOrHash },
options: { options: {
enabled: Boolean(!blockQuery.isPlaceholderData && blockQuery.data?.height && appConfig.beaconChain.hasBeaconChain && tab === 'withdrawals'), enabled: Boolean(!blockQuery.isPlaceholderData && blockQuery.data?.height && config.features.beaconChain.isEnabled && tab === 'withdrawals'),
placeholderData: generateListStub<'block_withdrawals'>(WITHDRAWAL, 50, { next_page_params: { placeholderData: generateListStub<'block_withdrawals'>(WITHDRAWAL, 50, { next_page_params: {
index: 5, index: 5,
items_count: 50, items_count: 50,
...@@ -81,7 +81,7 @@ const BlockPageContent = () => { ...@@ -81,7 +81,7 @@ const BlockPageContent = () => {
const tabs: Array<RoutedTab> = React.useMemo(() => ([ const tabs: Array<RoutedTab> = React.useMemo(() => ([
{ id: 'index', title: 'Details', component: <BlockDetails query={ blockQuery }/> }, { id: 'index', title: 'Details', component: <BlockDetails query={ blockQuery }/> },
{ id: 'txs', title: 'Transactions', component: <TxsContent query={ blockTxsQuery } showBlockInfo={ false } showSocketInfo={ false }/> }, { id: 'txs', title: 'Transactions', component: <TxsContent query={ blockTxsQuery } showBlockInfo={ false } showSocketInfo={ false }/> },
appConfig.beaconChain.hasBeaconChain && Boolean(blockQuery.data?.withdrawals_count) ? config.features.beaconChain.isEnabled && Boolean(blockQuery.data?.withdrawals_count) ?
{ id: 'withdrawals', title: 'Withdrawals', component: <BlockWithdrawals blockWithdrawalsQuery={ blockWithdrawalsQuery }/> } : { id: 'withdrawals', title: 'Withdrawals', component: <BlockWithdrawals blockWithdrawalsQuery={ blockWithdrawalsQuery }/> } :
null, null,
].filter(Boolean)), [ blockQuery, blockTxsQuery, blockWithdrawalsQuery ]); ].filter(Boolean)), [ blockQuery, blockTxsQuery, blockWithdrawalsQuery ]);
......
import { Box, Heading, Flex, LightMode } from '@chakra-ui/react'; import { Box, Heading, Flex, LightMode } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import ChainIndicators from 'ui/home/indicators/ChainIndicators'; import ChainIndicators from 'ui/home/indicators/ChainIndicators';
import LatestBlocks from 'ui/home/LatestBlocks'; import LatestBlocks from 'ui/home/LatestBlocks';
import Stats from 'ui/home/Stats'; import Stats from 'ui/home/Stats';
...@@ -16,7 +16,7 @@ const Home = () => { ...@@ -16,7 +16,7 @@ const Home = () => {
<Page isHomePage> <Page isHomePage>
<Box <Box
w="100%" w="100%"
background={ appConfig.homepage.plate.background } background={ config.UI.homepage.plate.background }
borderRadius="24px" borderRadius="24px"
padding={{ base: '24px', lg: '48px' }} padding={{ base: '24px', lg: '48px' }}
minW={{ base: 'unset', lg: '900px' }} minW={{ base: 'unset', lg: '900px' }}
...@@ -28,12 +28,12 @@ const Home = () => { ...@@ -28,12 +28,12 @@ const Home = () => {
size={{ base: 'md', lg: 'xl' }} size={{ base: 'md', lg: 'xl' }}
lineHeight={{ base: '32px', lg: '50px' }} lineHeight={{ base: '32px', lg: '50px' }}
fontWeight={ 600 } fontWeight={ 600 }
color={ appConfig.homepage.plate.textColor } color={ config.UI.homepage.plate.textColor }
> >
Welcome to { appConfig.network.name } explorer Welcome to { config.chain.name } explorer
</Heading> </Heading>
<Box display={{ base: 'none', lg: 'block' }}> <Box display={{ base: 'none', lg: 'block' }}>
{ appConfig.account.isEnabled && <ProfileMenuDesktop/> } { config.features.account.isEnabled && <ProfileMenuDesktop/> }
</Box> </Box>
</Flex> </Flex>
<LightMode> <LightMode>
......
...@@ -4,7 +4,7 @@ import mixpanel from 'mixpanel-browser'; ...@@ -4,7 +4,7 @@ import mixpanel from 'mixpanel-browser';
import type { ChangeEvent } from 'react'; import type { ChangeEvent } from 'react';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import useGradualIncrement from 'lib/hooks/useGradualIncrement'; import useGradualIncrement from 'lib/hooks/useGradualIncrement';
import useToast from 'lib/hooks/useToast'; import useToast from 'lib/hooks/useToast';
...@@ -21,7 +21,7 @@ const Login = () => { ...@@ -21,7 +21,7 @@ const Login = () => {
React.useEffect(() => { React.useEffect(() => {
const token = cookies.get(cookies.NAMES.API_TOKEN); const token = cookies.get(cookies.NAMES.API_TOKEN);
setFormVisibility(Boolean(!token && appConfig.account.isEnabled)); setFormVisibility(Boolean(!token && config.features.account.isEnabled));
}, []); }, []);
const checkSentry = React.useCallback(() => { const checkSentry = React.useCallback(() => {
......
import { Box, Icon, Link, Skeleton } from '@chakra-ui/react'; import { Box, Icon, Link, Skeleton } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import config from 'configs/app/config'; import config from 'configs/app';
import PlusIcon from 'icons/plus.svg'; import PlusIcon from 'icons/plus.svg';
import MarketplaceAppModal from 'ui/marketplace/MarketplaceAppModal'; import MarketplaceAppModal from 'ui/marketplace/MarketplaceAppModal';
import MarketplaceCategoriesMenu from 'ui/marketplace/MarketplaceCategoriesMenu'; import MarketplaceCategoriesMenu from 'ui/marketplace/MarketplaceCategoriesMenu';
...@@ -74,7 +74,7 @@ const Marketplace = () => { ...@@ -74,7 +74,7 @@ const Marketplace = () => {
/> />
) } ) }
{ config.marketplace.submitForm && ( { config.features.marketplace.isEnabled && (
<Skeleton <Skeleton
isLoaded={ !isPlaceholderData } isLoaded={ !isPlaceholderData }
marginTop={{ base: 8, sm: 16 }} marginTop={{ base: 8, sm: 16 }}
...@@ -84,7 +84,7 @@ const Marketplace = () => { ...@@ -84,7 +84,7 @@ const Marketplace = () => {
fontWeight="bold" fontWeight="bold"
display="inline-flex" display="inline-flex"
alignItems="baseline" alignItems="baseline"
href={ config.marketplace.submitForm } href={ config.features.marketplace.submitFormUrl }
isExternal isExternal
> >
<Icon <Icon
......
...@@ -6,7 +6,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; ...@@ -6,7 +6,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
import type { MarketplaceAppOverview } from 'types/client/marketplace'; import type { MarketplaceAppOverview } from 'types/client/marketplace';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import { useAppContext } from 'lib/contexts/app'; import { useAppContext } from 'lib/contexts/app';
import useApiFetch from 'lib/hooks/useFetch'; import useApiFetch from 'lib/hooks/useFetch';
...@@ -33,7 +33,7 @@ const MarketplaceApp = () => { ...@@ -33,7 +33,7 @@ const MarketplaceApp = () => {
const { isLoading, isError, error, data } = useQuery<unknown, ResourceError<unknown>, MarketplaceAppOverview>( const { isLoading, isError, error, data } = useQuery<unknown, ResourceError<unknown>, MarketplaceAppOverview>(
[ 'marketplace-apps', id ], [ 'marketplace-apps', id ],
async() => { async() => {
const result = await apiFetch<Array<MarketplaceAppOverview>, unknown>(appConfig.marketplace.configUrl || ''); const result = await apiFetch<Array<MarketplaceAppOverview>, unknown>(config.features.marketplace.configUrl);
if (!Array.isArray(result)) { if (!Array.isArray(result)) {
throw result; throw result;
} }
...@@ -58,13 +58,13 @@ const MarketplaceApp = () => { ...@@ -58,13 +58,13 @@ const MarketplaceApp = () => {
if (data && !isFrameLoading) { if (data && !isFrameLoading) {
const message = { const message = {
blockscoutColorMode: colorMode, blockscoutColorMode: colorMode,
blockscoutRootUrl: appConfig.app.baseUrl + route({ pathname: '/' }), blockscoutRootUrl: config.app.baseUrl + route({ pathname: '/' }),
blockscoutAddressExplorerUrl: appConfig.app.baseUrl + route({ pathname: '/address/[hash]', query: { hash: '' } }), blockscoutAddressExplorerUrl: config.app.baseUrl + route({ pathname: '/address/[hash]', query: { hash: '' } }),
blockscoutTransactionExplorerUrl: appConfig.app.baseUrl + route({ pathname: '/tx/[hash]', query: { hash: '' } }), blockscoutTransactionExplorerUrl: config.app.baseUrl + route({ pathname: '/tx/[hash]', query: { hash: '' } }),
blockscoutNetworkName: appConfig.network.name, blockscoutNetworkName: config.chain.name,
blockscoutNetworkId: Number(appConfig.network.id), blockscoutNetworkId: Number(config.chain.id),
blockscoutNetworkCurrency: appConfig.network.currency, blockscoutNetworkCurrency: config.chain.currency,
blockscoutNetworkRpc: appConfig.network.rpcUrl, blockscoutNetworkRpc: config.chain.rpcUrl,
}; };
ref?.current?.contentWindow?.postMessage(message, data.url); ref?.current?.contentWindow?.postMessage(message, data.url);
......
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
...@@ -26,7 +26,7 @@ const Stats = () => { ...@@ -26,7 +26,7 @@ const Stats = () => {
return ( return (
<Page> <Page>
<PageTitle title={ `${ appConfig.network.name } stats` }/> <PageTitle title={ `${ config.chain.name } stats` }/>
<Box mb={{ base: 6, sm: 8 }}> <Box mb={{ base: 6, sm: 8 }}>
<NumberWidgetsList/> <NumberWidgetsList/>
......
...@@ -8,7 +8,7 @@ import type { TokenInfo } from 'types/api/token'; ...@@ -8,7 +8,7 @@ import type { TokenInfo } from 'types/api/token';
import type { PaginationParams } from 'ui/shared/pagination/types'; import type { PaginationParams } from 'ui/shared/pagination/types';
import type { RoutedTab } from 'ui/shared/Tabs/types'; import type { RoutedTab } from 'ui/shared/Tabs/types';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import iconSuccess from 'icons/status/success.svg'; import iconSuccess from 'icons/status/success.svg';
import iconVerifiedToken from 'icons/verified_token.svg'; import iconVerifiedToken from 'icons/verified_token.svg';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
...@@ -145,10 +145,9 @@ const TokenPageContent = () => { ...@@ -145,10 +145,9 @@ const TokenPageContent = () => {
}, },
}); });
const isVerifiedInfoEnabled = Boolean(appConfig.contractInfoApi.endpoint);
const verifiedInfoQuery = useApiQuery('token_verified_info', { const verifiedInfoQuery = useApiQuery('token_verified_info', {
pathParams: { hash: hashString, chainId: appConfig.network.id }, pathParams: { hash: hashString, chainId: config.chain.id },
queryOptions: { enabled: Boolean(tokenQuery.data) && isVerifiedInfoEnabled }, queryOptions: { enabled: Boolean(tokenQuery.data) && config.features.verifiedTokens.isEnabled },
}); });
const contractTabs = useContractTabs(contractQuery.data); const contractTabs = useContractTabs(contractQuery.data);
...@@ -223,7 +222,7 @@ const TokenPageContent = () => { ...@@ -223,7 +222,7 @@ const TokenPageContent = () => {
const titleContentAfter = ( const titleContentAfter = (
<> <>
{ verifiedInfoQuery.data?.tokenAddress && ( { verifiedInfoQuery.data?.tokenAddress && (
<Tooltip label={ `Information on this token has been verified by ${ appConfig.network.name }` }> <Tooltip label={ `Information on this token has been verified by ${ config.chain.name }` }>
<Box boxSize={ 6 }> <Box boxSize={ 6 }>
<Icon as={ iconVerifiedToken } color="green.500" boxSize={ 6 } cursor="pointer"/> <Icon as={ iconVerifiedToken } color="green.500" boxSize={ 6 } cursor="pointer"/>
</Box> </Box>
...@@ -266,7 +265,7 @@ const TokenPageContent = () => { ...@@ -266,7 +265,7 @@ const TokenPageContent = () => {
contentAfter={ titleContentAfter } contentAfter={ titleContentAfter }
/> />
<TokenContractInfo tokenQuery={ tokenQuery } contractQuery={ contractQuery }/> <TokenContractInfo tokenQuery={ tokenQuery } contractQuery={ contractQuery }/>
<TokenVerifiedInfo verifiedInfoQuery={ verifiedInfoQuery } isVerifiedInfoEnabled={ isVerifiedInfoEnabled }/> <TokenVerifiedInfo verifiedInfoQuery={ verifiedInfoQuery }/>
<TokenDetails tokenQuery={ tokenQuery }/> <TokenDetails tokenQuery={ tokenQuery }/>
{ /* should stay before tabs to scroll up with pagination */ } { /* should stay before tabs to scroll up with pagination */ }
<Box ref={ scrollRef }></Box> <Box ref={ scrollRef }></Box>
......
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import type { RoutedTab } from 'ui/shared/Tabs/types'; import type { RoutedTab } from 'ui/shared/Tabs/types';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import useHasAccount from 'lib/hooks/useHasAccount'; import useHasAccount from 'lib/hooks/useHasAccount';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useNewTxsSocket from 'lib/hooks/useNewTxsSocket'; import useNewTxsSocket from 'lib/hooks/useNewTxsSocket';
...@@ -23,7 +23,7 @@ const TAB_LIST_PROPS = { ...@@ -23,7 +23,7 @@ const TAB_LIST_PROPS = {
}; };
const Transactions = () => { const Transactions = () => {
const verifiedTitle = appConfig.network.verificationType === 'validation' ? 'Validated' : 'Mined'; const verifiedTitle = config.chain.verificationType === 'validation' ? 'Validated' : 'Mined';
const router = useRouter(); const router = useRouter();
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const txsQuery = useQueryWithPages({ const txsQuery = useQueryWithPages({
......
...@@ -5,7 +5,7 @@ import React from 'react'; ...@@ -5,7 +5,7 @@ import React from 'react';
import type { VerifiedAddress, TokenInfoApplication, TokenInfoApplications, VerifiedAddressResponse } from 'types/api/account'; import type { VerifiedAddress, TokenInfoApplication, TokenInfoApplications, VerifiedAddressResponse } from 'types/api/account';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken'; import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
...@@ -37,13 +37,13 @@ const VerifiedAddresses = () => { ...@@ -37,13 +37,13 @@ const VerifiedAddresses = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const addressesQuery = useApiQuery('verified_addresses', { const addressesQuery = useApiQuery('verified_addresses', {
pathParams: { chainId: appConfig.network.id }, pathParams: { chainId: config.chain.id },
queryOptions: { queryOptions: {
placeholderData: { verifiedAddresses: Array(3).fill(VERIFIED_ADDRESS) }, placeholderData: { verifiedAddresses: Array(3).fill(VERIFIED_ADDRESS) },
}, },
}); });
const applicationsQuery = useApiQuery('token_info_applications', { const applicationsQuery = useApiQuery('token_info_applications', {
pathParams: { chainId: appConfig.network.id, id: undefined }, pathParams: { chainId: config.chain.id, id: undefined },
queryOptions: { queryOptions: {
placeholderData: { submissions: Array(3).fill(TOKEN_INFO_APPLICATION) }, placeholderData: { submissions: Array(3).fill(TOKEN_INFO_APPLICATION) },
select: (data) => { select: (data) => {
...@@ -70,7 +70,7 @@ const VerifiedAddresses = () => { ...@@ -70,7 +70,7 @@ const VerifiedAddresses = () => {
const handleAddressSubmit = React.useCallback((newItem: VerifiedAddress) => { const handleAddressSubmit = React.useCallback((newItem: VerifiedAddress) => {
queryClient.setQueryData( queryClient.setQueryData(
getResourceKey('verified_addresses', { pathParams: { chainId: appConfig.network.id } }), getResourceKey('verified_addresses', { pathParams: { chainId: config.chain.id } }),
(prevData: VerifiedAddressResponse | undefined) => { (prevData: VerifiedAddressResponse | undefined) => {
if (!prevData) { if (!prevData) {
return { verifiedAddresses: [ newItem ] }; return { verifiedAddresses: [ newItem ] };
...@@ -85,7 +85,7 @@ const VerifiedAddresses = () => { ...@@ -85,7 +85,7 @@ const VerifiedAddresses = () => {
const handleApplicationSubmit = React.useCallback((newItem: TokenInfoApplication) => { const handleApplicationSubmit = React.useCallback((newItem: TokenInfoApplication) => {
setSelectedAddress(undefined); setSelectedAddress(undefined);
queryClient.setQueryData( queryClient.setQueryData(
getResourceKey('token_info_applications', { pathParams: { chainId: appConfig.network.id, id: undefined } }), getResourceKey('token_info_applications', { pathParams: { chainId: config.chain.id, id: undefined } }),
(prevData: TokenInfoApplications | undefined) => { (prevData: TokenInfoApplications | undefined) => {
if (!prevData) { if (!prevData) {
return { submissions: [ newItem ] }; return { submissions: [ newItem ] };
...@@ -174,7 +174,7 @@ const VerifiedAddresses = () => { ...@@ -174,7 +174,7 @@ const VerifiedAddresses = () => {
Before starting, make sure that: Before starting, make sure that:
</chakra.p> </chakra.p>
<OrderedList ml={ 6 }> <OrderedList ml={ 6 }>
<ListItem>The source code for the smart contract is deployed on “{ appConfig.network.name }”.</ListItem> <ListItem>The source code for the smart contract is deployed on “{ config.chain.name }”.</ListItem>
<ListItem> <ListItem>
<span>The source code is verified (if not yet verified, you can use </span> <span>The source code is verified (if not yet verified, you can use </span>
<Link href="https://docs.blockscout.com/for-users/verifying-a-smart-contract" target="_blank">this tool</Link> <Link href="https://docs.blockscout.com/for-users/verifying-a-smart-contract" target="_blank">this tool</Link>
......
...@@ -2,7 +2,7 @@ import { Flex, Hide, Show, Skeleton, Text } from '@chakra-ui/react'; ...@@ -2,7 +2,7 @@ import { Flex, Hide, Show, Skeleton, Text } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import getCurrencyValue from 'lib/getCurrencyValue'; import getCurrencyValue from 'lib/getCurrencyValue';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
...@@ -68,7 +68,8 @@ const Withdrawals = () => { ...@@ -68,7 +68,8 @@ const Withdrawals = () => {
const { valueStr } = getCurrencyValue({ value: countersQuery.data.withdrawal_sum }); const { valueStr } = getCurrencyValue({ value: countersQuery.data.withdrawal_sum });
return ( return (
<Text mb={{ base: 6, lg: pagination.isVisible ? 0 : 6 }} lineHeight={{ base: '24px', lg: '32px' }}> <Text mb={{ base: 6, lg: pagination.isVisible ? 0 : 6 }} lineHeight={{ base: '24px', lg: '32px' }}>
{ BigNumber(countersQuery.data.withdrawal_count).toFormat() } withdrawals processed and { valueStr } { appConfig.beaconChain.currencySymbol } withdrawn { BigNumber(countersQuery.data.withdrawal_count).toFormat() } withdrawals processed
and { valueStr } { config.features.beaconChain.currency.symbol } withdrawn
</Text> </Text>
); );
})(); })();
......
...@@ -2,7 +2,7 @@ import { Button, Menu, MenuButton, MenuList, Icon, Flex, Skeleton } from '@chakr ...@@ -2,7 +2,7 @@ import { Button, Menu, MenuButton, MenuList, Icon, Flex, Skeleton } from '@chakr
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import iconArrow from 'icons/arrows/east-mini.svg'; import iconArrow from 'icons/arrows/east-mini.svg';
import useIsAccountActionAllowed from 'lib/hooks/useIsAccountActionAllowed'; import useIsAccountActionAllowed from 'lib/hooks/useIsAccountActionAllowed';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
...@@ -37,7 +37,7 @@ const AddressActions = ({ isLoading }: Props) => { ...@@ -37,7 +37,7 @@ const AddressActions = ({ isLoading }: Props) => {
</MenuButton> </MenuButton>
</Skeleton> </Skeleton>
<MenuList minWidth="180px" zIndex="popover"> <MenuList minWidth="180px" zIndex="popover">
{ isTokenPage && appConfig.contractInfoApi.endpoint && appConfig.adminServiceApi.endpoint && appConfig.account.isEnabled && { isTokenPage && config.features.addressVerification.isEnabled &&
<TokenInfoMenuItem py={ 2 } px={ 4 } hash={ hash } onBeforeClick={ isAccountActionAllowed }/> } <TokenInfoMenuItem py={ 2 } px={ 4 } hash={ hash } onBeforeClick={ isAccountActionAllowed }/> }
<PrivateTagMenuItem py={ 2 } px={ 4 } hash={ hash } onBeforeClick={ isAccountActionAllowed }/> <PrivateTagMenuItem py={ 2 } px={ 4 } hash={ hash } onBeforeClick={ isAccountActionAllowed }/>
<PublicTagMenuItem py={ 2 } px={ 4 } hash={ hash } onBeforeClick={ isAccountActionAllowed }/> <PublicTagMenuItem py={ 2 } px={ 4 } hash={ hash } onBeforeClick={ isAccountActionAllowed }/>
......
...@@ -3,7 +3,7 @@ import { useRouter } from 'next/router'; ...@@ -3,7 +3,7 @@ import { useRouter } from 'next/router';
import type { Route } from 'nextjs-routes'; import type { Route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import iconEdit from 'icons/edit.svg'; import iconEdit from 'icons/edit.svg';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import useHasAccount from 'lib/hooks/useHasAccount'; import useHasAccount from 'lib/hooks/useHasAccount';
...@@ -21,19 +21,19 @@ const TokenInfoMenuItem = ({ className, hash, onBeforeClick }: Props) => { ...@@ -21,19 +21,19 @@ const TokenInfoMenuItem = ({ className, hash, onBeforeClick }: Props) => {
const isAuth = useHasAccount(); const isAuth = useHasAccount();
const verifiedAddressesQuery = useApiQuery('verified_addresses', { const verifiedAddressesQuery = useApiQuery('verified_addresses', {
pathParams: { chainId: appConfig.network.id }, pathParams: { chainId: config.chain.id },
queryOptions: { queryOptions: {
enabled: isAuth, enabled: isAuth,
}, },
}); });
const applicationsQuery = useApiQuery('token_info_applications', { const applicationsQuery = useApiQuery('token_info_applications', {
pathParams: { chainId: appConfig.network.id, id: undefined }, pathParams: { chainId: config.chain.id, id: undefined },
queryOptions: { queryOptions: {
enabled: isAuth, enabled: isAuth,
}, },
}); });
const tokenInfoQuery = useApiQuery('token_verified_info', { const tokenInfoQuery = useApiQuery('token_verified_info', {
pathParams: { hash, chainId: appConfig.network.id }, pathParams: { hash, chainId: config.chain.id },
queryOptions: { queryOptions: {
refetchOnMount: false, refetchOnMount: false,
}, },
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { Address } from 'types/api/address'; import type { Address } from 'types/api/address';
import type { TokenInfo } from 'types/api/token'; import type { TokenInfo } from 'types/api/token';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import AddressFavoriteButton from 'ui/address/details/AddressFavoriteButton'; import AddressFavoriteButton from 'ui/address/details/AddressFavoriteButton';
import AddressQrCode from 'ui/address/details/AddressQrCode'; import AddressQrCode from 'ui/address/details/AddressQrCode';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet'; import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
...@@ -36,11 +36,11 @@ const AddressHeadingInfo = ({ address, token, isLinkDisabled, isLoading }: Props ...@@ -36,11 +36,11 @@ const AddressHeadingInfo = ({ address, token, isLinkDisabled, isLoading }: Props
/> />
<CopyToClipboard text={ address.hash } isLoading={ isLoading }/> <CopyToClipboard text={ address.hash } isLoading={ isLoading }/>
{ !isLoading && address.is_contract && token && <AddressAddToWallet ml={ 2 } token={ token }/> } { !isLoading && address.is_contract && token && <AddressAddToWallet ml={ 2 } token={ token }/> }
{ !isLoading && !address.is_contract && appConfig.account.isEnabled && ( { !isLoading && !address.is_contract && config.features.account.isEnabled && (
<AddressFavoriteButton hash={ address.hash } watchListId={ address.watchlist_address_id } ml={ 3 }/> <AddressFavoriteButton hash={ address.hash } watchListId={ address.watchlist_address_id } ml={ 3 }/>
) } ) }
<AddressQrCode hash={ address.hash } ml={ 2 } isLoading={ isLoading } flexShrink={ 0 }/> <AddressQrCode hash={ address.hash } ml={ 2 } isLoading={ isLoading } flexShrink={ 0 }/>
{ appConfig.account.isEnabled && <AddressActionsMenu isLoading={ isLoading }/> } { config.features.account.isEnabled && <AddressActionsMenu isLoading={ isLoading }/> }
</Flex> </Flex>
); );
}; };
......
...@@ -2,7 +2,7 @@ import { Box, Heading, Icon, Text, chakra } from '@chakra-ui/react'; ...@@ -2,7 +2,7 @@ import { Box, Heading, Icon, Text, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import ReCaptcha from 'react-google-recaptcha'; import ReCaptcha from 'react-google-recaptcha';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import icon429 from 'icons/error-pages/429.svg'; import icon429 from 'icons/error-pages/429.svg';
import buildUrl from 'lib/api/buildUrl'; import buildUrl from 'lib/api/buildUrl';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
...@@ -60,10 +60,10 @@ const AppErrorTooManyRequests = ({ className }: Props) => { ...@@ -60,10 +60,10 @@ const AppErrorTooManyRequests = ({ className }: Props) => {
<Text variant="secondary" mt={ 3 }> <Text variant="secondary" mt={ 3 }>
You have exceeded the request rate for a given time period. Please reduce the number of requests and try again soon. You have exceeded the request rate for a given time period. Please reduce the number of requests and try again soon.
</Text> </Text>
{ appConfig.reCaptcha.siteKey && ( { config.services.reCaptcha.siteKey && (
<ReCaptcha <ReCaptcha
className="recaptcha" className="recaptcha"
sitekey={ appConfig.reCaptcha.siteKey } sitekey={ config.services.reCaptcha.siteKey }
onChange={ handleReCaptchaChange } onChange={ handleReCaptchaChange }
/> />
) } ) }
......
import Script from 'next/script'; import Script from 'next/script';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
const GoogleAnalytics = () => { const GoogleAnalytics = () => {
if (!appConfig.googleAnalytics.propertyId) { if (!config.features.googleAnalytics.isEnabled) {
return null; return null;
} }
const id = appConfig.googleAnalytics.propertyId; const id = config.features.googleAnalytics.propertyId;
return ( return (
<> <>
......
import { Button, Icon, chakra } from '@chakra-ui/react'; import { Button, Icon, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import useToast from 'lib/hooks/useToast'; import useToast from 'lib/hooks/useToast';
import useProvider from 'lib/web3/useProvider'; import useProvider from 'lib/web3/useProvider';
import { WALLETS_INFO } from 'lib/web3/wallets'; import { WALLETS_INFO } from 'lib/web3/wallets';
...@@ -16,24 +16,24 @@ const NetworkAddToWallet = ({ className }: Props) => { ...@@ -16,24 +16,24 @@ const NetworkAddToWallet = ({ className }: Props) => {
const handleClick = React.useCallback(async() => { const handleClick = React.useCallback(async() => {
try { try {
const hexadecimalChainId = '0x' + Number(appConfig.network.id).toString(16); const hexadecimalChainId = '0x' + Number(config.chain.id).toString(16);
const config = { const params = {
method: 'wallet_addEthereumChain', method: 'wallet_addEthereumChain',
params: [ { params: [ {
chainId: hexadecimalChainId, chainId: hexadecimalChainId,
chainName: appConfig.network.name, chainName: config.chain.name,
nativeCurrency: { nativeCurrency: {
name: appConfig.network.currency.name, name: config.chain.currency.name,
symbol: appConfig.network.currency.symbol, symbol: config.chain.currency.symbol,
decimals: appConfig.network.currency.decimals, decimals: config.chain.currency.decimals,
}, },
rpcUrls: [ appConfig.network.rpcUrl ], rpcUrls: [ config.chain.rpcUrl ],
blockExplorerUrls: [ appConfig.app.baseUrl ], blockExplorerUrls: [ config.app.baseUrl ],
} ], } ],
// in wagmi types for wallet_addEthereumChain method is not provided // in wagmi types for wallet_addEthereumChain method is not provided
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any; } as any;
await provider?.request?.(config); await provider?.request?.(params);
toast({ toast({
position: 'top-right', position: 'top-right',
title: 'Success', title: 'Success',
...@@ -54,16 +54,20 @@ const NetworkAddToWallet = ({ className }: Props) => { ...@@ -54,16 +54,20 @@ const NetworkAddToWallet = ({ className }: Props) => {
} }
}, [ provider, toast ]); }, [ provider, toast ]);
if (!provider || !appConfig.network.rpcUrl) { if (!provider || !config.chain.rpcUrl) {
return null; return null;
} }
const defaultWallet = appConfig.web3.defaultWallet; const defaultWallet = config.features.web3Wallet.defaultWallet;
if (defaultWallet === 'none') {
return null;
}
return ( return (
<Button variant="outline" size="sm" onClick={ handleClick } className={ className }> <Button variant="outline" size="sm" onClick={ handleClick } className={ className }>
<Icon as={ WALLETS_INFO[defaultWallet].icon } boxSize={ 5 } mr={ 2 }/> <Icon as={ WALLETS_INFO[defaultWallet].icon } boxSize={ 5 } mr={ 2 }/>
Add { appConfig.network.name } Add { config.chain.name }
</Button> </Button>
); );
}; };
......
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import type { NetworkExplorer as TNetworkExplorer } from 'types/networks'; import type { NetworkExplorer as TNetworkExplorer } from 'types/networks';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import arrowIcon from 'icons/arrows/east-mini.svg'; import arrowIcon from 'icons/arrows/east-mini.svg';
import explorerIcon from 'icons/explorer.svg'; import explorerIcon from 'icons/explorer.svg';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/LinkExternal';
...@@ -18,7 +18,7 @@ interface Props { ...@@ -18,7 +18,7 @@ interface Props {
const NetworkExplorers = ({ className, type, pathParam, hideText }: Props) => { const NetworkExplorers = ({ className, type, pathParam, hideText }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure(); const { isOpen, onToggle, onClose } = useDisclosure();
const explorersLinks = appConfig.network.explorers const explorersLinks = config.UI.explorers.items
.filter((explorer) => explorer.paths[type]) .filter((explorer) => explorer.paths[type])
.map((explorer) => { .map((explorer) => {
const url = new URL(explorer.paths[type] + '/' + pathParam, explorer.baseUrl); const url = new URL(explorer.paths[type] + '/' + pathParam, explorer.baseUrl);
......
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import IndexingAlertBlocks from 'ui/home/IndexingAlertBlocks'; import IndexingAlertBlocks from 'ui/home/IndexingAlertBlocks';
interface Props { interface Props {
...@@ -18,7 +18,7 @@ const PageContent = ({ children, isHomePage }: Props) => { ...@@ -18,7 +18,7 @@ const PageContent = ({ children, isHomePage }: Props) => {
paddingBottom={ 10 } paddingBottom={ 10 }
paddingTop={{ base: isHomePage ? '88px' : '138px', lg: 0 }} paddingTop={{ base: isHomePage ? '88px' : '138px', lg: 0 }}
> >
{ !appConfig.hideIndexingAlert && <IndexingAlertBlocks display={{ base: 'block', lg: 'none' }}/> } { !config.UI.indexingAlert.isHidden && <IndexingAlertBlocks display={{ base: 'block', lg: 'none' }}/> }
{ children } { children }
</Box> </Box>
); );
......
...@@ -6,7 +6,7 @@ import type { TokenInfo } from 'types/api/token'; ...@@ -6,7 +6,7 @@ import type { TokenInfo } from 'types/api/token';
import TokenLogoPlaceholder from 'ui/shared/TokenLogoPlaceholder'; import TokenLogoPlaceholder from 'ui/shared/TokenLogoPlaceholder';
export interface Props { export interface Props {
data?: Pick<TokenInfo, 'address' | 'icon_url' | 'name'>; data?: Pick<TokenInfo, | 'icon_url' | 'name'>;
className?: string; className?: string;
isLoading?: boolean; isLoading?: boolean;
} }
......
...@@ -6,35 +6,35 @@ import React from 'react'; ...@@ -6,35 +6,35 @@ import React from 'react';
import type { Chain } from 'wagmi'; import type { Chain } from 'wagmi';
import { configureChains, createConfig, WagmiConfig } from 'wagmi'; import { configureChains, createConfig, WagmiConfig } from 'wagmi';
import appConfig from 'configs/app/config'; import config from 'configs/app';
const getConfig = () => { const getConfig = () => {
try { try {
if (!appConfig.walletConnect.projectId) { if (!config.features.blockchainInteraction.walletConnect.projectId) {
throw new Error('WalletConnect Project ID is not set'); throw new Error('WalletConnect Project ID is not set');
} }
const currentChain: Chain = { const currentChain: Chain = {
id: Number(appConfig.network.id), id: Number(config.chain.id),
name: appConfig.network.name || '', name: config.chain.name || '',
network: appConfig.network.name || '', network: config.chain.name || '',
nativeCurrency: { nativeCurrency: {
decimals: appConfig.network.currency.decimals, decimals: config.chain.currency.decimals,
name: appConfig.network.currency.name || '', name: config.chain.currency.name || '',
symbol: appConfig.network.currency.symbol || '', symbol: config.chain.currency.symbol || '',
}, },
rpcUrls: { rpcUrls: {
'public': { 'public': {
http: [ appConfig.network.rpcUrl || '' ], http: [ config.chain.rpcUrl || '' ],
}, },
'default': { 'default': {
http: [ appConfig.network.rpcUrl || '' ], http: [ config.chain.rpcUrl || '' ],
}, },
}, },
blockExplorers: { blockExplorers: {
'default': { 'default': {
name: 'Blockscout', name: 'Blockscout',
url: appConfig.app.baseUrl, url: config.app.baseUrl,
}, },
}, },
}; };
...@@ -44,13 +44,13 @@ const getConfig = () => { ...@@ -44,13 +44,13 @@ const getConfig = () => {
const { publicClient } = configureChains(chains, [ const { publicClient } = configureChains(chains, [
jsonRpcProvider({ jsonRpcProvider({
rpc: () => ({ rpc: () => ({
http: appConfig.network.rpcUrl || '', http: config.chain.rpcUrl || '',
}), }),
}), }),
]); ]);
const wagmiConfig = createConfig({ const wagmiConfig = createConfig({
autoConnect: true, autoConnect: true,
connectors: w3mConnectors({ projectId: appConfig.walletConnect.projectId, chains }), connectors: w3mConnectors({ projectId: config.features.blockchainInteraction.walletConnect.projectId, chains }),
publicClient, publicClient,
}); });
const ethereumClient = new EthereumClient(wagmiConfig, chains); const ethereumClient = new EthereumClient(wagmiConfig, chains);
...@@ -72,7 +72,7 @@ const Web3ModalProvider = ({ children, fallback }: Props) => { ...@@ -72,7 +72,7 @@ const Web3ModalProvider = ({ children, fallback }: Props) => {
const modalZIndex = useToken<string>('zIndices', 'modal'); const modalZIndex = useToken<string>('zIndices', 'modal');
const web3ModalTheme = useColorModeValue('light', 'dark'); const web3ModalTheme = useColorModeValue('light', 'dark');
if (!wagmiConfig || !ethereumClient || !appConfig.walletConnect.projectId) { if (!wagmiConfig || !ethereumClient || !config.features.blockchainInteraction.isEnabled) {
return typeof fallback === 'function' ? fallback() : (fallback || null); return typeof fallback === 'function' ? fallback() : (fallback || null);
} }
...@@ -82,7 +82,7 @@ const Web3ModalProvider = ({ children, fallback }: Props) => { ...@@ -82,7 +82,7 @@ const Web3ModalProvider = ({ children, fallback }: Props) => {
{ children } { children }
</WagmiConfig> </WagmiConfig>
<Web3Modal <Web3Modal
projectId={ appConfig.walletConnect.projectId } projectId={ config.features.blockchainInteraction.walletConnect.projectId }
ethereumClient={ ethereumClient } ethereumClient={ ethereumClient }
themeMode={ web3ModalTheme } themeMode={ web3ModalTheme }
themeVariables={{ themeVariables={{
......
import { chakra, Skeleton } from '@chakra-ui/react'; import { chakra, Skeleton } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import { useAppContext } from 'lib/contexts/app'; import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
...@@ -12,12 +12,12 @@ import SliseBanner from './SliseBanner'; ...@@ -12,12 +12,12 @@ import SliseBanner from './SliseBanner';
const AdBanner = ({ className, isLoading }: { className?: string; isLoading?: boolean }) => { const AdBanner = ({ className, isLoading }: { className?: string; isLoading?: boolean }) => {
const hasAdblockCookie = cookies.get(cookies.NAMES.ADBLOCK_DETECTED, useAppContext().cookies); const hasAdblockCookie = cookies.get(cookies.NAMES.ADBLOCK_DETECTED, useAppContext().cookies);
if (appConfig.ad.adBannerProvider === 'none' || hasAdblockCookie) { if (!config.features.adsBanner.isEnabled || hasAdblockCookie) {
return null; return null;
} }
const content = (() => { const content = (() => {
switch (appConfig.ad.adBannerProvider) { switch (config.features.adsBanner.provider) {
case 'adbutler': case 'adbutler':
return <AdbutlerBanner/>; return <AdbutlerBanner/>;
case 'coinzilla': case 'coinzilla':
...@@ -32,7 +32,7 @@ const AdBanner = ({ className, isLoading }: { className?: string; isLoading?: bo ...@@ -32,7 +32,7 @@ const AdBanner = ({ className, isLoading }: { className?: string; isLoading?: bo
className={ className } className={ className }
isLoaded={ !isLoading } isLoaded={ !isLoading }
borderRadius="none" borderRadius="none"
maxW={ appConfig.ad.adBannerProvider === 'adbutler' ? appConfig.ad.adButlerConfigDesktop?.width : '728px' } maxW={ config.features.adsBanner.provider === 'adbutler' ? config.features.adsBanner.adButler.config.desktop?.width : '728px' }
w="100%" w="100%"
> >
{ content } { content }
......
...@@ -3,7 +3,7 @@ import { useRouter } from 'next/navigation'; ...@@ -3,7 +3,7 @@ import { useRouter } from 'next/navigation';
import Script from 'next/script'; import Script from 'next/script';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import isBrowser from 'lib/isBrowser'; import isBrowser from 'lib/isBrowser';
import { connectAdbutler, placeAd, ADBUTLER_ACCOUNT } from 'ui/shared/ad/adbutlerScript'; import { connectAdbutler, placeAd, ADBUTLER_ACCOUNT } from 'ui/shared/ad/adbutlerScript';
...@@ -19,16 +19,22 @@ const AdbutlerBanner = ({ className }: { className?: string }) => { ...@@ -19,16 +19,22 @@ const AdbutlerBanner = ({ className }: { className?: string }) => {
} }
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: // @ts-ignore:
let plc = window[`plc${ appConfig.ad.adButlerConfigMobile?.id }`] || 0; let plc = window[`plc${ config.features.adsBanner.adButler.config.mobile?.id }`] || 0;
const config = isMobile ? appConfig.ad.adButlerConfigMobile : appConfig.ad.adButlerConfigDesktop; const adButlerConfig = isMobile ? config.features.adsBanner.adButler.config.mobile : config.features.adsBanner.adButler.config.desktop;
const banner = document.getElementById('ad-banner'); const banner = document.getElementById('ad-banner');
if (banner) { if (banner) {
banner.innerHTML = '<' + 'div id="placement_' + config?.id + '_' + plc + '"></' + 'div>'; banner.innerHTML = '<' + 'div id="placement_' + adButlerConfig?.id + '_' + plc + '"></' + 'div>';
} }
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: // @ts-ignore:
window.AdButler.ads.push({ handler: function(opt) { window.AdButler.ads.push({ handler: function(opt) {
window.AdButler.register(ADBUTLER_ACCOUNT, config?.id, [ config?.width, config?.height ], `placement_${ config?.id }_` + opt.place, opt); window.AdButler.register(
ADBUTLER_ACCOUNT,
adButlerConfig?.id,
[ adButlerConfig?.width, adButlerConfig?.height ],
`placement_${ adButlerConfig?.id }_` + opt.place,
opt,
);
}, opt: { place: plc++, keywords: abkw, domain: 'servedbyadbutler.com', click: 'CLICK_MACRO_PLACEHOLDER' } }); }, opt: { place: plc++, keywords: abkw, domain: 'servedbyadbutler.com', click: 'CLICK_MACRO_PLACEHOLDER' } });
} }
}, [ router, isMobile ]); }, [ router, isMobile ]);
......
...@@ -2,7 +2,7 @@ import { Flex, chakra } from '@chakra-ui/react'; ...@@ -2,7 +2,7 @@ import { Flex, chakra } from '@chakra-ui/react';
import { SliseAd } from '@slise/embed-react'; import { SliseAd } from '@slise/embed-react';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
const SliseBanner = ({ className }: { className?: string }) => { const SliseBanner = ({ className }: { className?: string }) => {
...@@ -10,14 +10,14 @@ const SliseBanner = ({ className }: { className?: string }) => { ...@@ -10,14 +10,14 @@ const SliseBanner = ({ className }: { className?: string }) => {
<> <>
<Flex className={ className } h="90px" display={{ base: 'none', lg: 'flex' }}> <Flex className={ className } h="90px" display={{ base: 'none', lg: 'flex' }}>
<SliseAd <SliseAd
slotId={ appConfig.network.name || '' } slotId={ config.chain.name || '' }
pub="pub-10" pub="pub-10"
format="728x90" format="728x90"
style={{ width: '728px', height: '90px' }}/> style={{ width: '728px', height: '90px' }}/>
</Flex> </Flex>
<Flex className={ className } h="90px" display={{ base: 'flex', lg: 'none' }}> <Flex className={ className } h="90px" display={{ base: 'flex', lg: 'none' }}>
<SliseAd <SliseAd
slotId={ appConfig.network.name || '' } slotId={ config.chain.name || '' }
pub="pub-10" pub="pub-10"
format="270x90" format="270x90"
style={{ width: '270px', height: '90px' }}/> style={{ width: '270px', height: '90px' }}/>
......
import { chakra } from '@chakra-ui/react'; import { chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import { useAppContext } from 'lib/contexts/app'; import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
...@@ -10,7 +10,7 @@ import CoinzillaTextAd from './CoinzillaTextAd'; ...@@ -10,7 +10,7 @@ import CoinzillaTextAd from './CoinzillaTextAd';
const TextAd = ({ className }: {className?: string}) => { const TextAd = ({ className }: {className?: string}) => {
const hasAdblockCookie = cookies.get(cookies.NAMES.ADBLOCK_DETECTED, useAppContext().cookies); const hasAdblockCookie = cookies.get(cookies.NAMES.ADBLOCK_DETECTED, useAppContext().cookies);
if (appConfig.ad.adTextProvider === 'none' || hasAdblockCookie) { if (!config.features.adsText.isEnabled || hasAdblockCookie) {
return null; return null;
} }
......
/* eslint-disable max-len */ /* eslint-disable max-len */
import appConfig from 'configs/app/config'; import config from 'configs/app';
export const ADBUTLER_ACCOUNT = 182226; export const ADBUTLER_ACCOUNT = 182226;
...@@ -10,12 +10,12 @@ var AdButler = AdButler || {}; AdButler.ads = AdButler.ads || []; ...@@ -10,12 +10,12 @@ var AdButler = AdButler || {}; AdButler.ads = AdButler.ads || [];
var abkw = window.abkw || ''; var abkw = window.abkw || '';
const isMobile = window.matchMedia("only screen and (max-width: 1000px)").matches; const isMobile = window.matchMedia("only screen and (max-width: 1000px)").matches;
if (isMobile) { if (isMobile) {
var plc${ appConfig.ad.adButlerConfigMobile?.id } = window.plc${ appConfig.ad.adButlerConfigMobile?.id } || 0; var plc${ config.features.adsBanner.adButler.config.mobile?.id } = window.plc${ config.features.adsBanner.adButler.config.mobile?.id } || 0;
document.getElementById('ad-banner').innerHTML = '<'+'div id="placement_${ appConfig.ad.adButlerConfigMobile?.id }_'+plc${ appConfig.ad.adButlerConfigMobile?.id }+'"></'+'div>'; document.getElementById('ad-banner').innerHTML = '<'+'div id="placement_${ config.features.adsBanner.adButler.config.mobile?.id }_'+plc${ config.features.adsBanner.adButler.config.mobile?.id }+'"></'+'div>';
AdButler.ads.push({handler: function(opt){ AdButler.register(${ ADBUTLER_ACCOUNT }, ${ appConfig.ad.adButlerConfigMobile?.id }, [${ appConfig.ad.adButlerConfigMobile?.width },${ appConfig.ad.adButlerConfigMobile?.height }], 'placement_${ appConfig.ad.adButlerConfigMobile?.id }_'+opt.place, opt); }, opt: { place: plc${ appConfig.ad.adButlerConfigMobile?.id }++, keywords: abkw, domain: 'servedbyadbutler.com', click:'CLICK_MACRO_PLACEHOLDER' }}); AdButler.ads.push({handler: function(opt){ AdButler.register(${ ADBUTLER_ACCOUNT }, ${ config.features.adsBanner.adButler.config.mobile?.id }, [${ config.features.adsBanner.adButler.config.mobile?.width },${ config.features.adsBanner.adButler.config.mobile?.height }], 'placement_${ config.features.adsBanner.adButler.config.mobile?.id }_'+opt.place, opt); }, opt: { place: plc${ config.features.adsBanner.adButler.config.mobile?.id }++, keywords: abkw, domain: 'servedbyadbutler.com', click:'CLICK_MACRO_PLACEHOLDER' }});
} else { } else {
var plc${ appConfig.ad.adButlerConfigDesktop?.id } = window.plc${ appConfig.ad.adButlerConfigDesktop?.id } || 0; var plc${ config.features.adsBanner.adButler.config.desktop?.id } = window.plc${ config.features.adsBanner.adButler.config.desktop?.id } || 0;
document.getElementById('ad-banner').innerHTML = '<'+'div id="placement_${ appConfig.ad.adButlerConfigDesktop?.id }_'+plc${ appConfig.ad.adButlerConfigDesktop?.id }+'"></'+'div>'; document.getElementById('ad-banner').innerHTML = '<'+'div id="placement_${ config.features.adsBanner.adButler.config.desktop?.id }_'+plc${ config.features.adsBanner.adButler.config.desktop?.id }+'"></'+'div>';
AdButler.ads.push({handler: function(opt){ AdButler.register(${ ADBUTLER_ACCOUNT }, ${ appConfig.ad.adButlerConfigDesktop?.id }, [${ appConfig.ad.adButlerConfigDesktop?.width },${ appConfig.ad.adButlerConfigDesktop?.height }], 'placement_${ appConfig.ad.adButlerConfigDesktop?.id }_'+opt.place, opt); }, opt: { place: plc${ appConfig.ad.adButlerConfigDesktop?.id }++, keywords: abkw, domain: 'servedbyadbutler.com', click:'CLICK_MACRO_PLACEHOLDER' }}); AdButler.ads.push({handler: function(opt){ AdButler.register(${ ADBUTLER_ACCOUNT }, ${ config.features.adsBanner.adButler.config.desktop?.id }, [${ config.features.adsBanner.adButler.config.desktop?.width },${ config.features.adsBanner.adButler.config.desktop?.height }], 'placement_${ config.features.adsBanner.adButler.config.desktop?.id }_'+opt.place, opt); }, opt: { place: plc${ config.features.adsBanner.adButler.config.desktop?.id }++, keywords: abkw, domain: 'servedbyadbutler.com', click:'CLICK_MACRO_PLACEHOLDER' }});
} }
`; `;
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import type { TokenInfo } from 'types/api/token'; import type { TokenInfo } from 'types/api/token';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import useToast from 'lib/hooks/useToast'; import useToast from 'lib/hooks/useToast';
import useProvider from 'lib/web3/useProvider'; import useProvider from 'lib/web3/useProvider';
import { WALLETS_INFO } from 'lib/web3/wallets'; import { WALLETS_INFO } from 'lib/web3/wallets';
...@@ -64,7 +64,11 @@ const AddressAddToWallet = ({ className, token, isLoading }: Props) => { ...@@ -64,7 +64,11 @@ const AddressAddToWallet = ({ className, token, isLoading }: Props) => {
return <Skeleton className={ className } boxSize={ 6 } borderRadius="base"/>; return <Skeleton className={ className } boxSize={ 6 } borderRadius="base"/>;
} }
const defaultWallet = appConfig.web3.defaultWallet; const defaultWallet = config.features.web3Wallet.defaultWallet;
if (defaultWallet === 'none') {
return null;
}
return ( return (
<Tooltip label={ `Add token to ${ WALLETS_INFO[defaultWallet].name }` }> <Tooltip label={ `Add token to ${ WALLETS_INFO[defaultWallet].name }` }>
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { CustomLinksGroup } from 'types/footerLinks'; import type { CustomLinksGroup } from 'types/footerLinks';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import discussionsIcon from 'icons/discussions.svg'; import discussionsIcon from 'icons/discussions.svg';
import editIcon from 'icons/edit.svg'; import editIcon from 'icons/edit.svg';
import discordIcon from 'icons/social/discord.svg'; import discordIcon from 'icons/social/discord.svg';
...@@ -23,7 +23,7 @@ import getApiVersionUrl from './utils/getApiVersionUrl'; ...@@ -23,7 +23,7 @@ import getApiVersionUrl from './utils/getApiVersionUrl';
const MAX_LINKS_COLUMNS = 3; const MAX_LINKS_COLUMNS = 3;
// const FRONT_VERSION_URL = `https://github.com/blockscout/frontend/tree/${ appConfig.footer.frontendVersion }`; // const FRONT_VERSION_URL = `https://github.com/blockscout/frontend/tree/${ config.UI.footer.frontendVersion }`;
const Footer = () => { const Footer = () => {
...@@ -71,9 +71,9 @@ const Footer = () => { ...@@ -71,9 +71,9 @@ const Footer = () => {
const { isLoading, data: linksData } = useQuery<unknown, ResourceError<unknown>, Array<CustomLinksGroup>>( const { isLoading, data: linksData } = useQuery<unknown, ResourceError<unknown>, Array<CustomLinksGroup>>(
[ 'footer-links' ], [ 'footer-links' ],
async() => fetch(appConfig.footer.links || ''), async() => fetch(config.UI.footer.links || ''),
{ {
enabled: Boolean(appConfig.footer.links), enabled: Boolean(config.UI.footer.links),
staleTime: Infinity, staleTime: Infinity,
}); });
...@@ -82,7 +82,7 @@ const Footer = () => { ...@@ -82,7 +82,7 @@ const Footer = () => {
<Box flexGrow="1" mb={{ base: 8, lg: 0 }}> <Box flexGrow="1" mb={{ base: 8, lg: 0 }}>
<Flex> <Flex>
<ColorModeToggler/> <ColorModeToggler/>
{ !appConfig.hideIndexingAlert && <IndexingAlertIntTxs ml={ 6 }/> } { !config.UI.indexingAlert.isHidden && <IndexingAlertIntTxs ml={ 6 }/> }
<NetworkAddToWallet ml={ 8 }/> <NetworkAddToWallet ml={ 8 }/>
</Flex> </Flex>
<Box mt={{ base: 5, lg: '44px' }}> <Box mt={{ base: 5, lg: '44px' }}>
...@@ -97,34 +97,34 @@ const Footer = () => { ...@@ -97,34 +97,34 @@ const Footer = () => {
Backend: <Link href={ apiVersionUrl } target="_blank">{ backendVersionData?.backend_version }</Link> Backend: <Link href={ apiVersionUrl } target="_blank">{ backendVersionData?.backend_version }</Link>
</Text> </Text>
) } ) }
{ (appConfig.footer.frontendVersion || appConfig.footer.frontendCommit) && ( { (config.UI.footer.frontendVersion || config.UI.footer.frontendCommit) && (
<Text fontSize="xs"> <Text fontSize="xs">
{ /* Frontend: <Link href={ FRONT_VERSION_URL } target="_blank">{ appConfig.footer.frontendVersion }</Link> */ } { /* Frontend: <Link href={ FRONT_VERSION_URL } target="_blank">{ config.UI.footer.frontendVersion }</Link> */ }
Frontend: { [ appConfig.footer.frontendVersion, appConfig.footer.frontendCommit ].filter(Boolean).join('+') } Frontend: { [ config.UI.footer.frontendVersion, config.UI.footer.frontendCommit ].filter(Boolean).join('+') }
</Text> </Text>
) } ) }
</VStack> </VStack>
</Box> </Box>
<Grid <Grid
gap={{ base: 6, lg: 12 }} gap={{ base: 6, lg: 12 }}
gridTemplateColumns={ appConfig.footer.links ? gridTemplateColumns={ config.UI.footer.links ?
{ base: 'repeat(auto-fill, 160px)', lg: `repeat(${ (linksData?.length || MAX_LINKS_COLUMNS) + 1 }, 160px)` } : { base: 'repeat(auto-fill, 160px)', lg: `repeat(${ (linksData?.length || MAX_LINKS_COLUMNS) + 1 }, 160px)` } :
'auto' 'auto'
} }
> >
<Box minW="160px" w={ appConfig.footer.links ? '160px' : '100%' }> <Box minW="160px" w={ config.UI.footer.links ? '160px' : '100%' }>
{ appConfig.footer.links && <Text fontWeight={ 500 } mb={ 3 }>Blockscout</Text> } { config.UI.footer.links && <Text fontWeight={ 500 } mb={ 3 }>Blockscout</Text> }
<Grid <Grid
gap={ 1 } gap={ 1 }
gridTemplateColumns={ appConfig.footer.links ? '160px' : { base: 'repeat(auto-fill, 160px)', lg: 'repeat(3, 160px)' } } gridTemplateColumns={ config.UI.footer.links ? '160px' : { base: 'repeat(auto-fill, 160px)', lg: 'repeat(3, 160px)' } }
gridTemplateRows={{ base: 'auto', lg: appConfig.footer.links ? 'auto' : 'repeat(2, auto)' }} gridTemplateRows={{ base: 'auto', lg: config.UI.footer.links ? 'auto' : 'repeat(2, auto)' }}
gridAutoFlow={{ base: 'row', lg: appConfig.footer.links ? 'row' : 'column' }} gridAutoFlow={{ base: 'row', lg: config.UI.footer.links ? 'row' : 'column' }}
mt={{ base: 0, lg: appConfig.footer.links ? 0 : '100px' }} mt={{ base: 0, lg: config.UI.footer.links ? 0 : '100px' }}
> >
{ BLOCKSCOUT_LINKS.map(link => <FooterLinkItem { ...link } key={ link.text }/>) } { BLOCKSCOUT_LINKS.map(link => <FooterLinkItem { ...link } key={ link.text }/>) }
</Grid> </Grid>
</Box> </Box>
{ appConfig.footer.links && isLoading && ( { config.UI.footer.links && isLoading && (
Array.from(Array(3)).map((i, index) => ( Array.from(Array(3)).map((i, index) => (
<Box minW="160px" key={ index }> <Box minW="160px" key={ index }>
<Skeleton w="120px" h="20px" mb={ 6 }/> <Skeleton w="120px" h="20px" mb={ 6 }/>
...@@ -134,7 +134,7 @@ const Footer = () => { ...@@ -134,7 +134,7 @@ const Footer = () => {
</Box> </Box>
)) ))
) } ) }
{ appConfig.footer.links && linksData && ( { config.UI.footer.links && linksData && (
linksData.slice(0, MAX_LINKS_COLUMNS).map(linkGroup => ( linksData.slice(0, MAX_LINKS_COLUMNS).map(linkGroup => (
<Box minW="160px" key={ linkGroup.title }> <Box minW="160px" key={ linkGroup.title }>
<Text fontWeight={ 500 } mb={ 3 }>{ linkGroup.title }</Text> <Text fontWeight={ 500 } mb={ 3 }>{ linkGroup.title }</Text>
......
import { Icon, Box, Flex, Drawer, DrawerOverlay, DrawerContent, DrawerBody, useColorModeValue, useDisclosure } from '@chakra-ui/react'; import { Icon, Box, Flex, Drawer, DrawerOverlay, DrawerContent, DrawerBody, useColorModeValue, useDisclosure } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import burgerIcon from 'icons/burger.svg'; import burgerIcon from 'icons/burger.svg';
import testnetIcon from 'icons/testnet.svg'; import testnetIcon from 'icons/testnet.svg';
import NavigationMobile from 'ui/snippets/navigation/NavigationMobile'; import NavigationMobile from 'ui/snippets/navigation/NavigationMobile';
...@@ -44,10 +44,10 @@ const Burger = () => { ...@@ -44,10 +44,10 @@ const Burger = () => {
<DrawerOverlay/> <DrawerOverlay/>
<DrawerContent maxWidth="260px"> <DrawerContent maxWidth="260px">
<DrawerBody p={ 6 } display="flex" flexDirection="column"> <DrawerBody p={ 6 } display="flex" flexDirection="column">
{ appConfig.network.isTestnet && <Icon as={ testnetIcon } h="14px" w="auto" color="red.400" alignSelf="flex-start"/> } { config.chain.isTestnet && <Icon as={ testnetIcon } h="14px" w="auto" color="red.400" alignSelf="flex-start"/> }
<Flex alignItems="center" justifyContent="space-between"> <Flex alignItems="center" justifyContent="space-between">
<NetworkLogo onClick={ handleNetworkLogoClick }/> <NetworkLogo onClick={ handleNetworkLogoClick }/>
{ appConfig.navigation.featuredNetworks ? ( { config.UI.sidebar.featuredNetworks ? (
<NetworkMenuButton <NetworkMenuButton
isMobile isMobile
isActive={ networkMenu.isOpen } isActive={ networkMenu.isOpen }
......
import { HStack, Box, Flex, useColorModeValue } from '@chakra-ui/react'; import { HStack, Box, Flex, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import { useScrollDirection } from 'lib/contexts/scrollDirection'; import { useScrollDirection } from 'lib/contexts/scrollDirection';
import IndexingAlertBlocks from 'ui/home/IndexingAlertBlocks'; import IndexingAlertBlocks from 'ui/home/IndexingAlertBlocks';
import NetworkLogo from 'ui/snippets/networkMenu/NetworkLogo'; import NetworkLogo from 'ui/snippets/networkMenu/NetworkLogo';
...@@ -43,7 +43,7 @@ const Header = ({ isHomePage, renderSearchBar }: Props) => { ...@@ -43,7 +43,7 @@ const Header = ({ isHomePage, renderSearchBar }: Props) => {
> >
<Burger/> <Burger/>
<NetworkLogo/> <NetworkLogo/>
{ appConfig.account.isEnabled ? <ProfileMenuMobile/> : <Box boxSize={ 10 }/> } { config.features.account.isEnabled ? <ProfileMenuMobile/> : <Box boxSize={ 10 }/> }
</Flex> </Flex>
{ !isHomePage && searchBar } { !isHomePage && searchBar }
</Box> </Box>
...@@ -52,7 +52,7 @@ const Header = ({ isHomePage, renderSearchBar }: Props) => { ...@@ -52,7 +52,7 @@ const Header = ({ isHomePage, renderSearchBar }: Props) => {
paddingTop={ 9 } paddingTop={ 9 }
display={{ base: 'none', lg: 'block' }} display={{ base: 'none', lg: 'block' }}
> >
{ !appConfig.hideIndexingAlert && <IndexingAlertBlocks/> } { !config.UI.indexingAlert.isHidden && <IndexingAlertBlocks/> }
{ !isHomePage && ( { !isHomePage && (
<HStack <HStack
as="header" as="header"
...@@ -65,7 +65,7 @@ const Header = ({ isHomePage, renderSearchBar }: Props) => { ...@@ -65,7 +65,7 @@ const Header = ({ isHomePage, renderSearchBar }: Props) => {
<Box width="100%"> <Box width="100%">
{ searchBar } { searchBar }
</Box> </Box>
{ appConfig.account.isEnabled && <ProfileMenuDesktop/> } { config.features.account.isEnabled && <ProfileMenuDesktop/> }
</HStack> </HStack>
) } ) }
</Box> </Box>
......
import { Flex, Box, VStack, Icon, useColorModeValue } from '@chakra-ui/react'; import { Flex, Box, VStack, Icon, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import chevronIcon from 'icons/arrows/east-mini.svg'; import chevronIcon from 'icons/arrows/east-mini.svg';
import testnetIcon from 'icons/testnet.svg'; import testnetIcon from 'icons/testnet.svg';
import { useAppContext } from 'lib/contexts/app'; import { useAppContext } from 'lib/contexts/app';
...@@ -60,7 +60,7 @@ const NavigationDesktop = () => { ...@@ -60,7 +60,7 @@ const NavigationDesktop = () => {
width={{ lg: isExpanded ? '229px' : '92px', xl: isCollapsed ? '92px' : '229px' }} width={{ lg: isExpanded ? '229px' : '92px', xl: isCollapsed ? '92px' : '229px' }}
{ ...getDefaultTransitionProps({ transitionProperty: 'width, padding' }) } { ...getDefaultTransitionProps({ transitionProperty: 'width, padding' }) }
> >
{ appConfig.network.isTestnet && <Icon as={ testnetIcon } h="14px" w="auto" color="red.400" pl={ 3 } alignSelf="flex-start"/> } { config.chain.isTestnet && <Icon as={ testnetIcon } h="14px" w="auto" color="red.400" pl={ 3 } alignSelf="flex-start"/> }
<Box <Box
as="header" as="header"
display="flex" display="flex"
...@@ -76,7 +76,7 @@ const NavigationDesktop = () => { ...@@ -76,7 +76,7 @@ const NavigationDesktop = () => {
transitionTimingFunction="ease" transitionTimingFunction="ease"
> >
<NetworkLogo isCollapsed={ isCollapsed }/> <NetworkLogo isCollapsed={ isCollapsed }/>
{ Boolean(appConfig.navigation.featuredNetworks) && <NetworkMenu isCollapsed={ isCollapsed }/> } { Boolean(config.UI.sidebar.featuredNetworks) && <NetworkMenu isCollapsed={ isCollapsed }/> }
</Box> </Box>
<Box as="nav" mt={ 8 } w="100%"> <Box as="nav" mt={ 8 } w="100%">
<VStack as="ul" spacing="1" alignItems="flex-start"> <VStack as="ul" spacing="1" alignItems="flex-start">
......
...@@ -2,7 +2,7 @@ import { Icon, Box, Image, useColorModeValue, Skeleton } from '@chakra-ui/react' ...@@ -2,7 +2,7 @@ import { Icon, Box, Image, useColorModeValue, Skeleton } from '@chakra-ui/react'
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import iconPlaceholder from 'icons/networks/icon-placeholder.svg'; import iconPlaceholder from 'icons/networks/icon-placeholder.svg';
import logoPlaceholder from 'icons/networks/logo-placeholder.svg'; import logoPlaceholder from 'icons/networks/logo-placeholder.svg';
...@@ -25,7 +25,7 @@ const LogoFallback = ({ isCollapsed, isSmall }: { isCollapsed?: boolean; isSmall ...@@ -25,7 +25,7 @@ const LogoFallback = ({ isCollapsed, isSmall }: { isCollapsed?: boolean; isSmall
xl: isCollapsed ? 'none' : 'block', xl: isCollapsed ? 'none' : 'block',
}; };
if (appConfig.network[field].default) { if (config.UI.sidebar[field].default) {
return <Skeleton w="100%" borderRadius="sm" display={ display }/>; return <Skeleton w="100%" borderRadius="sm" display={ display }/>;
} }
...@@ -42,11 +42,11 @@ const LogoFallback = ({ isCollapsed, isSmall }: { isCollapsed?: boolean; isSmall ...@@ -42,11 +42,11 @@ const LogoFallback = ({ isCollapsed, isSmall }: { isCollapsed?: boolean; isSmall
const NetworkLogo = ({ isCollapsed, onClick }: Props) => { const NetworkLogo = ({ isCollapsed, onClick }: Props) => {
const logoSrc = useColorModeValue(appConfig.network.logo.default, appConfig.network.logo.dark || appConfig.network.logo.default); const logoSrc = useColorModeValue(config.UI.sidebar.logo.default, config.UI.sidebar.logo.dark || config.UI.sidebar.logo.default);
const iconSrc = useColorModeValue(appConfig.network.icon.default, appConfig.network.icon.dark || appConfig.network.icon.default); const iconSrc = useColorModeValue(config.UI.sidebar.icon.default, config.UI.sidebar.icon.dark || config.UI.sidebar.icon.default);
const darkModeFilter = { filter: 'brightness(0) invert(1)' }; const darkModeFilter = { filter: 'brightness(0) invert(1)' };
const logoStyle = useColorModeValue({}, !appConfig.network.logo.dark ? darkModeFilter : {}); const logoStyle = useColorModeValue({}, !config.UI.sidebar.logo.dark ? darkModeFilter : {});
const iconStyle = useColorModeValue({}, !appConfig.network.icon.dark ? darkModeFilter : {}); const iconStyle = useColorModeValue({}, !config.UI.sidebar.icon.dark ? darkModeFilter : {});
return ( return (
// TODO switch to <NextLink href={ href } passHref> when main page for network will be ready // TODO switch to <NextLink href={ href } passHref> when main page for network will be ready
...@@ -66,7 +66,7 @@ const NetworkLogo = ({ isCollapsed, onClick }: Props) => { ...@@ -66,7 +66,7 @@ const NetworkLogo = ({ isCollapsed, onClick }: Props) => {
w="auto" w="auto"
h="100%" h="100%"
src={ logoSrc } src={ logoSrc }
alt={ `${ appConfig.network.name } network logo` } alt={ `${ config.chain.name } network logo` }
fallback={ <LogoFallback isCollapsed={ isCollapsed }/> } fallback={ <LogoFallback isCollapsed={ isCollapsed }/> }
display={{ base: 'block', lg: isCollapsed === false ? 'block' : 'none', xl: isCollapsed ? 'none' : 'block' }} display={{ base: 'block', lg: isCollapsed === false ? 'block' : 'none', xl: isCollapsed ? 'none' : 'block' }}
style={ logoStyle } style={ logoStyle }
...@@ -76,7 +76,7 @@ const NetworkLogo = ({ isCollapsed, onClick }: Props) => { ...@@ -76,7 +76,7 @@ const NetworkLogo = ({ isCollapsed, onClick }: Props) => {
w="auto" w="auto"
h="100%" h="100%"
src={ iconSrc } src={ iconSrc }
alt={ `${ appConfig.network.name } network logo` } alt={ `${ config.chain.name } network logo` }
fallback={ <LogoFallback isCollapsed={ isCollapsed } isSmall/> } fallback={ <LogoFallback isCollapsed={ isCollapsed } isSmall/> }
display={{ base: 'none', lg: isCollapsed === false ? 'none' : 'block', xl: isCollapsed ? 'block' : 'none' }} display={{ base: 'none', lg: isCollapsed === false ? 'none' : 'block', xl: isCollapsed ? 'block' : 'none' }}
style={ iconStyle } style={ iconStyle }
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { FeaturedNetwork, NetworkGroup } from 'types/networks'; import type { FeaturedNetwork, NetworkGroup } from 'types/networks';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import useApiFetch from 'lib/hooks/useFetch'; import useApiFetch from 'lib/hooks/useFetch';
...@@ -16,9 +16,9 @@ export default function useNetworkMenu() { ...@@ -16,9 +16,9 @@ export default function useNetworkMenu() {
const apiFetch = useApiFetch(); const apiFetch = useApiFetch();
const { isLoading, data } = useQuery<unknown, ResourceError<unknown>, Array<FeaturedNetwork>>( const { isLoading, data } = useQuery<unknown, ResourceError<unknown>, Array<FeaturedNetwork>>(
[ 'featured-network' ], [ 'featured-network' ],
async() => apiFetch(appConfig.navigation.featuredNetworks || ''), async() => apiFetch(config.UI.sidebar.featuredNetworks || ''),
{ {
enabled: Boolean(appConfig.navigation.featuredNetworks) && isOpen, enabled: Boolean(config.UI.sidebar.featuredNetworks) && isOpen,
staleTime: Infinity, staleTime: Infinity,
}); });
......
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import type { UserInfo } from 'types/api/account'; import type { UserInfo } from 'types/api/account';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import useNavItems from 'lib/hooks/useNavItems'; import useNavItems from 'lib/hooks/useNavItems';
import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps'; import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
import NavLink from 'ui/snippets/navigation/NavLink'; import NavLink from 'ui/snippets/navigation/NavLink';
...@@ -46,7 +46,7 @@ const ProfileMenuContent = ({ data }: Props) => { ...@@ -46,7 +46,7 @@ const ProfileMenuContent = ({ data }: Props) => {
</VStack> </VStack>
</Box> </Box>
<Box mt={ 2 } pt={ 3 } borderTopColor="divider" borderTopWidth="1px" { ...getDefaultTransitionProps() }> <Box mt={ 2 } pt={ 3 } borderTopColor="divider" borderTopWidth="1px" { ...getDefaultTransitionProps() }>
<Button size="sm" width="full" variant="outline" as="a" href={ appConfig.account.logoutUrl }>Sign Out</Button> <Button size="sm" width="full" variant="outline" as="a" href={ config.features.account.logoutUrl }>Sign Out</Button>
</Box> </Box>
</Box> </Box>
); );
......
...@@ -4,22 +4,22 @@ import React from 'react'; ...@@ -4,22 +4,22 @@ import React from 'react';
import type { TokenVerifiedInfo as TTokenVerifiedInfo } from 'types/api/token'; import type { TokenVerifiedInfo as TTokenVerifiedInfo } from 'types/api/token';
import config from 'configs/app';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/LinkExternal';
import TokenProjectInfo from './TokenProjectInfo'; import TokenProjectInfo from './TokenProjectInfo';
interface Props { interface Props {
verifiedInfoQuery: UseQueryResult<TTokenVerifiedInfo>; verifiedInfoQuery: UseQueryResult<TTokenVerifiedInfo>;
isVerifiedInfoEnabled: boolean;
} }
const TokenVerifiedInfo = ({ verifiedInfoQuery, isVerifiedInfoEnabled }: Props) => { const TokenVerifiedInfo = ({ verifiedInfoQuery }: Props) => {
const { data, isLoading, isError } = verifiedInfoQuery; const { data, isLoading, isError } = verifiedInfoQuery;
const websiteLinkBg = useColorModeValue('gray.100', 'gray.700'); const websiteLinkBg = useColorModeValue('gray.100', 'gray.700');
const content = (() => { const content = (() => {
if (!isVerifiedInfoEnabled) { if (!config.features.verifiedTokens.isEnabled) {
return null; return null;
} }
......
...@@ -6,7 +6,7 @@ import { useForm } from 'react-hook-form'; ...@@ -6,7 +6,7 @@ import { useForm } from 'react-hook-form';
import type { Fields } from './types'; import type { Fields } from './types';
import type { TokenInfoApplication } from 'types/api/account'; import type { TokenInfoApplication } from 'types/api/account';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
...@@ -49,7 +49,7 @@ const TokenInfoForm = ({ address, tokenName, application, onSubmit }: Props) => ...@@ -49,7 +49,7 @@ const TokenInfoForm = ({ address, tokenName, application, onSubmit }: Props) =>
const toast = useToast(); const toast = useToast();
const configQuery = useApiQuery('token_info_applications_config', { const configQuery = useApiQuery('token_info_applications_config', {
pathParams: { chainId: appConfig.network.id }, pathParams: { chainId: config.chain.id },
}); });
const formApi = useForm<Fields>({ const formApi = useForm<Fields>({
...@@ -64,7 +64,7 @@ const TokenInfoForm = ({ address, tokenName, application, onSubmit }: Props) => ...@@ -64,7 +64,7 @@ const TokenInfoForm = ({ address, tokenName, application, onSubmit }: Props) =>
const isNewApplication = !application?.id || [ 'REJECTED', 'APPROVED' ].includes(application.status); const isNewApplication = !application?.id || [ 'REJECTED', 'APPROVED' ].includes(application.status);
const result = await apiFetch<'token_info_applications', TokenInfoApplication, { message: string }>('token_info_applications', { const result = await apiFetch<'token_info_applications', TokenInfoApplication, { message: string }>('token_info_applications', {
pathParams: { chainId: appConfig.network.id, id: !isNewApplication ? application.id : undefined }, pathParams: { chainId: config.chain.id, id: !isNewApplication ? application.id : undefined },
fetchParams: { fetchParams: {
method: isNewApplication ? 'POST' : 'PUT', method: isNewApplication ? 'POST' : 'PUT',
body: { submission }, body: { submission },
......
...@@ -18,7 +18,7 @@ import { route } from 'nextjs-routes'; ...@@ -18,7 +18,7 @@ import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import { scroller, Element } from 'react-scroll'; import { scroller, Element } from 'react-scroll';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import clockIcon from 'icons/clock.svg'; import clockIcon from 'icons/clock.svg';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.svg';
import errorIcon from 'icons/status/error.svg'; import errorIcon from 'icons/status/error.svg';
...@@ -124,7 +124,7 @@ const TxDetails = () => { ...@@ -124,7 +124,7 @@ const TxDetails = () => {
return ( return (
<> <>
{ appConfig.network.isTestnet && <Alert status="warning" mb={ 6 }>This is a { appConfig.network.name } testnet transaction only</Alert> } { config.chain.isTestnet && <Alert status="warning" mb={ 6 }>This is a { config.chain.name } testnet transaction only</Alert> }
<Grid columnGap={ 8 } rowGap={{ base: 3, lg: 3 }} templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }}> <Grid columnGap={ 8 } rowGap={{ base: 3, lg: 3 }} templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }}>
{ socketStatus && ( { socketStatus && (
<GridItem colSpan={{ base: undefined, lg: 2 }} mb={ 2 }> <GridItem colSpan={{ base: undefined, lg: 2 }} mb={ 2 }>
...@@ -281,7 +281,7 @@ const TxDetails = () => { ...@@ -281,7 +281,7 @@ const TxDetails = () => {
> >
<CurrencyValue <CurrencyValue
value={ data.value } value={ data.value }
currency={ appConfig.network.currency.symbol } currency={ config.chain.currency.symbol }
exchangeRate={ data.exchange_rate } exchangeRate={ data.exchange_rate }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
/> />
...@@ -293,7 +293,7 @@ const TxDetails = () => { ...@@ -293,7 +293,7 @@ const TxDetails = () => {
> >
<CurrencyValue <CurrencyValue
value={ data.fee.value } value={ data.fee.value }
currency={ appConfig.network.currency.symbol } currency={ config.chain.currency.symbol }
exchangeRate={ data.exchange_rate } exchangeRate={ data.exchange_rate }
flexWrap="wrap" flexWrap="wrap"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
...@@ -305,7 +305,7 @@ const TxDetails = () => { ...@@ -305,7 +305,7 @@ const TxDetails = () => {
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
<Skeleton isLoaded={ !isPlaceholderData } mr={ 1 }> <Skeleton isLoaded={ !isPlaceholderData } mr={ 1 }>
{ BigNumber(data.gas_price).dividedBy(WEI).toFixed() } { appConfig.network.currency.symbol } { BigNumber(data.gas_price).dividedBy(WEI).toFixed() } { config.chain.currency.symbol }
</Skeleton> </Skeleton>
<Skeleton isLoaded={ !isPlaceholderData } color="text_secondary"> <Skeleton isLoaded={ !isPlaceholderData } color="text_secondary">
<span>({ BigNumber(data.gas_price).dividedBy(WEI_IN_GWEI).toFixed() } Gwei)</span> <span>({ BigNumber(data.gas_price).dividedBy(WEI_IN_GWEI).toFixed() } Gwei)</span>
...@@ -354,22 +354,22 @@ const TxDetails = () => { ...@@ -354,22 +354,22 @@ const TxDetails = () => {
) } ) }
</DetailsInfoItem> </DetailsInfoItem>
) } ) }
{ data.tx_burnt_fee && !appConfig.L2.isL2Network && ( { data.tx_burnt_fee && !config.features.rollup.isEnabled && (
<DetailsInfoItem <DetailsInfoItem
title="Burnt fees" title="Burnt fees"
hint={ `Amount of ${ appConfig.network.currency.symbol } burned for this transaction. Equals Block Base Fee per Gas * Gas Used` } hint={ `Amount of ${ config.chain.currency.symbol } burned for this transaction. Equals Block Base Fee per Gas * Gas Used` }
> >
<Icon as={ flameIcon } boxSize={ 5 } color="gray.500"/> <Icon as={ flameIcon } boxSize={ 5 } color="gray.500"/>
<CurrencyValue <CurrencyValue
value={ String(data.tx_burnt_fee) } value={ String(data.tx_burnt_fee) }
currency={ appConfig.network.currency.symbol } currency={ config.chain.currency.symbol }
exchangeRate={ data.exchange_rate } exchangeRate={ data.exchange_rate }
flexWrap="wrap" flexWrap="wrap"
ml={ 1 } ml={ 1 }
/> />
</DetailsInfoItem> </DetailsInfoItem>
) } ) }
{ appConfig.L2.isL2Network && ( { config.features.rollup.isEnabled && (
<> <>
{ data.l1_gas_used && ( { data.l1_gas_used && (
<DetailsInfoItem <DetailsInfoItem
...@@ -386,7 +386,7 @@ const TxDetails = () => { ...@@ -386,7 +386,7 @@ const TxDetails = () => {
hint="L1 gas price" hint="L1 gas price"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
<Text mr={ 1 }>{ BigNumber(data.l1_gas_price).dividedBy(WEI).toFixed() } { appConfig.network.currency.symbol }</Text> <Text mr={ 1 }>{ BigNumber(data.l1_gas_price).dividedBy(WEI).toFixed() } { config.chain.currency.symbol }</Text>
<Text variant="secondary">({ BigNumber(data.l1_gas_price).dividedBy(WEI_IN_GWEI).toFixed() } Gwei)</Text> <Text variant="secondary">({ BigNumber(data.l1_gas_price).dividedBy(WEI_IN_GWEI).toFixed() } Gwei)</Text>
</DetailsInfoItem> </DetailsInfoItem>
) } ) }
...@@ -399,7 +399,7 @@ const TxDetails = () => { ...@@ -399,7 +399,7 @@ const TxDetails = () => {
> >
<CurrencyValue <CurrencyValue
value={ data.l1_fee } value={ data.l1_fee }
currency={ appConfig.network.currency.symbol } currency={ config.chain.currency.symbol }
exchangeRate={ data.exchange_rate } exchangeRate={ data.exchange_rate }
flexWrap="wrap" flexWrap="wrap"
/> />
......
...@@ -5,7 +5,7 @@ import React from 'react'; ...@@ -5,7 +5,7 @@ import React from 'react';
import type { TxAction, TxActionGeneral } from 'types/api/txAction'; import type { TxAction, TxActionGeneral } from 'types/api/txAction';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import uniswapIcon from 'icons/uniswap.svg'; import uniswapIcon from 'icons/uniswap.svg';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet'; import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
...@@ -39,15 +39,15 @@ const TxDetailsAction = ({ action }: Props) => { ...@@ -39,15 +39,15 @@ const TxDetailsAction = ({ action }: Props) => {
const amount1 = BigNumber(data.amount1).toFormat(); const amount1 = BigNumber(data.amount1).toFormat();
const [ text0, text1 ] = getActionText(type); const [ text0, text1 ] = getActionText(type);
const token0 = { const token0 = {
address: data.symbol0 === 'Ether' ? appConfig.network.currency.address || '' : data.address0, address: data.symbol0 === 'Ether' ? '' : data.address0,
name: data.symbol0 === 'Ether' ? appConfig.network.currency.symbol || null : data.symbol0, name: data.symbol0 === 'Ether' ? config.chain.currency.symbol || null : data.symbol0,
type: 'ERC-20', type: 'ERC-20',
symbol: null, symbol: null,
icon_url: null, icon_url: null,
}; };
const token1 = { const token1 = {
address: data.symbol1 === 'Ether' ? appConfig.network.currency.address || '' : data.address1, address: data.symbol1 === 'Ether' ? '' : data.address1,
name: data.symbol1 === 'Ether' ? appConfig.network.currency.symbol || null : data.symbol1, name: data.symbol1 === 'Ether' ? config.chain.currency.symbol || null : data.symbol1,
type: 'ERC-20', type: 'ERC-20',
symbol: null, symbol: null,
icon_url: null, icon_url: null,
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction'; 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 eastArrowIcon from 'icons/arrows/east.svg';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
...@@ -44,9 +44,9 @@ const TxInternalsListItem = ({ type, from, to, value, success, error, gas_limit: ...@@ -44,9 +44,9 @@ const TxInternalsListItem = ({ type, from, to, value, success, error, gas_limit:
) } ) }
</Box> </Box>
<HStack spacing={ 3 }> <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"> <Skeleton isLoaded={ !isLoading } fontSize="sm" color="text_secondary">
{ BigNumber(value).div(BigNumber(10 ** appConfig.network.currency.decimals)).toFormat() } { BigNumber(value).div(BigNumber(10 ** config.chain.currency.decimals)).toFormat() }
</Skeleton> </Skeleton>
</HStack> </HStack>
<HStack spacing={ 3 }> <HStack spacing={ 3 }>
......
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction'; import type { InternalTransaction } from 'types/api/internalTransaction';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import arrowIcon from 'icons/arrows/east.svg'; import arrowIcon from 'icons/arrows/east.svg';
import { default as Thead } from 'ui/shared/TheadSticky'; import { default as Thead } from 'ui/shared/TheadSticky';
import TxInternalsTableItem from 'ui/tx/internals/TxInternalsTableItem'; import TxInternalsTableItem from 'ui/tx/internals/TxInternalsTableItem';
...@@ -31,13 +31,13 @@ const TxInternalsTable = ({ data, sort, onSortToggle, top, isLoading }: Props) = ...@@ -31,13 +31,13 @@ const TxInternalsTable = ({ data, sort, onSortToggle, top, isLoading }: Props) =
<Th width="16%" isNumeric> <Th width="16%" isNumeric>
<Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ onSortToggle('value') } columnGap={ 1 }> <Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ onSortToggle('value') } columnGap={ 1 }>
{ sort?.includes('value') && <Icon as={ arrowIcon } boxSize={ 4 } transform={ sortIconTransform }/> } { sort?.includes('value') && <Icon as={ arrowIcon } boxSize={ 4 } transform={ sortIconTransform }/> }
Value { appConfig.network.currency.symbol } Value { config.chain.currency.symbol }
</Link> </Link>
</Th> </Th>
<Th width="16%" isNumeric> <Th width="16%" isNumeric>
<Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ onSortToggle('gas-limit') } columnGap={ 1 }> <Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ onSortToggle('gas-limit') } columnGap={ 1 }>
{ sort?.includes('gas-limit') && <Icon as={ arrowIcon } boxSize={ 4 } transform={ sortIconTransform }/> } { sort?.includes('gas-limit') && <Icon as={ arrowIcon } boxSize={ 4 } transform={ sortIconTransform }/> }
Gas limit { appConfig.network.currency.symbol } Gas limit { config.chain.currency.symbol }
</Link> </Link>
</Th> </Th>
</Tr> </Tr>
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction'; 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 rightArrowIcon from 'icons/arrows/east.svg';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
...@@ -56,7 +56,7 @@ const TxInternalTableItem = ({ type, from, to, value, success, error, gas_limit: ...@@ -56,7 +56,7 @@ const TxInternalTableItem = ({ type, from, to, value, success, error, gas_limit:
</Td> </Td>
<Td isNumeric verticalAlign="middle"> <Td isNumeric verticalAlign="middle">
<Skeleton isLoaded={ !isLoading } display="inline-block"> <Skeleton isLoaded={ !isLoading } display="inline-block">
{ BigNumber(value).div(BigNumber(10 ** appConfig.network.currency.decimals)).toFormat() } { BigNumber(value).div(BigNumber(10 ** config.chain.currency.decimals)).toFormat() }
</Skeleton> </Skeleton>
</Td> </Td>
<Td isNumeric verticalAlign="middle"> <Td isNumeric verticalAlign="middle">
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { TxStateChange } from 'types/api/txStateChanges'; import type { TxStateChange } from 'types/api/txStateChanges';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import { ZERO_ADDRESS } from 'lib/consts'; import { ZERO_ADDRESS } from 'lib/consts';
import { nbsp, space } from 'lib/html-entities'; import { nbsp, space } from 'lib/html-entities';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
...@@ -50,8 +50,8 @@ export function getStateElements(data: TxStateChange, isLoading?: boolean) { ...@@ -50,8 +50,8 @@ export function getStateElements(data: TxStateChange, isLoading?: boolean) {
switch (data.type) { switch (data.type) {
case 'coin': { case 'coin': {
const beforeBn = BigNumber(data.balance_before || '0').div(10 ** appConfig.network.currency.decimals); const beforeBn = BigNumber(data.balance_before || '0').div(10 ** config.chain.currency.decimals);
const afterBn = BigNumber(data.balance_after || '0').div(10 ** appConfig.network.currency.decimals); const afterBn = BigNumber(data.balance_after || '0').div(10 ** config.chain.currency.decimals);
const differenceBn = afterBn.minus(beforeBn); const differenceBn = afterBn.minus(beforeBn);
const changeColor = beforeBn.lte(afterBn) ? 'green.500' : 'red.500'; const changeColor = beforeBn.lte(afterBn) ? 'green.500' : 'red.500';
const changeSign = beforeBn.lte(afterBn) ? '+' : '-'; const changeSign = beforeBn.lte(afterBn) ? '+' : '-';
...@@ -59,12 +59,12 @@ export function getStateElements(data: TxStateChange, isLoading?: boolean) { ...@@ -59,12 +59,12 @@ export function getStateElements(data: TxStateChange, isLoading?: boolean) {
return { return {
before: ( before: (
<Skeleton isLoaded={ !isLoading } wordBreak="break-all" display="inline-block"> <Skeleton isLoaded={ !isLoading } wordBreak="break-all" display="inline-block">
{ beforeBn.toFormat() } { appConfig.network.currency.symbol } { beforeBn.toFormat() } { config.chain.currency.symbol }
</Skeleton> </Skeleton>
), ),
after: ( after: (
<Skeleton isLoaded={ !isLoading } wordBreak="break-all" display="inline-block"> <Skeleton isLoaded={ !isLoading } wordBreak="break-all" display="inline-block">
{ afterBn.toFormat() } { appConfig.network.currency.symbol } { afterBn.toFormat() } { config.chain.currency.symbol }
</Skeleton> </Skeleton>
), ),
change: ( change: (
......
...@@ -5,7 +5,7 @@ import React from 'react'; ...@@ -5,7 +5,7 @@ import React from 'react';
import type { Transaction } from 'types/api/transaction'; import type { Transaction } from 'types/api/transaction';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import getValueWithUnit from 'lib/getValueWithUnit'; import getValueWithUnit from 'lib/getValueWithUnit';
import CurrencyValue from 'ui/shared/CurrencyValue'; import CurrencyValue from 'ui/shared/CurrencyValue';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
...@@ -34,7 +34,7 @@ const TxAdditionalInfoContent = ({ tx }: { tx: Transaction }) => { ...@@ -34,7 +34,7 @@ const TxAdditionalInfoContent = ({ tx }: { tx: Transaction }) => {
<Flex> <Flex>
<CurrencyValue <CurrencyValue
value={ tx.fee.value } value={ tx.fee.value }
currency={ appConfig.network.currency.symbol } currency={ config.chain.currency.symbol }
exchangeRate={ tx.exchange_rate } exchangeRate={ tx.exchange_rate }
accuracyUsd={ 2 } accuracyUsd={ 2 }
/> />
......
...@@ -9,7 +9,7 @@ import React from 'react'; ...@@ -9,7 +9,7 @@ import React from 'react';
import type { Transaction } from 'types/api/transaction'; import type { Transaction } from 'types/api/transaction';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import transactionIcon from 'icons/transactions.svg'; import transactionIcon from 'icons/transactions.svg';
import getValueWithUnit from 'lib/getValueWithUnit'; import getValueWithUnit from 'lib/getValueWithUnit';
...@@ -142,11 +142,11 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI ...@@ -142,11 +142,11 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI
) : '-' } ) : '-' }
</Flex> </Flex>
<Box mt={ 2 }> <Box mt={ 2 }>
<Skeleton isLoaded={ !isLoading } display="inline-block" whiteSpace="pre">Value { appConfig.network.currency.symbol } </Skeleton> <Skeleton isLoaded={ !isLoading } display="inline-block" whiteSpace="pre">Value { config.chain.currency.symbol } </Skeleton>
<Skeleton isLoaded={ !isLoading } display="inline-block" variant="text_secondary">{ getValueWithUnit(tx.value).toFormat() }</Skeleton> <Skeleton isLoaded={ !isLoading } display="inline-block" variant="text_secondary">{ getValueWithUnit(tx.value).toFormat() }</Skeleton>
</Box> </Box>
<Box mt={ 2 } mb={ 3 }> <Box mt={ 2 } mb={ 3 }>
<Skeleton isLoaded={ !isLoading } display="inline-block" whiteSpace="pre">Fee { appConfig.network.currency.symbol } </Skeleton> <Skeleton isLoaded={ !isLoading } display="inline-block" whiteSpace="pre">Fee { config.chain.currency.symbol } </Skeleton>
<Skeleton isLoaded={ !isLoading } display="inline-block" variant="text_secondary">{ getValueWithUnit(tx.fee.value).toFormat() }</Skeleton> <Skeleton isLoaded={ !isLoading } display="inline-block" variant="text_secondary">{ getValueWithUnit(tx.fee.value).toFormat() }</Skeleton>
</Box> </Box>
</ListItemMobile> </ListItemMobile>
......
...@@ -5,7 +5,7 @@ import React from 'react'; ...@@ -5,7 +5,7 @@ import React from 'react';
import type { Transaction } from 'types/api/transaction'; import type { Transaction } from 'types/api/transaction';
import type { Sort } from 'types/client/txs-sort'; import type { Sort } from 'types/client/txs-sort';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import TheadSticky from 'ui/shared/TheadSticky'; import TheadSticky from 'ui/shared/TheadSticky';
...@@ -55,14 +55,14 @@ const TxsTable = ({ ...@@ -55,14 +55,14 @@ const TxsTable = ({
<Link onClick={ sort('val') } display="flex" justifyContent="end"> <Link onClick={ sort('val') } display="flex" justifyContent="end">
{ sorting === 'val-asc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(-90deg)"/> } { sorting === 'val-asc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(-90deg)"/> }
{ sorting === 'val-desc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(90deg)"/> } { sorting === 'val-desc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(90deg)"/> }
{ `Value ${ appConfig.network.currency.symbol }` } { `Value ${ config.chain.currency.symbol }` }
</Link> </Link>
</Th> </Th>
<Th width="20%" isNumeric pr={ 5 }> <Th width="20%" isNumeric pr={ 5 }>
<Link onClick={ sort('fee') } display="flex" justifyContent="end"> <Link onClick={ sort('fee') } display="flex" justifyContent="end">
{ sorting === 'fee-asc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(-90deg)"/> } { sorting === 'fee-asc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(-90deg)"/> }
{ sorting === 'fee-desc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(90deg)"/> } { sorting === 'fee-desc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(90deg)"/> }
{ `Fee ${ appConfig.network.currency.symbol }` } { `Fee ${ config.chain.currency.symbol }` }
</Link> </Link>
</Th> </Th>
</Tr> </Tr>
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { VerifiedContract } from 'types/api/contracts'; import type { VerifiedContract } from 'types/api/contracts';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import iconCheck from 'icons/check.svg'; import iconCheck from 'icons/check.svg';
import iconCross from 'icons/cross.svg'; import iconCross from 'icons/cross.svg';
import iconSuccess from 'icons/status/success.svg'; import iconSuccess from 'icons/status/success.svg';
...@@ -24,7 +24,7 @@ interface Props { ...@@ -24,7 +24,7 @@ interface Props {
const VerifiedContractsListItem = ({ data, isLoading }: Props) => { const VerifiedContractsListItem = ({ data, isLoading }: Props) => {
const balance = data.coin_balance && data.coin_balance !== '0' ? const balance = data.coin_balance && data.coin_balance !== '0' ?
BigNumber(data.coin_balance).div(10 ** appConfig.network.currency.decimals).dp(6).toFormat() : BigNumber(data.coin_balance).div(10 ** config.chain.currency.decimals).dp(6).toFormat() :
'0'; '0';
return ( return (
...@@ -38,7 +38,7 @@ const VerifiedContractsListItem = ({ data, isLoading }: Props) => { ...@@ -38,7 +38,7 @@ const VerifiedContractsListItem = ({ data, isLoading }: Props) => {
<CopyToClipboard text={ data.address.hash } ml={ -1 } isLoading={ isLoading }/> <CopyToClipboard text={ data.address.hash } ml={ -1 } isLoading={ isLoading }/>
</Address> </Address>
<Flex columnGap={ 3 }> <Flex columnGap={ 3 }>
<Skeleton isLoaded={ !isLoading } fontWeight={ 500 }>Balance { appConfig.network.currency.symbol }</Skeleton> <Skeleton isLoaded={ !isLoading } fontWeight={ 500 }>Balance { config.chain.currency.symbol }</Skeleton>
<Skeleton isLoaded={ !isLoading } color="text_secondary"> <Skeleton isLoaded={ !isLoading } color="text_secondary">
<span>{ balance }</span> <span>{ balance }</span>
</Skeleton> </Skeleton>
......
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import type { VerifiedContract } from 'types/api/contracts'; import type { VerifiedContract } from 'types/api/contracts';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import arrowIcon from 'icons/arrows/east.svg'; import arrowIcon from 'icons/arrows/east.svg';
import { default as Thead } from 'ui/shared/TheadSticky'; import { default as Thead } from 'ui/shared/TheadSticky';
...@@ -28,7 +28,7 @@ const VerifiedContractsTable = ({ data, sort, onSortToggle, isLoading }: Props) ...@@ -28,7 +28,7 @@ const VerifiedContractsTable = ({ data, sort, onSortToggle, isLoading }: Props)
<Th width="130px" isNumeric> <Th width="130px" isNumeric>
<Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ isLoading ? undefined : onSortToggle('balance') } columnGap={ 1 }> <Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ isLoading ? undefined : onSortToggle('balance') } columnGap={ 1 }>
{ sort?.includes('balance') && <Icon as={ arrowIcon } boxSize={ 4 } transform={ sortIconTransform }/> } { sort?.includes('balance') && <Icon as={ arrowIcon } boxSize={ 4 } transform={ sortIconTransform }/> }
Balance { appConfig.network.currency.symbol } Balance { config.chain.currency.symbol }
</Link> </Link>
</Th> </Th>
<Th width="130px" isNumeric> <Th width="130px" isNumeric>
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { VerifiedContract } from 'types/api/contracts'; import type { VerifiedContract } from 'types/api/contracts';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import iconCheck from 'icons/check.svg'; import iconCheck from 'icons/check.svg';
import iconCross from 'icons/cross.svg'; import iconCross from 'icons/cross.svg';
import iconSuccess from 'icons/status/success.svg'; import iconSuccess from 'icons/status/success.svg';
...@@ -22,7 +22,7 @@ interface Props { ...@@ -22,7 +22,7 @@ interface Props {
const VerifiedContractsTableItem = ({ data, isLoading }: Props) => { const VerifiedContractsTableItem = ({ data, isLoading }: Props) => {
const balance = data.coin_balance && data.coin_balance !== '0' ? const balance = data.coin_balance && data.coin_balance !== '0' ?
BigNumber(data.coin_balance).div(10 ** appConfig.network.currency.decimals).dp(6).toFormat() : BigNumber(data.coin_balance).div(10 ** config.chain.currency.decimals).dp(6).toFormat() :
'0'; '0';
return ( return (
......
...@@ -3,12 +3,12 @@ import React, { useCallback } from 'react'; ...@@ -3,12 +3,12 @@ import React, { useCallback } from 'react';
import { Controller } from 'react-hook-form'; import { Controller } from 'react-hook-form';
import type { Path, ControllerRenderProps, FieldValues, Control } from 'react-hook-form'; import type { Path, ControllerRenderProps, FieldValues, Control } from 'react-hook-form';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import CheckboxInput from 'ui/shared/CheckboxInput'; import CheckboxInput from 'ui/shared/CheckboxInput';
// does it depend on the network? // does it depend on the network?
const NOTIFICATIONS = [ 'native', 'ERC-20', 'ERC-721' ] as const; const NOTIFICATIONS = [ 'native', 'ERC-20', 'ERC-721' ] as const;
const NOTIFICATIONS_NAMES = [ appConfig.network.currency.symbol, 'ERC-20', 'ERC-721, ERC-1155 (NFT)' ]; const NOTIFICATIONS_NAMES = [ config.chain.currency.symbol, 'ERC-20', 'ERC-721, ERC-1155 (NFT)' ];
type Props<Inputs extends FieldValues> = { type Props<Inputs extends FieldValues> = {
control: Control<Inputs>; control: Control<Inputs>;
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { WatchlistAddress } from 'types/api/account'; import type { WatchlistAddress } from 'types/api/account';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import TokensIcon from 'icons/tokens.svg'; import TokensIcon from 'icons/tokens.svg';
import WalletIcon from 'icons/wallet.svg'; import WalletIcon from 'icons/wallet.svg';
import getCurrencyValue from 'lib/getCurrencyValue'; import getCurrencyValue from 'lib/getCurrencyValue';
...@@ -18,8 +18,7 @@ const WatchListAddressItem = ({ item, isLoading }: { item: WatchlistAddress; isL ...@@ -18,8 +18,7 @@ const WatchListAddressItem = ({ item, isLoading }: { item: WatchlistAddress; isL
const infoItemsPaddingLeft = { base: 1, lg: 8 }; const infoItemsPaddingLeft = { base: 1, lg: 8 };
const nativeTokenData = React.useMemo(() => ({ const nativeTokenData = React.useMemo(() => ({
address: appConfig.network.currency.address || '', name: config.chain.currency.name || '',
name: appConfig.network.currency.name || '',
icon_url: '', icon_url: '',
}), [ ]); }), [ ]);
...@@ -29,21 +28,19 @@ const WatchListAddressItem = ({ item, isLoading }: { item: WatchlistAddress; isL ...@@ -29,21 +28,19 @@ const WatchListAddressItem = ({ item, isLoading }: { item: WatchlistAddress; isL
<VStack spacing={ 2 } align="stretch" fontWeight={ 500 }> <VStack spacing={ 2 } align="stretch" fontWeight={ 500 }>
<AddressSnippet address={ item.address } isLoading={ isLoading }/> <AddressSnippet address={ item.address } isLoading={ isLoading }/>
<Flex fontSize="sm" pl={ infoItemsPaddingLeft } flexWrap="wrap" alignItems="center" rowGap={ 1 }> <Flex fontSize="sm" pl={ infoItemsPaddingLeft } flexWrap="wrap" alignItems="center" rowGap={ 1 }>
{ appConfig.network.currency.address && ( <TokenLogo
<TokenLogo data={ nativeTokenData }
data={ nativeTokenData } boxSize={ 5 }
boxSize={ 5 } borderRadius="sm"
borderRadius="sm" mr={ 2 }
mr={ 2 } isLoading={ isLoading }
isLoading={ isLoading } />
/>
) }
<Skeleton isLoaded={ !isLoading } whiteSpace="pre" display="inline-flex"> <Skeleton isLoaded={ !isLoading } whiteSpace="pre" display="inline-flex">
<span>{ appConfig.network.currency.symbol } balance: </span> <span>{ config.chain.currency.symbol } balance: </span>
<CurrencyValue <CurrencyValue
value={ item.address_balance } value={ item.address_balance }
exchangeRate={ item.exchange_rate } exchangeRate={ item.exchange_rate }
decimals={ String(appConfig.network.currency.decimals) } decimals={ String(config.chain.currency.decimals) }
accuracy={ 2 } accuracy={ 2 }
accuracyUsd={ 2 } accuracyUsd={ 2 }
/> />
......
...@@ -6,7 +6,7 @@ import type { AddressWithdrawalsItem } from 'types/api/address'; ...@@ -6,7 +6,7 @@ import type { AddressWithdrawalsItem } from 'types/api/address';
import type { BlockWithdrawalsItem } from 'types/api/block'; import type { BlockWithdrawalsItem } from 'types/api/block';
import type { WithdrawalsItem } from 'types/api/withdrawals'; import type { WithdrawalsItem } from 'types/api/withdrawals';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import blockIcon from 'icons/block.svg'; import blockIcon from 'icons/block.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
...@@ -85,7 +85,7 @@ const WithdrawalsListItem = ({ item, isLoading, view }: Props) => { ...@@ -85,7 +85,7 @@ const WithdrawalsListItem = ({ item, isLoading, view }: Props) => {
<ListItemMobileGrid.Label isLoading={ isLoading }>Value</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>Value</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value> <ListItemMobileGrid.Value>
<CurrencyValue value={ item.amount } currency={ appConfig.beaconChain.currencySymbol } isLoading={ isLoading }/> <CurrencyValue value={ item.amount } currency={ config.features.beaconChain.currency.symbol } isLoading={ isLoading }/>
</ListItemMobileGrid.Value> </ListItemMobileGrid.Value>
</> </>
) } ) }
......
...@@ -5,7 +5,7 @@ import type { AddressWithdrawalsItem } from 'types/api/address'; ...@@ -5,7 +5,7 @@ import type { AddressWithdrawalsItem } from 'types/api/address';
import type { BlockWithdrawalsItem } from 'types/api/block'; import type { BlockWithdrawalsItem } from 'types/api/block';
import type { WithdrawalsItem } from 'types/api/withdrawals'; import type { WithdrawalsItem } from 'types/api/withdrawals';
import appConfig from 'configs/app/config'; import config from 'configs/app';
import { default as Thead } from 'ui/shared/TheadSticky'; import { default as Thead } from 'ui/shared/TheadSticky';
import WithdrawalsTableItem from './WithdrawalsTableItem'; import WithdrawalsTableItem from './WithdrawalsTableItem';
...@@ -34,7 +34,7 @@ const WithdrawalsTable = ({ items, isLoading, top, view = 'list' }: Props) => { ...@@ -34,7 +34,7 @@ const WithdrawalsTable = ({ items, isLoading, top, view = 'list' }: Props) => {
{ view !== 'block' && <Th w="25%">Block</Th> } { view !== 'block' && <Th w="25%">Block</Th> }
{ view !== 'address' && <Th w="25%">To</Th> } { view !== 'address' && <Th w="25%">To</Th> }
{ view !== 'block' && <Th w="25%">Age</Th> } { view !== 'block' && <Th w="25%">Age</Th> }
<Th w="25%">{ `Value ${ appConfig.beaconChain.currencySymbol }` }</Th> <Th w="25%">{ `Value ${ config.features.beaconChain.currency.symbol }` }</Th>
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
......
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