Commit 4a3627c7 authored by tom's avatar tom

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

parents 58b6067b ae759780
......@@ -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 }...`);
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
\ No newline at end of file
NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/hiddenBlockBadge
\ No newline at end of file
......@@ -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
\ No newline at end of file
NEXT_PUBLIC_DEX_POOLS_ENABLED=true
......@@ -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,15 +6,27 @@ 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) {
if (error.name === 'AbortError') {
console.log('🚨 [next-sitemap] Request timeout for resource:', url);
} else {
console.log('🚨 [next-sitemap] Error fetching resource:', url, error);
}
}
};
const siteUrl = [
......@@ -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+ |
&nbsp;
......
......@@ -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
......
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 ]);
}
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 ]);
}
import { get } from 'es-toolkit/compat';
import React from 'react';
import config from 'configs/app';
import getErrorObj from 'lib/errors/getErrorObj';
import useAddChain from './useAddChain';
import useProvider from './useProvider';
import useSwitchChain from './useSwitchChain';
export default function useAddOrSwitchChain() {
export default function useSwitchOrAddChain() {
const { wallet, provider } = useProvider();
const addChain = useAddChain();
const switchChain = useSwitchChain();
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 } ],
});
return switchChain();
} catch (error) {
const errorObj = getErrorObj(error);
const code = errorObj && 'code' in errorObj ? errorObj.code : undefined;
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) {
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,
});
return addChain();
}
throw error;
}
}, [ provider, wallet ]);
}, [ addChain, provider, wallet, switchChain ]);
}
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;
}
......
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;
}
......
......@@ -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,
......
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
......
......@@ -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 }>
......
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