Commit b6072dd4 authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend into chakra-v3

parents 341acb38 540d95ee
......@@ -25,3 +25,7 @@ jobs:
name: Publish Docker image
uses: './.github/workflows/publish-image.yml'
secrets: inherit
with:
tags: |
type=ref,event=branch
type=raw,value=unstable
......@@ -21,6 +21,7 @@ on:
- eth_sepolia
- eth_goerli
- filecoin
- neon_devnet
- optimism
- optimism_celestia
- optimism_sepolia
......@@ -53,7 +54,8 @@ jobs:
needs: make_slug
uses: './.github/workflows/publish-image.yml'
with:
tags: ghcr.io/blockscout/frontend:review-${{ needs.make_slug.outputs.REF_SLUG }}
tags: |
type=raw,value=review-${{ needs.make_slug.outputs.REF_SLUG }}
build_args: ENVS_PRESET=${{ inputs.envs_preset }}
secrets: inherit
......
......@@ -21,10 +21,13 @@ on:
- eth_sepolia
- eth_goerli
- filecoin
- mekong
- neon_devnet
- optimism
- optimism_celestia
- optimism_sepolia
- polygon
- rari_testnet
- rootstock
- shibarium
- scroll_sepolia
......@@ -53,7 +56,8 @@ jobs:
needs: make_slug
uses: './.github/workflows/publish-image.yml'
with:
tags: ghcr.io/blockscout/frontend:review-${{ needs.make_slug.outputs.REF_SLUG }}
tags: |
type=raw,value=review-${{ needs.make_slug.outputs.REF_SLUG }}
build_args: ENVS_PRESET=${{ inputs.envs_preset }}
secrets: inherit
......
......@@ -13,6 +13,10 @@ jobs:
name: Publish Docker image
uses: './.github/workflows/publish-image.yml'
secrets: inherit
with:
# NOTE: by default the image will be built with type=ref,event=tag; so we don't need to specify it here
tags: |
type=raw,value=e2e
deploy_e2e:
name: Deploy E2E instance
......
......@@ -4,7 +4,7 @@ on:
workflow_dispatch:
inputs:
tags:
description: Image tags
description: Image tags (e.g. "type=raw,value=foo")
required: false
type: string
build_args:
......@@ -19,7 +19,7 @@ on:
workflow_call:
inputs:
tags:
description: Image tags
description: Image tags (e.g. "type=raw,value=foo")
required: false
type: string
build_args:
......@@ -49,6 +49,11 @@ jobs:
uses: docker/metadata-action@v5
with:
images: ghcr.io/blockscout/frontend
flavor: |
latest=false
tags: |
type=ref,event=tag
${{ inputs.tags }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
......@@ -75,7 +80,7 @@ jobs:
file: ./Dockerfile
push: true
cache-from: type=gha
tags: ${{ inputs.tags || steps.meta.outputs.tags }}
tags: ${{ steps.meta.outputs.tags }}
platforms: ${{ inputs.platforms }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
......
......@@ -80,6 +80,9 @@ jobs:
secrets: inherit
with:
platforms: linux/amd64,linux/arm64/v8
# NOTE: by default the image will be built with type=ref,event=tag; so we need to specify it here
tags: |
type=raw,value=latest
sync_envs_docs:
name: Sync ENV variables docs
......
......@@ -369,10 +369,12 @@
"eth_goerli",
"eth_sepolia",
"filecoin",
"mekong",
"optimism",
"optimism_celestia",
"optimism_sepolia",
"polygon",
"rari_testnet",
"rootstock_testnet",
"shibarium",
"stability_testnet",
......
import type { Feature } from './types';
import type { TxExternalTxsConfig } from 'types/client/externalTxsConfig';
import { getEnvValue, parseEnvJson } from '../utils';
const externalTransactionsConfig = parseEnvJson<TxExternalTxsConfig>(getEnvValue('NEXT_PUBLIC_TX_EXTERNAL_TRANSACTIONS_CONFIG'));
const title = 'External transactions';
const config: Feature<{ chainName: string; chainLogoUrl: string; explorerUrlTemplate: string }> = (() => {
if (externalTransactionsConfig) {
return Object.freeze({
title,
isEnabled: true,
chainName: externalTransactionsConfig.chain_name,
chainLogoUrl: externalTransactionsConfig.chain_logo_url,
explorerUrlTemplate: externalTransactionsConfig.explorer_url_template,
});
}
return Object.freeze({
title,
isEnabled: false,
});
})();
export default config;
......@@ -12,6 +12,7 @@ export { default as csvExport } from './csvExport';
export { default as dataAvailability } from './dataAvailability';
export { default as deFiDropdown } from './deFiDropdown';
export { default as easterEggBadge } from './easterEggBadge';
export { default as externalTxs } from './externalTxs';
export { default as faultProofSystem } from './faultProofSystem';
export { default as gasTracker } from './gasTracker';
export { default as getGasButton } from './getGasButton';
......
import type { Feature } from './types';
import type { RollupType } from 'types/client/rollup';
import type { ParentChain, RollupType } from 'types/client/rollup';
import { ROLLUP_TYPES } from 'types/client/rollup';
import stripTrailingSlash from 'lib/stripTrailingSlash';
import { getEnvValue } from '../utils';
import { getEnvValue, parseEnvJson } from '../utils';
const type = (() => {
const envValue = getEnvValue('NEXT_PUBLIC_ROLLUP_TYPE');
return ROLLUP_TYPES.find((type) => type === envValue);
})();
const L1BaseUrl = getEnvValue('NEXT_PUBLIC_ROLLUP_L1_BASE_URL');
const L2WithdrawalUrl = getEnvValue('NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL');
const parentChain: ParentChain | undefined = (() => {
const envValue = parseEnvJson<ParentChain>(getEnvValue('NEXT_PUBLIC_ROLLUP_PARENT_CHAIN'));
const baseUrl = stripTrailingSlash(getEnvValue('NEXT_PUBLIC_ROLLUP_L1_BASE_URL') || '');
const chainName = getEnvValue('NEXT_PUBLIC_ROLLUP_PARENT_CHAIN_NAME');
if (!baseUrl && !envValue?.baseUrl) {
return;
}
return {
...envValue,
name: chainName ?? envValue?.name,
baseUrl: baseUrl ?? envValue?.baseUrl,
};
})();
const title = 'Rollup (L2) chain';
const config: Feature<{
type: RollupType;
L1BaseUrl: string;
homepage: { showLatestBlocks: boolean };
outputRootsEnabled: boolean;
L2WithdrawalUrl: string | undefined;
parentChainName: string | undefined;
parentChain: ParentChain;
DA: {
celestia: {
namespace: string | undefined;
};
};
}> = (() => {
if (type && L1BaseUrl) {
if (type && parentChain) {
return Object.freeze({
title,
isEnabled: true,
type,
L1BaseUrl: stripTrailingSlash(L1BaseUrl),
L2WithdrawalUrl: type === 'optimistic' ? L2WithdrawalUrl : undefined,
outputRootsEnabled: type === 'optimistic' && getEnvValue('NEXT_PUBLIC_ROLLUP_OUTPUT_ROOTS_ENABLED') === 'true',
parentChainName: type === 'arbitrum' ? getEnvValue('NEXT_PUBLIC_ROLLUP_PARENT_CHAIN_NAME') : undefined,
homepage: {
showLatestBlocks: getEnvValue('NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS') === 'true',
},
parentChain,
DA: {
celestia: {
namespace: type === 'arbitrum' ? getEnvValue('NEXT_PUBLIC_ROLLUP_DA_CELESTIA_NAMESPACE') : undefined,
},
},
});
}
......
export { default as address } from './address';
export { default as block } from './block';
export { default as nft } from './nft';
export { default as token } from './token';
export { default as tx } from './tx';
import { getEnvValue } from 'configs/app/utils';
const config = Object.freeze({
hideScamTokensEnabled: getEnvValue('NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED') === 'true',
});
export default config;
......@@ -9,6 +9,8 @@ NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
NEXT_PUBLIC_ROLLUP_PARENT_CHAIN={'baseUrl':'https://eth.blockscout.com','currency':{'name':'Ether','symbol':'ETH','decimals':18},'isTestnet':false,'id':1,'name':'Ethereum mainnet','rpcUrls':['https://eth.drpc.org']}
# Instance ENVs
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/
......@@ -16,13 +18,27 @@ NEXT_PUBLIC_API_HOST=arbitrum.blockscout.com
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'swoop-exchange'},{'text':'Disperse','icon':'txn_batches_slim','dappId':'smol'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'},{'text':'Get gas','icon':'gas','dappId':'smol-refuel'}]
NEXT_PUBLIC_DEX_POOLS_ENABLED=true
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/arbitrum-one.json
NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x37c798810d49ba132b40efe7f4fdf6806a8fc58226bb5e185ddc91f896577abf
NEXT_PUBLIC_HAS_CONTRACT_AUDIT_REPORTS=true
NEXT_PUBLIC_HAS_USER_OPS=true
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=rgba(27, 74, 221, 1)
NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['rgba(27, 74, 221, 1)']}
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_LOGOUT_URL=https://blockscout-arbitrum.us.auth0.com/v2/logout
NEXT_PUBLIC_MARKETPLACE_ENABLED=false
NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE=<p>Joined recent campaigns? Mint your Merit Badge <a href="https://badges.blockscout.com?utm_source=instance&utm_medium=arbitrum">here</a></p>
NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL=https://gist.githubusercontent.com/maikReal/974c47f86a3158c1a86b092ae2f044b3/raw/abcc7e02150cd85d4974503a0357162c0a2c35a9/merits-banner.html
NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL=https://swap.blockscout.com?utm_source=blockscout&utm_medium=arbitrum
NEXT_PUBLIC_MARKETPLACE_ENABLED=true
NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID=appGkvtmKI7fXE4Vs
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview?utm_source=blockscout&utm_medium=address', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'}]
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/pools']
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
......@@ -33,12 +49,15 @@ NEXT_PUBLIC_NETWORK_ID=42161
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/arbitrum-one-logo-light.svg
NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/arbitrum-one-logo-dark.svg
NEXT_PUBLIC_NETWORK_NAME=Arbitrum One
NEXT_PUBLIC_NETWORK_RPC_URL=https://arbitrum.llamarpc.com
NEXT_PUBLIC_NETWORK_RPC_URL=https://arbitrum-one.publicnode.com
NEXT_PUBLIC_NETWORK_SHORT_NAME=Arbitrum One
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/arbitrum-one.png
NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://eth.blockscout.com
NEXT_PUBLIC_ROLLUP_PARENT_CHAIN_NAME=Ethereum
NEXT_PUBLIC_ROLLUP_TYPE=arbitrum
NEXT_PUBLIC_STATS_API_HOST=https://stats-arbitrum-one-nitro.k8s-prod-2.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_XSTAR_SCORE_URL=https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm?utm_source=blockscout&utm_medium=address
\ No newline at end of file
......@@ -41,6 +41,7 @@ NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/arbitrum-sepolia.png
NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://eth-sepolia.blockscout.com
NEXT_PUBLIC_ROLLUP_TYPE=arbitrum
NEXT_PUBLIC_ROLLUP_PARENT_CHAIN={'baseUrl':'https://eth-sepolia.blockscout.com','currency':{'name':'Ether','symbol':'ETH','decimals':18},'isTestnet':true,'id':11155111,'name':'Sepolia','rpcUrls':['https://eth-sepolia.public.blastapi.io']}
NEXT_PUBLIC_SENTRY_DSN=https://fdcd971162e04694bf03564c5be3d291@o1222505.ingest.sentry.io/4503902500421632
NEXT_PUBLIC_STATS_API_HOST=https://stats-arbitrum-sepolia.k8s-prod-2.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
......
......@@ -10,6 +10,8 @@ NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_APP_INSTANCE=rubber_duck
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED=true
# Instance ENVs
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP={ "id": "632019", "width": "728", "height": "90" }
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE={ "id": "632018", "width": "320", "height": "100" }
......
# Set of ENVs for Mekong network explorer
# https://mekong.blockscout.com
# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=mekong"
# Local ENVs
NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
# Instance ENVs
NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=mekong.blockscout.com
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x7c7d9e09a5e0e6441a81efe57dbcf08848cd18a1f4238e28152faead390066a4
NEXT_PUBLIC_HAS_BEACON_CHAIN=true
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_IS_TESTNET=true
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_ID=7078815900
NEXT_PUBLIC_NETWORK_NAME=Mekong
NEXT_PUBLIC_NETWORK_RPC_URL=https://rpc.mekong.ethpandaops.io
NEXT_PUBLIC_NETWORK_SHORT_NAME=Mekong
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
\ No newline at end of file
# Set of ENVs for Neon Devnet network explorer
# https://neon-devnet.blockscout.com
# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=neon_devnet"
# Local ENVs
NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
# Instance ENVs
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=neon-devnet.blockscout.com
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/neon-devnet.json
NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x0716b7a70a1c3b83f731084d7c1449148392512318c2ce0fd812d029204707b5
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['linear-gradient(0, rgb(223, 66, 171), rgb(176, 40, 209))'],'text_color':['rgba(255, 255, 255, 1)']}
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_IS_TESTNET=true
NEXT_PUBLIC_LOGOUT_URL=https://blockscout-neon.us.auth0.com/v2/logout
NEXT_PUBLIC_MARKETPLACE_ENABLED=false
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=NEON
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=NEON
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/neon-short.svg
NEXT_PUBLIC_NETWORK_ID=245022926
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/neon.svg
NEXT_PUBLIC_NETWORK_NAME=Neon Devnet
NEXT_PUBLIC_NETWORK_RPC_URL=https://devnet.neonevm.org
NEXT_PUBLIC_NETWORK_SHORT_NAME=Neon
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/neon-devnet.png
NEXT_PUBLIC_STATS_API_HOST=https://stats-neon-devnet.k8s.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_TX_EXTERNAL_TRANSACTIONS_CONFIG={'chain_name':'Solana','chain_logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/refs/heads/main/configs/network-icons/solana.svg','explorer_url_template':'https://solscan.io/tx/{hash}'}
# Set of ENVs for Rari Testnet network explorer
# https://rari-testnet.cloud.blockscout.com
# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=rari_testnet"
# Local ENVs
NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
# Instance ENVs
NEXT_PUBLIC_AD_BANNER_PROVIDER=slise
NEXT_PUBLIC_AD_TEXT_PROVIDER=coinzilla
NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=rari-testnet.cloud.blockscout.com
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_COLOR_THEME_DEFAULT=light
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xbf69c7abc4fee283b59a9633dadfdaedde5c5ee0fba3e80a08b5b8a3acbd4363
NEXT_PUBLIC_HAS_BEACON_CHAIN=true
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=radial-gradient(farthest-corner at 0% 0%, rgba(183, 148, 244, 0.80) 0%, rgba(0, 163, 196, 0.80) 100%)
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR=rgb(255,255,255)
NEXT_PUBLIC_IS_TESTNET=true
NEXT_PUBLIC_NAVIGATION_HIDDEN_LINKS=[]
NEXT_PUBLIC_NAVIGATION_LAYOUT=vertical
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_ID=1
NEXT_PUBLIC_NETWORK_NAME=Rari Testnet
NEXT_PUBLIC_NETWORK_SHORT_NAME=Rari Testnet
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=false
NEXT_PUBLIC_OTHER_LINKS=[]
NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://arbitrum-sepolia.blockscout.com/
NEXT_PUBLIC_ROLLUP_TYPE=arbitrum
NEXT_PUBLIC_SEO_ENHANCED_DATA_ENABLED=false
NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE=jazzicon
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_ROLLUP_DA_CELESTIA_NAMESPACE=0x00000000000000000000000000000000000000ca1de12a9905be97beaf
\ No newline at end of file
......@@ -9,14 +9,12 @@ NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
NEXT_PUBLIC_VIEWS_CONTRACT_LANGUAGE_FILTERS=['solidity','vyper','yul','scilla']
# Instance ENVs
NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=zilliqa-prototestnet.blockscout.com
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]
NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x3d1ded3a7924cd3256a4b1a447c9bfb194f54b9a8ceb441edb8bb01563b516db
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['linear-gradient(90deg, rgba(0, 208, 198, 1) 0.06%, rgba(43, 146, 151, 1) 99.97%)','linear-gradient(90deg, rgba(0, 208, 198, 1) 0.06%, rgba(43, 146, 151, 1) 50.02%, rgba(0, 0, 0, 1) 99.97%)'],'text_color':['rgba(255, 255, 255, 1)','rgba(255, 255, 255, 1)'],'button':{'_default':{'background':['rgba(38, 6, 124, 1)']},'_hover':{'background':['rgba(17, 4, 87, 1)']}}}
......@@ -38,4 +36,6 @@ NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-c
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
NEXT_PUBLIC_VIEWS_ADDRESS_BECH_32_PREFIX=zil
NEXT_PUBLIC_VIEWS_ADDRESS_FORMAT=["base16", "bech32"]
NEXT_PUBLIC_VIEWS_CONTRACT_LANGUAGE_FILTERS=['solidity','vyper','yul','scilla']
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE=zilliqa
\ No newline at end of file
......@@ -157,6 +157,16 @@ function printDeprecationWarning(envsMap: Record<string, string>) {
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗\n');
}
if (
envsMap.NEXT_PUBLIC_ROLLUP_PARENT_CHAIN_NAME ||
envsMap.NEXT_PUBLIC_ROLLUP_L1_BASE_URL
) {
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗');
// eslint-disable-next-line max-len
console.warn('The NEXT_PUBLIC_ROLLUP_L1_BASE_URL and NEXT_PUBLIC_ROLLUP_PARENT_CHAIN_NAME variables are now deprecated and will be removed in the next release. Please migrate to the NEXT_PUBLIC_ROLLUP_PARENT_CHAIN variable.');
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗\n');
}
if (
envsMap.NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR ||
envsMap.NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND
......
......@@ -43,6 +43,7 @@ import type { NftMarketplaceItem } from '../../../types/views/nft';
import type { TxAdditionalFieldsId, TxFieldsId } from '../../../types/views/tx';
import { TX_ADDITIONAL_FIELDS_IDS, TX_FIELDS_IDS } from '../../../types/views/tx';
import type { VerifiedContractsFilter } from '../../../types/api/contracts';
import type { TxExternalTxsConfig } from '../../../types/client/externalTxsConfig';
import { replaceQuotes } from '../../../configs/app/utils';
import * as regexp from '../../../lib/regexp';
......@@ -70,6 +71,14 @@ const urlTest: yup.TestConfig = {
exclusive: true,
};
const getYupValidationErrorMessage = (error: unknown) =>
typeof error === 'object' &&
error !== null &&
'errors' in error &&
Array.isArray(error.errors) ?
error.errors.join(', ') :
'';
const marketplaceAppSchema: yup.ObjectSchema<MarketplaceAppOverview> = yup
.object({
id: yup.string().required(),
......@@ -270,6 +279,44 @@ const beaconChainSchema = yup
}),
});
const parentChainCurrencySchema = yup
.object()
.shape({
name: yup.string().required(),
symbol: yup.string().required(),
decimals: yup.number().required(),
});
const parentChainSchema = yup
.object()
.transform(replaceQuotes)
.json()
.shape({
id: yup.number(),
name: yup.string(),
baseUrl: yup.string().test(urlTest).required(),
rpcUrls: yup.array().of(yup.string().test(urlTest)),
currency: yup
.mixed()
.test(
'shape',
(ctx) => {
try {
parentChainCurrencySchema.validateSync(ctx.originalValue);
throw new Error('Unknown validation error');
} catch (error: unknown) {
const message = getYupValidationErrorMessage(error);
return 'in \"currency\" property ' + (message ? `${ message }` : '');
}
},
(data) => {
const isUndefined = data === undefined;
return isUndefined || parentChainCurrencySchema.isValidSync(data);
},
),
isTestnet: yup.boolean(),
});
const rollupSchema = yup
.object()
.shape({
......@@ -321,6 +368,44 @@ const rollupSchema = yup
value => value === undefined,
),
}),
NEXT_PUBLIC_ROLLUP_PARENT_CHAIN: yup
.mixed()
.when('NEXT_PUBLIC_ROLLUP_TYPE', {
is: (value: string) => value,
then: (schema) => {
return schema.test(
'shape',
(ctx) => {
try {
parentChainSchema.validateSync(ctx.originalValue);
throw new Error('Unknown validation error');
} catch (error: unknown) {
const message = getYupValidationErrorMessage(error);
return 'Invalid schema were provided for NEXT_PUBLIC_ROLLUP_TYPE' + (message ? `: ${ message }` : '');
}
},
(data) => {
const isUndefined = data === undefined;
return isUndefined || parentChainSchema.isValidSync(data);
}
)
},
otherwise: (schema) => schema.test(
'not-exist',
'NEXT_PUBLIC_ROLLUP_PARENT_CHAIN cannot not be used if NEXT_PUBLIC_ROLLUP_TYPE is not defined',
value => value === undefined,
),
}),
NEXT_PUBLIC_ROLLUP_DA_CELESTIA_NAMESPACE: yup
.string()
.min(60)
.max(60)
.matches(regexp.HEX_REGEXP_WITH_0X)
.when('NEXT_PUBLIC_ROLLUP_TYPE', {
is: (value: string) => value === 'arbitrum',
then: (schema) => schema,
otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_ROLLUP_DA_CELESTIA_NAMESPACE can only be used if NEXT_PUBLIC_ROLLUP_TYPE is set to \'arbitrum\' '),
}),
});
const celoSchema = yup
......@@ -563,6 +648,12 @@ const multichainProviderConfigSchema: yup.ObjectSchema<MultichainProviderConfig>
dapp_id: yup.string(),
});
const externalTxsConfigSchema: yup.ObjectSchema<TxExternalTxsConfig> = yup.object({
chain_name: yup.string().required(),
chain_logo_url: yup.string().required(),
explorer_url_template: yup.string().required(),
});
const schema = yup
.object()
.noUnknown(true, (params) => {
......@@ -635,12 +726,34 @@ const schema = yup
.array()
.transform(replaceQuotes)
.json()
.of(yup.string<ChainIndicatorId>().oneOf(CHAIN_INDICATOR_IDS)),
.of(yup.string<ChainIndicatorId>().oneOf(CHAIN_INDICATOR_IDS))
.test(
'stats-api-required',
'NEXT_PUBLIC_STATS_API_HOST is required when daily_operational_txs is enabled in NEXT_PUBLIC_HOMEPAGE_CHARTS',
function(value) {
// daily_operational_txs is presented only in stats microservice
if (value?.includes('daily_operational_txs')) {
return Boolean(this.parent.NEXT_PUBLIC_STATS_API_HOST);
}
return true;
}
),
NEXT_PUBLIC_HOMEPAGE_STATS: yup
.array()
.transform(replaceQuotes)
.json()
.of(yup.string<HomeStatsWidgetId>().oneOf(HOME_STATS_WIDGET_IDS)),
.of(yup.string<HomeStatsWidgetId>().oneOf(HOME_STATS_WIDGET_IDS))
.test(
'stats-api-required',
'NEXT_PUBLIC_STATS_API_HOST is required when total_operational_txs is enabled in NEXT_PUBLIC_HOMEPAGE_STATS',
function(value) {
// total_operational_txs is presented only in stats microservice
if (value?.includes('total_operational_txs')) {
return Boolean(this.parent.NEXT_PUBLIC_STATS_API_HOST);
}
return true;
}
),
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR: yup.string(),
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND: yup.string(),
NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG: yup
......@@ -652,7 +765,7 @@ const schema = yup
heroBannerSchema.validateSync(ctx.originalValue);
throw new Error('Unknown validation error');
} catch (error: unknown) {
const message = typeof error === 'object' && error !== null && 'errors' in error && Array.isArray(error.errors) ? error.errors.join(', ') : '';
const message = getYupValidationErrorMessage(error);
return 'Invalid schema were provided for NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG' + (message ? `: ${ message }` : '');
}
},
......@@ -755,6 +868,7 @@ const schema = yup
.transform(replaceQuotes)
.json()
.of(nftMarketplaceSchema),
NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED: yup.boolean(),
NEXT_PUBLIC_HELIA_VERIFIED_FETCH_ENABLED: yup.boolean(),
// e. misc
......@@ -912,6 +1026,19 @@ const schema = yup
NEXT_PUBLIC_REWARDS_SERVICE_API_HOST: yup.string().test(urlTest),
NEXT_PUBLIC_XSTAR_SCORE_URL: yup.string().test(urlTest),
NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK: yup.string().test(urlTest),
NEXT_PUBLIC_TX_EXTERNAL_TRANSACTIONS_CONFIG: yup.mixed().test(
'shape',
'Invalid schema were provided for NEXT_PUBLIC_TX_EXTERNAL_TRANSACTIONS_CONFIG, it should have chain_name, chain_logo_url, and explorer_url_template',
(data) => {
const isUndefined = data === undefined;
const valueSchema = yup.object<TxExternalTxsConfig>().transform(replaceQuotes).json().shape({
chain_name: yup.string().required(),
chain_logo_url: yup.string().required(),
explorer_url_template: yup.string().required(),
});
return isUndefined || valueSchema.isValidSync(data);
}),
// 6. External services envs
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: yup.string(),
......
......@@ -2,3 +2,6 @@ NEXT_PUBLIC_ROLLUP_TYPE=arbitrum
NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://example.com
NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS=true
NEXT_PUBLIC_ROLLUP_PARENT_CHAIN_NAME=DuckChain
NEXT_PUBLIC_ROLLUP_PARENT_CHAIN={'baseUrl':'https://explorer.duckchain.io','currency':{'name':'Quack','symbol':'QUACK','decimals':18},'isTestnet':true,'id':42,'name':'DuckChain','rpcUrls':['https://rpc.duckchain.io']}
NEXT_PUBLIC_ROLLUP_DA_CELESTIA_NAMESPACE=0x00000000000000000000000000000000000000ca1de12a9905be97beaf
\ No newline at end of file
NEXT_PUBLIC_TX_EXTERNAL_TRANSACTIONS_CONFIG={'chain_name':'Solana','chain_logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/refs/heads/main/configs/network-icons/solana.svg','explorer_url_template':'https://solscan.io/tx/{hash}'}
\ No newline at end of file
......@@ -4,3 +4,4 @@ NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL=https://example.com
NEXT_PUBLIC_FAULT_PROOF_ENABLED=true
NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS=true
NEXT_PUBLIC_ROLLUP_OUTPUT_ROOTS_ENABLED=false
NEXT_PUBLIC_ROLLUP_PARENT_CHAIN={'baseUrl':'https://explorer.duckchain.io'}
......@@ -78,6 +78,7 @@ frontend:
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES: "['/apps']"
PROMETHEUS_METRICS_ENABLED: true
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED: true
NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED: true
envFromSecret:
NEXT_PUBLIC_AUTH0_CLIENT_ID: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_AUTH0_CLIENT_ID
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID
......
......@@ -4,6 +4,16 @@ Thanks for showing interest to contribute to Blockscout. The following steps wil
&nbsp;
## Our guidelines: what we are looking for
We welcome contributions that enhance the project and improve the overall quality of our codebase. While we appreciate the effort that goes into making contributions, we kindly ask that contributors focus on the following types of changes:
- **Feature Enhancements:** Substantial improvements or new features that add significant value to the project.
- **Bug Fixes:** Fixes for known bugs or issues that impact functionality.
- **Documentation Improvements:** Comprehensive updates to documentation that clarify usage, installation, or project structure.
- **Performance Improvements:** Changes that enhance the performance or efficiency of the application.
&nbsp;
## Project setup
1. Fork the repo by clicking <kbd>Fork</kbd> button at the top of the repo main page and name it appropriately
......
......@@ -126,8 +126,8 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_HOMEPAGE_CHARTS | `Array<'daily_txs' \| 'coin_price' \| 'secondary_coin_price' \| 'market_cap' \| 'tvl'>` | List of charts displayed on the home page | - | - | `['daily_txs','coin_price','market_cap']` | v1.0.x+ |
| NEXT_PUBLIC_HOMEPAGE_STATS | `Array<'latest_batch' \| 'total_blocks' \| 'average_block_time' \| 'total_txs' \| 'latest_l1_state_batch' \| 'wallet_addresses' \| 'gas_tracker' \| 'btc_locked' \| 'current_epoch'>` | List of stats widgets displayed on the home page | - | For zkSync, zkEvm and Arbitrum rollups: `['latest_batch','average_block_time','total_txs','wallet_addresses','gas_tracker']`, for other cases: `['total_blocks','average_block_time','total_txs','wallet_addresses','gas_tracker']` | `['total_blocks','total_txs','wallet_addresses']` | v1.35.x+ |
| NEXT_PUBLIC_HOMEPAGE_CHARTS | `Array<'daily_txs' \| 'daily_operational_txs' \| 'coin_price' \| 'secondary_coin_price' \| 'market_cap' \| 'tvl'>` | List of charts displayed on the home page | - | - | `['daily_txs','coin_price','market_cap']` | v1.0.x+ |
| NEXT_PUBLIC_HOMEPAGE_STATS | `Array<'latest_batch' \| 'total_blocks' \| 'average_block_time' \| 'total_txs' \| 'total_operational_txs' \| 'latest_l1_state_batch' \| 'wallet_addresses' \| 'gas_tracker' \| 'btc_locked' \| 'current_epoch'>` | List of stats widgets displayed on the home page | - | For zkSync, zkEvm and Arbitrum rollups: `['latest_batch','average_block_time','total_txs','wallet_addresses','gas_tracker']`, for other cases: `['total_blocks','average_block_time','total_txs','wallet_addresses','gas_tracker']` | `['total_blocks','total_txs','wallet_addresses']` | v1.35.x+ |
| 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). **DEPRECATED** _Use `NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG` instead_ | - | `white` | `\#DCFE76` | v1.0.x+ |
| 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). **DEPRECATED** _Use `NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG` 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)` | v1.1.0+ |
| NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG | `HeroBannerConfig`, see details [below](#hero-banner-configuration-properties) | Configuration of hero banner appearance. | - | - | See [below](#hero-banner-configuration-properties) | v1.35.0+ |
......@@ -280,6 +280,13 @@ Settings for meta tags, OG tags and SEO
&nbsp;
#### Token views
| Variable | Type | Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED | `boolean` | Show the "Hide scam tokens" toggle in the site settings dropdown. This option controls the visibility of tokens with a poor reputation in the search results. | - | - | `'["value","tx_fee"]'` | v1.38.0+ |
&nbsp;
#### NFT views
| Variable | Type | Description | Compulsoriness | Default value | Example value | Version |
......@@ -450,13 +457,27 @@ This feature is **enabled by default** with the `coinzilla` ads provider. To swi
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_ROLLUP_TYPE | `'optimistic' \| 'arbitrum' \| 'shibarium' \| 'zkEvm' \| 'zkSync' \| 'scroll'` | Rollup chain type | Required | - | `'optimistic'` | v1.24.0+ |
| NEXT_PUBLIC_ROLLUP_L1_BASE_URL | `string` | Blockscout base URL for L1 network | Required | - | `'http://eth-goerli.blockscout.com'` | v1.24.0+ |
| NEXT_PUBLIC_ROLLUP_L1_BASE_URL | `string` | Blockscout base URL for L1 network. **DEPRECATED** _Use `NEXT_PUBLIC_ROLLUP_PARENT_CHAIN` instead_ | Required | - | `'http://eth-goerli.blockscout.com'` | v1.24.0+ |
| NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL | `string` | URL for L2 -> L1 withdrawals (Optimistic stack only) | Required for `optimistic` rollups | - | `https://app.optimism.io/bridge/withdraw` | v1.24.0+ |
| NEXT_PUBLIC_FAULT_PROOF_ENABLED | `boolean` | Set to `true` for chains with fault proof system enabled (Optimistic stack only) | - | - | `true` | v1.31.0+ |
| NEXT_PUBLIC_HAS_MUD_FRAMEWORK | `boolean` | Set to `true` for instances that use MUD framework (Optimistic stack only) | - | - | `true` | v1.33.0+ |
| NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS | `boolean` | Set to `true` to display "Latest blocks" widget instead of "Latest batches" on the home page | - | - | `true` | v1.36.0+ |
| NEXT_PUBLIC_ROLLUP_OUTPUT_ROOTS_ENABLED | `boolean` | Enables "Output roots" page (Optimistic stack only) | - | `false` | `true` | v1.37.0+ |
| NEXT_PUBLIC_ROLLUP_PARENT_CHAIN_NAME | `string` | Set to customize L1 transaction status labels in the UI (e.g., "Sent to <chain-name>"). This setting is applicable only for Arbitrum-based chains. | - | - | `DuckChain` | v1.37.0+ |
| NEXT_PUBLIC_ROLLUP_PARENT_CHAIN_NAME | `string` | Set to customize L1 transaction status labels in the UI (e.g., "Sent to <chain-name>"). This setting is applicable only for Arbitrum-based chains. **DEPRECATED** _Use `NEXT_PUBLIC_ROLLUP_PARENT_CHAIN` instead_ | - | - | `DuckChain` | v1.37.0+ |
| NEXT_PUBLIC_ROLLUP_PARENT_CHAIN | `ParentChain`, see details [below](#parent-chain-configuration-properties) | Configuration parameters for the parent chain. | - | - | `{'baseUrl':'https://explorer.duckchain.io'}` | v1.38.0+ |
| NEXT_PUBLIC_ROLLUP_DA_CELESTIA_NAMESPACE | `string` | Hex-string for creating a link to the transaction batch on the Seleneium explorer. "0x"-format and 60 symbol length. Available only for Arbitrum roll-ups. | - | - | `0x00000000000000000000000000000000000000ca1de12a9905be97beaf` | v1.38.0+ |
#### Parent chain configuration properties
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| id | `number` | Chain id, see [https://chainlist.org](https://chainlist.org) for the reference. | - | - | `42` |
| name | `string` | Displayed name of the chain. Set to customize L1 transaction status labels in the UI (e.g., "Sent to <chain-name>"). Currently, this setting is applicable only for Arbitrum-based chains. | - | - | `DuckChain` |
| baseUrl | `string` | Base url of the chain explorer. | Required | - | `https://explorer.duckchain.io` |
| rpcUrls | `Array<string>` | Chain public RPC server urls, see [https://chainlist.org](https://chainlist.org) for the reference. | - | - | `['https://rpc.duckchain.io']` |
| currency | `{ name: string; symbol: string; decimals: number; }` | Chain currency config. | - | - | `{ name: Quack, symbol: QUA, decimals: 18 }` |
| isTestnet | `boolean` | Set to true if network is testnet. | - | - | `true` |
&nbsp;
......@@ -590,6 +611,14 @@ This feature is **enabled by default** with the `['metamask']` value. To switch
&nbsp;
### External transactions
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_TX_EXTERNAL_TRANSACTIONS_CONFIG | `{ chain_name: string; chain_logo_url: string; explorer_url_template: string; }` | Configuration of the external transactions links that should be added to the transaction details. | - | - | `{ chain_name: 'ethereum', chain_logo_url: 'https://example.com/logo.png', explorer_url_template: 'https://explorer.com/tx/{hash}' }` | v1.38.0+ |
&nbsp;
### Verified tokens info
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
......@@ -749,7 +778,7 @@ The feature enables the Validators page which provides detailed information abou
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE | `'stability' \| 'blackfort'` | Chain type | Required | - | `'stability'` | v1.25.0+ |
| NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE | `'stability' \| 'blackfort' \| 'zilliqa'` | Chain type | Required | - | `'stability'` | v1.25.0+ |
&nbsp;
......
......@@ -42,6 +42,11 @@ const RESTRICTED_MODULES = {
],
message: 'Please use corresponding component or hook from ui/shared/chakra component instead',
},
{
name: 'next/link',
importNames: [ 'default' ],
message: 'Please use ui/shared/NextLink component instead',
},
],
patterns: [
'icons/*',
......
<svg viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="m5.29 9.053 3.723-3.828a.972.972 0 0 1 1.336.06 1.051 1.051 0 0 1 .058 1.394L8.4 8.751h14.37c.262 0 .514.107.7.3.188.191.294.454.294.729 0 .275-.106.537-.293.73a.976.976 0 0 1-.7.298H5.987a.969.969 0 0 1-.55-.17 1.02 1.02 0 0 1-.367-.46 1.062 1.062 0 0 1 .22-1.125Zm18.724 6a.97.97 0 0 1 .55.17c.163.111.291.271.367.46a1.062 1.062 0 0 1-.22 1.125l-3.737 3.841-.006.008a.997.997 0 0 1-.322.255.966.966 0 0 1-1.13-.197 1.06 1.06 0 0 1 .056-1.513l.008-.006 2.021-2.087H15.72c.103-.684.183-1.403 0-2.072l8.294.016Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="m5.29 9.053 3.723-3.828a.972.972 0 0 1 1.336.06 1.051 1.051 0 0 1 .058 1.394L8.4 8.751h14.37c.262 0 .514.107.7.3.188.191.294.454.294.729 0 .275-.106.537-.293.73a.976.976 0 0 1-.7.298H5.987a.969.969 0 0 1-.55-.17 1.02 1.02 0 0 1-.367-.46 1.062 1.062 0 0 1 .22-1.125Zm18.724 6a.97.97 0 0 1 .55.17c.163.111.291.271.367.46a1.062 1.062 0 0 1-.22 1.125l-3.737 3.841-.006.008a.997.997 0 0 1-.322.255.966.966 0 0 1-1.13-.197 1.06 1.06 0 0 1 .056-1.513l.008-.006 2.021-2.087H15.72c.103-.684.183-1.403 0-2.072l8.294.016ZM6.209 13.24a.67.67 0 0 1 .504-.24h4.277c.095 0 .186.043.252.12l2.496 2.865c.067.077.104.181.104.29v6.55a.884.884 0 0 1-.209.578.67.67 0 0 1-.504.24H6.713a.67.67 0 0 1-.504-.24.884.884 0 0 1-.209-.579V13.82c0-.217.075-.426.209-.58Zm4.634.579h-4.13v9.005h6.416v-6.38l-2.286-2.625Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.209 13.24a.67.67 0 0 1 .504-.24h4.277c.095 0 .186.043.252.12l2.496 2.865c.067.077.104.181.104.29v6.55a.884.884 0 0 1-.209.578.67.67 0 0 1-.504.24H6.713a.67.67 0 0 1-.504-.24.884.884 0 0 1-.209-.579V13.82c0-.217.075-.426.209-.58Zm4.634.579h-4.13v9.005h6.416v-6.38l-2.286-2.625Z" fill="currentColor"/>
<path clip-rule="evenodd" d="M6.209 13.24a.67.67 0 0 1 .504-.24h4.277c.095 0 .186.043.252.12l2.496 2.865c.067.077.104.181.104.29v6.55a.884.884 0 0 1-.209.578.67.67 0 0 1-.504.24H6.713a.67.67 0 0 1-.504-.24.884.884 0 0 1-.209-.579V13.82c0-.217.075-.426.209-.58Zm4.634.579h-4.13v9.005h6.416v-6.38l-2.286-2.625Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path clip-rule="evenodd" d="M6.209 13.24a.67.67 0 0 1 .504-.24h4.277c.095 0 .186.043.252.12l2.496 2.865c.067.077.104.181.104.29v6.55a.884.884 0 0 1-.209.578.67.67 0 0 1-.504.24H6.713a.67.67 0 0 1-.504-.24.884.884 0 0 1-.209-.579V13.82c0-.217.075-.426.209-.58Zm4.634.579h-4.13v9.005h6.416v-6.38l-2.286-2.625Z" stroke="currentColor" stroke-opacity=".2" stroke-linecap="round" stroke-linejoin="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.743 13.224c.206 0 .373.167.373.373v2.24h2.24a.373.373 0 1 1 0 .748h-2.613a.373.373 0 0 1-.374-.374v-2.614c0-.206.167-.373.374-.373Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.743 13.224c.206 0 .373.167.373.373v2.24h2.24a.373.373 0 1 1 0 .748h-2.613a.373.373 0 0 1-.374-.374v-2.614c0-.206.167-.373.374-.373Z" fill="currentColor"/>
<path clip-rule="evenodd" d="M10.743 13.224c.206 0 .373.167.373.373v2.24h2.24a.373.373 0 1 1 0 .748h-2.613a.373.373 0 0 1-.374-.374v-2.614c0-.206.167-.373.374-.373Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path clip-rule="evenodd" d="M10.743 13.224c.206 0 .373.167.373.373v2.24h2.24a.373.373 0 1 1 0 .748h-2.613a.373.373 0 0 1-.374-.374v-2.614c0-.206.167-.373.374-.373Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
......@@ -18,6 +18,7 @@ const PAGE_PROPS = {
query: {},
adBannerProvider: null,
apiData: null,
uuid: '123',
};
const TestApp = ({ children }: { children: React.ReactNode }) => {
......
This diff is collapsed.
......@@ -20,10 +20,26 @@ export default function useQueryClientConfig() {
queries: {
refetchOnWindowFocus: false,
retry,
throwOnError: (error) => {
throwOnError: (error, query) => {
const status = getErrorObjStatusCode(error);
// don't catch error for "Too many requests" response
return status === 429;
// we don't catch error only for "Too many requests" response
if (status !== 429) {
return false;
}
const EXTERNAL_API_RESOURCES = [
'safe_transaction_api',
'contract_solidity_scan_report',
'address_xstar_score',
'noves_transaction',
'noves_address_history',
'noves_describe_txs',
'gas_hawk_saving_potential',
];
const isExternalApiResource = EXTERNAL_API_RESOURCES.some((resource) => query.queryKey[0] === resource);
return !isExternalApiResource;
},
},
},
......
......@@ -14,6 +14,7 @@ const AppContext = createContext<PageProps>({
query: {},
adBannerProvider: null,
apiData: null,
uuid: '',
});
export function AppContextProvider({ children, pageProps }: Props) {
......
......@@ -44,6 +44,7 @@ type TRewardsContext = {
isLoginModalOpen: boolean;
openLoginModal: () => void;
closeLoginModal: () => void;
saveApiToken: (token: string | undefined) => void;
login: (refCode: string) => Promise<{ isNewUser?: boolean; invalidRefCodeError?: boolean }>;
claim: () => Promise<void>;
};
......@@ -68,6 +69,7 @@ const initialState = {
isLoginModalOpen: false,
openLoginModal: () => {},
closeLoginModal: () => {},
saveApiToken: () => {},
login: async() => ({}),
claim: async() => {},
};
......@@ -268,6 +270,7 @@ export function RewardsContextProvider({ children }: Props) {
rewardsConfigQuery,
checkUserQuery,
apiToken,
saveApiToken,
isInitialized,
isLoginModalOpen,
openLoginModal,
......@@ -277,7 +280,7 @@ export function RewardsContextProvider({ children }: Props) {
};
}, [
balancesQuery, dailyRewardQuery, checkUserQuery,
apiToken, login, claim, referralsQuery, rewardsConfigQuery, isInitialized,
apiToken, login, claim, referralsQuery, rewardsConfigQuery, isInitialized, saveApiToken,
isLoginModalOpen, openLoginModal, closeLoginModal,
]);
......
......@@ -17,6 +17,7 @@ export enum NAMES {
MIXPANEL_DEBUG = '_mixpanel_debug',
ADDRESS_NFT_DISPLAY_TYPE = 'address_nft_display_type',
UUID = 'uuid',
SHOW_SCAM_TOKENS = 'show_scam_tokens',
}
export function get(name?: NAMES | undefined | null, serverCookie?: string) {
......
import getErrorObj from './getErrorObj';
export default function getErrorProp<T extends unknown>(error: unknown, prop: string): T | undefined {
const errorObj = getErrorObj(error);
return errorObj && prop in errorObj ?
(errorObj[prop as keyof typeof errorObj] as T) :
undefined;
}
......@@ -9,7 +9,7 @@ export interface GrowthBookFeatures {
test_value: string;
}
export const growthBook = (() => {
export const initGrowthBook = (uuid: string) => {
const feature = config.features.growthBook;
if (!feature.isEnabled) {
......@@ -21,7 +21,7 @@ export const growthBook = (() => {
clientKey: feature.clientKey,
enableDevMode: config.app.isDev,
attributes: {
id: mixpanel.getUuid(),
id: uuid,
chain_id: config.chain.id,
},
trackingCallback: (experiment, result) => {
......@@ -37,7 +37,7 @@ export const growthBook = (() => {
});
},
});
})();
};
function getStorageValue(): Array<unknown> | undefined {
const item = window.localStorage.getItem(STORAGE_KEY);
......
......@@ -2,9 +2,10 @@ import React from 'react';
import { SECOND } from 'lib/consts';
import { growthBook } from './init';
import { initGrowthBook } from './init';
export default function useLoadFeatures() {
export default function useLoadFeatures(uuid: string) {
const growthBook = initGrowthBook(uuid);
React.useEffect(() => {
growthBook?.setAttributes({
...growthBook.getAttributes(),
......@@ -13,5 +14,5 @@ export default function useLoadFeatures() {
});
growthBook?.loadFeatures({ timeout: SECOND });
}, []);
}, [ growthBook ]);
}
......@@ -44,6 +44,12 @@ export default function useNavItems(): ReturnType {
icon: 'transactions',
isActive: pathname === '/txs' || pathname === '/tx/[hash]',
};
const internalTxs: NavItem | null = {
text: 'Internal transactions',
nextRoute: { pathname: '/internal-txs' as const },
icon: 'internal_txns',
isActive: pathname === '/internal-txs',
};
const userOps: NavItem | null = config.features.userOps.isEnabled ? {
text: 'User operations',
nextRoute: { pathname: '/ops' as const },
......@@ -68,7 +74,7 @@ export default function useNavItems(): ReturnType {
text: 'Top validators',
nextRoute: { pathname: '/validators' as const },
icon: 'validator',
isActive: pathname === '/validators',
isActive: pathname === '/validators' || pathname === '/validators/[id]',
} : null;
const rollupDeposits = {
text: `Deposits (L1${ rightLineArrow }L2)`,
......@@ -118,6 +124,7 @@ export default function useNavItems(): ReturnType {
blockchainNavItems = [
[
txs,
internalTxs,
rollupDeposits,
rollupWithdrawals,
],
......@@ -140,6 +147,7 @@ export default function useNavItems(): ReturnType {
blockchainNavItems = [
[
txs,
internalTxs,
rollupDeposits,
rollupWithdrawals,
],
......@@ -155,6 +163,7 @@ export default function useNavItems(): ReturnType {
blockchainNavItems = [
[
txs,
internalTxs,
userOps,
blocks,
rollupTxnBatches,
......@@ -169,6 +178,7 @@ export default function useNavItems(): ReturnType {
} else {
blockchainNavItems = [
txs,
internalTxs,
userOps,
blocks,
topAccounts,
......@@ -246,6 +256,11 @@ export default function useNavItems(): ReturnType {
nextRoute: { pathname: '/public-tags/submit' as const },
isActive: pathname.startsWith('/public-tags/submit'),
},
rollupFeature.isEnabled && rollupFeature.type === 'arbitrum' && {
text: 'Txn withdrawals',
nextRoute: { pathname: '/txn-withdrawals' as const },
isActive: pathname.startsWith('/txn-withdrawals'),
},
...config.UI.navigation.otherLinks,
].filter(Boolean);
......
......@@ -5,6 +5,7 @@ type OGPageType = 'Homepage' | 'Root page' | 'Regular page';
const OG_TYPE_DICT: Record<Route['pathname'], OGPageType> = {
'/': 'Homepage',
'/txs': 'Root page',
'/internal-txs': 'Root page',
'/txs/kettle/[hash]': 'Regular page',
'/tx/[hash]': 'Regular page',
'/blocks': 'Root page',
......@@ -28,7 +29,7 @@ const OG_TYPE_DICT: Record<Route['pathname'], OGPageType> = {
'/graphiql': 'Regular page',
'/search-results': 'Regular page',
'/auth/profile': 'Root page',
'/account/rewards': 'Regular page',
'/account/merits': 'Regular page',
'/account/watchlist': 'Regular page',
'/account/api-key': 'Regular page',
'/account/custom-abi': 'Regular page',
......@@ -36,6 +37,7 @@ const OG_TYPE_DICT: Record<Route['pathname'], OGPageType> = {
'/account/verified-addresses': 'Root page',
'/public-tags/submit': 'Regular page',
'/withdrawals': 'Root page',
'/txn-withdrawals': 'Root page',
'/visualize/sol2uml': 'Regular page',
'/csv-export': 'Regular page',
'/deposits': 'Root page',
......@@ -43,6 +45,7 @@ const OG_TYPE_DICT: Record<Route['pathname'], OGPageType> = {
'/dispute-games': 'Root page',
'/batches': 'Root page',
'/batches/[number]': 'Regular page',
'/batches/celestia/[height]/[commitment]': 'Regular page',
'/blobs/[hash]': 'Regular page',
'/ops': 'Root page',
'/op/[hash]': 'Regular page',
......@@ -50,6 +53,7 @@ const OG_TYPE_DICT: Record<Route['pathname'], OGPageType> = {
'/name-domains': 'Root page',
'/name-domains/[name]': 'Regular page',
'/validators': 'Root page',
'/validators/[id]': 'Regular page',
'/gas-tracker': 'Root page',
'/mud-worlds': 'Root page',
'/token-transfers': 'Root page',
......
......@@ -8,6 +8,7 @@ const DEFAULT_TEMPLATE = 'Open-source block explorer by Blockscout. Search trans
const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/': DEFAULT_TEMPLATE,
'/txs': DEFAULT_TEMPLATE,
'/internal-txs': DEFAULT_TEMPLATE,
'/txs/kettle/[hash]': DEFAULT_TEMPLATE,
'/tx/[hash]': 'View transaction %hash% on %network_title%',
'/blocks': DEFAULT_TEMPLATE,
......@@ -31,7 +32,7 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/graphiql': DEFAULT_TEMPLATE,
'/search-results': DEFAULT_TEMPLATE,
'/auth/profile': DEFAULT_TEMPLATE,
'/account/rewards': DEFAULT_TEMPLATE,
'/account/merits': DEFAULT_TEMPLATE,
'/account/watchlist': DEFAULT_TEMPLATE,
'/account/api-key': DEFAULT_TEMPLATE,
'/account/custom-abi': DEFAULT_TEMPLATE,
......@@ -39,6 +40,7 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/account/verified-addresses': DEFAULT_TEMPLATE,
'/public-tags/submit': 'Propose a new public tag for your address, contract or set of contracts for your dApp. Our team will review and approve your submission. Public tags are incredible tool which helps users identify contracts and addresses.',
'/withdrawals': DEFAULT_TEMPLATE,
'/txn-withdrawals': DEFAULT_TEMPLATE,
'/visualize/sol2uml': DEFAULT_TEMPLATE,
'/csv-export': DEFAULT_TEMPLATE,
'/deposits': DEFAULT_TEMPLATE,
......@@ -46,6 +48,7 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/dispute-games': DEFAULT_TEMPLATE,
'/batches': DEFAULT_TEMPLATE,
'/batches/[number]': DEFAULT_TEMPLATE,
'/batches/celestia/[height]/[commitment]': DEFAULT_TEMPLATE,
'/blobs/[hash]': DEFAULT_TEMPLATE,
'/ops': DEFAULT_TEMPLATE,
'/op/[hash]': DEFAULT_TEMPLATE,
......@@ -53,6 +56,7 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/name-domains': DEFAULT_TEMPLATE,
'/name-domains/[name]': DEFAULT_TEMPLATE,
'/validators': DEFAULT_TEMPLATE,
'/validators/[id]': DEFAULT_TEMPLATE,
'/gas-tracker': 'Explore real-time %network_title% gas fees with Blockscout\'s advanced gas fee tracker. Get accurate %network_gwei% estimates and track transaction costs live.',
'/mud-worlds': DEFAULT_TEMPLATE,
'/token-transfers': DEFAULT_TEMPLATE,
......
......@@ -5,6 +5,7 @@ import config from 'configs/app';
const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/': '%network_name% blockchain explorer - View %network_name% stats',
'/txs': '%network_name% transactions - %network_name% explorer',
'/internal-txs': '%network_name% internal transactions - %network_name% explorer',
'/txs/kettle/[hash]': '%network_name% kettle %hash% transactions',
'/tx/[hash]': '%network_name% transaction %hash%',
'/blocks': '%network_name% blocks',
......@@ -28,7 +29,7 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/graphiql': 'GraphQL for %network_name% - %network_name% data query',
'/search-results': '%network_name% search result for %q%',
'/auth/profile': '%network_name% - my profile',
'/account/rewards': '%network_name% - rewards',
'/account/merits': '%network_name% - Merits',
'/account/watchlist': '%network_name% - watchlist',
'/account/api-key': '%network_name% - API keys',
'/account/custom-abi': '%network_name% - custom ABI',
......@@ -36,6 +37,7 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/account/verified-addresses': '%network_name% - my verified addresses',
'/public-tags/submit': '%network_name% - public tag requests',
'/withdrawals': '%network_name% withdrawals - track on %network_name% explorer',
'/txn-withdrawals': '%network_name% L2 to L1 message relayer',
'/visualize/sol2uml': '%network_name% Solidity UML diagram',
'/csv-export': '%network_name% export data to CSV',
'/deposits': '%network_name% deposits (L1 > L2)',
......@@ -43,6 +45,7 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/dispute-games': '%network_name% dispute games',
'/batches': '%network_name% txn batches',
'/batches/[number]': '%network_name% L2 txn batch %number%',
'/batches/celestia/[height]/[commitment]': '%network_name% L2 txn batch %height% %commitment%',
'/blobs/[hash]': '%network_name% blob %hash% details',
'/ops': 'User operations on %network_name% - %network_name% explorer',
'/op/[hash]': '%network_name% user operation %hash%',
......@@ -50,6 +53,7 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/name-domains': '%network_name% name domains - %network_name% explorer',
'/name-domains/[name]': '%network_name% %name% domain details',
'/validators': '%network_name% validators list',
'/validators/[id]': '%network_name% validator %id% details',
'/gas-tracker': 'Track %network_name% gas fees in %network_gwei%',
'/mud-worlds': '%network_name% MUD worlds list',
'/token-transfers': '%network_name% token transfers',
......
......@@ -3,6 +3,7 @@ import type { Route } from 'nextjs-routes';
export const PAGE_TYPE_DICT: Record<Route['pathname'], string> = {
'/': 'Homepage',
'/txs': 'Transactions',
'/internal-txs': 'Internal transactions',
'/txs/kettle/[hash]': 'Kettle transactions',
'/tx/[hash]': 'Transaction details',
'/blocks': 'Blocks',
......@@ -26,7 +27,7 @@ export const PAGE_TYPE_DICT: Record<Route['pathname'], string> = {
'/graphiql': 'GraphQL',
'/search-results': 'Search results',
'/auth/profile': 'Profile',
'/account/rewards': 'Merits',
'/account/merits': 'Merits',
'/account/watchlist': 'Watchlist',
'/account/api-key': 'API keys',
'/account/custom-abi': 'Custom ABI',
......@@ -34,6 +35,7 @@ export const PAGE_TYPE_DICT: Record<Route['pathname'], string> = {
'/account/verified-addresses': 'Verified addresses',
'/public-tags/submit': 'Submit public tag',
'/withdrawals': 'Withdrawals',
'/txn-withdrawals': 'Txn withdrawals',
'/visualize/sol2uml': 'Solidity UML diagram',
'/csv-export': 'Export data to CSV file',
'/deposits': 'Deposits (L1 > L2)',
......@@ -41,6 +43,7 @@ export const PAGE_TYPE_DICT: Record<Route['pathname'], string> = {
'/dispute-games': 'Dispute games',
'/batches': 'Txn batches',
'/batches/[number]': 'L2 txn batch details',
'/batches/celestia/[height]/[commitment]': 'L2 txn batch details',
'/blobs/[hash]': 'Blob details',
'/ops': 'User operations',
'/op/[hash]': 'User operation details',
......@@ -48,6 +51,7 @@ export const PAGE_TYPE_DICT: Record<Route['pathname'], string> = {
'/name-domains': 'Domains search and resolve',
'/name-domains/[name]': 'Domain details',
'/validators': 'Validators list',
'/validators/[id]': 'Validator details',
'/gas-tracker': 'Gas tracker',
'/mud-worlds': 'MUD worlds',
'/token-transfers': 'Token transfers',
......
import * as cookies from 'lib/cookies';
import * as growthBook from 'lib/growthbook/consts';
import isBrowser from 'lib/isBrowser';
export default function getUuid() {
const cookie = cookies.get(cookies.NAMES.UUID);
if (cookie) {
return cookie;
}
const uuid = crypto.randomUUID();
cookies.set(cookies.NAMES.UUID, uuid);
if (isBrowser()) {
window.localStorage.removeItem(growthBook.STORAGE_KEY);
}
return uuid;
}
import getPageType from './getPageType';
import getUuid from './getUuid';
import logEvent from './logEvent';
import reset from './reset';
import useInit from './useInit';
......@@ -12,7 +11,6 @@ export {
useLogPageView,
logEvent,
getPageType,
getUuid,
userProfile,
reset,
};
......@@ -10,7 +10,6 @@ import * as cookies from 'lib/cookies';
import dayjs from 'lib/date/dayjs';
import getQueryParamString from 'lib/router/getQueryParamString';
import getUuid from './getUuid';
import * as userProfile from './userProfile';
export default function useMixpanelInit() {
......@@ -30,7 +29,8 @@ export default function useMixpanelInit() {
debug: Boolean(debugFlagQuery.current || debugFlagCookie),
};
const isAuth = Boolean(cookies.get(cookies.NAMES.API_TOKEN));
const userId = getUuid();
const uuid = cookies.get(cookies.NAMES.UUID);
mixpanel.init(feature.projectToken, mixpanelConfig);
mixpanel.register({
......@@ -41,9 +41,9 @@ export default function useMixpanelInit() {
'Viewport height': window.innerHeight,
Language: window.navigator.language,
'Device type': capitalize(deviceType),
'User id': userId,
'User id': uuid,
});
mixpanel.identify(userId);
mixpanel.identify(uuid);
userProfile.set({
'Device Type': capitalize(deviceType),
...(isAuth ? { 'With Account': true } : {}),
......@@ -56,7 +56,7 @@ export default function useMixpanelInit() {
if (debugFlagQuery.current && !debugFlagCookie) {
cookies.set(cookies.NAMES.MIXPANEL_DEBUG, 'true');
}
}, []);
}, [ ]);
return isInited;
}
......@@ -3,6 +3,7 @@ export const URL_PREFIX = /^https?:\/\//i;
export const IPFS_PREFIX = /^ipfs:\/\//i;
export const HEX_REGEXP = /^(?:0x)?[\da-fA-F]+$/;
export const HEX_REGEXP_WITH_0X = /^0x[\da-fA-F]+$/;
export const FILE_EXTENSION = /\.([\da-z]+)$/i;
......
......@@ -6,7 +6,7 @@ import config from 'configs/app';
import { ABSENT_PARAM_ERROR_MESSAGE } from 'lib/errors/throwOnAbsentParamError';
import { RESOURCE_LOAD_ERROR_MESSAGE } from 'lib/errors/throwOnResourceLoadError';
import { isBot, isHeadlessBrowser, isNextJsChunkError, getRequestInfo } from './utils';
import { isBot, isHeadlessBrowser, isNextJsChunkError, getRequestInfo, getExceptionClass } from './utils';
const feature = config.features.rollbar;
......@@ -37,16 +37,23 @@ export const clientConfig: Configuration | undefined = feature.isEnabled ? {
return true;
}
return false;
},
hostSafeList: [ config.app.host ].filter(Boolean),
ignoredMessages: [
const exceptionClass = getExceptionClass(item);
const IGNORED_EXCEPTION_CLASSES = [
// these are React errors - "NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node."
// they could be caused by browser extensions
// one of the examples - https://github.com/facebook/react/issues/11538
// we can ignore them for now
'NotFoundError',
];
if (exceptionClass && IGNORED_EXCEPTION_CLASSES.includes(exceptionClass)) {
return true;
}
return false;
},
hostSafeList: [ config.app.host ].filter(Boolean),
ignoredMessages: [
// these are errors that we throw on when make a call to the API
RESOURCE_LOAD_ERROR_MESSAGE,
ABSENT_PARAM_ERROR_MESSAGE,
......
import { get } from 'es-toolkit/compat';
import type { Dictionary } from 'rollbar';
export function isBot(userAgent: string | undefined) {
......@@ -56,3 +57,9 @@ export function getRequestInfo(item: Dictionary): { url: string } | undefined {
}
return { url: item.request.url };
}
export function getExceptionClass(item: Dictionary) {
const exceptionClass = get(item, 'body.trace.exception.class');
return typeof exceptionClass === 'string' ? exceptionClass : undefined;
}
......@@ -10,11 +10,13 @@ type Args = {
confirmation_transaction: ArbitrumL2TxData;
};
const parentChainName = rollupFeature.isEnabled ? rollupFeature.parentChain.name : undefined;
export const VERIFICATION_STEPS_MAP: Record<ArbitrumBatchStatus, string> = {
'Processed on rollup': 'Processed on rollup',
'Sent to base': rollupFeature.isEnabled && rollupFeature.parentChainName ? `Sent to ${ rollupFeature.parentChainName }` : 'Sent to parent chain',
'Confirmed on base': rollupFeature.isEnabled && rollupFeature.parentChainName ?
`Confirmed on ${ rollupFeature.parentChainName }` :
'Sent to base': parentChainName ? `Sent to ${ parentChainName }` : 'Sent to parent chain',
'Confirmed on base': parentChainName ?
`Confirmed on ${ parentChainName }` :
'Confirmed on parent chain',
};
......
/* eslint-disable max-len */
import type { ArbitrumMessageStatus } from 'types/api/transaction';
export const MESSAGE_DESCRIPTIONS: Record<ArbitrumMessageStatus, string> = {
'Syncing with base layer': 'The incoming message was discovered on the rollup, but the corresponding message on L1 has not yet been found',
'Settlement pending': 'The transaction with the message was included in a rollup block, but there is no batch on L1 containing the block yet',
'Waiting for confirmation': 'The rollup block with the transaction containing the message was included in a batch on L1, but it is still waiting for the expiration of the fraud proof countdown',
'Ready for relay': 'The rollup state was confirmed successfully, and the message can be executed—funds can be claimed on L1',
Relayed: '',
};
......@@ -2,7 +2,7 @@ import { type Chain } from 'viem';
import config from 'configs/app';
const currentChain = {
export const currentChain: Chain = {
id: Number(config.chain.id),
name: config.chain.name ?? '',
nativeCurrency: {
......@@ -22,6 +22,36 @@ const currentChain = {
},
},
testnet: config.chain.isTestnet,
} as const satisfies Chain;
};
export default currentChain;
export const parentChain: Chain | undefined = (() => {
const rollupFeature = config.features.rollup;
const parentChain = rollupFeature.isEnabled && rollupFeature.parentChain;
if (!parentChain) {
return;
}
if (!parentChain.id || !parentChain.name || !parentChain.rpcUrls || !parentChain.baseUrl || !parentChain.currency) {
return;
}
return {
id: parentChain.id,
name: parentChain.name,
nativeCurrency: parentChain.currency,
rpcUrls: {
'default': {
http: parentChain.rpcUrls,
},
},
blockExplorers: {
'default': {
name: 'Blockscout',
url: parentChain.baseUrl,
},
},
testnet: parentChain.isTestnet,
};
})();
import { createPublicClient, http } from 'viem';
import currentChain from './currentChain';
import { currentChain } from './chains';
export const publicClient = (() => {
if (currentChain.rpcUrls.default.http.filter(Boolean).length === 0) {
......
......@@ -7,9 +7,10 @@ import useAccount from 'lib/web3/useAccount';
interface Params {
source: mixpanel.EventPayload<mixpanel.EventTypes.WALLET_CONNECT>['Source'];
onConnect?: () => void;
}
export default function useWeb3Wallet({ source }: Params) {
export default function useWeb3Wallet({ source, onConnect }: Params) {
const { open: openModal } = useAppKit();
const { open: isOpen } = useAppKitState();
const { disconnect } = useDisconnect();
......@@ -35,9 +36,10 @@ export default function useWeb3Wallet({ source }: Params) {
mixpanel.userProfile.setOnce({
'With Connected Wallet': true,
});
onConnect?.();
}
isConnectionStarted.current = false;
}, [ source ]);
}, [ source, onConnect ]);
const handleDisconnect = React.useCallback(() => {
disconnect();
......
import { WagmiAdapter } from '@reown/appkit-adapter-wagmi';
import type { AppKitNetwork } from '@reown/appkit/networks';
import type { Chain } from 'viem';
import { fallback, http } from 'viem';
import { createConfig } from 'wagmi';
import config from 'configs/app';
import currentChain from 'lib/web3/currentChain';
import { currentChain, parentChain } from 'lib/web3/chains';
const feature = config.features.blockchainInteraction;
const chains = [ currentChain, parentChain ].filter(Boolean);
const wagmi = (() => {
const chains = [ currentChain ];
if (!feature.isEnabled) {
const wagmiConfig = createConfig({
chains: [ currentChain ],
chains: chains as [Chain, ...Array<Chain>],
transports: {
[currentChain.id]: fallback(
config.chain.rpcUrls
.map((url) => http(url))
.concat(http(`${ config.api.endpoint }/api/eth-rpc`)),
),
...(parentChain ? { [parentChain.id]: http(parentChain.rpcUrls.default.http[0]) } : {}),
},
ssr: true,
batch: { multicall: { wait: 100 } },
......@@ -27,10 +32,11 @@ const wagmi = (() => {
}
const wagmiAdapter = new WagmiAdapter({
networks: chains,
networks: chains as Array<AppKitNetwork>,
multiInjectedProviderDiscovery: true,
transports: {
[currentChain.id]: fallback(config.chain.rpcUrls.map((url) => http(url))),
...(parentChain ? { [parentChain.id]: http() } : {}),
},
projectId: feature.walletConnect.projectId,
ssr: true,
......
......@@ -23,6 +23,7 @@ export function middleware(req: NextRequest) {
middlewares.colorTheme(req, res);
middlewares.addressFormat(req, res);
middlewares.scamTokens(req, res);
const end = Date.now();
......
......@@ -60,6 +60,12 @@ export const withoutName: AddressParam = {
ens_domain_name: null,
};
export const delegated: AddressParam = {
...withoutName,
is_verified: true,
proxy_type: 'eip7702',
};
export const token: Address = {
hash: hash,
implementations: null,
......
......@@ -38,3 +38,16 @@ export const batchDataAnytrust: ArbitrumL2TxnBatch = {
],
},
};
export const batchDataCelestia: ArbitrumL2TxnBatch = {
...finalized,
after_acc: '0xcd064f3409015e8e6407e492e5275a185e492c6b43ccf127f22092d8057a9ffb',
before_acc: '0x2ed7c4985eb778d76ec400a43805e7feecc8c2afcdb492dbe5caf227de6d37bc',
start_block: 1245209,
end_block: 1245490,
data_availability: {
batch_data_container: 'in_celestia',
height: 4520041,
transaction_commitment: '0x3ebe5a43f47fbf69db003e543bb27e4875929ede2fa9a25d09f0bd082d5d20f0',
},
};
import type { ArbitrumL2TxnWithdrawalsItem } from 'types/api/arbitrumL2';
export const unclaimed: ArbitrumL2TxnWithdrawalsItem = {
arb_block_number: 115114348,
caller: '0x07e1e36fe70cd58a05c00812d573dc39a127ee6d',
callvalue: '21000000000000000000',
completion_transaction_hash: null,
data: '0x',
destination: '0x07e1e36fe70cd58a05c00812d573dc39a127ee6d',
eth_block_number: 7503173,
id: 59874,
l2_timestamp: 1737020350,
status: 'confirmed',
token: null,
};
export const claimed: ArbitrumL2TxnWithdrawalsItem = {
arb_block_number: 115114348,
caller: '0x07e1e36fe70cd58a05c00812d573dc39a127ee6d',
callvalue: '21000000000000000000',
completion_transaction_hash: '0x215382498438cb6532a5e5fb07d664bbf912187866591470d47c3cfbce2dc4a8',
data: '0x',
destination: '0x07e1e36fe70cd58a05c00812d573dc39a127ee6d',
eth_block_number: 7503173,
id: 59875,
l2_timestamp: 1737020350,
status: 'relayed',
token: {
address: '0x0000000000000000000000000000000000000000',
symbol: 'USDC',
name: 'USDC Token',
decimals: 6,
amount: '10000000000',
destination: '0x07e1e36fe70cd58a05c00812d573dc39a127ee6d',
},
};
......@@ -9,8 +9,10 @@ export const base: Pool = {
quote_token_address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
quote_token_symbol: 'WETH',
quote_token_icon_url: 'https://localhost:3000/secondary_utia.jpg',
fully_diluted_valuation_usd: '75486579078',
market_cap_usd: '139312819076.195',
base_token_fully_diluted_valuation_usd: '75486579078',
base_token_market_cap_usd: '139312819076.195',
quote_token_fully_diluted_valuation_usd: '486579078',
quote_token_market_cap_usd: '312819076.195',
liquidity: '2099941.2238',
dex: { id: 'sushiswap', name: 'SushiSwap' },
fee: '0.03',
......
......@@ -7,4 +7,7 @@ export const base: RewardsConfigResponse = {
daily_claim: '10',
referral_share: '0.1',
},
auth: {
shared_siwe_login: true,
},
};
......@@ -8,6 +8,7 @@ import type {
SearchResultUserOp,
SearchResultBlob,
SearchResultDomain,
SearchResultMetadataTag,
} from 'types/api/search';
export const token1: SearchResultToken = {
......@@ -147,6 +148,42 @@ export const domain1: SearchResultDomain = {
url: '/address/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
};
export const metatag1: SearchResultMetadataTag = {
...address1,
type: 'metadata_tag',
metadata: {
name: 'utko',
slug: 'utko',
meta: {},
tagType: 'name',
ordinal: 1,
},
};
export const metatag2: SearchResultMetadataTag = {
...address2,
type: 'metadata_tag',
metadata: {
name: 'utko',
slug: 'utko',
meta: {},
tagType: 'name',
ordinal: 1,
},
};
export const metatag3: SearchResultMetadataTag = {
...contract2,
type: 'metadata_tag',
metadata: {
name: 'super utko',
slug: 'super-utko',
meta: {},
tagType: 'protocol',
ordinal: 1,
},
};
export const baseResponse: SearchResult = {
items: [
token1,
......@@ -157,6 +194,8 @@ export const baseResponse: SearchResult = {
tx1,
blob1,
domain1,
metatag1,
],
next_page_params: null,
};
import type * as stats from '@blockscout/stats-types';
import { averageGasPrice } from './line';
export const base: stats.MainPageStats = {
average_block_time: {
id: 'averageBlockTime',
value: '14.909090909090908',
title: 'Average block time',
units: 's',
description: 'Average time taken in seconds for a block to be included in the blockchain',
},
total_addresses: {
id: 'totalAddresses',
value: '113606435',
title: 'Total addresses',
description: 'Number of addresses that participated in the blockchain',
},
total_blocks: {
id: 'totalBlocks',
value: '7660515',
title: 'Total blocks',
description: 'Number of blocks over all time',
},
total_transactions: {
id: 'totalTxns',
value: '411264599',
title: 'Total txns',
description: 'All transactions including pending, dropped, replaced, failed transactions',
},
yesterday_transactions: {
id: 'yesterdayTxns',
value: '213019',
title: 'Yesterday txns',
description: 'Number of transactions yesterday (0:00 - 23:59 UTC)',
},
total_operational_transactions: {
id: 'totalOperationalTxns',
value: '403598877',
title: 'Total operational txns',
description: '\'Total txns\' without block creation transactions',
},
yesterday_operational_transactions: {
id: 'yesterdayOperationalTxns',
value: '210852',
title: 'Yesterday operational txns',
description: 'Number of transactions yesterday (0:00 - 23:59 UTC) without block creation transactions',
},
daily_new_transactions: {
chart: averageGasPrice.chart,
info: {
id: 'newTxnsWindow',
title: 'Daily transactions',
description: 'The chart displays daily transactions for the past 30 days',
resolutions: [
'DAY',
],
},
},
daily_new_operational_transactions: {
chart: averageGasPrice.chart,
info: {
id: 'newOperationalTxnsWindow',
title: 'Daily operational transactions',
description: 'The chart displays daily transactions for the past 30 days (without block creation transactions)',
resolutions: [
'DAY',
],
},
},
};
......@@ -60,7 +60,7 @@ export const base: Transaction = {
},
token_transfers: [],
token_transfers_overflow: false,
tx_burnt_fee: '461030000000000',
transaction_burnt_fee: '461030000000000',
transaction_tag: null,
transaction_types: [
'contract_call',
......@@ -196,7 +196,7 @@ export const pending: Transaction = {
revert_reason: null,
status: null,
timestamp: null,
tx_burnt_fee: null,
transaction_burnt_fee: null,
transaction_tag: null,
type: null,
value: '0',
......
......@@ -66,6 +66,14 @@ export const userOpData: UserOp = {
},
call_data: '0xb61d27f600000000000000000000000059f6aa952df7f048fd076e33e0ea8bb552d5ffd8000000000000000000000000000000000000000000000000003f3d017500800000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
execute_call_data: '0x3cf80e6c',
execute_target: {
ens_domain_name: null,
hash: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
implementations: null,
is_contract: true,
is_verified: true,
name: 'FiatTokenProxy',
},
decoded_call_data: {
method_call: 'execute(address dest, uint256 value, bytes func)',
method_id: 'b61d27f6',
......
import type { ValidatorZilliqa, ValidatorsZilliqaItem, ValidatorsZilliqaResponse } from 'types/api/validators';
export const validator1: ValidatorsZilliqaItem = {
index: 420,
bls_public_key: '0x95125dca41be848801f9bd75254f1faf1ae3194b1da53e9a5684ed7f67b729542482bc521924603b9703c33bf831a100',
balance: '1000000000000000000',
};
export const validatorsResponse: ValidatorsZilliqaResponse = {
items: [ validator1 ],
next_page_params: null,
};
export const validatorDetails: ValidatorZilliqa = {
added_at_block_number: 7527600,
balance: '20000000000000000000000000',
bls_public_key: '0x95125dca41be848801f9bd75254f1faf1ae3194b1da53e9a5684ed7f67b729542482bc521924603b9703c33bf831a100',
control_address: {
ens_domain_name: null,
hash: '0xB4492C468Fe97CB73Ea70a9A712cdd5B5aB621c3',
implementations: [],
is_contract: false,
is_verified: null,
metadata: null,
name: null,
private_tags: [],
proxy_type: null,
public_tags: [],
watchlist_names: [],
},
index: 1,
peer_id: '0x002408011220a8ce8c9a146f3dc411cd72ba845b76722824c55824ac74b3362f070a332d85f2',
reward_address: {
ens_domain_name: null,
hash: '0x0000000000000000000000000000000000000000',
implementations: [],
is_contract: false,
is_verified: null,
metadata: null,
name: null,
private_tags: [],
proxy_type: null,
public_tags: [],
watchlist_names: [],
},
signing_address: {
ens_domain_name: null,
hash: '0x0000000000000000000000000000000000000026',
implementations: [],
is_contract: false,
is_verified: null,
metadata: null,
name: null,
private_tags: [],
proxy_type: null,
public_tags: [],
watchlist_names: [],
},
stake_updated_at_block_number: 7527642,
};
......@@ -52,6 +52,7 @@ export function app(): CspDev.DirectiveDescriptor {
// chain RPC server
...config.chain.rpcUrls,
...(getFeaturePayload(config.features.rollup)?.parentChain?.rpcUrls ?? []),
'https://infragrid.v.network', // RPC providers
// github (spec for api-docs page)
......
......@@ -8,6 +8,9 @@ export function googleReCaptcha(): CspDev.DirectiveDescriptor {
}
return {
'connect-src': [
'https://www.google.com/recaptcha/api2/clr',
],
'script-src': [
'https://www.google.com/recaptcha/api.js',
'https://www.gstatic.com',
......
......@@ -9,6 +9,7 @@ import config from 'configs/app';
const rollupFeature = config.features.rollup;
const adBannerFeature = config.features.adsBanner;
import isNeedProxy from 'lib/api/isNeedProxy';
import * as cookies from 'lib/cookies';
import type * as metadata from 'lib/metadata';
export interface Props<Pathname extends Route['pathname'] = never> {
......@@ -19,9 +20,10 @@ export interface Props<Pathname extends Route['pathname'] = never> {
// if apiData is undefined, Next.js will complain that it is not serializable
// so we force it to be always present in the props but it can be null
apiData: metadata.ApiData<Pathname> | null;
uuid: string;
}
export const base = async <Pathname extends Route['pathname'] = never>({ req, query }: GetServerSidePropsContext):
export const base = async <Pathname extends Route['pathname'] = never>({ req, res, query }: GetServerSidePropsContext):
Promise<GetServerSidePropsResult<Props<Pathname>>> => {
const adBannerProvider = (() => {
if (adBannerFeature.isEnabled) {
......@@ -36,6 +38,36 @@ Promise<GetServerSidePropsResult<Props<Pathname>>> => {
return null;
})();
let uuid = cookies.getFromCookieString(req.headers.cookie || '', cookies.NAMES.UUID);
if (!uuid) {
uuid = crypto.randomUUID();
res.setHeader('Set-Cookie', `${ cookies.NAMES.UUID }=${ uuid }`);
}
const isTrackingDisabled = process.env.DISABLE_TRACKING === 'true';
if (!isTrackingDisabled) {
// log pageview
const hostname = req.headers.host;
const timestamp = new Date().toISOString();
const chainId = process.env.NEXT_PUBLIC_NETWORK_ID;
const chainName = process.env.NEXT_PUBLIC_NETWORK_NAME;
const publicRPC = process.env.NEXT_PUBLIC_NETWORK_RPC_URL;
fetch('https://monitor.blockscout.com/count', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
hostname,
timestamp,
chainId,
chainName,
publicRPC,
uuid,
}),
});
}
return {
props: {
query,
......@@ -43,6 +75,7 @@ Promise<GetServerSidePropsResult<Props<Pathname>>> => {
referrer: req.headers.referer || '',
adBannerProvider: adBannerProvider,
apiData: null,
uuid,
},
};
};
......@@ -92,6 +125,16 @@ export const withdrawals: GetServerSideProps<Props> = async(context) => {
return base(context);
};
export const txnWithdrawals: GetServerSideProps<Props> = async(context) => {
if (!(rollupFeature.isEnabled && rollupFeature.type === 'arbitrum')) {
return {
notFound: true,
};
}
return base(context);
};
export const rollup: GetServerSideProps<Props> = async(context) => {
if (!config.features.rollup.isEnabled) {
return {
......@@ -123,6 +166,16 @@ export const batch: GetServerSideProps<Props> = async(context) => {
return base(context);
};
export const batchCelestia: GetServerSideProps<Props> = async(context) => {
if (!(rollupFeature.isEnabled && (rollupFeature.type === 'arbitrum' || rollupFeature.type === 'optimistic'))) {
return {
notFound: true,
};
}
return base(context);
};
export const marketplace = async <Pathname extends Route['pathname'] = never>(context: GetServerSidePropsContext):
Promise<GetServerSidePropsResult<Props<Pathname>>> => {
if (!config.features.marketplace.isEnabled) {
......@@ -234,6 +287,17 @@ export const validators: GetServerSideProps<Props> = async(context) => {
return base(context);
};
export const validatorDetails: GetServerSideProps<Props> = async(context) => {
const feature = config.features.validators;
if (!feature.isEnabled || feature.chainType !== 'zilliqa') {
return {
notFound: true,
};
}
return base(context);
};
export const gasTracker: GetServerSideProps<Props> = async(context) => {
if (!config.features.gasTracker.isEnabled) {
return {
......
export { account } from './account';
export { default as colorTheme } from './colorTheme';
export { default as addressFormat } from './addressFormat';
export { default as scamTokens } from './scamTokens';
import type { NextRequest, NextResponse } from 'next/server';
import config from 'configs/app';
import * as cookiesLib from 'lib/cookies';
export default function scamTokensMiddleware(req: NextRequest, res: NextResponse) {
if (config.UI.views.token.hideScamTokensEnabled) {
const showScamTokensCookie = req.cookies.get(cookiesLib.NAMES.SHOW_SCAM_TOKENS);
if (!showScamTokensCookie) {
res.cookies.set(cookiesLib.NAMES.SHOW_SCAM_TOKENS, 'false', { path: '/' });
}
}
}
......@@ -9,7 +9,7 @@ declare module "nextjs-routes" {
| StaticRoute<"/404">
| StaticRoute<"/account/api-key">
| StaticRoute<"/account/custom-abi">
| StaticRoute<"/account/rewards">
| StaticRoute<"/account/merits">
| StaticRoute<"/account/tag-address">
| StaticRoute<"/account/verified-addresses">
| StaticRoute<"/account/watchlist">
......@@ -32,6 +32,7 @@ declare module "nextjs-routes" {
| StaticRoute<"/apps">
| StaticRoute<"/auth/profile">
| DynamicRoute<"/batches/[number]", { "number": string }>
| DynamicRoute<"/batches/celestia/[height]/[commitment]", { "height": string; "commitment": string }>
| StaticRoute<"/batches">
| DynamicRoute<"/blobs/[hash]", { "hash": string }>
| DynamicRoute<"/block/[height_or_hash]", { "height_or_hash": string }>
......@@ -46,6 +47,7 @@ declare module "nextjs-routes" {
| StaticRoute<"/gas-tracker">
| StaticRoute<"/graphiql">
| StaticRoute<"/">
| StaticRoute<"/internal-txs">
| StaticRoute<"/login">
| StaticRoute<"/mud-worlds">
| DynamicRoute<"/name-domains/[name]", { "name": string }>
......@@ -65,8 +67,10 @@ declare module "nextjs-routes" {
| StaticRoute<"/token-transfers">
| StaticRoute<"/tokens">
| DynamicRoute<"/tx/[hash]", { "hash": string }>
| StaticRoute<"/txn-withdrawals">
| StaticRoute<"/txs">
| DynamicRoute<"/txs/kettle/[hash]", { "hash": string }>
| DynamicRoute<"/validators/[id]", { "id": string }>
| StaticRoute<"/validators">
| StaticRoute<"/verified-contracts">
| StaticRoute<"/visualize/sol2uml">
......
......@@ -52,6 +52,10 @@ const oldUrls = [
source: '/account/public-tags-request',
destination: '/public-tags/submit',
},
{
source: '/account/rewards',
destination: '/account/merits',
},
// TRANSACTIONS
{
......
......@@ -7,20 +7,20 @@ import nodeFetch from 'node-fetch';
import { httpLogger } from 'nextjs/utils/logger';
import * as cookies from 'lib/cookies';
export default function fetchFactory(
_req: NextApiRequest | (IncomingMessage & { cookies: NextApiRequestCookies }),
) {
// first arg can be only a string
// FIXME migrate to RequestInfo later if needed
return function fetch(url: string, init?: RequestInit): Promise<Response> {
const apiToken = _req.cookies[cookies.NAMES.API_TOKEN];
const cookie = Object.entries(_req.cookies)
.map(([ key, value ]) => `${ key }=${ value }`)
.join('; ');
const headers = {
accept: _req.headers['accept'] || 'application/json',
'content-type': _req.headers['content-type'] || 'application/json',
cookie: apiToken ? `${ cookies.NAMES.API_TOKEN }=${ apiToken }` : '',
cookie,
...pick(_req.headers, [
'x-csrf-token',
'Authorization', // the old value, just in case
......
......@@ -14,7 +14,7 @@ import { MarketplaceContextProvider } from 'lib/contexts/marketplace';
import { RewardsContextProvider } from 'lib/contexts/rewards';
import { ScrollDirectionProvider } from 'lib/contexts/scrollDirection';
import { SettingsContextProvider } from 'lib/contexts/settings';
import { growthBook } from 'lib/growthbook/init';
import { initGrowthBook } from 'lib/growthbook/init';
import useLoadFeatures from 'lib/growthbook/useLoadFeatures';
import useNotifyOnNavigation from 'lib/hooks/useNotifyOnNavigation';
import { clientConfig as rollbarConfig, Provider as RollbarProvider } from 'lib/rollbar';
......@@ -57,9 +57,10 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
setMounted(true);
}, []);
useLoadFeatures();
useLoadFeatures(pageProps.uuid);
useNotifyOnNavigation();
const growthBook = initGrowthBook(pageProps.uuid);
const queryClient = useQueryClientConfig();
if (!mounted) {
......
......@@ -4,12 +4,12 @@ import React from 'react';
import PageNextJs from 'nextjs/PageNextJs';
// const RewardsDashboard = dynamic(() => import('ui/pages/RewardsDashboard'), { ssr: false });
const RewardsDashboard = dynamic(() => import('ui/pages/RewardsDashboard'), { ssr: false });
const Page: NextPage = () => {
return (
<PageNextJs pathname="/account/rewards">
{ /* <RewardsDashboard/> */ }
<PageNextJs pathname="/account/merits">
<RewardsDashboard/>
</PageNextJs>
);
};
......
import type { NextPage } from 'next';
import dynamic from 'next/dynamic';
import React from 'react';
import type { Props } from 'nextjs/getServerSideProps';
import PageNextJs from 'nextjs/PageNextJs';
import config from 'configs/app';
const rollupFeature = config.features.rollup;
const Batch = dynamic(() => {
if (!rollupFeature.isEnabled) {
throw new Error('Rollup feature is not enabled.');
}
switch (rollupFeature.type) {
case 'arbitrum':
return import('ui/pages/ArbitrumL2TxnBatch');
case 'optimistic':
return import('ui/pages/OptimisticL2TxnBatch');
}
throw new Error('Celestia txn batches feature is not enabled.');
}, { ssr: false });
const Page: NextPage<Props> = (props: Props) => {
return (
<PageNextJs pathname="/batches/celestia/[height]/[commitment]" query={ props.query }>
<Batch/>
</PageNextJs>
);
};
export default Page;
export { batchCelestia as getServerSideProps } from 'nextjs/getServerSideProps';
import type { NextPage } from 'next';
import dynamic from 'next/dynamic';
import React from 'react';
import PageNextJs from 'nextjs/PageNextJs';
const InternalTxs = dynamic(() => import('ui/pages/InternalTxs'), { ssr: false });
const Page: NextPage = () => {
return (
<PageNextJs pathname="/internal-txs">
<InternalTxs/>
</PageNextJs>
);
};
export default Page;
export { base as getServerSideProps } from 'nextjs/getServerSideProps';
import type { NextPage } from 'next';
import dynamic from 'next/dynamic';
import React from 'react';
import PageNextJs from 'nextjs/PageNextJs';
import config from 'configs/app';
const rollupFeature = config.features.rollup;
const Withdrawals = dynamic(() => {
if (rollupFeature.isEnabled && rollupFeature.type === 'arbitrum') {
return import('ui/pages/ArbitrumL2TxnWithdrawals');
}
throw new Error('Txn withdrawals feature is not enabled.');
}, { ssr: false });
const Page: NextPage = () => {
return (
<PageNextJs pathname="/txn-withdrawals">
<Withdrawals/>
</PageNextJs>
);
};
export default Page;
export { txnWithdrawals as getServerSideProps } from 'nextjs/getServerSideProps';
import type { NextPage } from 'next';
import dynamic from 'next/dynamic';
import React from 'react';
import type { Props } from 'nextjs/getServerSideProps';
import PageNextJs from 'nextjs/PageNextJs';
import config from 'configs/app';
const validatorsFeature = config.features.validators;
const ValidatorDetails = dynamic(() => {
if (validatorsFeature.isEnabled && validatorsFeature.chainType === 'zilliqa') {
return import('ui/pages/ValidatorZilliqa');
}
throw new Error('Validators feature is not enabled.');
}, { ssr: false });
const Page: NextPage<Props> = (props) => {
return (
<PageNextJs pathname="/validators/[id]" query={ props.query }>
<ValidatorDetails/>
</PageNextJs>
);
};
export default Page;
export { validatorDetails as getServerSideProps } from 'nextjs/getServerSideProps';
......@@ -8,22 +8,26 @@ import config from 'configs/app';
const validatorsFeature = config.features.validators;
// const Validators = dynamic(() => {
// if (validatorsFeature.isEnabled && validatorsFeature.chainType === 'stability') {
// return import('ui/pages/ValidatorsStability');
// }
const Validators = dynamic(() => {
if (validatorsFeature.isEnabled && validatorsFeature.chainType === 'stability') {
return import('ui/pages/ValidatorsStability');
}
// if (validatorsFeature.isEnabled && validatorsFeature.chainType === 'blackfort') {
// return import('ui/pages/ValidatorsBlackfort');
// }
if (validatorsFeature.isEnabled && validatorsFeature.chainType === 'blackfort') {
return import('ui/pages/ValidatorsBlackfort');
}
// throw new Error('Validators feature is not enabled.');
// }, { ssr: false });
if (validatorsFeature.isEnabled && validatorsFeature.chainType === 'zilliqa') {
return import('ui/pages/ValidatorsZilliqa');
}
throw new Error('Validators feature is not enabled.');
}, { ssr: false });
const Page: NextPage = () => {
return (
<PageNextJs pathname="/validators">
{ /* <Validators/> */ }
<Validators/>
</PageNextJs>
);
};
......
......@@ -13,7 +13,7 @@ import { MarketplaceContext } from 'lib/contexts/marketplace';
import { RewardsContextProvider } from 'lib/contexts/rewards';
import { SettingsContextProvider } from 'lib/contexts/settings';
import { SocketProvider } from 'lib/socket/context';
import currentChain from 'lib/web3/currentChain';
import { currentChain } from 'lib/web3/chains';
import { Provider as ChakraProvider } from 'toolkit/chakra/provider';
import { port as socketPort } from './utils/socket';
......@@ -38,6 +38,7 @@ const defaultAppContext = {
query: {},
adBannerProvider: 'slise' as const,
apiData: null,
uuid: '123',
},
};
......
......@@ -26,6 +26,7 @@ export const ENVS_MAP: Record<string, Array<[string, string]>> = {
[ 'NEXT_PUBLIC_ROLLUP_TYPE', 'arbitrum' ],
[ 'NEXT_PUBLIC_ROLLUP_L1_BASE_URL', 'https://localhost:3101' ],
[ 'NEXT_PUBLIC_ROLLUP_PARENT_CHAIN_NAME', 'DuckChain' ],
[ 'NEXT_PUBLIC_ROLLUP_DA_CELESTIA_NAMESPACE', '0x1234' ],
],
shibariumRollup: [
[ 'NEXT_PUBLIC_ROLLUP_TYPE', 'shibarium' ],
......@@ -93,4 +94,7 @@ export const ENVS_MAP: Record<string, Array<[string, string]>> = {
[ 'NEXT_PUBLIC_ADDRESS_FORMAT', '["bech32","base16"]' ],
[ 'NEXT_PUBLIC_VIEWS_ADDRESS_BECH_32_PREFIX', 'tom' ],
],
externalTxs: [
[ 'NEXT_PUBLIC_TX_EXTERNAL_TRANSACTIONS_CONFIG', '{"chain_name": "Solana", "chain_logo_url": "http://example.url", "explorer_url_template": "https://scan.io/tx/{hash}"}' ],
],
};
......@@ -84,6 +84,7 @@
| "info"
| "integration/full"
| "integration/partial"
| "internal_txns"
| "key"
| "lightning_navbar"
| "lightning"
......
......@@ -23,7 +23,7 @@
<path d="M223.424 78.967a11.355 11.355 0 0 0 .001 0h-.001Z" fill="url(#h)"/>
<path d="M195.759 63.318c.861 1.087.532.618 1.771 1.334l20.691 11.96a11.175 11.175 0 0 0 11.19 0l1.637-.945c-.715-4.798-2.488-7.243-4.079-9.436-1.74-2.398-3.261-4.495-2.941-9.04.392-5.57-.354-8.481-.999-11.003-.239-.934-.465-1.815-.614-2.758-2.56-5.59-3.746-10.222-4.126-14.142L235.64 25.8l-17.402-1.72c.186-2.314.635-4.337 1.15-6.154l.022-.08-5.809 2.608-2.722 3.012-2.008 5.822-.903 6.423c-1.486 1.522-2.857 3.095-4.472 5.813l-1.935 2.687c-.129.218-1.214 2.407-2.301 4.603h-.001l-.001.004-.001.002c-1.095 2.21-2.192 4.426-2.318 4.636-.367.612-1.181 3.51-1.921 6.141v.001l-.001.004-.001.003c-.231.822.943 3.02.743 3.713Z" fill="url(#i)"/>
<path d="M214.861 30.622c1.5-3.43 5.354-3.49 8.579-3.646 3.225.156 7.081.06 8.58 3.646 1.305 3.123-.869 9.783-2.589 11.45-2.006 1.944-4.492 1.56-5.991 1.56-1.5 0-3.984.384-5.99-1.56-1.721-1.667-3.942-8.354-2.589-11.45Z" fill="#D47B3A"/>
<path d="M230.164 36.42s2.765 1.587 4.98.358m-5.152 1.342s2.218 1.831 4.084 1.55m-16.766-3.25s-2.765 1.587-4.98.358m5.154 1.342s-1.966 1.831-3.832 1.55m14.068-1.755c-.883 1.534-4.037 1.943-4.037-1.125v-1.739M219.645 37.915c.883 1.534 4.038 1.943 4.038-1.125v-1.739" stroke="#562E11" stroke-width=".645" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M230.164 36.42s2.765 1.587 4.98.358m-5.152 1.342s2.218 1.831 4.084 1.55m-16.766-3.25s-2.765 1.587-4.98.358m5.154 1.342s-1.966 1.831-3.832 1.55m14.068-1.755c-.883 1.534-4.037 1.943-4.037-1.125v-1.739m-4.038 2.864c.883 1.534 4.038 1.943 4.038-1.125v-1.739" stroke="#562E11" stroke-width=".645" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M217.471 32.91c.291-.261 3.192 1.19 3.086 1.728-.107.537-1.164 1.045-2.147.372-.983-.674-1.23-1.839-.939-2.1Zm12.15-.035c-.297-.267-3.261 1.217-3.152 1.765.109.549 1.189 1.068 2.193.38 1.004-.688 1.256-1.878.959-2.145Z" fill="#562E11"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="m250.988 53.697-.11-.063.452 2.944c-3.234-9.948-9.11-17.306-11.206-19.93a31.059 31.059 0 0 1-.619-.789l-.005-.007-.059-.08-.073-.1a.323.323 0 0 1-.061-.163c-.649-7.64-2.675-12.064-5.37-14.584-2.691-2.516-6.106-3.188-9.666-3.188-2.571 0-6.551.291-9.885 2.635-3.311 2.328-6.053 6.733-6.053 15.11a.321.321 0 0 1-.088.222c-1.73 1.822-9.289 10.593-12.68 22.383l.255-2.913c3.651-10.361 10.11-17.943 11.868-19.82.028-8.449 2.82-13.044 6.327-15.51 3.504-2.463 7.654-2.752 10.256-2.752 3.632 0 7.243.684 10.107 3.362 2.848 2.664 4.9 7.248 5.564 14.91l.082.111v.002c.125.169.331.426.603.766 1.89 2.364 6.985 8.737 10.361 17.454Z" fill="#562E11"/>
<ellipse cx="234.613" cy="28.595" rx="2.042" ry="2.58" fill="#333" stroke="#000" stroke-width=".645"/>
......@@ -173,7 +173,7 @@
<path d="M101.522 61.915a11.168 11.168 0 0 0 3.168 2.737l20.692 11.96a11.175 11.175 0 0 0 11.19 0l1.637-.945c-.715-4.798-2.489-7.243-4.08-9.436-1.739-2.398-3.261-4.495-2.941-9.04.392-5.57-.353-8.481-.999-11.003-.239-.934-.464-1.815-.613-2.758-2.56-5.59-3.746-10.222-4.126-14.142L142.8 25.8l-17.402-1.72c.187-2.314.636-4.337 1.15-6.154l.023-.08-5.809 2.608-2.723 3.012-2.007 5.822-.904 6.423c-1.485 1.522-2.856 3.095-4.471 5.813l-1.935 2.687c-.129.218-1.214 2.407-2.301 4.603h-.001l-.002.004v.002c-1.095 2.21-2.193 4.426-2.319 4.636-.367.612-1.181 3.51-1.92 6.141v.001l-.001.004-.001.003c-.231.822-.455 1.618-.655 2.31Z" fill="url(#H)"/>
<path d="M101.522 61.916a11.169 11.169 0 0 0 3.168 2.736l8.644 4.996c3.337-2.408 5.6-7.084 5.6-12.458 0-7.241-4.109-13.216-9.417-14.082l-.794 1.103c-.13.217-1.214 2.407-2.302 4.602v.001c-1.096 2.213-2.195 4.432-2.321 4.642-.367.611-1.181 3.51-1.92 6.14v.001l-.001.003c-.232.825-.456 1.622-.657 2.316Zm.297 1.618.001.002c.721.713 1.538 1.34 2.438 1.86l8.267 4.78.001-.001-8.266-4.778a12.03 12.03 0 0 1-2.441-1.863Z" fill="#AE5F25"/>
<path d="M122.08 30.622c1.5-3.43 5.354-3.49 8.579-3.646 3.225.156 7.081.06 8.58 3.646 1.305 3.123-.868 9.783-2.589 11.45-2.006 1.944-4.491 1.56-5.991 1.56-1.499 0-3.984.384-5.99-1.56-1.72-1.667-3.942-8.354-2.589-11.45Z" fill="#D47B3A"/>
<path d="M137.383 36.42s2.765 1.587 4.981.358m-5.153 1.342s2.218 1.831 4.084 1.55m-16.766-3.25s-2.765 1.587-4.98.358m5.154 1.342s-1.965 1.831-3.832 1.55m14.069-1.755c-.884 1.534-4.038 1.943-4.038-1.125v-1.739M126.864 37.915c.884 1.534 4.038 1.943 4.038-1.125v-1.739" stroke="#562E11" stroke-width=".645" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M137.383 36.42s2.765 1.587 4.981.358m-5.153 1.342s2.218 1.831 4.084 1.55m-16.766-3.25s-2.765 1.587-4.98.358m5.154 1.342s-1.965 1.831-3.832 1.55m14.069-1.755c-.884 1.534-4.038 1.943-4.038-1.125v-1.739m-4.038 2.864c.884 1.534 4.038 1.943 4.038-1.125v-1.739" stroke="#562E11" stroke-width=".645" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M124.69 32.91c.291-.261 3.192 1.19 3.086 1.728-.107.537-1.164 1.045-2.147.372-.983-.674-1.23-1.839-.939-2.1Zm12.15-.035c-.297-.267-3.26 1.217-3.152 1.765.109.549 1.189 1.068 2.193.38 1.004-.688 1.257-1.878.959-2.145Z" fill="#562E11"/>
<path d="M160.039 62.385c.185-.21.363-.425.531-.648-2.552-12.772-10.292-22.452-12.725-25.494a31.1 31.1 0 0 1-.602-.766v-.001h-.001l-.082-.112c-.664-7.662-2.715-12.246-5.564-14.91-2.863-2.678-6.474-3.361-10.106-3.361-2.602 0-6.752.288-10.256 2.751-3.507 2.466-6.299 7.061-6.327 15.51-2.103 2.245-10.937 12.66-13.638 26.229.167.228.344.45.529.666 2.57-13.853 11.738-24.515 13.665-26.545a.322.322 0 0 0 .089-.222c0-8.377 2.742-12.782 6.053-15.11 3.334-2.344 7.313-2.634 9.885-2.634 3.56 0 6.975.67 9.666 3.187 2.694 2.52 4.72 6.944 5.37 14.584.005.06.026.116.061.164l.073.1.059.08.004.005.001.001c.13.176.341.441.619.79 2.485 3.111 10.289 12.883 12.696 25.736Zm-58.979.325c.171.203.348.401.533.593l-.001.002a11.91 11.91 0 0 1-.532-.593v-.003Z" fill="#562E11"/>
<ellipse cx="141.832" cy="28.595" rx="2.042" ry="2.58" fill="#333" stroke="#000" stroke-width=".645"/>
......
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 99 16">
<g clip-path="url(#a)">
<mask id="b" width="99" height="16" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:luminance">
<path fill="#fff" d="M98.647.001H0V16h98.647z"/>
</mask>
<g mask="url(#b)">
<path fill="#fff" d="M29.348.04h3.25l5.409 7.47c.873 1.19 1.867 2.887 1.867 2.887V.039h3.396v15.718h-3.275l-5.24-7.18c-1.043-1.407-2.014-3.08-2.014-3.08v10.26h-3.396V.04zm16.834 9.653c0-3.518 2.862-6.307 6.476-6.307 3.615 0 6.5 2.789 6.5 6.307 0 3.517-2.861 6.306-6.5 6.306s-6.476-2.789-6.476-6.306m9.654 0c0-1.892-1.358-3.3-3.178-3.3s-3.177 1.407-3.177 3.3 1.359 3.3 3.177 3.3 3.178-1.383 3.178-3.3m4.487-6.063h3.25l2.256 6.234c.46 1.261.849 2.522.898 2.692.024-.17.436-1.48.874-2.668l2.256-6.258h3.225l-4.779 12.15h-3.2zm13.924 6.063c0-3.494 2.716-6.307 6.282-6.307 3.3 0 5.895 2.813 5.895 6.258 0 .582-.073 1.165-.073 1.165h-8.805c.387 1.673 1.77 2.474 3.372 2.474 1.165 0 2.377-.34 3.275-.947l1.214 2.33a8.17 8.17 0 0 1-4.513 1.333c-3.542 0-6.646-2.304-6.646-6.306zm9.12-1.044c-.243-1.528-1.456-2.547-2.935-2.547s-2.62 1.02-2.935 2.547zm5.045 5.701 1.165-2.498c.824.631 1.965 1.285 3.784 1.285.75 0 1.988-.29 1.988-1.019s-1.044-.898-2.087-1.188c-2.402-.632-4.365-1.431-4.365-3.76 0-2.474 2.425-3.784 4.706-3.784 1.988 0 3.249.484 4.512 1.213l-1.092 2.426c-1.188-.582-2.304-.85-3.372-.85-.751 0-1.455.34-1.455.85 0 .68 1.02.897 2.135 1.14 2.062.484 4.317 1.382 4.317 3.857 0 2.983-3.031 3.977-5.287 3.977-2.596 0-4.269-1.019-4.949-1.649"/>
<path fill="url(#c)" d="M21.666 11.897v3.86h-2.368a15.95 15.95 0 0 1-7.292-1.764l-4.714-2.422A16 16 0 0 0 0 9.807V5.95h.933c2.536 0 5.035.604 7.29 1.762l4.717 2.422a15.96 15.96 0 0 0 7.288 1.763h1.436z"/>
<path fill="url(#d)" d="M0 11.907v3.85h6.042v-2.456A13.9 13.9 0 0 0 0 11.907"/>
<path fill="url(#e)" d="M21.666 5.95v3.858h-.937c-2.536 0-5.034-.604-7.29-1.764L8.723 5.62a15.96 15.96 0 0 0-7.29-1.763H0V0h2.366C4.902 0 7.4.604 9.655 1.762l4.719 2.424a15.95 15.95 0 0 0 7.289 1.762h.003"/>
<path fill="url(#f)" d="M21.666 3.848V0h-6.042v2.455a13.9 13.9 0 0 0 6.04 1.392z"/>
</g>
</g>
<defs>
<linearGradient id="c" x1="27.02" x2="6.463" y1=".199" y2="13.729" gradientUnits="userSpaceOnUse">
<stop stop-color="#3CF"/>
<stop offset="1" stop-color="#93C"/>
</linearGradient>
<linearGradient id="d" x1="26.026" x2="5.472" y1="-1.311" y2="12.22" gradientUnits="userSpaceOnUse">
<stop stop-color="#3CF"/>
<stop offset="1" stop-color="#93C"/>
</linearGradient>
<linearGradient id="e" x1="24.287" x2="3.732" y1="-3.951" y2="9.578" gradientUnits="userSpaceOnUse">
<stop stop-color="#3CF"/>
<stop offset="1" stop-color="#93C"/>
</linearGradient>
<linearGradient id="f" x1="25.28" x2="4.725" y1="-2.443" y2="11.086" gradientUnits="userSpaceOnUse">
<stop stop-color="#3CF"/>
<stop offset="1" stop-color="#93C"/>
</linearGradient>
<clipPath id="a">
<path fill="#fff" d="M0 0h98.667v16H0z"/>
</clipPath>
</defs>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 99 16">
<g clip-path="url(#a)">
<mask id="b" width="99" height="16" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:luminance">
<path fill="#fff" d="M98.647.001H0V16h98.647z"/>
</mask>
<g mask="url(#b)">
<path fill="#404141" d="M29.348.04h3.25l5.409 7.47c.873 1.19 1.867 2.887 1.867 2.887V.039h3.396v15.718h-3.275l-5.24-7.18c-1.043-1.407-2.014-3.08-2.014-3.08v10.26h-3.396V.04zm16.834 9.653c0-3.518 2.862-6.307 6.476-6.307 3.615 0 6.5 2.789 6.5 6.307 0 3.517-2.861 6.306-6.5 6.306s-6.476-2.789-6.476-6.306m9.654 0c0-1.892-1.358-3.3-3.178-3.3s-3.177 1.407-3.177 3.3 1.359 3.3 3.177 3.3 3.178-1.383 3.178-3.3m4.487-6.063h3.25l2.256 6.234c.46 1.261.849 2.522.898 2.692.024-.17.436-1.48.874-2.668l2.256-6.258h3.225l-4.779 12.15h-3.2zm13.924 6.063c0-3.494 2.716-6.307 6.282-6.307 3.3 0 5.895 2.813 5.895 6.258 0 .582-.073 1.165-.073 1.165h-8.805c.387 1.673 1.77 2.474 3.372 2.474 1.165 0 2.377-.34 3.275-.947l1.214 2.33a8.17 8.17 0 0 1-4.513 1.333c-3.542 0-6.646-2.304-6.646-6.306zm9.12-1.044c-.243-1.528-1.456-2.547-2.935-2.547s-2.62 1.02-2.935 2.547zm5.045 5.701 1.165-2.498c.824.631 1.965 1.285 3.784 1.285.75 0 1.988-.29 1.988-1.019s-1.044-.898-2.087-1.188c-2.402-.632-4.365-1.431-4.365-3.76 0-2.474 2.425-3.784 4.706-3.784 1.988 0 3.249.484 4.512 1.213l-1.092 2.426c-1.188-.582-2.304-.85-3.372-.85-.751 0-1.455.34-1.455.85 0 .68 1.02.897 2.135 1.14 2.062.484 4.317 1.382 4.317 3.857 0 2.983-3.031 3.977-5.287 3.977-2.596 0-4.269-1.019-4.949-1.649"/>
<path fill="url(#c)" d="M21.666 11.897v3.86h-2.368a15.95 15.95 0 0 1-7.292-1.764l-4.714-2.422A16 16 0 0 0 0 9.807V5.95h.933c2.536 0 5.035.604 7.29 1.762l4.717 2.422a15.96 15.96 0 0 0 7.288 1.763h1.436z"/>
<path fill="url(#d)" d="M0 11.907v3.85h6.042v-2.456A13.9 13.9 0 0 0 0 11.907"/>
<path fill="url(#e)" d="M21.666 5.95v3.858h-.937c-2.536 0-5.034-.604-7.29-1.764L8.723 5.62a15.96 15.96 0 0 0-7.29-1.763H0V0h2.366C4.902 0 7.4.604 9.655 1.762l4.719 2.424a15.95 15.95 0 0 0 7.289 1.762h.003"/>
<path fill="url(#f)" d="M21.666 3.848V0h-6.042v2.455a13.9 13.9 0 0 0 6.04 1.392z"/>
</g>
</g>
<defs>
<linearGradient id="c" x1="27.02" x2="6.463" y1=".199" y2="13.729" gradientUnits="userSpaceOnUse">
<stop stop-color="#3CF"/>
<stop offset="1" stop-color="#93C"/>
</linearGradient>
<linearGradient id="d" x1="26.026" x2="5.472" y1="-1.311" y2="12.22" gradientUnits="userSpaceOnUse">
<stop stop-color="#3CF"/>
<stop offset="1" stop-color="#93C"/>
</linearGradient>
<linearGradient id="e" x1="24.287" x2="3.732" y1="-3.951" y2="9.578" gradientUnits="userSpaceOnUse">
<stop stop-color="#3CF"/>
<stop offset="1" stop-color="#93C"/>
</linearGradient>
<linearGradient id="f" x1="25.28" x2="4.725" y1="-2.443" y2="11.086" gradientUnits="userSpaceOnUse">
<stop stop-color="#3CF"/>
<stop offset="1" stop-color="#93C"/>
</linearGradient>
<clipPath id="a">
<path fill="#fff" d="M0 0h98.667v16H0z"/>
</clipPath>
</defs>
</svg>
import type { ArbitrumL2TxnBatchesItem, ArbitrumL2TxnBatch, ArbitrumL2MessagesItem } from 'types/api/arbitrumL2';
import type { ArbitrumL2TxnBatchesItem, ArbitrumL2TxnBatch, ArbitrumL2MessagesItem, ArbitrumL2TxnWithdrawalsItem } from 'types/api/arbitrumL2';
import { ADDRESS_HASH } from './addressParams';
import { TX_HASH } from './tx';
......@@ -36,3 +36,17 @@ export const ARBITRUM_L2_TXN_BATCH: ArbitrumL2TxnBatch = {
batch_data_container: 'in_blob4844',
},
};
export const ARBITRUM_L2_TXN_WITHDRAWALS_ITEM: ArbitrumL2TxnWithdrawalsItem = {
arb_block_number: 70889261,
caller: '0x507f55d716340fc836ba52c1a8daebcfeedeef1a',
completion_transaction_hash: null,
callvalue: '100000000000000',
data: '0x',
destination: '0x507f55d716340fc836ba52c1a8daebcfeedeef1a',
eth_block_number: 6494128,
id: 43685,
l2_timestamp: 1723578569,
status: 'relayed',
token: null,
};
import type * as stats from '@blockscout/stats-types';
import type { SmartContract, SmartContractMudSystemsResponse } from 'types/api/contract';
import type { VerifiedContract, VerifiedContractsCounters } from 'types/api/contracts';
import type { SolidityScanReport } from 'lib/solidityScan/schema';
import { ADDRESS_PARAMS, ADDRESS_HASH } from './addressParams';
import { STATS_COUNTER } from './stats';
export const CONTRACT_CODE_UNVERIFIED = {
creation_bytecode: '0x60806040526e',
......@@ -81,6 +83,13 @@ export const VERIFIED_CONTRACTS_COUNTERS: VerifiedContractsCounters = {
new_verified_smart_contracts_24h: '1234',
};
export const VERIFIED_CONTRACTS_COUNTERS_MICROSERVICE: stats.ContractsPageStats = {
total_contracts: STATS_COUNTER,
new_contracts_24h: STATS_COUNTER,
total_verified_contracts: STATS_COUNTER,
new_verified_contracts_24h: STATS_COUNTER,
};
export const SOLIDITY_SCAN_REPORT: SolidityScanReport = {
scan_report: {
contractname: 'BullRunners',
......
......@@ -7,8 +7,10 @@ export const POOL = {
quote_token_address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
quote_token_symbol: 'WETH',
quote_token_icon_url: 'https://coin-images.coingecko.com/coins/images/2518/small/weth.png?1696503332',
fully_diluted_valuation_usd: '15211385',
market_cap_usd: '15211385',
base_token_fully_diluted_valuation_usd: '15211385',
base_token_market_cap_usd: '15211385',
quote_token_fully_diluted_valuation_usd: '15211385',
quote_token_market_cap_usd: '15211385',
liquidity: '394101.2428',
dex: { id: 'uniswap_v2', name: 'Uniswap V2' },
coin_gecko_terminal_url: 'https://www.geckoterminal.com/eth/pools/0x6a1041865b76d1dc33da0257582591227c57832c',
......
......@@ -42,17 +42,19 @@ export const HOMEPAGE_STATS: HomeStats = {
tvl: '1767425.102766552',
};
export const STATS_CHARTS_SECTION: stats.LineChartSection = {
id: 'placeholder',
title: 'Placeholder',
charts: [
{
const STATS_CHART_INFO: stats.LineChartInfo = {
id: 'chart_0',
title: 'Average transaction fee',
description: 'The average amount in ETH spent per transaction',
units: 'ETH',
resolutions: [ 'DAY', 'MONTH' ],
},
};
export const STATS_CHARTS_SECTION: stats.LineChartSection = {
id: 'placeholder',
title: 'Placeholder',
charts: [
STATS_CHART_INFO,
{
id: 'chart_1',
title: 'Transactions fees',
......@@ -88,3 +90,21 @@ export const STATS_COUNTER: stats.Counter = {
description: 'Placeholder description',
units: '',
};
export const HOMEPAGE_STATS_MICROSERVICE: stats.MainPageStats = {
average_block_time: STATS_COUNTER,
total_addresses: STATS_COUNTER,
total_blocks: STATS_COUNTER,
total_transactions: STATS_COUNTER,
yesterday_transactions: STATS_COUNTER,
total_operational_transactions: STATS_COUNTER,
yesterday_operational_transactions: STATS_COUNTER,
daily_new_transactions: {
chart: [],
info: STATS_CHART_INFO,
},
daily_new_operational_transactions: {
chart: [],
info: STATS_CHART_INFO,
},
};
import type * as stats from '@blockscout/stats-types';
import type { RawTracesResponse } from 'types/api/rawTrace';
import type { Transaction, TransactionsStats } from 'types/api/transaction';
import { ADDRESS_PARAMS } from './addressParams';
import { STATS_COUNTER } from './stats';
export const TX_HASH = '0x3ed9d81e7c1001bdda1caa1dc62c0acbbe3d2c671cdc20dc1e65efdaa4186967';
......@@ -19,7 +21,7 @@ export const TX: Transaction = {
type: 0,
exchange_rate: '1828.71',
to: ADDRESS_PARAMS,
tx_burnt_fee: null,
transaction_burnt_fee: null,
max_fee_per_gas: null,
result: 'success',
hash: '0x2b824349b320cfa72f292ab26bf525adb00083ba9fa097141896c3c8c74567cc',
......@@ -66,3 +68,11 @@ export const TXS_STATS: TransactionsStats = {
transaction_fees_sum_24h: '22184012506492688277',
transactions_count_24h: '992890',
};
export const TXS_STATS_MICROSERVICE: stats.TransactionsPageStats = {
pending_transactions_30m: STATS_COUNTER,
transactions_24h: STATS_COUNTER,
operational_transactions_24h: STATS_COUNTER,
transactions_fee_24h: STATS_COUNTER,
average_transactions_fee_24h: STATS_COUNTER,
};
......@@ -3,6 +3,8 @@ import type {
ValidatorsStabilityCountersResponse,
ValidatorBlackfort,
ValidatorsBlackfortCountersResponse,
ValidatorsZilliqaItem,
ValidatorZilliqa,
} from 'types/api/validators';
import { ADDRESS_PARAMS } from './addressParams';
......@@ -32,3 +34,21 @@ export const VALIDATORS_BLACKFORT_COUNTERS: ValidatorsBlackfortCountersResponse
new_validators_counter_24h: '11',
validators_counter: '140',
};
export const VALIDATORS_ZILLIQA_ITEM: ValidatorsZilliqaItem = {
index: 420,
bls_public_key: '0x95125dca41be848801f9bd75254f1faf1ae3194b1da53e9a5684ed7f67b729542482bc521924603b9703c33bf831a100',
balance: '1000000000000000000',
};
export const VALIDATOR_ZILLIQA: ValidatorZilliqa = {
index: 420,
bls_public_key: '0x95125dca41be848801f9bd75254f1faf1ae3194b1da53e9a5684ed7f67b729542482bc521924603b9703c33bf831a100',
balance: '1000000000000000000',
added_at_block_number: 1234567890,
control_address: ADDRESS_PARAMS,
peer_id: '1234567890',
reward_address: ADDRESS_PARAMS,
signing_address: ADDRESS_PARAMS,
stake_updated_at_block_number: 1234567890,
};
......@@ -14,10 +14,13 @@ const PRESETS = {
garnet: 'https://explorer.garnetchain.com',
filecoin: 'https://filecoin.blockscout.com',
gnosis: 'https://gnosis.blockscout.com',
mekong: 'https://mekong.blockscout.com',
neon_devnet: 'https://neon-devnet.blockscout.com',
optimism: 'https://optimism.blockscout.com',
optimism_celestia: 'https://opcelestia-raspberry.gelatoscout.com',
optimism_sepolia: 'https://optimism-sepolia.blockscout.com',
polygon: 'https://polygon.blockscout.com',
rari_testnet: 'https://rari-testnet.cloud.blockscout.com',
rootstock_testnet: 'https://rootstock-testnet.blockscout.com',
scroll_sepolia: 'https://scroll-sepolia.blockscout.com',
shibarium: 'https://www.shibariumscan.io',
......
......@@ -2,6 +2,7 @@ import type { Transaction } from 'types/api/transaction';
import type { UserTags, AddressImplementation, AddressParam, AddressFilecoinParams } from './addressParams';
import type { Block, EpochRewardsType } from './block';
import type { SmartContractProxyType } from './contract';
import type { InternalTransaction } from './internalTransaction';
import type { MudWorldSchema, MudWorldTable } from './mudWorlds';
import type { NFTTokenType, TokenInfo, TokenInstance, TokenType } from './token';
......@@ -31,6 +32,7 @@ export interface Address extends UserTags {
name: string | null;
token: TokenInfo | null;
watchlist_address_id: number | null;
proxy_type?: SmartContractProxyType | null;
}
export interface AddressZilliqaParams {
......
import type { AddressMetadataTagApi } from './addressMetadata';
import type { SmartContractProxyType } from './contract';
export interface AddressImplementation {
address: string;
......@@ -59,6 +60,7 @@ export type AddressParamBasic = {
tags: Array<AddressMetadataTagApi>;
} | null;
filecoin?: AddressFilecoinParams;
proxy_type?: SmartContractProxyType | null;
};
export type AddressParam = UserTags & AddressParamBasic;
......@@ -2,7 +2,7 @@ import type { AddressParam } from './addressParams';
import type { TokenInfo } from './token';
export type AdvancedFilterParams = {
tx_types?: Array<AdvancedFilterType>;
transaction_types?: Array<AdvancedFilterType>;
methods?: Array<string>;
methods_names?: Array<string>; /* frontend only */
age_from?: string;
......
......@@ -12,6 +12,8 @@ export interface ArbitrumLatestDepositsResponse {
items: Array<ArbitrumLatestDepositsItem>;
}
export type ArbitrumL2MessageStatus = 'initiated' | 'sent' | 'confirmed' | 'relayed';
export type ArbitrumL2MessagesItem = {
completion_transaction_hash: string | null;
id: number;
......@@ -19,7 +21,7 @@ export type ArbitrumL2MessagesItem = {
origination_timestamp: string | null;
origination_transaction_block_number: number | null;
origination_transaction_hash: string;
status: 'initiated' | 'sent' | 'confirmed' | 'relayed';
status: ArbitrumL2MessageStatus;
};
export type ArbitrumL2MessagesResponse = {
......@@ -74,8 +76,14 @@ export type ArbitrumL2TxnBatchDAAnytrust = {
}>;
};
export type ArbitrumL2TxnBatchDataAvailability = ArbitrumL2TxnBatchDAAnytrust | {
batch_data_container: Exclude<BatchDataContainer, 'in_anytrust'>;
export type ArbitrumL2TxnBatchDACelestia = {
batch_data_container: 'in_celestia';
height: number;
transaction_commitment: string;
};
export type ArbitrumL2TxnBatchDataAvailability = ArbitrumL2TxnBatchDAAnytrust | ArbitrumL2TxnBatchDACelestia | {
batch_data_container: Exclude<BatchDataContainer, 'in_anytrust' | 'in_celestia'>;
};
export type ArbitrumL2TxnBatch = {
......@@ -108,6 +116,36 @@ export type ArbitrumL2BatchBlocks = {
} | null;
};
export interface ArbitrumL2TxnWithdrawalsItem {
arb_block_number: number;
caller: string;
callvalue: string;
completion_transaction_hash: string | null;
data: string;
destination: string;
eth_block_number: number;
id: number;
l2_timestamp: number;
status: ArbitrumL2MessageStatus;
token: {
address: string;
amount: string | null;
destination: string | null;
name: string | null;
symbol: string | null;
decimals: number | null;
} | null;
}
export interface ArbitrumL2TxnWithdrawalsResponse {
items: Array<ArbitrumL2TxnWithdrawalsItem>;
}
export interface ArbitrumL2MessageClaimResponse {
calldata: string;
outbox_address: string;
}
export const ARBITRUM_L2_TX_BATCH_STATUSES = [
'Processed on rollup' as const,
'Sent to base' as const,
......
......@@ -25,6 +25,7 @@ export type SmartContractProxyType =
| 'eip1822'
| 'eip930'
| 'eip2535'
| 'eip7702'
| 'master_copy'
| 'basic_implementation'
| 'basic_get_implementation'
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment