Merge branch 'main' of github.com:blockscout/frontend into chakra-v3
@@ -97,11 +97,11 @@ jobs:
|
||||
}
|
||||
|
||||
if (projects.length > 1) {
|
||||
core.info(`Fould ${ projects.length } with the similar name:`);
|
||||
core.info(`Found ${ projects.length } with the similar name:`);
|
||||
projects.forEach((issue) => {
|
||||
core.info(` #${ projects.number } - ${ projects.title }`);
|
||||
})
|
||||
core.setFailed('Fould multiple projects with the similar name. Cannot determine which one to use.');
|
||||
core.setFailed('Found multiple projects with the similar name. Cannot determine which one to use.');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -212,8 +212,12 @@ jobs:
|
||||
|
||||
for (const item of items) {
|
||||
core.info(`Changing field value for item ${ item }...`);
|
||||
await changeItemFieldValue(item);
|
||||
core.info('Done.\n');
|
||||
try {
|
||||
await changeItemFieldValue(item);
|
||||
core.info('Done.\n');
|
||||
} catch (error) {
|
||||
core.info(`Error: ${ error.message }\n`);
|
||||
}
|
||||
}
|
||||
|
||||
async function changeItemFieldValue(itemId) {
|
||||
|
||||
@@ -28,6 +28,15 @@
|
||||
|
||||
**Full list of the ENV variables**: [v1.2.3](https://github.com/blockscout/frontend/blob/v1.2.3/docs/ENVS.md)
|
||||
|
||||
## 💑 Compatibility
|
||||
From this version onward, the app is compatible only with the following API versions:
|
||||
|
||||
| Service | Version |
|
||||
| --- | --- |
|
||||
| Blockscout API | ... |
|
||||
| Stats microservice API | ... |
|
||||
| ... | ... |
|
||||
|
||||
## 🦄 New Contributors
|
||||
- @contributor1 made their first contribution in https://github.com/blockscout/frontend/pull/1
|
||||
- @contributor2 made their first contribution in https://github.com/blockscout/frontend/pull/2
|
||||
|
||||
@@ -10,9 +10,6 @@ NEXT_PUBLIC_APP_ENV=development
|
||||
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
|
||||
|
||||
# Instance ENVs
|
||||
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP={ "id": "728471", "width": "728", "height": "90" }
|
||||
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE={ "id": "728470", "width": "320", "height": "100" }
|
||||
NEXT_PUBLIC_AD_BANNER_ADDITIONAL_PROVIDER=adbutler
|
||||
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
|
||||
NEXT_PUBLIC_API_BASE_PATH=/
|
||||
NEXT_PUBLIC_API_HOST=eth.blockscout.com
|
||||
@@ -20,9 +17,11 @@ NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout
|
||||
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_DATA_AVAILABILITY_ENABLED=true
|
||||
NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swapscout','icon':'swap','dappId':'swapscout'},{'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_DEFI_DROPDOWN_ITEMS=[{'text':'Swapscout','icon':'swap','dappId':'swapscout'},{'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/eth.json
|
||||
NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/eth-mainnet.json
|
||||
NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge
|
||||
NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG={'name': 'Need gas?', 'url_template': 'https://smolrefuel.com/?outboundChain={chainId}&partner=blockscout&utm_source=blockscout&disableBridges=true', 'dapp_id': 'smol-refuel', 'logo': 'https://blockscout-content.s3.amazonaws.com/smolrefuel-logo-action-button.png'}
|
||||
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xd01175f1efa23f36c5579b3c13e2bbd0885017643a7efef5cbcb6b474384dfa8
|
||||
NEXT_PUBLIC_HAS_BEACON_CHAIN=true
|
||||
@@ -32,7 +31,7 @@ NEXT_PUBLIC_HIDE_INDEXING_ALERT_BLOCKS=true
|
||||
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs', 'coin_price', 'market_cap']
|
||||
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
|
||||
NEXT_PUBLIC_LOGOUT_URL=https://ethereum-mainnet.us.auth0.com/v2/logout
|
||||
NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE=<p>Participated in our recent Blockscout activities? <a href="https://badges.blockscout.com?utm_source=instance&utm_medium=eth" target="_blank">Check your eligibility</a> and claim your NFT Scout badges. More exciting things are coming soon!</p>
|
||||
NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE=<p>Joined recent campaigns? Mint your Merit Badge <a href="https://badges.blockscout.com?utm_source=instance&utm_medium=eth">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=eth
|
||||
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json
|
||||
@@ -45,11 +44,11 @@ NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
|
||||
NEXT_PUBLIC_METASUITES_ENABLED=true
|
||||
NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'},{'name': 'zapper', 'url_template': 'https://zapper.xyz/account/{address}', 'logo': 'https://blockscout-content.s3.amazonaws.com/zapper-icon.png'}]
|
||||
NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com
|
||||
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/apps']
|
||||
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/pools']
|
||||
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
|
||||
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
|
||||
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
|
||||
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'GeckoTerminal','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/geckoterminal.png','baseUrl':'https://www.geckoterminal.com/','paths':{'token':'/eth/pools'}},{'title':'Etherscan','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/etherscan.png','baseUrl':'https://etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}, {'title':'Blockchair','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/blockchair.png','baseUrl':'https://blockchair.com/','paths':{'tx':'/ethereum/transaction','address':'/ethereum/address','token':'/ethereum/erc-20/token','block':'/ethereum/block'}},{'title':'Sentio','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/sentio.png','baseUrl':'https://app.sentio.xyz/','paths':{'tx':'/tx/1','address':'/contract/1'}}, {'title':'Tenderly','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/tenderly.png','baseUrl':'https://dashboard.tenderly.co','paths':{'tx':'/tx/mainnet'}}, {'title':'0xPPL','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/0xPPL.png','baseUrl':'https://0xppl.com','paths':{'tx':'/Ethereum/tx','address':'/','token':'/c/Ethereum'}}, {'title':'3xpl','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/3xpl.png','baseUrl':'https://3xpl.com/','paths':{'tx':'/ethereum/transaction','address':'/ethereum/address'}} ]
|
||||
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Moralis','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/moralis.png','baseUrl':'https://moralis.com/','paths':{'token':'/chain/ethereum/token/price'}},{'title':'GeckoTerminal','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/geckoterminal.png','baseUrl':'https://www.geckoterminal.com/','paths':{'token':'/eth/pools'}},{'title':'Etherscan','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/etherscan.png','baseUrl':'https://etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}, {'title':'Blockchair','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/blockchair.png','baseUrl':'https://blockchair.com/','paths':{'tx':'/ethereum/transaction','address':'/ethereum/address','token':'/ethereum/erc-20/token','block':'/ethereum/block'}},{'title':'Sentio','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/sentio.png','baseUrl':'https://app.sentio.xyz/','paths':{'tx':'/tx/1','address':'/contract/1'}}, {'title':'Tenderly','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/tenderly.png','baseUrl':'https://dashboard.tenderly.co','paths':{'tx':'/tx/mainnet'}}, {'title':'0xPPL','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/0xPPL.png','baseUrl':'https://0xppl.com','paths':{'tx':'/Ethereum/tx','address':'/','token':'/c/Ethereum'}}, {'title':'3xpl','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/3xpl.png','baseUrl':'https://3xpl.com/','paths':{'tx':'/ethereum/transaction','address':'/ethereum/address'}} ]
|
||||
NEXT_PUBLIC_NETWORK_ID=1
|
||||
NEXT_PUBLIC_NETWORK_NAME=Ethereum
|
||||
NEXT_PUBLIC_NETWORK_RPC_URL=https://eth.drpc.org
|
||||
@@ -61,10 +60,10 @@ NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://eth.drpc.org?ref=559183','text':'Public
|
||||
NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://merits.blockscout.com
|
||||
NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-mainnet.safe.global
|
||||
NEXT_PUBLIC_SAVE_ON_GAS_ENABLED=true
|
||||
NEXT_PUBLIC_SEO_ENHANCED_DATA_ENABLED=true
|
||||
NEXT_PUBLIC_STATS_API_HOST=https://stats-eth-main.k8s-prod-1.blockscout.com
|
||||
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
|
||||
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
|
||||
NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES=[{'name':'Rarible','collection_url':'https://rarible.com/collection/{hash}/items','instance_url':'https://rarible.com/token/{hash}:{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/rarible.png'},{'name':'Blur','collection_url':'https://blur.io/eth/collection/{hash}','instance_url':'https://blur.io/eth/asset/{hash}/{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/blur.png'},{'name':'Nftrade','collection_url':'https://nftrade.com/assets/eth/{hash}','instance_url':'https://nftrade.com/assets/eth/{hash}/{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/nftrade.png'},{'name':'MagicEden','collection_url':'https://magiceden.io/collections/ethereum/{hash}','instance_url':'https://magiceden.io/item-details/ethereum/{hash}/{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/magiceden.png'}]
|
||||
NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_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
|
||||
NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/hiddenBlockBadge
|
||||
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
|
||||
@@ -14,14 +14,15 @@ NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP={ "id": "632019", "width": "728", "height
|
||||
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE={ "id": "632018", "width": "320", "height": "100" }
|
||||
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
|
||||
NEXT_PUBLIC_API_BASE_PATH=/
|
||||
NEXT_PUBLIC_API_HOST=eth-sepolia.k8s-dev.blockscout.com
|
||||
NEXT_PUBLIC_API_HOST=eth-sepolia.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-test.k8s-dev.blockscout.com
|
||||
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
|
||||
NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED=true
|
||||
NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'cow-swap'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'}]
|
||||
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/eth-sepolia.json
|
||||
NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/sepolia.json
|
||||
NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge
|
||||
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xbf69c7abc4fee283b59a9633dadfdaedde5c5ee0fba3e80a08b5b8a3acbd4363
|
||||
NEXT_PUBLIC_HAS_BEACON_CHAIN=true
|
||||
NEXT_PUBLIC_HAS_USER_OPS=true
|
||||
@@ -30,12 +31,11 @@ NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['rgba(51, 53, 67, 1)'],'t
|
||||
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
|
||||
NEXT_PUBLIC_IS_TESTNET=true
|
||||
NEXT_PUBLIC_LOGOUT_URL=https://blockscout-goerli.us.auth0.com/v2/logout
|
||||
NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE=<p>Participated in our recent Blockscout activities? <a href="https://badges.blockscout.com?utm_source=instance&utm_medium=sepolia" target="_blank">Check your eligibility</a> and claim your NFT Scout badges. More exciting things are coming soon!</p>
|
||||
NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE=<p>Joined recent campaigns? Mint your Merit Badge <a href="https://badges.blockscout.com?utm_source=instance&utm_medium=sepolia">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=eth-sepolia
|
||||
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json
|
||||
NEXT_PUBLIC_MARKETPLACE_ENABLED=true
|
||||
NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY=patbqG4V2CI998jAq.9810c58c9de973ba2650621c94559088cbdfa1a914498e385621ed035d33c0d0
|
||||
NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID=appGkvtmKI7fXE4Vs
|
||||
NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-security-reports/default.json
|
||||
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL
|
||||
@@ -62,11 +62,10 @@ NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
|
||||
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true
|
||||
NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/sepolia-testnet.png
|
||||
NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://sepolia.drpc.org?ref=559183','text':'Public RPC'}]
|
||||
NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://points.k8s-dev.blockscout.com
|
||||
NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://merits.blockscout.com
|
||||
NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-sepolia.safe.global
|
||||
NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s.blockscout.com
|
||||
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=noves
|
||||
NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.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
|
||||
NEXT_PUBLIC_DEX_POOLS_ENABLED=true
|
||||
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
|
||||
@@ -87,8 +87,11 @@ download_and_save_asset() {
|
||||
else
|
||||
# Check if the value is a URL
|
||||
if [[ "$url" == http* ]]; then
|
||||
# Download the asset using curl
|
||||
curl -s -o "$destination" "$url"
|
||||
# Download the asset using curl with timeouts
|
||||
if ! curl -f -s --connect-timeout 5 --max-time 15 -o "$destination" "$url"; then
|
||||
echo " [-] $env_var: Failed to download from $url (timeout or connection error)"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
# Convert single-quoted JSON-like content to valid JSON
|
||||
json_content=$(echo "${!env_var}" | sed "s/'/\"/g")
|
||||
|
||||
@@ -32,14 +32,20 @@ if (process.env.NEXT_PUBLIC_OG_IMAGE_URL) {
|
||||
|
||||
console.log('⏳ Making request to OG image generator service...');
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 30_000);
|
||||
|
||||
const response = await fetch('https://bigs.services.blockscout.com/generate/og', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (response.ok) {
|
||||
console.log('⬇️ Downloading the image...');
|
||||
const buffer = await response.arrayBuffer();
|
||||
|
||||
@@ -141,40 +141,32 @@ function getEnvsPlaceholders(filePath: string): Promise<Array<string>> {
|
||||
|
||||
function printDeprecationWarning(envsMap: Record<string, string>) {
|
||||
if (envsMap.NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY && envsMap.NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY) {
|
||||
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗');
|
||||
// eslint-disable-next-line max-len
|
||||
console.warn('The NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY variable is now deprecated and will be removed in the next release. Please migrate to the NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY variable.');
|
||||
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗\n');
|
||||
console.warn('❗ The NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY variable is now deprecated and will be removed in the next release. Please migrate to the NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY variable.');
|
||||
}
|
||||
|
||||
if (
|
||||
(envsMap.NEXT_PUBLIC_SENTRY_DSN || envsMap.SENTRY_CSP_REPORT_URI || envsMap.NEXT_PUBLIC_SENTRY_ENABLE_TRACING) &&
|
||||
envsMap.NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN
|
||||
) {
|
||||
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗');
|
||||
// eslint-disable-next-line max-len
|
||||
console.warn('The Sentry monitoring is now deprecated and will be removed in the next release. Please migrate to the Rollbar error monitoring.');
|
||||
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗\n');
|
||||
console.warn('❗ The Sentry monitoring is now deprecated and will be removed in the next release. Please migrate to the Rollbar error monitoring.');
|
||||
}
|
||||
|
||||
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');
|
||||
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.');
|
||||
}
|
||||
|
||||
if (
|
||||
envsMap.NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR ||
|
||||
envsMap.NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND
|
||||
) {
|
||||
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗');
|
||||
// eslint-disable-next-line max-len
|
||||
console.warn('The NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR and NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND variables are now deprecated and will be removed in the next release. Please migrate to the NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG variable.');
|
||||
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗\n');
|
||||
console.warn('❗ The NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR and NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND variables are now deprecated and will be removed in the next release. Please migrate to the NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG variable.');
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -182,10 +174,8 @@ function printDeprecationWarning(envsMap: Record<string, string>) {
|
||||
envsMap.NEXT_PUBLIC_AUTH_URL ||
|
||||
envsMap.NEXT_PUBLIC_LOGOUT_URL
|
||||
) {
|
||||
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗');
|
||||
// eslint-disable-next-line max-len
|
||||
console.warn('The NEXT_PUBLIC_AUTH0_CLIENT_ID, NEXT_PUBLIC_AUTH_URL and NEXT_PUBLIC_LOGOUT_URL variables are now deprecated and will be removed in the next release.');
|
||||
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗\n');
|
||||
console.warn('❗ The NEXT_PUBLIC_AUTH0_CLIENT_ID, NEXT_PUBLIC_AUTH_URL and NEXT_PUBLIC_LOGOUT_URL variables are now deprecated and will be removed in the next release.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,26 @@ const stripTrailingSlash = (str) => str[str.length - 1] === '/' ? str.slice(0, -
|
||||
const fetchResource = async(url, formatter) => {
|
||||
console.log('🌀 [next-sitemap] Fetching resource:', url);
|
||||
try {
|
||||
const res = await fetch(url);
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 15_000);
|
||||
|
||||
const res = await fetch(url, {
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
console.log('✅ [next-sitemap] Data fetched for resource:', url);
|
||||
return formatter(data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('🚨 [next-sitemap] Error fetching resource:', url, error);
|
||||
if (error.name === 'AbortError') {
|
||||
console.log('🚨 [next-sitemap] Request timeout for resource:', url);
|
||||
} else {
|
||||
console.log('🚨 [next-sitemap] Error fetching resource:', url, error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -42,6 +54,15 @@ module.exports = {
|
||||
siteUrl,
|
||||
generateIndexSitemap: false,
|
||||
generateRobotsTxt: true,
|
||||
robotsTxtOptions: {
|
||||
policies: [
|
||||
{
|
||||
userAgent: '*',
|
||||
allow: '/',
|
||||
disallow: ['/auth/*', '/login', '/sprite', '/account/*', '/api/*', '/node-api/*'],
|
||||
},
|
||||
],
|
||||
},
|
||||
sourceDir: path.resolve(process.cwd(), '../../../.next'),
|
||||
outDir: path.resolve(process.cwd(), '../../../public'),
|
||||
exclude: [
|
||||
|
||||
@@ -283,7 +283,7 @@ Settings for meta tags, OG tags and SEO
|
||||
#### 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+ |
|
||||
| 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. | - | `false` | `true` | v1.38.0+ |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,12 +2,18 @@ import { getAddress } from 'viem';
|
||||
|
||||
import config from 'configs/app';
|
||||
|
||||
const ERC1191_CHAIN_IDS = [
|
||||
'30', // RSK Mainnet
|
||||
'31', // RSK Testnet
|
||||
];
|
||||
|
||||
export default function getCheckedSummedAddress(address: string): string {
|
||||
try {
|
||||
return getAddress(
|
||||
address,
|
||||
// We need to pass chainId to getAddress to make it work correctly for some chains, e.g. Rootstock
|
||||
config.chain.id ? Number(config.chain.id) : undefined,
|
||||
// We need to pass chainId to getAddress to make it work correctly for chains that support ERC-1191
|
||||
// https://eips.ethereum.org/EIPS/eip-1191#usage--table
|
||||
ERC1191_CHAIN_IDS.includes(config.chain.id ?? '') ? Number(config.chain.id) : undefined,
|
||||
);
|
||||
} catch (error) {
|
||||
return address;
|
||||
|
||||
@@ -635,7 +635,7 @@ export const RESOURCES = {
|
||||
filterFields: [],
|
||||
},
|
||||
address_xstar_score: {
|
||||
path: '/api/v2/proxy/3rdparty/xname/addresses/:hash',
|
||||
path: '/api/v2/proxy/3dparty/xname/addresses/:hash',
|
||||
pathParams: [ 'hash' as const ],
|
||||
},
|
||||
|
||||
@@ -1090,16 +1090,16 @@ export const RESOURCES = {
|
||||
|
||||
// NOVES-FI
|
||||
noves_transaction: {
|
||||
path: '/api/v2/proxy/3rdparty/noves-fi/transactions/:hash',
|
||||
path: '/api/v2/proxy/3dparty/noves-fi/transactions/:hash',
|
||||
pathParams: [ 'hash' as const ],
|
||||
},
|
||||
noves_address_history: {
|
||||
path: '/api/v2/proxy/3rdparty/noves-fi/addresses/:address/transactions',
|
||||
path: '/api/v2/proxy/3dparty/noves-fi/addresses/:address/transactions',
|
||||
pathParams: [ 'address' as const ],
|
||||
filterFields: [],
|
||||
},
|
||||
noves_describe_txs: {
|
||||
path: '/api/v2/proxy/3rdparty/noves-fi/transaction-descriptions',
|
||||
path: '/api/v2/proxy/3dparty/noves-fi/transaction-descriptions',
|
||||
},
|
||||
|
||||
// USER OPS
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import type { AddEthereumChainParameter } from 'viem';
|
||||
|
||||
import config from 'configs/app';
|
||||
|
||||
import useProvider from './useProvider';
|
||||
import { getHexadecimalChainId } from './utils';
|
||||
|
||||
function getParams(): AddEthereumChainParameter {
|
||||
if (!config.chain.id) {
|
||||
throw new Error('Missing required chain config');
|
||||
}
|
||||
|
||||
return {
|
||||
chainId: getHexadecimalChainId(Number(config.chain.id)),
|
||||
chainName: config.chain.name ?? '',
|
||||
nativeCurrency: {
|
||||
name: config.chain.currency.name ?? '',
|
||||
symbol: config.chain.currency.symbol ?? '',
|
||||
decimals: config.chain.currency.decimals ?? 18,
|
||||
},
|
||||
rpcUrls: config.chain.rpcUrls,
|
||||
blockExplorerUrls: [ config.app.baseUrl ],
|
||||
};
|
||||
}
|
||||
|
||||
export default function useAddChain() {
|
||||
const { wallet, provider } = useProvider();
|
||||
|
||||
return React.useCallback(() => {
|
||||
if (!wallet || !provider) {
|
||||
throw new Error('Wallet or provider not found');
|
||||
}
|
||||
|
||||
return provider.request({
|
||||
method: 'wallet_addEthereumChain',
|
||||
params: [ getParams() ],
|
||||
});
|
||||
}, [ wallet, provider ]);
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import { get } from 'es-toolkit/compat';
|
||||
import React from 'react';
|
||||
|
||||
import config from 'configs/app';
|
||||
import getErrorObj from 'lib/errors/getErrorObj';
|
||||
|
||||
import useProvider from './useProvider';
|
||||
|
||||
export default function useAddOrSwitchChain() {
|
||||
const { wallet, provider } = useProvider();
|
||||
|
||||
return React.useCallback(async() => {
|
||||
if (!wallet || !provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hexadecimalChainId = '0x' + Number(config.chain.id).toString(16);
|
||||
|
||||
try {
|
||||
return await provider.request({
|
||||
method: 'wallet_switchEthereumChain',
|
||||
params: [ { chainId: hexadecimalChainId } ],
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
const errorObj = getErrorObj(error);
|
||||
const code = errorObj && 'code' in errorObj ? errorObj.code : undefined;
|
||||
const originalErrorCode = get(errorObj, 'data.originalError.code');
|
||||
|
||||
// This error code indicates that the chain has not been added to Wallet.
|
||||
if (code === 4902 || originalErrorCode === 4902) {
|
||||
const params = [ {
|
||||
chainId: hexadecimalChainId,
|
||||
chainName: config.chain.name,
|
||||
nativeCurrency: {
|
||||
name: config.chain.currency.name,
|
||||
symbol: config.chain.currency.symbol,
|
||||
decimals: config.chain.currency.decimals,
|
||||
},
|
||||
rpcUrls: config.chain.rpcUrls,
|
||||
blockExplorerUrls: [ config.app.baseUrl ],
|
||||
} ] as never;
|
||||
// in wagmi types for wallet_addEthereumChain method is not provided
|
||||
|
||||
return await provider.request({
|
||||
method: 'wallet_addEthereumChain',
|
||||
params: params,
|
||||
});
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}, [ provider, wallet ]);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
|
||||
import config from 'configs/app';
|
||||
|
||||
import useProvider from './useProvider';
|
||||
import { getHexadecimalChainId } from './utils';
|
||||
|
||||
function getParams(): { chainId: string } {
|
||||
if (!config.chain.id) {
|
||||
throw new Error('Missing required chain config');
|
||||
}
|
||||
|
||||
return { chainId: getHexadecimalChainId(Number(config.chain.id)) };
|
||||
}
|
||||
|
||||
export default function useSwitchChain() {
|
||||
const { wallet, provider } = useProvider();
|
||||
|
||||
return React.useCallback(() => {
|
||||
if (!wallet || !provider) {
|
||||
throw new Error('Wallet or provider not found');
|
||||
}
|
||||
|
||||
return provider.request({
|
||||
method: 'wallet_switchEthereumChain',
|
||||
params: [ getParams() ],
|
||||
});
|
||||
}, [ wallet, provider ]);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { get } from 'es-toolkit/compat';
|
||||
import React from 'react';
|
||||
|
||||
import getErrorObj from 'lib/errors/getErrorObj';
|
||||
|
||||
import useAddChain from './useAddChain';
|
||||
import useProvider from './useProvider';
|
||||
import useSwitchChain from './useSwitchChain';
|
||||
|
||||
export default function useSwitchOrAddChain() {
|
||||
const { wallet, provider } = useProvider();
|
||||
const addChain = useAddChain();
|
||||
const switchChain = useSwitchChain();
|
||||
|
||||
return React.useCallback(async() => {
|
||||
if (!wallet || !provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
return switchChain();
|
||||
} catch (error) {
|
||||
const errorObj = getErrorObj(error);
|
||||
const code = get(errorObj, 'code');
|
||||
const originalErrorCode = get(errorObj, 'data.originalError.code');
|
||||
|
||||
// This error code indicates that the chain has not been added to Wallet.
|
||||
if (code === 4902 || originalErrorCode === 4902) {
|
||||
return addChain();
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}, [ addChain, provider, wallet, switchChain ]);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export function getHexadecimalChainId(chainId: number) {
|
||||
return '0x' + Number(chainId).toString(16);
|
||||
}
|
||||
@@ -82,7 +82,6 @@ export const token: Address = {
|
||||
creation_transaction_hash: '0xc38cf7377bf72d6436f63c37b01b24d032101f20ec1849286dc703c712f10c98',
|
||||
creator_address_hash: '0x34A9c688512ebdB575e82C50c9803F6ba2916E72',
|
||||
exchange_rate: '0.04311',
|
||||
has_decompiled_code: false,
|
||||
has_logs: false,
|
||||
has_token_transfers: true,
|
||||
has_tokens: true,
|
||||
@@ -96,7 +95,6 @@ export const eoa: Address = {
|
||||
creation_transaction_hash: '0xf2aff6501b632604c39978b47d309813d8a1bcca721864bbe86abf59704f195e',
|
||||
creator_address_hash: '0x803ad3F50b9e1fF68615e8B053A186e1be288943',
|
||||
exchange_rate: '0.04311',
|
||||
has_decompiled_code: false,
|
||||
has_logs: true,
|
||||
has_token_transfers: false,
|
||||
has_tokens: true,
|
||||
@@ -120,7 +118,6 @@ export const contract: Address = {
|
||||
creation_transaction_hash: '0xf2aff6501b632604c39978b47d309813d8a1bcca721864bbe86abf59704f195e',
|
||||
creator_address_hash: '0x803ad3F50b9e1fF68615e8B053A186e1be288943',
|
||||
exchange_rate: '0.04311',
|
||||
has_decompiled_code: false,
|
||||
has_logs: true,
|
||||
has_token_transfers: false,
|
||||
has_tokens: false,
|
||||
@@ -146,7 +143,6 @@ export const validator: Address = {
|
||||
creation_transaction_hash: null,
|
||||
creator_address_hash: null,
|
||||
exchange_rate: '0.00432018',
|
||||
has_decompiled_code: false,
|
||||
has_logs: false,
|
||||
has_token_transfers: false,
|
||||
has_tokens: false,
|
||||
|
||||
@@ -46,7 +46,7 @@ Promise<GetServerSidePropsResult<Props<Pathname>>> => {
|
||||
|
||||
const isTrackingDisabled = process.env.DISABLE_TRACKING === 'true';
|
||||
|
||||
if (!isTrackingDisabled) {
|
||||
if (!isTrackingDisabled && !config.app.isDev) {
|
||||
// log pageview
|
||||
const hostname = req.headers.host;
|
||||
const timestamp = new Date().toISOString();
|
||||
|
||||
@@ -15,4 +15,4 @@ const Page: NextPage = () => {
|
||||
|
||||
export default Page;
|
||||
|
||||
export { dev as getServerSideProps } from 'nextjs/getServerSideProps';
|
||||
export { base as getServerSideProps } from 'nextjs/getServerSideProps';
|
||||
|
||||
@@ -22,7 +22,6 @@ export const ADDRESS_INFO: Address = {
|
||||
creation_transaction_hash: null,
|
||||
creator_address_hash: ADDRESS_HASH,
|
||||
exchange_rate: null,
|
||||
has_decompiled_code: false,
|
||||
has_logs: true,
|
||||
has_token_transfers: false,
|
||||
has_tokens: false,
|
||||
|
||||
@@ -4,13 +4,21 @@ import { ThemeProvider, useTheme } from 'next-themes';
|
||||
import type { ThemeProviderProps } from 'next-themes';
|
||||
import * as React from 'react';
|
||||
|
||||
import config from 'configs/app';
|
||||
|
||||
export interface ColorModeProviderProps extends ThemeProviderProps {}
|
||||
|
||||
export type ColorMode = 'light' | 'dark';
|
||||
|
||||
export function ColorModeProvider(props: ColorModeProviderProps) {
|
||||
return (
|
||||
<ThemeProvider attribute="class" scriptProps={{ 'data-cfasync': 'false' }} disableTransitionOnChange { ...props }/>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
scriptProps={{ 'data-cfasync': 'false' }}
|
||||
enableSystem={ false }
|
||||
defaultTheme={ config.UI.colorTheme.default?.colorMode }
|
||||
disableTransitionOnChange { ...props }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ export interface Address extends UserTags {
|
||||
zilliqa?: AddressZilliqaParams;
|
||||
// TODO: if we are happy with tabs-counters method, should we delete has_something fields?
|
||||
has_beacon_chain_withdrawals?: boolean;
|
||||
has_decompiled_code: boolean;
|
||||
has_logs: boolean;
|
||||
has_token_transfers: boolean;
|
||||
has_tokens: boolean;
|
||||
|
||||
@@ -172,8 +172,8 @@ export interface BlockEpoch {
|
||||
carbon_offsetting_transfer: TokenTransfer | null;
|
||||
community_transfer: TokenTransfer | null;
|
||||
reserve_bolster_transfer: TokenTransfer | null;
|
||||
};
|
||||
aggregated_election_rewards: Record<EpochRewardsType, BlockEpochElectionReward | null>;
|
||||
} | null;
|
||||
aggregated_election_rewards: Record<EpochRewardsType, BlockEpochElectionReward | null> | null;
|
||||
}
|
||||
|
||||
export interface BlockEpochElectionRewardDetails {
|
||||
|
||||
@@ -15,10 +15,10 @@ export type Pool = {
|
||||
quote_token_address: string;
|
||||
quote_token_symbol: string;
|
||||
quote_token_icon_url: string | null;
|
||||
base_token_fully_diluted_valuation_usd: string;
|
||||
base_token_market_cap_usd: string;
|
||||
quote_token_fully_diluted_valuation_usd: string;
|
||||
quote_token_market_cap_usd: string;
|
||||
base_token_fully_diluted_valuation_usd: string | null;
|
||||
base_token_market_cap_usd: string | null;
|
||||
quote_token_fully_diluted_valuation_usd: string | null;
|
||||
quote_token_market_cap_usd: string | null;
|
||||
liquidity: string;
|
||||
dex: {
|
||||
id: string;
|
||||
|
||||
@@ -10,7 +10,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const ContractDetailsAlertVerificationSource = ({ data }: Props) => {
|
||||
if (data?.is_verified_via_eth_bytecode_db) {
|
||||
if (data?.is_verified && data?.is_verified_via_eth_bytecode_db) {
|
||||
return (
|
||||
<Alert status="warning" whiteSpace="pre-wrap">
|
||||
<span>This contract has been { data.is_partially_verified ? 'partially ' : '' }verified using </span>
|
||||
@@ -25,7 +25,7 @@ const ContractDetailsAlertVerificationSource = ({ data }: Props) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (data?.is_verified_via_sourcify) {
|
||||
if (data?.is_verified && data?.is_verified_via_sourcify) {
|
||||
return (
|
||||
<Alert status="warning" whiteSpace="pre-wrap">
|
||||
<span>This contract has been { data.is_partially_verified ? 'partially ' : '' }verified via Sourcify. </span>
|
||||
|
||||
@@ -77,7 +77,6 @@ export default function useAddressQuery({ hash, isEnabled = true }: Params): Add
|
||||
creation_transaction_hash: null,
|
||||
exchange_rate: null,
|
||||
ens_domain_name: null,
|
||||
has_decompiled_code: false,
|
||||
has_logs: false,
|
||||
has_token_transfers: false,
|
||||
has_tokens: false,
|
||||
|
||||
@@ -25,7 +25,7 @@ const BlockEpochRewards = ({ heightOrHash }: Props) => {
|
||||
return <DataFetchAlert/>;
|
||||
}
|
||||
|
||||
if (!query.data) {
|
||||
if (!query.data || (!query.data.aggregated_election_rewards && !query.data.distribution)) {
|
||||
return <span>No block epoch rewards data</span>;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,10 @@ interface Props {
|
||||
}
|
||||
|
||||
const BlockEpochElectionRewards = ({ data, isLoading }: Props) => {
|
||||
if (!data.aggregated_election_rewards) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box mt={ 8 }>
|
||||
<Heading level="3" mb={ 3 }>Election rewards</Heading>
|
||||
|
||||
@@ -15,6 +15,10 @@ interface Props {
|
||||
const BlockEpochRewardsDistribution = ({ data, isLoading }: Props) => {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
if (!data.distribution) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!data.distribution.community_transfer && !data.distribution.carbon_offsetting_transfer && !data.distribution.reserve_bolster_transfer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { BlockEpoch } from 'types/api/block';
|
||||
import type { ExcludeNull } from 'types/utils';
|
||||
|
||||
export function getRewardNumText(type: keyof BlockEpoch['aggregated_election_rewards'], num: number) {
|
||||
const postfix1 = num !== 1 ? 's' : '';
|
||||
@@ -26,7 +27,7 @@ export function getRewardNumText(type: keyof BlockEpoch['aggregated_election_rew
|
||||
return `${ num } ${ text }`;
|
||||
}
|
||||
|
||||
export function getRewardDetailsTableTitles(type: keyof BlockEpoch['aggregated_election_rewards']): [string, string] {
|
||||
export function getRewardDetailsTableTitles(type: keyof ExcludeNull<BlockEpoch['aggregated_election_rewards']>): [string, string] {
|
||||
switch (type) {
|
||||
case 'delegated_payment':
|
||||
return [ 'Beneficiary', 'Validator' ];
|
||||
@@ -39,6 +40,6 @@ export function getRewardDetailsTableTitles(type: keyof BlockEpoch['aggregated_e
|
||||
}
|
||||
}
|
||||
|
||||
export function formatRewardType(type: keyof BlockEpoch['aggregated_election_rewards']) {
|
||||
export function formatRewardType(type: keyof ExcludeNull<BlockEpoch['aggregated_election_rewards']>) {
|
||||
return type.replaceAll('_', '-');
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ const UserOp = () => {
|
||||
if (!userOpQuery.data) {
|
||||
return true;
|
||||
} else {
|
||||
if (!userOpQuery.data.user_logs_start_index || !userOpQuery.data.user_logs_count) {
|
||||
return false;
|
||||
}
|
||||
if (inRange(
|
||||
Number(tt.log_index),
|
||||
userOpQuery.data?.user_logs_start_index,
|
||||
@@ -58,6 +61,9 @@ const UserOp = () => {
|
||||
if (!userOpQuery.data) {
|
||||
return true;
|
||||
} else {
|
||||
if (!userOpQuery.data.user_logs_start_index || !userOpQuery.data.user_logs_count) {
|
||||
return false;
|
||||
}
|
||||
if (inRange(log.index, userOpQuery.data?.user_logs_start_index, userOpQuery.data?.user_logs_start_index + userOpQuery.data?.user_logs_count)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
@@ -67,7 +67,10 @@ const PoolInfo = ({ data, isPlaceholderData }: Props) => {
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue>
|
||||
<Skeleton loading={ isPlaceholderData }>
|
||||
${ Number(data.base_token_fully_diluted_valuation_usd).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }) }
|
||||
{ data.base_token_fully_diluted_valuation_usd ?
|
||||
`$${ Number(data.base_token_fully_diluted_valuation_usd).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }) }` :
|
||||
'N/A'
|
||||
}
|
||||
</Skeleton>
|
||||
</DetailedInfo.ItemValue>
|
||||
|
||||
@@ -79,7 +82,10 @@ const PoolInfo = ({ data, isPlaceholderData }: Props) => {
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue>
|
||||
<Skeleton loading={ isPlaceholderData }>
|
||||
${ Number(data.base_token_market_cap_usd).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }) }
|
||||
{ data.base_token_market_cap_usd ?
|
||||
`$${ Number(data.base_token_market_cap_usd).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }) }` :
|
||||
'N/A'
|
||||
}
|
||||
</Skeleton>
|
||||
</DetailedInfo.ItemValue>
|
||||
|
||||
@@ -91,7 +97,10 @@ const PoolInfo = ({ data, isPlaceholderData }: Props) => {
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue>
|
||||
<Skeleton loading={ isPlaceholderData }>
|
||||
${ Number(data.quote_token_fully_diluted_valuation_usd).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }) }
|
||||
{ data.quote_token_fully_diluted_valuation_usd ?
|
||||
`$${ Number(data.quote_token_fully_diluted_valuation_usd).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }) }` :
|
||||
'N/A'
|
||||
}
|
||||
</Skeleton>
|
||||
</DetailedInfo.ItemValue>
|
||||
|
||||
@@ -103,7 +112,10 @@ const PoolInfo = ({ data, isPlaceholderData }: Props) => {
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue>
|
||||
<Skeleton loading={ isPlaceholderData }>
|
||||
${ Number(data.quote_token_market_cap_usd).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }) }
|
||||
{ data.quote_token_market_cap_usd ?
|
||||
`$${ Number(data.quote_token_market_cap_usd).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }) }` :
|
||||
'N/A'
|
||||
}
|
||||
</Skeleton>
|
||||
</DetailedInfo.ItemValue>
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@ import React from 'react';
|
||||
|
||||
import config from 'configs/app';
|
||||
import * as mixpanel from 'lib/mixpanel/index';
|
||||
import useAddOrSwitchChain from 'lib/web3/useAddOrSwitchChain';
|
||||
import useAddChain from 'lib/web3/useAddChain';
|
||||
import useProvider from 'lib/web3/useProvider';
|
||||
import useSwitchChain from 'lib/web3/useSwitchChain';
|
||||
import { WALLETS_INFO } from 'lib/web3/wallets';
|
||||
import { Button } from 'toolkit/chakra/button';
|
||||
import { toaster } from 'toolkit/chakra/toaster';
|
||||
@@ -13,7 +14,8 @@ const feature = config.features.web3Wallet;
|
||||
|
||||
const NetworkAddToWallet = () => {
|
||||
const { provider, wallet } = useProvider();
|
||||
const addOrSwitchChain = useAddOrSwitchChain();
|
||||
const addChain = useAddChain();
|
||||
const switchChain = useSwitchChain();
|
||||
|
||||
const handleClick = React.useCallback(async() => {
|
||||
if (!wallet || !provider) {
|
||||
@@ -21,7 +23,8 @@ const NetworkAddToWallet = () => {
|
||||
}
|
||||
|
||||
try {
|
||||
await addOrSwitchChain();
|
||||
await addChain();
|
||||
await switchChain();
|
||||
|
||||
toaster.success({
|
||||
title: 'Success',
|
||||
@@ -39,7 +42,7 @@ const NetworkAddToWallet = () => {
|
||||
description: (error as Error)?.message || 'Something went wrong',
|
||||
});
|
||||
}
|
||||
}, [ addOrSwitchChain, provider, wallet ]);
|
||||
}, [ addChain, provider, wallet, switchChain ]);
|
||||
|
||||
if (!provider || !wallet || !config.chain.rpcUrls.length || !feature.isEnabled) {
|
||||
return null;
|
||||
|
||||
@@ -31,7 +31,7 @@ const init = () => {
|
||||
projectId: feature.walletConnect.projectId,
|
||||
features: {
|
||||
analytics: false,
|
||||
email: true,
|
||||
email: false,
|
||||
socials: [],
|
||||
onramp: false,
|
||||
swaps: false,
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { Box, chakra } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import type { WatchAssetParams } from 'viem';
|
||||
|
||||
import type { TokenInfo } from 'types/api/token';
|
||||
|
||||
import config from 'configs/app';
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import * as mixpanel from 'lib/mixpanel/index';
|
||||
import useAddOrSwitchChain from 'lib/web3/useAddOrSwitchChain';
|
||||
import useProvider from 'lib/web3/useProvider';
|
||||
import useSwitchOrAddChain from 'lib/web3/useSwitchOrAddChain';
|
||||
import { WALLETS_INFO } from 'lib/web3/wallets';
|
||||
import { IconButton } from 'toolkit/chakra/icon-button';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
@@ -16,17 +18,50 @@ import IconSvg from 'ui/shared/IconSvg';
|
||||
|
||||
const feature = config.features.web3Wallet;
|
||||
|
||||
function getRequestParams(token: TokenInfo, tokenId?: string): WatchAssetParams | undefined {
|
||||
switch (token.type) {
|
||||
case 'ERC-20':
|
||||
return {
|
||||
type: 'ERC20',
|
||||
options: {
|
||||
address: token.address,
|
||||
symbol: token.symbol || '',
|
||||
decimals: Number(token.decimals) || 18,
|
||||
image: token.icon_url || '',
|
||||
},
|
||||
};
|
||||
case 'ERC-721':
|
||||
case 'ERC-1155': {
|
||||
if (!tokenId) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
type: token.type === 'ERC-721' ? 'ERC721' : 'ERC1155',
|
||||
options: {
|
||||
address: token.address,
|
||||
tokenId: tokenId,
|
||||
},
|
||||
} as never; // There is no official EIP, and therefore no typings for these token types.
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
token: TokenInfo;
|
||||
tokenId?: string;
|
||||
isLoading?: boolean;
|
||||
variant?: 'icon' | 'button';
|
||||
iconSize?: number;
|
||||
}
|
||||
|
||||
const AddressAddToWallet = ({ className, token, isLoading, variant = 'icon', iconSize = 6 }: Props) => {
|
||||
const AddressAddToWallet = ({ className, token, tokenId, isLoading, variant = 'icon', iconSize = 6 }: Props) => {
|
||||
const { provider, wallet } = useProvider();
|
||||
const addOrSwitchChain = useAddOrSwitchChain();
|
||||
const switchOrAddChain = useSwitchOrAddChain();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const handleClick = React.useCallback(async() => {
|
||||
if (!wallet) {
|
||||
@@ -34,20 +69,18 @@ const AddressAddToWallet = ({ className, token, isLoading, variant = 'icon', ico
|
||||
}
|
||||
|
||||
try {
|
||||
const params = getRequestParams(token, tokenId);
|
||||
|
||||
if (!params) {
|
||||
throw new Error('Unsupported token type');
|
||||
}
|
||||
|
||||
// switch to the correct network otherwise the token will be added to the wrong one
|
||||
await addOrSwitchChain();
|
||||
await switchOrAddChain();
|
||||
|
||||
const wasAdded = await provider?.request?.({
|
||||
method: 'wallet_watchAsset',
|
||||
params: {
|
||||
type: 'ERC20', // Initially only supports ERC20, but eventually more!
|
||||
options: {
|
||||
address: token.address,
|
||||
symbol: token.symbol || '',
|
||||
decimals: Number(token.decimals) || 18,
|
||||
image: token.icon_url || '',
|
||||
},
|
||||
},
|
||||
params,
|
||||
});
|
||||
|
||||
if (wasAdded) {
|
||||
@@ -68,7 +101,7 @@ const AddressAddToWallet = ({ className, token, isLoading, variant = 'icon', ico
|
||||
description: (error as Error)?.message || 'Something went wrong',
|
||||
});
|
||||
}
|
||||
}, [ token, provider, wallet, addOrSwitchChain ]);
|
||||
}, [ wallet, token, tokenId, switchOrAddChain, provider ]);
|
||||
|
||||
if (!provider || !wallet) {
|
||||
return null;
|
||||
@@ -78,7 +111,16 @@ const AddressAddToWallet = ({ className, token, isLoading, variant = 'icon', ico
|
||||
return <Skeleton loading className={ className } boxSize={ iconSize } borderRadius="base"/>;
|
||||
}
|
||||
|
||||
if (!feature.isEnabled) {
|
||||
const canBeAdded = (
|
||||
// MetaMask can add NFTs now, but this is still experimental feature, and doesn't work on mobile devices
|
||||
// https://docs.metamask.io/wallet/how-to/display/tokens/#display-nfts
|
||||
wallet === 'metamask' &&
|
||||
[ 'ERC-721', 'ERC-1155' ].includes(token.type) &&
|
||||
tokenId &&
|
||||
!isMobile
|
||||
) || token.type === 'ERC-20';
|
||||
|
||||
if (!feature.isEnabled || !canBeAdded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ interface Props extends FeaturedNetwork {
|
||||
isMobile?: boolean;
|
||||
}
|
||||
|
||||
const NetworkMenuLink = ({ title, icon, isActive, isMobile, url, invertIconInDarkMode }: Props) => {
|
||||
const NetworkMenuLink = ({ title, icon, isActive: isActiveProp, isMobile, url, invertIconInDarkMode }: Props) => {
|
||||
const darkModeFilter = { filter: 'brightness(0) invert(1)' };
|
||||
const style = useColorModeValue({}, invertIconInDarkMode ? darkModeFilter : {});
|
||||
|
||||
@@ -26,6 +26,21 @@ const NetworkMenuLink = ({ title, icon, isActive, isMobile, url, invertIconInDar
|
||||
/>
|
||||
);
|
||||
|
||||
const isActive = (() => {
|
||||
if (isActiveProp !== undefined) {
|
||||
return isActiveProp;
|
||||
}
|
||||
|
||||
try {
|
||||
const itemOrigin = new URL(url).origin;
|
||||
const currentOrigin = window.location.origin;
|
||||
|
||||
return itemOrigin === currentOrigin;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
})();
|
||||
|
||||
return (
|
||||
<Box as="li" listStyleType="none">
|
||||
<chakra.a
|
||||
|
||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
@@ -101,7 +101,7 @@ const TokenInstancePageTitle = ({ isLoading, token, instance, hash }: Props) =>
|
||||
maxW="700px"
|
||||
/>
|
||||
) }
|
||||
{ !isLoading && token && <AddressAddToWallet token={ token } variant="button"/> }
|
||||
{ !isLoading && token && <AddressAddToWallet token={ token } tokenId={ instance?.id } variant="button"/> }
|
||||
<AddressQrCode hash={ address.hash } isLoading={ isLoading }/>
|
||||
<AccountActionsMenu isLoading={ isLoading } showUpdateMetadataItem/>
|
||||
{ appLink }
|
||||
|
||||
@@ -76,7 +76,9 @@ const TxsStats = () => {
|
||||
>
|
||||
{ txCount24h && (
|
||||
<StatsWidget
|
||||
label={ txsStatsQuery.data?.transactions_24h?.title || 'Transactions' }
|
||||
label={ txsStatsQuery.data?.transactions_24h?.title ?
|
||||
getLabelFromTitle(txsStatsQuery.data?.transactions_24h?.title) :
|
||||
'Transactions' }
|
||||
value={ Number(txCount24h).toLocaleString() }
|
||||
period="24h"
|
||||
isLoading={ isLoading }
|
||||
@@ -85,7 +87,9 @@ const TxsStats = () => {
|
||||
) }
|
||||
{ operationalTxns24h && (
|
||||
<StatsWidget
|
||||
label={ txsStatsQuery.data?.operational_transactions_24h?.title || 'Daily op txns' }
|
||||
label={ txsStatsQuery.data?.operational_transactions_24h?.title ?
|
||||
getLabelFromTitle(txsStatsQuery.data?.operational_transactions_24h?.title) :
|
||||
'Daily op txns' }
|
||||
value={ Number(operationalTxns24h).toLocaleString() }
|
||||
period="24h"
|
||||
isLoading={ isLoading }
|
||||
@@ -93,7 +97,9 @@ const TxsStats = () => {
|
||||
) }
|
||||
{ pendingTxns && (
|
||||
<StatsWidget
|
||||
label={ txsStatsQuery.data?.pending_transactions_30m?.title || 'Pending transactions' }
|
||||
label={ txsStatsQuery.data?.pending_transactions_30m?.title ?
|
||||
getLabelFromTitle(txsStatsQuery.data?.pending_transactions_30m?.title) :
|
||||
'Pending transactions' }
|
||||
value={ Number(pendingTxns).toLocaleString() }
|
||||
period={ isStatsFeatureEnabled ? '30min' : '1h' }
|
||||
isLoading={ isLoading }
|
||||
@@ -101,7 +107,9 @@ const TxsStats = () => {
|
||||
) }
|
||||
{ txFeeSum24h && (
|
||||
<StatsWidget
|
||||
label={ txsStatsQuery.data?.transactions_fee_24h?.title || 'Transactions fees' }
|
||||
label={ txsStatsQuery.data?.transactions_fee_24h?.title ?
|
||||
getLabelFromTitle(txsStatsQuery.data?.transactions_fee_24h?.title) :
|
||||
'Transactions fees' }
|
||||
value={ txFeeSum24h.toLocaleString(undefined, { maximumFractionDigits: 2 }) }
|
||||
valuePostfix={ thinsp + config.chain.currency.symbol }
|
||||
period="24h"
|
||||
@@ -111,7 +119,9 @@ const TxsStats = () => {
|
||||
) }
|
||||
{ txFeeAvg && (
|
||||
<StatsWidget
|
||||
label={ txsStatsQuery.data?.average_transactions_fee_24h?.title || 'Avg. transaction fee' }
|
||||
label={ txsStatsQuery.data?.average_transactions_fee_24h?.title ?
|
||||
getLabelFromTitle(txsStatsQuery.data?.average_transactions_fee_24h?.title) :
|
||||
'Avg. transaction fee' }
|
||||
value={ txFeeAvg.usd ? txFeeAvg.usd : txFeeAvg.valueStr }
|
||||
valuePrefix={ txFeeAvg.usd ? '$' : undefined }
|
||||
valuePostfix={ txFeeAvg.usd ? undefined : thinsp + config.chain.currency.symbol }
|
||||
@@ -124,4 +134,9 @@ const TxsStats = () => {
|
||||
);
|
||||
};
|
||||
|
||||
// remove period from title
|
||||
function getLabelFromTitle(title: string) {
|
||||
return title.replace(/\s*\([^)]*\)\s*$/, '');
|
||||
}
|
||||
|
||||
export default React.memo(TxsStats);
|
||||
|
||||
@@ -14,6 +14,7 @@ import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
|
||||
import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip';
|
||||
import TruncatedValue from 'ui/shared/TruncatedValue';
|
||||
|
||||
interface Props {
|
||||
data: VerifiedContract;
|
||||
@@ -57,11 +58,12 @@ const VerifiedContractsListItem = ({ data, isLoading }: Props) => {
|
||||
flexShrink={ 0 }
|
||||
/>
|
||||
</Flex>
|
||||
<Flex columnGap={ 3 }>
|
||||
<Skeleton loading={ isLoading } fontWeight={ 500 }>Balance { currencyUnits.ether }</Skeleton>
|
||||
<Skeleton loading={ isLoading } color="text.secondary">
|
||||
<span>{ balance }</span>
|
||||
</Skeleton>
|
||||
<Flex columnGap={ 3 } w="100%">
|
||||
<Skeleton loading={ isLoading } fontWeight={ 500 } flexShrink="0">Balance { currencyUnits.ether }</Skeleton>
|
||||
<TruncatedValue
|
||||
value={ balance }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
</Flex>
|
||||
<Flex columnGap={ 3 }>
|
||||
<Skeleton loading={ isLoading } fontWeight={ 500 }>Txs count</Skeleton>
|
||||
|
||||
@@ -14,6 +14,7 @@ import ContractCertifiedLabel from 'ui/shared/ContractCertifiedLabel';
|
||||
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip';
|
||||
import TruncatedValue from 'ui/shared/TruncatedValue';
|
||||
|
||||
interface Props {
|
||||
data: VerifiedContract;
|
||||
@@ -59,9 +60,12 @@ const VerifiedContractsTableItem = ({ data, isLoading }: Props) => {
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell isNumeric>
|
||||
<Skeleton loading={ isLoading } display="inline-block" my={ 1 }>
|
||||
{ balance }
|
||||
</Skeleton>
|
||||
<TruncatedValue
|
||||
value={ balance }
|
||||
isLoading={ isLoading }
|
||||
my={ 1 }
|
||||
w="100%"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell isNumeric>
|
||||
<Skeleton loading={ isLoading } display="inline-block" my={ 1 }>
|
||||
|
||||